From d4eed90b54caf5a0fde33abfbb7caf3027732f80 Mon Sep 17 00:00:00 2001 From: Merijn Date: Mon, 5 Oct 2026 19:12:03 +0200 Subject: [PATCH] fix(videoout): report unavailable output modes instead of throwing sceVideoOutIsOutputSupported threw VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE for every mode other than the default and 119.88 Hz, and sceVideoOutConfigureOutput threw for an unavailable 119.88 Hz mode. Titles probe and request modes the display may lack and handle the result: PPSA12544 asks for 0xd000000a and falls back to 3840 pixels when it is not supported, and its sceVideoOutConfigureOutput caller switches on the returned error codes, including VIDEO_OUT_ERROR_UNAVAILABLE_OUTPUT_MODE. Every mode except the default now reports 0, and configuring one returns VIDEO_OUT_ERROR_UNAVAILABLE_OUTPUT_MODE. Co-Authored-By: Claude Opus 5.5 --- core/libs/prx/libSceVideoOut/src/Output.cpp | 9 +++------ core/libs/prx/libSceVideoOut/tests/Flip.cpp | 7 +++++++ docs/dev/TechnicalDebt.md | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/core/libs/prx/libSceVideoOut/src/Output.cpp b/core/libs/prx/libSceVideoOut/src/Output.cpp index c595e3022..72f82ba16 100644 --- a/core/libs/prx/libSceVideoOut/src/Output.cpp +++ b/core/libs/prx/libSceVideoOut/src/Output.cpp @@ -25,9 +25,6 @@ static int validateOutputConfig(int handle, uint64_t mode, const VideoOutOutputO } } } - if (mode != VIDEO_OUT_OUTPUT_MODE_DEFAULT && mode != VIDEO_OUT_OUTPUT_MODE_119_88HZ) { - throw std::runtime_error(std::string(__func__) + ": VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE"); - } return 0; } @@ -190,7 +187,7 @@ int APS5_VABI sceVideoOutIsOutputSupported(int handle, uint64_t mode, const Vide if (result != 0) { return result; } - return (mode == VIDEO_OUT_OUTPUT_MODE_119_88HZ) ? 0 : 1; + return mode == VIDEO_OUT_OUTPUT_MODE_DEFAULT ? 1 : 0; } catch (const ProcessShutdown&) { LibcAwaitExit_nid_postfix(); } @@ -200,8 +197,8 @@ int APS5_VABI sceVideoOutConfigureOutput(int handle, uint64_t mode, const VideoO if (supported < 0) { return supported; } - if (supported == 0 && mode == VIDEO_OUT_OUTPUT_MODE_119_88HZ) { - throw std::runtime_error(std::string(__func__) + ": VIDEO_OUT_ERROR_UNAVAILABLE_OUTPUT_MODE"); + if (supported == 0) { + return VIDEO_OUT_ERROR_UNAVAILABLE_OUTPUT_MODE; } auto cfg = VideoOutDriver::Get().GetConfig(handle); if (cfg == nullptr) { diff --git a/core/libs/prx/libSceVideoOut/tests/Flip.cpp b/core/libs/prx/libSceVideoOut/tests/Flip.cpp index 9dd1463b0..42a509b76 100644 --- a/core/libs/prx/libSceVideoOut/tests/Flip.cpp +++ b/core/libs/prx/libSceVideoOut/tests/Flip.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -208,6 +209,12 @@ void testControls() { const auto handle = sceVideoOutOpen(255, 0, 0, openParam.data()); check(handle >= 0, "open with the highest priority on every CPU failed"); const auto cfg = VideoOutDriver::Get().GetConfig(handle); + check(sceVideoOutIsOutputSupported(handle, VIDEO_OUT_OUTPUT_MODE_DEFAULT, nullptr, nullptr, 0) == 1, "default output mode is unsupported"); + for (const uint64_t unsupported : std::initializer_list{VIDEO_OUT_OUTPUT_MODE_119_88HZ, 0xd000000aull}) { + check(sceVideoOutIsOutputSupported(handle, unsupported, nullptr, nullptr, 0) == 0, "unavailable output mode is supported"); + check(sceVideoOutConfigureOutput(handle, unsupported, nullptr, nullptr, 0) == VIDEO_OUT_ERROR_UNAVAILABLE_OUTPUT_MODE, "unavailable output mode was configured"); + } + check(sceVideoOutConfigureOutput(handle, VIDEO_OUT_OUTPUT_MODE_DEFAULT, nullptr, nullptr, 0) == 0, "default output mode was rejected"); for (int rate = 0; rate <= 2; ++rate) { check(sceVideoOutSetFlipRate(handle, rate) == 0 && cfg->flipRate == rate, "flip rate was not applied"); } diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index c457ddd14..a13771a7a 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -29,6 +29,7 @@ Throughout the project, every function at every stage either **does exactly what - [sceAvPlayerAddSource](../../core/libs/prx/libSceAvPlayer/src/Source.cpp) (libSceAvPlayer) - requests up to 4 additional decode-ahead video framebuffers from the memory replacement beyond `num_output_video_framebuffers` when memory is available; the console's internal buffering behaviour is unknown - [sceVideoOutRegisterBuffers2](../../core/libs/prx/libSceVideoOut/src/Buffer.cpp) (libSceVideoOut) - buffer option 8 (STRICT_COLORIMETRY) is presented as option 0, with the stored values unchanged; what the console changes for it is unknown. Only options 0 and 8 are accepted +- [sceVideoOutIsOutputSupported](../../core/libs/prx/libSceVideoOut/src/Output.cpp) (libSceVideoOut) - output mode encoding unknown: every mode except the default reports not supported (0), and `sceVideoOutConfigureOutput` returns `VIDEO_OUT_ERROR_UNAVAILABLE_OUTPUT_MODE` for it, instead of returning `VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE` for values the console does not define. PPSA12544 probes `0xd000000a` and selects 7680 pixels when it is supported, 3840 otherwise - [sceVideoOutOpen](../../core/libs/prx/libSceVideoOut/src/Output.cpp) (libSceVideoOut) - the open param's first word is unknown (PPSA21564 passes 16; it is not the byte size, since the affinity mask is at offset 16); only 16 is accepted. Whether the param continues past offset 24 is also unknown - [sceRtcParseDateTime](../../core/libs/prx/libSceRtc/Export.cpp) (libSceRtc) - accepted format assumed to be the ISO 8601 subset `YYYY-MM-DDTHH:MM:SS[.ffffff]` (T/t/space separator, optional fractional seconds) with the sibling RFC 3339 zone suffix (`Z` or `±HH:MM`); a string without a zone is taken as UTC, and anything outside this shape throws instead of returning a parse error, unverified against hardware - [AMPR WriteAddressFromCounterPairOnCompletion](../../core/libs/prx/libSceAmpr/Export.cpp) (libSceAmpr) - the pair of an even counter n is assumed to give counter n in the low 32 bits and n + 1 in the high 32 bits. Wait compare values, wait flush and counter index ranges follow the [ampr_emu](https://github.com/drakmor/ampr_emu) reimplementation; the flush is accepted and has no effect, as commands run in order