#pragma once #include #include namespace rex::graphics { enum class NativeGuestOutputBackend : uint32_t { kUnknown = 0, kD3D12, kVulkan, }; // Per-frame context handed to the registered native guest-output renderer. // Backend-agnostic: all device access and command recording goes through the // native-render RHI (rex/graphics/native_rhi.h); the command processor owns // the nrhi::Device and the per-frame nrhi::Cmd. struct NativeGuestOutputRenderContext { NativeGuestOutputBackend backend = NativeGuestOutputBackend::kUnknown; uint32_t guest_output_width = 0; uint32_t guest_output_height = 0; uint32_t display_width = 0; uint32_t display_height = 0; // Device-level RHI: resource/pipeline creation, submission counters. // Stable across frames for the lifetime of the graphics system. nrhi::Device* device = nullptr; // Frame-scoped command recording into the command processor's deferred // command list. Only valid during the callback. nrhi::Cmd* cmd = nullptr; // The presenter's guest output image, wrapped as an RHI texture. In // nrhi::ResourceState::kGuestOutput at entry (or kCommon the very first // frame it exists); must be returned to kGuestOutput before the callback // returns true. The pointer is stable while the underlying image is (it // changes on output resize). nrhi::Texture* guest_output = nullptr; }; using NativeGuestOutputRenderer = bool (*)(const NativeGuestOutputRenderContext& context, void* user_data); void SetNativeGuestOutputRenderer(NativeGuestOutputRenderer renderer, void* user_data); bool TryRenderNativeGuestOutput(const NativeGuestOutputRenderContext& context); // Whether a renderer is registered at all; command processors skip RHI // setup entirely when none is (non-Skate titles / renderer disabled). bool HasNativeGuestOutputRenderer(); // True while the registered renderer actually replaced the last presented // frame (false when it yields to the emulated output). bool IsNativeGuestOutputActive(); // ---- Wide guest output ---------------------------------------------------- // Display aspect the native renderer wants to draw at when it is wider than // the guest frontbuffer's own aspect (ultrawide). 0 = disabled. Set once at // app startup; consumed by the command processors when sizing the guest // output for a swap. void SetNativeGuestOutputWideAspect(double aspect); double GetNativeGuestOutputWideAspect(); // Applies the wide aspect to this swap's guest output and display // dimensions: widens guest_output_width to guest_output_height * aspect and // makes the display aspect match. Only widens while a renderer is registered // AND it served the last presented frame, so emulated fallback frames size // back to the frontbuffer aspect and present pillarboxed by the presenter's // letterbox path. Returns whether the output was widened; when it was and // the renderer then yields the frame, the refresh callback must return false // (keeping the previously presented image) instead of running the emulated // blit, which writes frontbuffer-aspect content. bool ApplyNativeGuestOutputWideAspect(uint32_t& guest_output_width, uint32_t guest_output_height, uint32_t& display_width, uint32_t& display_height); // native_render_suppress_emulated_draws && IsNativeGuestOutputActive(): // command processors skip emulated draw/resolve execution (memexport draws, // fences, queries and PM4 parsing still run). bool ShouldSuppressEmulatedDraws(); // While the native guest-output renderer is active: should the emulated pass // currently targeting `surface_pitch`-wide surfaces be suppressed? Driven by // the native_render_suppress_mode cvar; shared by both command processors. // Draw and resolve suppression must agree: executed passes need their // resolves, suppressed passes leave garbage EDRAM that must never be copied // out. bool ShouldSuppressPassAtPitch(uint32_t surface_pitch); // Within a suppression-EXEMPT pass: should a draw with no pixel shader // (depth/stencil-only: shadow casters, z-prepasses) be skipped anyway? // Driven by native_render_suppress_exempt_depth_only; their output feeds // only suppressed scene passes (the native renderer shadows itself). bool ShouldSuppressExemptDepthOnlyDraws(); // ---- Guest-output post-processor ------------------------------------------ // Host effect over the EMULATED guest-output path (e.g. the settings-menu // backdrop blur): invoked by the command processors at the very end of the // emulated gamma/FXAA refresh, after the guest output image has been fully // written, with the same context contract as the renderer callback (the // image arrives in kGuestOutput state and must be returned to it). When the // native renderer handled the frame this is NOT invoked; the renderer // applies its own effects inline. Only invoked while the request flag is set // (a cheap gate so the common no-effect frame skips the RHI frame setup); // the post-processor clears the flag itself once its effect has fully // decayed. using NativeGuestOutputPostProcessor = void (*)(const NativeGuestOutputRenderContext& context, void* user_data); void SetNativeGuestOutputPostProcessor(NativeGuestOutputPostProcessor post_processor, void* user_data); bool HasNativeGuestOutputPostProcessor(); void InvokeNativeGuestOutputPostProcessor(const NativeGuestOutputRenderContext& context); void RequestNativeGuestOutputPostProcess(bool requested); bool IsNativeGuestOutputPostProcessRequested(); } // namespace rex::graphics