Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions core/libs/prx/libSceVideoOut/include/Output.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "SceTypes.hpp"

int ValidateVideoOutOutputMode_nid_no_patch(uint64_t mode, const VideoOutOutputOptions* options, int displayRefreshRate);
int VideoOutOutputSupport_nid_no_patch(uint64_t mode, const VideoOutOutputOptions* options, int displayRefreshRate);

extern "C" {

Expand Down
15 changes: 9 additions & 6 deletions core/libs/prx/libSceVideoOut/src/Output.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,16 @@ int ValidateVideoOutOutputMode_nid_no_patch(uint64_t mode, const VideoOutOutputO
return 0;
}

int VideoOutOutputSupport_nid_no_patch(uint64_t mode, const VideoOutOutputOptions* options, int displayRefreshRate) {
const int result = ValidateVideoOutOutputMode_nid_no_patch(mode, options, displayRefreshRate);
if (mode == VIDEO_OUT_OUTPUT_MODE_119_88HZ && result == VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE) return 0;
return result != 0 ? result : 1;
}

static int validateOutputConfig(int handle, uint64_t mode, const VideoOutOutputOptions* options, void* reservedPtr, uint64_t reserved) {
if (!VideoOutDriver::Get().HasConfig(handle) || !VideoOutDriver::Get().IsOpen(handle)) return VIDEO_OUT_ERROR_INVALID_HANDLE;
if (reservedPtr != nullptr || reserved != 0) return VIDEO_OUT_ERROR_INVALID_VALUE;
return ValidateVideoOutOutputMode_nid_no_patch(mode, options, VideoOutDriver::Get().GetDisplayRefreshRate());
return VideoOutOutputSupport_nid_no_patch(mode, options, VideoOutDriver::Get().GetDisplayRefreshRate());
}

static void validateOpenParam(const void* param) {
Expand Down Expand Up @@ -295,11 +301,7 @@ int APS5_VABI sceVideoOutInitializeOutputOptions(VideoOutOutputOptions* options)
}

int APS5_VABI sceVideoOutIsOutputSupported(int handle, uint64_t mode, const VideoOutOutputOptions* options, void* reservedPtr, uint64_t reserved) try {
const int result = validateOutputConfig(handle, mode, options, reservedPtr, reserved);
if (result != 0) {
return result;
}
return 1;
return validateOutputConfig(handle, mode, options, reservedPtr, reserved);
} catch (const ProcessShutdown&) {
LibcAwaitExit_nid_postfix();
}
Expand All @@ -310,6 +312,7 @@ int APS5_VABI sceVideoOutConfigureOutput(int handle, uint64_t mode, const VideoO
return supported;
}
if (mode == VIDEO_OUT_OUTPUT_MODE_119_88HZ && (GetAppAttribute3_nid_no_patch() & 0x40u) == 0) return VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE;
if (supported == 0) return VIDEO_OUT_ERROR_UNAVAILABLE_OUTPUT_MODE;
auto cfg = VideoOutDriver::Get().GetConfig(handle);
if (cfg == nullptr) {
throw std::runtime_error(std::string(__func__) + ": VIDEO_OUT_ERROR_INVALID_HANDLE");
Expand Down
2 changes: 1 addition & 1 deletion core/libs/prx/libSceVideoOut/tests/Flip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ void testControls() {
const auto cfg = VideoOutDriver::Get().GetConfig(handle);
check(sceVideoOutIsOutputSupported(handle, VIDEO_OUT_OUTPUT_MODE_DEFAULT, nullptr, nullptr, 0) == 1, "default output mode is unsupported");
const int highFrameRate = sceVideoOutIsOutputSupported(handle, VIDEO_OUT_OUTPUT_MODE_119_88HZ, nullptr, nullptr, 0);
check(highFrameRate == 1 || highFrameRate == VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE, "unexpected high-frame-rate support result");
check(highFrameRate == 1 || highFrameRate == 0, "unexpected high-frame-rate support result");
check(sceVideoOutConfigureOutput(handle, VIDEO_OUT_OUTPUT_MODE_119_88HZ, nullptr, nullptr, 0) == VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE, "unavailable output mode was configured");
check(sceVideoOutIsOutputSupported(handle, 0xd000000aull, nullptr, nullptr, 0) == VIDEO_OUT_ERROR_UNKNOWN_OUTPUT_MODE, "unknown output mode is supported");
check(sceVideoOutConfigureOutput(handle, 0xd000000aull, nullptr, nullptr, 0) == VIDEO_OUT_ERROR_UNKNOWN_OUTPUT_MODE, "unknown output mode was configured");
Expand Down
6 changes: 6 additions & 0 deletions core/libs/prx/libSceVideoOut/tests/OutputModePolicy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ int main() {
Require(ValidateVideoOutOutputMode_nid_no_patch(mode, &options, 240) == unsupported, "unimplemented output mode was accepted on a fast display");
}
Require(ValidateVideoOutOutputMode_nid_no_patch(0xd000000a, &options, 240) == unknownMode, "unknown output mode was accepted");
for (int refresh : {59, 60, 119, 120, 144}) {
Require(VideoOutOutputSupport_nid_no_patch(15, &options, refresh) == (refresh < 120 ? 0 : 1), "mode 15 support did not answer 0 or 1 for the display");
Require(VideoOutOutputSupport_nid_no_patch(1, &options, refresh) == 1, "default mode was not supported");
Require(VideoOutOutputSupport_nid_no_patch(4, &options, refresh) == unsupported, "mode 4 did not stay unsupported");
Require(VideoOutOutputSupport_nid_no_patch(0xd000000a, &options, refresh) == unknownMode, "unknown mode did not stay unknown");
}
VideoOutOutputOptions changed = options;
changed.internalData[0] = 0;
Require(ValidateVideoOutOutputMode_nid_no_patch(15, &changed, 120) == invalidValue, "mode 15 accepted invalid output options");
Expand Down
6 changes: 3 additions & 3 deletions core/libs/tests/GuestVideoOutHighFrameRate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#include <fstream>
#include <stdexcept>

static constexpr int Unsupported = static_cast<int>(0x80290016u);
static constexpr int Unavailable = static_cast<int>(0x80290019u);

static void Require(bool value, const char* message) {
if (!value) throw std::runtime_error(message);
Expand Down Expand Up @@ -38,8 +38,8 @@ int main(int argc, char** argv) {
return 77;
}
const int supported = sceVideoOutIsOutputSupported(handle, 15, nullptr, nullptr, 0);
if (supported == Unsupported && !requireDisplay) {
Require(sceVideoOutConfigureOutput(handle, 15, nullptr, nullptr, 0) == Unsupported, "unsupported display accepted high-frame-rate output");
if (supported == 0 && !requireDisplay) {
Require(sceVideoOutConfigureOutput(handle, 15, nullptr, nullptr, 0) == Unavailable, "an output without 120 Hz accepted high-frame-rate output");
Require(sceVideoOutClose(handle) == 0, "close failed");
LibcRunShutdown_nid_postfix();
std::puts("skipped, current display does not provide 120 Hz");
Expand Down
2 changes: 1 addition & 1 deletion core/libs/tests/GuestVideoOutOutputMode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ int main() {
Require(sceVideoOutIsOutputSupported(handle, 1, &options, nullptr, 0) == 1);
Require(sceVideoOutIsOutputSupported(handle, 1, nullptr, nullptr, 0) == 1);
const int highFrameRate = sceVideoOutIsOutputSupported(handle, 15, &options, nullptr, 0);
Require(highFrameRate == 1 || highFrameRate == UNSUPPORTED_OUTPUT_MODE);
Require(highFrameRate == 1 || highFrameRate == 0);
Require(sceVideoOutConfigureOutput(handle, 15, &options, nullptr, 0) == UNSUPPORTED_OUTPUT_MODE);
for (std::uint64_t mode : {0x4ull, 0x7ull, 0x8ull, 0xCull, 0xDull, 0xEull, 0x10ull, 0x11ull, 0x13ull}) {
Require(sceVideoOutIsOutputSupported(handle, mode, &options, nullptr, 0) == UNSUPPORTED_OUTPUT_MODE);
Expand Down
2 changes: 1 addition & 1 deletion docs/dev/debt/libSceVideoOut.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
- [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
- [sceVideoOutAddBufferAttributeOption](../../../core/libs/prx/libSceVideoOut/src/Buffer.cpp) (libSceVideoOut, NID `MBlv8YWoyOk`) - the signature `(VideoOutBufferAttribute2*, uint64_t option)` comes from [SharpProspero](https://github.com/SvenGDK/SharpProspero/blob/60977b29103452b9681ae10c7a9011d44965bf16/src/SharpProspero/Interop/VideoOut/VideoOut.cs#L174-L176) and the call site in PPSA25380, which passes 0x20 and ignores the result. The option is ORed into `attribute->option`; whether the console validates the bits, keeps them elsewhere or fails on an unknown option is unknown. A null attribute throws. Option 0x20 makes `sceVideoOutRegisterBuffers2` throw, as every option other than 0 and 8; what it selects for an overlay buffer is unknown
- [sceVideoOutRegisterBuffers2](../../../core/libs/prx/libSceVideoOut/src/Buffer.cpp) (libSceVideoOut) - DCC buffers (category 1): `dcc_control` is taken as the CB_DCC_CONTROL block layout, only bits 0x10026c are accepted; `dcc_cb_register_clear_color` as the register-clear texel, only 32-bit values are presented
- [sceVideoOutIsOutputSupported](../../../core/libs/prx/libSceVideoOut/src/Output.cpp) (libSceVideoOut) - mode 1 is supported; mode 15 is supported when the current host display runs at 120 Hz or above. Configuring mode 15 requires attribute3 bit 0x40; this single-bit mask is inferred from the measured full value 0x80040, not isolated by a console measurement. Vblanks run at 119.88 Hz until mode 1 restores 59.94 Hz. Display modes are read through SDL; a move to a slower display falls back the configured output mode to mode 1, fires the output-mode event and continues at 59.94 Hz. The console's exact fallback behaviour when a 120 Hz display is lost has not been measured; this fallback avoids the abort in #7877 and matches the HDMI output mode renegotiation that a display change requires. Selecting another host display mode and VRR are not implemented. The console answers per display and per title: with a 120 Hz TV it also answers 1 for mode 15 (119.88 Hz), and [PS5_Vulkan](https://github.com/mihawk-99/PS5_Vulkan/blob/5b5e4fc2d80fdb67a4f61ba9e6a8a424026bb8a5/docs/HARDWARE_FINDINGS.md) measured that `sceVideoOutConfigureOutput` accepts 15 only from a title whose param.json declares high-frame-rate output (attribute3 0x80040). Modes 4, 7, 8, 12-14, 16-17 and 19 return `VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE` here, as they did on the measured display. Mode 18 throws, because one console answered 0x80290016 and two others 0x8029001E, the last a PS5 on 13.40 with a 120 Hz TV (measured 2026-10-19 inside a retail title), whose answers for every other mode match the ones here. The name `VIDEO_OUT_ERROR_UNKNOWN_OUTPUT_MODE` for 0x8029001E is ours. Word 3 of the output options is accepted with any value, as on the console, and its meaning is unknown. PPSA12544 probes `0xd000000a`, which the console answers with 0x8029001E, so the title selects 3840 pixels; PPSA26344 at `0xCD0946` passes `(handle, 0xf, nullptr, nullptr, 0)` and branches on the result equalling 1
- [sceVideoOutIsOutputSupported](../../../core/libs/prx/libSceVideoOut/src/Output.cpp) (libSceVideoOut) - mode 1 is supported; mode 15 is supported when the current host display runs at 120 Hz or above, and otherwise answers 0 while `sceVideoOutConfigureOutput(15)` returns `UNAVAILABLE_OUTPUT_MODE` (0x80290019): [PS5_Vulkan measured](https://github.com/mihawk-99/PS5_Vulkan/blob/f410e67a0241b3e8edcdbcd2a9b285dfc4a412ed/docs/HARDWARE_FINDINGS.md#L3793-L3835) both on a base PS5 whose output was set to 1080p, in a title declaring high-frame-rate output, and 1 and 0 at 1440p. Which code a title without the declaration gets when both apply is not measured; the declaration is checked first here. Configuring mode 15 requires attribute3 bit 0x40; this single-bit mask is inferred from the measured full value 0x80040, not isolated by a console measurement. Vblanks run at 119.88 Hz until mode 1 restores 59.94 Hz. Display modes are read through SDL; a move to a slower display falls back the configured output mode to mode 1, fires the output-mode event and continues at 59.94 Hz. The console's exact fallback behaviour when a 120 Hz display is lost has not been measured; this fallback avoids the abort in #7877 and matches the HDMI output mode renegotiation that a display change requires. Selecting another host display mode and VRR are not implemented. The console answers per display and per title: with a 120 Hz TV it also answers 1 for mode 15 (119.88 Hz), and [PS5_Vulkan](https://github.com/mihawk-99/PS5_Vulkan/blob/5b5e4fc2d80fdb67a4f61ba9e6a8a424026bb8a5/docs/HARDWARE_FINDINGS.md) measured that `sceVideoOutConfigureOutput` accepts 15 only from a title whose param.json declares high-frame-rate output (attribute3 0x80040). Modes 4, 7, 8, 12-14, 16-17 and 19 return `VIDEO_OUT_ERROR_UNSUPPORTED_OUTPUT_MODE` here, as they did on the measured display. Mode 18 throws, because one console answered 0x80290016 and two others 0x8029001E, the last a PS5 on 13.40 with a 120 Hz TV (measured 2026-10-19 inside a retail title), whose answers for every other mode match the ones here. The name `VIDEO_OUT_ERROR_UNKNOWN_OUTPUT_MODE` for 0x8029001E is ours. Word 3 of the output options is accepted with any value, as on the console, and its meaning is unknown. PPSA12544 probes `0xd000000a`, which the console answers with 0x8029001E, so the title selects 3840 pixels; PPSA26344 at `0xCD0946` passes `(handle, 0xf, nullptr, nullptr, 0)` and branches on the result equalling 1

Check warning on line 20 in docs/dev/debt/libSceVideoOut.md

View workflow job for this annotation

GitHub Actions / conventions

debt-length

Technical debt entry over 1000 characters: state the gap first (what differs from the console, what is unverified, what throws) and keep only the behaviour it needs: 2479 characters
- [sceVideoOutGetResolutionStatus](../../../core/libs/prx/libSceVideoOut/src/Output.cpp) (libSceVideoOut) - `screenSizeInInch` is 0, the value EDID uses for an undefined size, since the host display's size is not read; a PS5 (firmware 13.40, measured 2026-10-19 inside a retail title) reported its TV's 60.0f. The 48-byte layout, from the declarations in ps5-opengl and EVO-PLAYER-PS5 (#1578), is confirmed there: full size and pane 3840 x 2160, `refreshRate` 3, `flags` 0 and the rest 0 on the title's handle, 0x8029000B for handles 0 to 8. `flags` is 0 here too; its bits are unknown. The pane equals the full size because `sceVideoOutSetWindowModeMargins` applies no margins
- [sceVideoOutGetOutputStatus](../../../core/libs/prx/libSceVideoOut/src/Output.cpp) (libSceVideoOut) - the status after a buffer is registered, a 1440p output and a display below 4K with the console's resolution on Automatic were not measured. A payload process on a PS5 (firmware 12.70) with no buffer registered reported `resolution` 2 right after `sceVideoOutOpen(0xff, 0, 0, NULL)` with the console's resolution on Automatic on a 4K display and 1 with it set to 1080p, and `dynamicRange` 2 with `flags` 0 on an HDR display with HDR on, while this code sets flag bit 0 whenever it reports dynamic range 2. `resolution` is 2 when the current display mode SDL reports for the window's display is at least 3840 pixels wide or 2160 high and 1 otherwise, read with the refresh rate and independent of the registered buffers; on Wayland SDL's mode is the output's logical size (from SDL's source, not run), so a scaled 3840x2160 output reports 1. `APS5_OUTPUT_RESOLUTION` replaces that size
- [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. PPSA26344's only call at `0xCCF5EA` passes `(0xFF, 0, 0, nullptr)`. Whether a non-null param continues past offset 24 is also unknown. Seven titles disassembled for this entry: PPSA18194 01.004.003 (0xABA44D) and PPSA21564 01.007.000 (0x2A0E65) pass a stack param with words 16, 0, 0, 1 and affinity 0x3E (PPSA18194) or 16, 1, 256, 1 and affinity 0x1FFF (PPSA21564), and zero its bytes 24 to 88 first. The other nine calls pass no param, as `(0xFF, 0, 0, nullptr)` (PPSA01494 01.100.002 0x26C661A, PPSA03984 01.020.000 0x4FAD792, PPSA21564 0x464783, PPSA30839 01.003.000 0x4D60AB, PPSA31162 02.000.001 0x54D3CB, PPSA34502 01.010.000 0x62AECA) or with a bus type read from a field (PPSA01494 0x2197A25, PPSA30839 0x54C6845, PPSA31162 0x69310F5)
Expand Down
Loading