// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #pragma once #include "types.h" #include "util/gpu_device.h" #include #include #include class Error; class Image; class MediaCapture; class SettingsInterface; enum class DisplayScreenshotMode : u8; class GPUBackend; struct GPUSettings; struct GPUBackendUpdateDisplayCommand; struct GPUBackendFramePresentationParameters; namespace PostProcessing { class Chain; } class ALIGN_TO_CACHE_LINE GPUPresenter final { public: GPUPresenter(); virtual ~GPUPresenter(); ALWAYS_INLINE s32 GetDisplayWidth() const { return m_display_width; } ALWAYS_INLINE s32 GetDisplayHeight() const { return m_display_height; } ALWAYS_INLINE s32 GetDisplayVRAMWidth() const { return m_display_vram_width; } ALWAYS_INLINE s32 GetDisplayVRAMHeight() const { return m_display_vram_height; } ALWAYS_INLINE s32 GetDisplayTextureViewX() const { return m_display_texture_view_x; } ALWAYS_INLINE s32 GetDisplayTextureViewY() const { return m_display_texture_view_y; } ALWAYS_INLINE s32 GetDisplayTextureViewWidth() const { return m_display_texture_view_width; } ALWAYS_INLINE s32 GetDisplayTextureViewHeight() const { return m_display_texture_view_height; } ALWAYS_INLINE GPUTexture* GetDisplayTexture() const { return m_display_texture; } ALWAYS_INLINE bool HasDisplayTexture() const { return m_display_texture; } bool Initialize(Error* error); bool UpdateSettings(const GPUSettings& old_settings, Error* error); bool IsDisplayPostProcessingActive() const; bool UpdatePostProcessingSettings(bool force_reload, Error* error); void ClearDisplay(); void ClearDisplayTexture(); void SetDisplayParameters(u16 display_width, u16 display_height, u16 display_origin_left, u16 display_origin_top, u16 display_vram_width, u16 display_vram_height, float display_pixel_aspect_ratio); void SetDisplayTexture(GPUTexture* texture, s32 view_x, s32 view_y, s32 view_width, s32 view_height); bool Deinterlace(u32 field); bool ApplyChromaSmoothing(); /// Helper function for computing the draw rectangle in a larger window. void CalculateDrawRect(s32 window_width, s32 window_height, bool apply_rotation, bool apply_aspect_ratio, GSVector4i* display_rect, GSVector4i* draw_rect) const; /// Helper function for computing screenshot bounds. void CalculateScreenshotSize(DisplayScreenshotMode mode, u32* width, u32* height, GSVector4i* display_rect, GSVector4i* draw_rect) const; /// Renders the display, optionally with postprocessing to the specified image. bool RenderScreenshotToBuffer(u32 width, u32 height, const GSVector4i display_rect, const GSVector4i draw_rect, bool postfx, Image* out_image); /// Sends the current frame to media capture. void SendDisplayToMediaCapture(MediaCapture* cap); /// Main frame presenter - used both when a game is and is not running. static bool PresentFrame(GPUPresenter* presenter, GPUBackend* backend, bool allow_skip_present, u64 present_time); /// Returns a list of border overlay presets. static std::vector EnumerateBorderOverlayPresets(); /// Returns the settings interface to use for loading post-processing shader configuration. /// Assumes the settings lock is being held. static SettingsInterface& GetPostProcessingSettingsInterface(const char* section); /// Toggles post-processing. Only callable from the CPU thread. static void TogglePostProcessing(); /// Reloads post-processing settings. Only callable from the CPU thread. static void ReloadPostProcessingSettings(bool display, bool internal, bool reload_shaders); private: enum : u32 { DEINTERLACE_BUFFER_COUNT = 4, MAX_SKIPPED_PRESENT_COUNT = 50, }; static void SleepUntilPresentTime(u64 present_time); /// Draws the current display texture, with any post-processing. GPUDevice::PresentResult PresentDisplay(); bool CompileDisplayPipelines(bool display, bool deinterlace, bool chroma_smoothing, Error* error); GPUDevice::PresentResult RenderDisplay(GPUTexture* target, const GSVector4i overlay_rect, const GSVector4i display_rect, const GSVector4i draw_rect, bool postfx); void DrawDisplay(const GSVector2i target_size, const GSVector4i display_rect, bool dst_alpha_blend, DisplayRotation rotation, WindowInfo::PreRotation prerotation); GPUDevice::PresentResult ApplyDisplayPostProcess(GPUTexture* target, GPUTexture* input, const GSVector4i display_rect); void DrawTextureCopy(const GSVector2i target_size, const GSVector4i draw_rect, GPUTexture* input, bool dst_alpha_blend, bool linear, WindowInfo::PreRotation prerotation); void DrawScreenQuad(const GSVector4i rect, const GSVector4 uv_rect, const GSVector2i target_size, DisplayRotation uv_rotation, WindowInfo::PreRotation prerotation); bool DeinterlaceSetTargetSize(u32 width, u32 height, bool preserve); void DestroyDeinterlaceTextures(); void LoadPostProcessingSettings(bool force_load); /// Returns true if the image path or alpha blend option has changed. bool LoadOverlaySettings(); bool LoadOverlayTexture(); bool LoadOverlayPreset(Error* error, Image* image); s32 m_display_width = 0; s32 m_display_height = 0; s32 m_display_origin_left = 0; s32 m_display_origin_top = 0; s32 m_display_vram_width = 0; s32 m_display_vram_height = 0; float m_display_pixel_aspect_ratio = 1.0f; u32 m_current_deinterlace_buffer = 0; std::unique_ptr m_deinterlace_pipeline; std::array, DEINTERLACE_BUFFER_COUNT> m_deinterlace_buffers; std::unique_ptr m_deinterlace_texture; std::unique_ptr m_chroma_smoothing_pipeline; std::unique_ptr m_chroma_smoothing_texture; std::unique_ptr m_display_pipeline; GPUTexture* m_display_texture = nullptr; s32 m_display_texture_view_x = 0; s32 m_display_texture_view_y = 0; s32 m_display_texture_view_width = 0; s32 m_display_texture_view_height = 0; u32 m_skipped_present_count = 0; GPUTexture::Format m_present_format = GPUTexture::Format::Unknown; bool m_border_overlay_alpha_blend = false; std::unique_ptr m_present_copy_pipeline; std::unique_ptr m_display_postfx; // blended variants of pipelines, used when overlays are enabled std::unique_ptr m_display_blend_pipeline; std::unique_ptr m_present_copy_blend_pipeline; std::unique_ptr m_border_overlay_texture; GSVector4i m_border_overlay_display_rect = GSVector4i::zero(); // Low-traffic variables down here. std::string m_border_overlay_image_path; }; namespace Host { /// Called at the end of the frame, before presentation. void FrameDoneOnGPUThread(GPUPresenter* gpu_presenter, u32 frame_number); } // namespace Host