diff --git a/core/libs/prx/libSceVideoOut/include/Event.hpp b/core/libs/prx/libSceVideoOut/include/Event.hpp index 8b07233df..af8d10be4 100644 --- a/core/libs/prx/libSceVideoOut/include/Event.hpp +++ b/core/libs/prx/libSceVideoOut/include/Event.hpp @@ -12,6 +12,7 @@ int APS5_VABI sceVideoOutAddFlipEvent(KernelEqueue eq, int handle, void* udata); int APS5_VABI sceVideoOutAddOutputModeEvent(KernelEqueue eq, int handle, void* udata); int APS5_VABI sceVideoOutAddPreVblankStartEvent(KernelEqueue eq, int handle, void* udata); int APS5_VABI sceVideoOutAddVblankEvent(KernelEqueue eq, int handle, void* udata); +int APS5_VABI sceVideoOutAddVrrActiveStatusEvent(KernelEqueue eq, int handle, void* udata); int APS5_VABI sceVideoOutDeleteFlipEvent(KernelEqueue eq, int handle); int APS5_VABI sceVideoOutDeletePreVblankStartEvent(KernelEqueue eq, int handle); diff --git a/core/libs/prx/libSceVideoOut/include/VideoOutDriver.hpp b/core/libs/prx/libSceVideoOut/include/VideoOutDriver.hpp index 02adf6ea0..afd66844c 100644 --- a/core/libs/prx/libSceVideoOut/include/VideoOutDriver.hpp +++ b/core/libs/prx/libSceVideoOut/include/VideoOutDriver.hpp @@ -74,6 +74,7 @@ static constexpr int VIDEO_OUT_EVENT_FLIP = 0; static constexpr int VIDEO_OUT_EVENT_VBLANK = 1; static constexpr int VIDEO_OUT_EVENT_PRE_VBLANK_START = 2; static constexpr int VIDEO_OUT_EVENT_SET_MODE = 8; +static constexpr int VIDEO_OUT_EVENT_VRR_STATUS = 16; static constexpr int VIDEO_OUT_FLIP_MODE_VSYNC = 1; static constexpr int VIDEO_OUT_FLIP_MODE_VSYNC_MULTI = 4; @@ -116,6 +117,7 @@ struct VideoOutConfig { std::vector vblankEvents; std::vector preVblankEvents; std::vector outputModeEvents; + std::vector vrrStatusEvents; uint32_t width = VIDEO_OUT_DEFAULT_WIDTH; uint32_t height = VIDEO_OUT_DEFAULT_HEIGHT; diff --git a/core/libs/prx/libSceVideoOut/src/Event.cpp b/core/libs/prx/libSceVideoOut/src/Event.cpp index c70ba7557..6bd6420a5 100644 --- a/core/libs/prx/libSceVideoOut/src/Event.cpp +++ b/core/libs/prx/libSceVideoOut/src/Event.cpp @@ -13,6 +13,7 @@ static std::vector* getEventList(VideoOutConfig& cfg, int16_t if (eventKind == VIDEO_OUT_EVENT_VBLANK) return &cfg.vblankEvents; if (eventKind == VIDEO_OUT_EVENT_PRE_VBLANK_START) return &cfg.preVblankEvents; if (eventKind == VIDEO_OUT_EVENT_SET_MODE) return &cfg.outputModeEvents; + if (eventKind == VIDEO_OUT_EVENT_VRR_STATUS) return &cfg.vrrStatusEvents; throw std::runtime_error("getEventList: unknown event kind"); } @@ -134,6 +135,13 @@ int APS5_VABI sceVideoOutAddOutputModeEvent(KernelEqueue eq, int handle, void* u LibcAwaitExit_nid_postfix(); } +APS5_EXPORT("LibwuIonIBw", sceVideoOutAddVrrActiveStatusEvent); +int APS5_VABI sceVideoOutAddVrrActiveStatusEvent(KernelEqueue eq, int handle, void* udata) try { + return registerVideoOutEvent(handle, eq, VIDEO_OUT_EVENT_VRR_STATUS, udata); +} catch (const ProcessShutdown&) { + LibcAwaitExit_nid_postfix(); +} + int APS5_VABI sceVideoOutDeleteFlipEvent(KernelEqueue eq, int handle) try { return deleteVideoOutEvent(handle, eq, VIDEO_OUT_EVENT_FLIP); } catch (const ProcessShutdown&) { @@ -160,7 +168,7 @@ int APS5_VABI sceVideoOutGetEventId(const KernelEvent* ev) { throw std::runtime_error(std::string(__func__) + ": VIDEO_OUT_ERROR_INVALID_EVENT"); } const int ident = static_cast(ev->ident); - if (ident != VIDEO_OUT_EVENT_FLIP && ident != VIDEO_OUT_EVENT_VBLANK && ident != VIDEO_OUT_EVENT_PRE_VBLANK_START && ident != VIDEO_OUT_EVENT_SET_MODE) { + if (ident != VIDEO_OUT_EVENT_FLIP && ident != VIDEO_OUT_EVENT_VBLANK && ident != VIDEO_OUT_EVENT_PRE_VBLANK_START && ident != VIDEO_OUT_EVENT_SET_MODE && ident != VIDEO_OUT_EVENT_VRR_STATUS) { throw std::runtime_error(std::string(__func__) + ": VIDEO_OUT_ERROR_INVALID_EVENT"); } return ident; diff --git a/core/libs/prx/libSceVideoOut/src/Output.cpp b/core/libs/prx/libSceVideoOut/src/Output.cpp index af435d9a3..fc43c7537 100644 --- a/core/libs/prx/libSceVideoOut/src/Output.cpp +++ b/core/libs/prx/libSceVideoOut/src/Output.cpp @@ -290,20 +290,9 @@ int APS5_VABI sceVideoOutVrrPegToFixedRate() try { LibcAwaitExit_nid_postfix(); } -APS5_EXPORT("kP2L8t3j-aM", sceVideoOutUnknown00); -int APS5_VABI sceVideoOutUnknown00() try { - NotImplemented_nid_no_patch(__func__); +APS5_EXPORT("kP2L8t3j-aM", sceVideoOutAddVrrStatusFlagsPrivilege); +int APS5_VABI sceVideoOutAddVrrStatusFlagsPrivilege() { return 0; -} catch (const ProcessShutdown&) { - LibcAwaitExit_nid_postfix(); -} - -APS5_EXPORT("LibwuIonIBw", sceVideoOutUnknown01); -int APS5_VABI sceVideoOutUnknown01() try { - NotImplemented_nid_no_patch(__func__); - return 0; -} catch (const ProcessShutdown&) { - LibcAwaitExit_nid_postfix(); } } diff --git a/core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp b/core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp index 8f185445a..c13f19041 100644 --- a/core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp +++ b/core/libs/prx/libSceVideoOut/src/VideoOutDriver.cpp @@ -342,10 +342,12 @@ bool VideoOutDriver::close(int handle) { removeEvents(cfg->vblankEvents, VIDEO_OUT_EVENT_VBLANK); removeEvents(cfg->preVblankEvents, VIDEO_OUT_EVENT_PRE_VBLANK_START); removeEvents(cfg->outputModeEvents, VIDEO_OUT_EVENT_SET_MODE); + removeEvents(cfg->vrrStatusEvents, VIDEO_OUT_EVENT_VRR_STATUS); cfg->flipEvents.clear(); cfg->vblankEvents.clear(); cfg->preVblankEvents.clear(); cfg->outputModeEvents.clear(); + cfg->vrrStatusEvents.clear(); cfg->vblankCond.notify_all(); flipQueue->changed.notify_all(); return true; diff --git a/core/libs/prx/libSceVideoOut/tests/Flip.cpp b/core/libs/prx/libSceVideoOut/tests/Flip.cpp index ed59a5177..a75ecd82c 100644 --- a/core/libs/prx/libSceVideoOut/tests/Flip.cpp +++ b/core/libs/prx/libSceVideoOut/tests/Flip.cpp @@ -256,6 +256,11 @@ void testControls() { std::lock_guard lock(cfg->mutex); check(cfg->vblankEvents.size() == 1, "duplicate vblank subscription"); } + check(sceVideoOutAddVrrActiveStatusEvent(queue, handle, &settings) == 0, "VRR status subscription failed"); + { + std::lock_guard lock(cfg->mutex); + check(cfg->vrrStatusEvents.size() == 1, "VRR status subscription was not recorded"); + } check(sceVideoOutWaitVblank(handle) == 0, "vblank wait failed"); check(owner->GetTriggeredEvents(&event, 1) == 1 && event.udata == &settings && sceVideoOutGetEventId(&event) == VIDEO_OUT_EVENT_VBLANK, "vblank event or updated user data missing"); std::vector allocation(65536 + 65535); @@ -267,6 +272,11 @@ void testControls() { attribute.pixel_format = 0x8000000000000000ull; check(sceVideoOutRegisterBuffers2(handle, 0, 0, &buffer, 1, &attribute, 0, nullptr) == 0, "buffers with set reserved pointers were rejected"); check(sceVideoOutUnregisterBuffers(handle, 0) == 0, "buffer unregistration failed"); + check(owner->GetTriggeredEvents(&event, 1) == 0, "VRR status event triggered without a VRR change"); + KernelEvent vrrStatus{}; + vrrStatus.ident = VIDEO_OUT_EVENT_VRR_STATUS; + vrrStatus.filter = EVFILT_VIDEO_OUT; + check(sceVideoOutGetEventId(&vrrStatus) == VIDEO_OUT_EVENT_VRR_STATUS, "VRR status event id rejected"); sceVideoOutClose(handle); check(owner->GetTriggeredEvents(&event, 1) == 0, "closed port retained pending events"); check(sceKernelDeleteEqueue(queue) == 0, "event queue deletion failed"); diff --git a/docs/dev/TechnicalDebt.md b/docs/dev/TechnicalDebt.md index b3a0ac2b1..11f0837a8 100644 --- a/docs/dev/TechnicalDebt.md +++ b/docs/dev/TechnicalDebt.md @@ -25,6 +25,7 @@ Throughout the project, every function at every stage either **does exactly what - [libSceSystemService](../../core/libs/prx/libSceSystemService/Export.cpp) - `sceSystemServicePowerTick`, `sceSystemServiceReportAbnormalTermination`, `sceSystemServiceDisableMusicPlayer`, `sceSystemServiceReenableMusicPlayer`, `sceSystemServiceDisableMediaPlay` and `sceSystemServiceReenableMediaPlay` return success and do nothing: the host has no auto power-off timer to reset, no system crash reporter to notify, no system music player to pause or resume and no system media playback to disable. PPSA01521 calls `sceSystemServiceDisableMediaPlay` during start-up, and its wrappers for both pass no arguments - [libSceBluetoothHid](../../core/libs/prx/libSceBluetoothHid/Export.cpp) implements only the no-device path: initialization and callback and device registration succeed, the callback is never called because no device connects, and calls that need a connected device throw - [sceVideoOutOpen](../../core/libs/prx/libSceVideoOut/src/Output.cpp) validates the priority and CPU affinity that the open param requests for the VideoOut service thread but does not apply them: the port's present and vblank threads are host threads, and guest priorities and affinities do not reach host scheduling +- [sceVideoOutAddVrrStatusFlagsPrivilege](../../core/libs/prx/libSceVideoOut/src/Output.cpp) returns 0 and does nothing: the host output has no VRR state to report. The name comes from an assert message in a title that requires `SCE_VIDEO_OUT_OK`; the NID hashes to it, the arguments are unknown (it is called without any) - Without `VK_KHR_fragment_shader_barycentric` the shader recompiler [does not load the barycentric VGPRs](../../core/shader/recompiler/Translation/src/InstructionTranslator.cpp): [`v_interp_p1_f32` is dropped and `v_interp_p2_f32` reads the attribute Vulkan interpolated](../../core/shader/recompiler/Translation/src/AttributeInstructions.cpp), so pixel shaders that use the barycentrics for anything other than `v_interp` get wrong values. - [libSceAudio3d](../../core/libs/prx/libSceAudio3d/Export.cpp) - the port produces no sound: objects can be reserved but the library has no bed exports or `sceAudio3dObjectSetAttributes` to give them audio, so `sceAudio3dPortPush` only paces the queue at `granularity` samples per frame at 48 kHz. `sceAudio3dPortSetAttribute` accepts the late reverb level and the downmix spread attributes and ignores them - [libSceTextToSpeech2](../../core/libs/prx/libSceTextToSpeech2/Export.cpp) has no speech engine: `sceTextToSpeech2Initialize` fails and the other exports throw. PPSA01317 skips its narration set-up when initialization fails @@ -97,8 +98,6 @@ Throughout the project, every function at every stage either **does exactly what - [sceUserServiceGetNpAccountId](../../core/libs/prx/libSceUserService/Export.cpp) (libSceUserService) - the account id for a user with no linked PSN account is assumed to be 0 with success; libSceNpManager's `sceNpGetAccountIdA` answers the same question with `SCE_NP_ERROR_SIGNED_OUT` - [sceUserServiceGetUserColor](../../core/libs/prx/libSceUserService/Export.cpp) (libSceUserService) - the default profile colour is assumed to be blue (0) - [libSceVdecsw](../../core/libs/prx/libSceVdecsw/Export.cpp) (libSceVdecsw) - structure layouts follow shadPS4's `vdecsw.h` and were checked against PPSA05684's movie decoder (`movie-decoder-vdecsw.cpp`), which asserts `pictureCount == 1` at offset 0xb of `OutputInfo` and reads `InputResult` offset 0x10 as the number of frames an access unit produced. The compute, decoder memory and decode input structures and the first 0x28 bytes of the 0x50-byte decoder config are those of libSceVideodec2, and decoding goes through it. The 0x78-byte AVC picture info is filled with Videodec2's leading fields and the cropping fields only. OUTPUT_PENDING (0x81510115), INPUT_QUEUE_EMPTY (0x81510116) and DECODE_PENDING (0x81510117) follow from the codes each call's assert accepts. At most four decoded pictures are queued and the last one is held until `sceVdecswFinalizeDecodeSequence`; the console's queue depth is unknown -- [kP2L8t3j-aM](../../core/libs/prx/libSceVideoOut/src/Output.cpp) (libSceVideoOut) - unknown name, signature -- [LibwuIonIBw](../../core/libs/prx/libSceVideoOut/src/Output.cpp) (libSceVideoOut) - unknown name, signature - [sceAgcDriverGetEqContextId](../../core/libs/prx/libSceAgcDriver/Eq/src/Query.cpp) (libSceAgcDriver) - returns `ident`: an end-of-pipe interrupt is delivered only to the registration whose id equals the raising queue, so `ident` is that context's id (titles register 0 or 1 for graphics and `0x20 + 8 * pipe + queue` for compute). The console's value is not known; KytyPS5 returns the event's `data`, which it fills with the interrupt's context id, whereas `data` here is the EV_CLEAR trigger count. Other event filters throw - [sceAgcSetSubmitMode](../../core/libs/prx/libSceAgc/Misc/src/Suspend.cpp) (libSceAgc) - mode values unknown; only 0 is accepted - [User data registers](../../core/libs/prx/libSceAgcDriver/Execution/src/Driver/Diagnostics.cpp) (AGC driver) - a `SPI_SHADER_USER_DATA_*` or `COMPUTE_USER_DATA_*` register the title never wrote reads as 0 when a stage's RSRC2 count covers it, as Kyty (zeroed `UserSgprInfo`) and SharpEmu (missing registers read 0) do, and as AMD PAL and Mesa initialize them; the console's value is not confirmed