How sceVideoOut Is Implemented in SharpEmu: PS5 Video-Out Emulation Explained
SharpEmu implements the PlayStation 5's sceVideoOut API as a high-level emulation (HLE) layer in C#, using the SharpEmu.Libs.VideoOut namespace to manage video ports, buffer registration, and Vulkan-based frame presentation.
The par274/sharpemu repository provides a complete software implementation of the PlayStation 5's video output subsystem, enabling games to render frames through the sceVideoOut system call interface. This implementation bridges guest GPU memory to the host display using Vulkan, while accurately simulating vblank timing and flip events to maintain compatibility with commercial titles.
Core Components of the sceVideoOut Implementation
SharpEmu's video output system consists of three architectural layers defined in the SharpEmu.Libs.VideoOut namespace:
- API Entry Points: Exported functions marked with
[SysAbiExport]insrc/SharpEmu.Libs/VideoOut/VideoOutExports.csexposesceVideoOutOpen,sceVideoOutSubmitFlip, andsceVideoOutAddVblankEventto the emulator's HLE dispatcher. - Port State Management: The
VideoOutPortStateclass tracks per-handle data including buffer groups, flip counters, and timing parameters, protected by the static_stateGatelock. - Presentation Layer:
VulkanVideoPresenterinsrc/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.csconsumes frames fromGuestGpuand displays them to the host window.
Opening and Configuring Video Ports
The initialization sequence begins with sceVideoOutOpen, which validates that the busType equals SceVideoOutBusTypeMain, the index is 0, and the userId is 0 or 255. Upon validation, it instantiates a VideoOutPortState object, stores it in the static _ports dictionary, and returns a unique handle to the guest application.
Configuration continues through sceVideoOutSetFlipRate, sceVideoOutSetBufferAttribute, and sceVideoOutRegisterBuffers. These methods populate the VideoOutPortState fields:
FlipRate: Set to 30 Hz, 20 Hz, or 60 HzOutputWidth,OutputHeight, andRefreshRate: Define display dimensionsGroupsandBufferSlots: Store metadata and guest memory addresses for registered buffers
Submitting Flips and Managing Frame Delivery
The sceVideoOutSubmitFlip syscall triggers the frame presentation pipeline. Internally, this calls SubmitFlip, which performs several critical operations:
- Validates the port handle and buffer index against registered slots.
- Updates
CurrentBufferand incrementsFlipCount. - Constructs a
SceVideoOutInternalEventFliphint for event notification. - Invokes
GuestGpu.Current.TrySubmitGuestImageto upload the guest display buffer to the host GPU, converting pixel formats viaMapPixelFormatToGuestTextureFormat. - Optionally dumps the frame if
_dumpVideoOutis enabled. - Calls
PaceFlipto enforce the requested flip rate unlessSHARPEMU_NO_FLIP_PACING=1is set. - Triggers flip events directly or through
VulkanVideoPresenter.SubmitOrderedGuestActionto maintain GPU work ordering.
Vblank Timing and Event Handling
SharpEmu simulates hardware vblank signals through a dedicated background thread. The StartVblankThreadOnce method initializes VblankTickLoop, which runs at the port's refresh cadence and increments VblankCount each cycle.
Event registration works through sceVideoOutAddFlipEvent and sceVideoOutAddVblankEvent, which add entries to the port's FlipEvents and VblankEvents lists. When vblank occurs, the loop triggers events via KernelEventQueueCompatExports.TriggerDisplayEvent.
For precise timing, sceVideoOutWaitVblank uses HostTiming.SleepUntil with a high-resolution Stopwatch to calculate the next vblank deadline, avoiding scheduler quantum overshoot that would disrupt frame pacing.
Vulkan Integration and Pixel Format Validation
The presentation layer relies on VulkanVideoPresenter to consume frames from GuestGpu and display them in a host window. This class handles ordering of GPU work and maintains performance counters.
During initialization, the static constructor in VideoOutExports.cs invokes RunPixelFormatSelfChecks() to verify that PS5 pixel-format constants correctly map to the emulator's internal texture formats, preventing display corruption before the first frame renders.
Shutdown Coordination and Lifecycle Management
When the presentation window closes or the host requests termination, NotifyPresentationWindowClosed and NotifyHostInterrupt initiate a graceful shutdown sequence. These methods shut down the Vulkan presenter, coordinate with AudioOutExports.cs to stop audio output, and terminate the process cleanly.
Practical Usage Example
// Open a video-out port (equivalent to sceVideoOutOpen)
int handle = VideoOutExports.VideoOutOpen(ctx);
// Configure 30 Hz output (sceVideoOutSetFlipRate)
VideoOutExports.VideoOutSetFlipRate(ctx);
// Register display buffers (sceVideoOutRegisterBuffers)
ulong bufferAddr = 0x1234_5678_9000;
VideoOutExports.VideoOutSetBufferAttribute(ctx);
VideoOutExports.VideoOutRegisterBuffers(ctx);
// Submit a frame (sceVideoOutSubmitFlip)
VideoOutExports.VideoOutSubmitFlip(ctx);
// Wait for vblank (sceVideoOutWaitVblank)
VideoOutExports.VideoOutWaitVblank(ctx);
Each function receives a CpuContext ctx containing guest registers and memory, with arguments extracted from ctx[CpuRegister.*].
Summary
- SharpEmu implements sceVideoOut as a high-level emulation layer in the
SharpEmu.Libs.VideoOutnamespace. - VideoOutExports.cs contains the syscall implementations and
VideoOutPortStatemanagement logic. - VulkanVideoPresenter.cs handles the actual frame display using the host GPU.
- The implementation supports configurable flip rates (20/30/60 Hz), precise vblank timing via
HostTiming.SleepUntil, and pixel format validation. - Frame submission coordinates with
GuestGputo copy guest memory to Vulkan textures while respecting flip pacing settings.
Frequently Asked Questions
How does SharpEmu handle the PS5's video output without actual hardware?
SharpEmu uses high-level emulation (HLE) where the sceVideoOut syscalls are intercepted and implemented in C# rather than passed to physical hardware. The VideoOutExports.cs file provides software implementations that translate guest GPU commands into Vulkan API calls on the host, effectively simulating the video-out bus in software.
What determines the frame rate in SharpEmu's sceVideoOut implementation?
The frame rate is controlled by sceVideoOutSetFlipRate, which configures the VideoOutPortState.FlipRate field to 20 Hz, 30 Hz, or 60 Hz. The PaceFlip method enforces this timing unless the SHARPEMU_NO_FLIP_PACING environment variable is set to 1, which disables pacing for debugging or performance analysis.
Where does the actual image rendering happen in the sceVideoOut pipeline?
While sceVideoOutSubmitFlip initiates the process, the actual host-side rendering occurs in VulkanVideoPresenter.cs. This class receives the guest image via GuestGpu.Current.TrySubmitGuestImage, handles pixel format conversion through MapPixelFormatToGuestTextureFormat, and presents the final frame to the host display using Vulkan.
How does the emulator maintain accurate vblank timing for games?
A dedicated background thread started by StartVblankThreadOnce runs VblankTickLoop at the port's refresh rate. For synchronous waits, sceVideoOutWaitVblank uses HostTiming.SleepUntil with high-resolution timer calculations to sleep precisely until the next vblank deadline, minimizing timing drift that could cause frame stuttering.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →