libkernel.prx does not export TimedWait::*, so libSceVideoOut.prx had an
unresolved import and titles stopped at load. Compile TimedWait.cpp into
libSceVideoOut, and use NotifyAll for the notify added on main.
The flip queue's condition variable went through winpthreads, whose
timed waits end on GetTickCount64 steps: the present thread's 10 ms poll
for SDL events and the pad, taken while no flip is queued, ended 11 to
24 ms later on Windows, and WaitForFlipRoom's 60 s bound had the same
granularity. Use TimedWait::Condition, which waits on an event and a
high-resolution timer, so the poll ends at 10 ms and a submitted flip
still wakes the present thread at once.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Unsubscribe an event queue from the output mode events that
sceVideoOutAddOutputModeEvent registers, the same way the flip and
vblank events are removed. Deleting a registration that does not exist
succeeds, and a null queue or an invalid handle throws.
Co-Authored-By: Gemini 3.8 Flash <noreply@google.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
sceVideoOutVrrPegToFixedRate and sceVideoOutVrrUnpegFromFixedRate were
NotImplemented without arguments. Both take the output handle; the peg
also takes two further arguments whose meaning is unknown. VRR is not
modelled and the output always runs at a fixed rate, so both validate
the handle and return 0; non-zero peg arguments throw. Recorded in
TechnicalDebt.
Uncharted: Legacy of Thieves Collection (PPSA05684) stops on main with
"sceVideoOutVrrUnpegFromFixedRate not implemented"; it passes its
output handle to both (and zeros to the peg) and ignores the results.
A flip packet names a buffer index; the buffer was looked up when the
command buffer was submitted, so a title that registers its buffers
after submitting the first frame failed with "flip buffer is not
registered". The buffer is now resolved when the GPU reaches the flip,
as the hardware does. A flip whose buffer is still unregistered then
presents black and completes (see TechnicalDebt).
Uncharted: Legacy of Thieves Collection (PPSA05684) submits its first flip before registering its buffers and stopped with "flip buffer is not registered".
A title that opens a main and an overlay port flips both, and every flip
was presented as a whole frame, so the window alternated between the two
buses: PPSA01736 shows its intro on the main bus and an empty overlay,
and the intro blinked black on every other frame. Overlay flips now
complete and signal their events without being shown. Compositing the
overlay over the main output is not implemented.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
video_out_flip_tests writes app0/sce_sys/param.json in the shared test
directory on every run, and every VideoOut window reads it for its
title. Under ctest --parallel one test read the file while another
rewrote it: 'unexpected end of param.json' in video_out_flip_controls.
Give all of them one RESOURCE_LOCK.
guest_video_out_output_mode (#1496) opens VideoOut directly, which needs
a Vulkan-capable SDL window, and aborts on the CI runners that have none.
Skip with 77 like the other VideoOut window tests, unless
ANYPS5_REQUIRE_DISPLAY is set.
Main's Linux job fails since #1028 and #1714: guest_video_out_resolution_status aborts when SDL's offscreen
driver has no Vulkan window, and check_skips.py rejects the skips of guest_video_out_wqhd_detection and the
flip tests because "no display" is not among the device limitations it accepts.
The resolution status test now skips with code 77 like its wqhd sibling, the flip tests print the same skip
reason, and check_skips.py accepts "no display" as a device limitation. ANYPS5_REQUIRE_DISPLAY still turns
the skip into a failure.
Measured on a PS5 (firmware 13.60) through dlopen of
libSceVideoOut.sprx on a handle the payload opened.
sceVideoOutInitializeOutputOptions writes 0xFF to byte 2 of the 64-byte
options and returns INVALID_ADDRESS for a null pointer.
sceVideoOutIsOutputSupported checks the handle, then the reserved
arguments, then the mode, then the options. It never answers 0: a mode
the display lacks returns UNSUPPORTED_OUTPUT_MODE, and a value that is
not a mode returns 0x8029001E. Options must keep the initialized words,
except word 3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The named VRR functions hash to LibwuIonIBw and kP2L8t3j-aM already.
Their explicit aliases therefore create second PE exports with the same
patched NIDs and prevent Windows builds from completing.
Keep the named function exports and remove their redundant aliases.
The NID patcher's duplicate-export validation remains unchanged.
LibwuIonIBw registers video out event id 16, which the host never triggers because its VRR state does not change; kP2L8t3j-aM returns 0 without effect.
A flip still queued or in presentation when the title closes its port is discarded, as on hardware, rather than stored as a driver failure. A closed port no longer terminates the presentation thread nor surfaces from Shutdown. The lifetime test now expects cancellation and the reopen mode is registered in ctest.
The offscreen SDL driver cannot create a Vulkan window on Windows since
the VideoOut window uses SDL_WINDOW_VULKAN, so the tests that open a port
now exit 77 (skipped) like the other display tests, unless
ANYPS5_REQUIRE_DISPLAY is set. The tests also write app0/sce_sys/param.json,
which AppMetadata now requires.
presentLoop called AgcDriverShutdown outside its try block, so a stored
worker failure rethrown there terminated the process. Store it in
flipQueue->failure so VideoOutDriver::Shutdown rethrows it after the join.
Register the lifetime, decode and controls modes of video_out_flip_tests
in ctest.
sceVideoOutAddPreVblankStartEvent threw "physical pre-vblank timing is
not implemented", so titles that pace on it stopped at startup. It now
registers the event like the vblank event does, and each emulated vblank
tick triggers the pre-vblank start events just before the vblank events,
with the pre-vblank count as data. The console's lead time is unknown.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Three homebrew titles from #1578 import it and stop at start-up without
it: ps5-opengl (PPSA99005), EVO-PLAYER-PS5 (PPSA99039) and
ProsperoRadio (PPSA99001).
It reports the port's resolution as the full and pane size and the
refresh rate ID as sceVideoOutGetOutputStatus does. The 48-byte layout
follows the declarations in ps5-opengl and EVO-PLAYER-PS5, which agree;
the screen size is 0 and the flags are 0. An invalid handle or a null
status throws, as in the other status getters.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The console's DCC of a single-level 4-byte SW_64KB_R_X surface is
ceil(W/512) * ceil(H/512) * 4096 bytes, 49152 for 1920x1080, but the
driver stored and read one key per 256 surface bytes (34560).
PPSA26344 retiles its render DCC into the displayable copy it
registers (cs_dcc_retile_32bpp); a retile of the stale tail gives the
mixed keys PPSA12544 stops on.
DccKeyCount gives that extent. Color target and storage image key
stores cover it when the DCC is pipe-aligned (CB_COLOR_ATTRIB3 bit 30,
T# word 6 bit 19), whose CB keys reach its last meta block, and throw
where it is not writable; unaligned DCC keeps one key per 256 bytes,
as do texture and color target key reads. DisplayBufferKeys reads the
display DCC over the extent, presents only when every key agrees,
names the first differing key's pixels and checks a pending image
against those keys.
On Windows the vblank thread's condition_variable::wait_until went
through winpthreads, which ends a timed wait on a 15.6 ms
GetTickCount64 step even with the scheduler tick raised to 0.5 ms.
Vblank intervals ranged from 2 ms to 34 ms instead of 16.68 ms. The
thread now sleeps to each deadline with PreciseSleepUs and checks for
a stop or a presentation failure after each sleep.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The driver created a windowless Vulkan device at the first dispatch and
replaced it with a windowed one at the first flip. The replacement built a
second device and re-imported every host import, and the first flip paid
for it.
VideoOut now creates its window when it starts (the first VideoOut call),
at the default 1920x1080 output, and attaches it to the driver
(AgcDriverAttachWindow) before the call returns. The driver creates its
device with that window, so GPU work and presentation use one device.
Present no longer creates or replaces a device. A device created by GPU
work before VideoOut started is still replaced, now in AttachWindow.
A failure to create the window or the device is thrown from that first
VideoOut call instead of terminating the presentation thread.
Tests:
- video_out_flip_tests onedevice: GPU work, then a flip, leaves one device.
- pacing makes its first presentation span vblanks with GPU work queued
ahead of the flip; it relied on the device being created at the flip.
- unavailable expects the failure from sceVideoOutOpen.
- guest_video_out_wqhd_detection writes a param.json for the window title
and is skipped without a display or Vulkan device unless
ANYPS5_REQUIRE_DISPLAY is set.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The display window now takes its icon from /app0/sce_sys/icon0.png when
the game has one, decoded with the existing PNG decoder and set through
SDL_SetWindowIcon. Linux executables have no icon resource, so the
window, taskbar and alt-tab showed a generic icon; on Windows the
embedded resource from #350 already covers this and the call only
reinforces it. Without icon0.png nothing changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SDL_CreateWindow with SDL_WINDOW_VULKAN loads and queries the Vulkan loader
while the render thread can be inside vkCreateInstance. On Windows the two
together intermittently made the NVIDIA ICD fail instance creation (-3) and
the loader return VK_ERROR_INCOMPATIBLE_DRIVER. libSceAgcDriver now exports
a loader lock that the VulkanDevice constructor and DisplayWindow::create
both hold.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lets an open port report 2560x1440 output (resolution 4). The output
status here is 1080p or 2160p, so the call changes nothing it reports.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sceVideoOutRegisterBuffers2 threw "DCC presentation is not implemented"
for category 1 (DCC-compressed) buffers, which PPSA26344 registers. Such
a buffer now needs DCC metadata and the tiled layout, and dcc_control may
only use the CB_DCC_CONTROL block layout bits 0x10026c.
At a flip the keys are read behind their recorded writers, one byte per
256 bytes as in the driver's DCC model. Uncompressed keys present the
stored texels as before; a uniform clear code presents its color with a
GPU clear of the scaler's source image; a pending storage image is used
when it serves the keys. Mixed or unreadable keys, clear codes the
display format has no DCC encoding for, register-clear keys over a
pending image and a clear color wider than 32 bits throw. Buffers
without DCC take the old path.