// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #pragma once #include "util/gpu_device.h" #include "gpu_thread_commands.h" #include class Error; class SmallStringBase; class GPUFramebuffer; class GPUPipeline; struct GPUSettings; class StateWrapper; class GPUPresenter; namespace System { struct MemorySaveState; } // DESIGN NOTE: Only static methods should be called on the CPU thread. // You specifically don't have a global pointer available for this reason. class ALIGN_TO_CACHE_LINE GPUBackend { public: static GPUThreadCommand* NewClearVRAMCommand(); static GPUThreadCommand* NewClearDisplayCommand(); static GPUBackendUpdateDisplayCommand* NewUpdateDisplayCommand(); static GPUBackendSubmitFrameCommand* NewSubmitFrameCommand(); static GPUThreadCommand* NewClearCacheCommand(); static GPUThreadCommand* NewBufferSwappedCommand(); static GPUBackendReadVRAMCommand* NewReadVRAMCommand(); static GPUBackendFillVRAMCommand* NewFillVRAMCommand(); static GPUBackendUpdateVRAMCommand* NewUpdateVRAMCommand(u32 num_words); static GPUBackendCopyVRAMCommand* NewCopyVRAMCommand(); static GPUBackendSetDrawingAreaCommand* NewSetDrawingAreaCommand(); static GPUBackendUpdateCLUTCommand* NewUpdateCLUTCommand(); static GPUBackendDrawPolygonCommand* NewDrawPolygonCommand(u32 num_vertices); static GPUBackendDrawPrecisePolygonCommand* NewDrawPrecisePolygonCommand(u32 num_vertices); static GPUBackendDrawRectangleCommand* NewDrawRectangleCommand(); static GPUBackendDrawLineCommand* NewDrawLineCommand(u32 num_vertices); static GPUBackendDrawPreciseLineCommand* NewDrawPreciseLineCommand(u32 num_vertices); static void PushCommand(GPUThreadCommand* cmd); static void PushCommandAndWakeThread(GPUThreadCommand* cmd); static void PushCommandAndSync(GPUThreadCommand* cmd, bool spin); static void SyncGPUThread(bool spin); static bool IsUsingHardwareBackend(); static std::unique_ptr CreateHardwareBackend(GPUPresenter& presenter); static std::unique_ptr CreateSoftwareBackend(GPUPresenter& presenter); static std::unique_ptr CreateNullBackend(GPUPresenter& presenter); static bool RenderScreenshotToBuffer(u32 width, u32 height, bool postfx, bool apply_aspect_ratio, Image* out_image, Error* error); static void RenderScreenshotToFile(const std::string_view path, DisplayScreenshotMode mode, u8 quality, bool show_osd_message); static bool BeginQueueFrame(); static void WaitForOneQueuedFrame(); static u32 GetQueuedFrameCount(); static bool AllocateMemorySaveStates(std::span states, Error* error); public: GPUBackend(GPUPresenter& presenter); virtual ~GPUBackend(); ALWAYS_INLINE const GPUPresenter& GetPresenter() const { return m_presenter; } ALWAYS_INLINE GPUPresenter& GetPresenter() { return m_presenter; } virtual bool Initialize(bool upload_vram, Error* error); virtual bool UpdateSettings(const GPUSettings& old_settings, Error* error); virtual void UpdatePostProcessingSettings(bool force_reload); /// Returns the current resolution scale. virtual u32 GetResolutionScale() const = 0; // Graphics API state reset/restore - call when drawing the UI etc. // TODO: replace with "invalidate cached state" virtual void RestoreDeviceContext() = 0; /// Ensures all pending draws are flushed to the host GPU. virtual void FlushRender() = 0; /// Main command handler for GPU thread. void HandleCommand(const GPUThreadCommand* cmd); void GetStatsString(SmallStringBase& str) const; void GetMemoryStatsString(SmallStringBase& str) const; void ResetStatistics(); void UpdateStatistics(u32 frame_count); /// Screen-aligned vertex type for various draw types. struct ScreenVertex { float x; float y; float u; float v; ALWAYS_INLINE void Set(const GSVector2& xy, const GSVector2& uv) { GSVector4::store(this, GSVector4::xyxy(xy, uv)); } }; static void SetScreenQuadInputLayout(GPUPipeline::GraphicsConfig& config); static GSVector4 GetScreenQuadClipSpaceCoordinates(const GSVector4i bounds, const GSVector2i rt_size); static void DrawScreenQuad(const GSVector4i bounds, const GSVector2i rt_size, const GSVector4 uv_bounds = GSVector4::cxpr(0.0f, 0.0f, 1.0f, 1.0f)); protected: enum : u32 { DEINTERLACE_BUFFER_COUNT = 4, }; struct Counters { u32 num_reads; u32 num_writes; u32 num_copies; u32 num_vertices; u32 num_primitives; u32 num_depth_buffer_clears; }; struct Stats : Counters { size_t host_buffer_streamed; u32 host_num_draws; u32 host_num_barriers; u32 host_num_render_passes; u32 host_num_copies; u32 host_num_downloads; u32 host_num_uploads; }; virtual void ReadVRAM(u32 x, u32 y, u32 width, u32 height) = 0; virtual void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color, bool interlaced_rendering, u8 interlaced_display_field) = 0; virtual void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, bool set_mask, bool check_mask) = 0; virtual void CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height, bool set_mask, bool check_mask) = 0; virtual void DrawPolygon(const GPUBackendDrawPolygonCommand* cmd) = 0; virtual void DrawPrecisePolygon(const GPUBackendDrawPrecisePolygonCommand* cmd) = 0; virtual void DrawSprite(const GPUBackendDrawRectangleCommand* cmd) = 0; virtual void DrawLine(const GPUBackendDrawLineCommand* cmd) = 0; virtual void DrawPreciseLine(const GPUBackendDrawPreciseLineCommand* cmd) = 0; virtual void DrawingAreaChanged() = 0; virtual void ClearCache() = 0; virtual void OnBufferSwapped() = 0; virtual void ClearVRAM() = 0; virtual void UpdateDisplay(const GPUBackendUpdateDisplayCommand* cmd) = 0; virtual void LoadState(const GPUBackendLoadStateCommand* cmd) = 0; virtual bool AllocateMemorySaveState(System::MemorySaveState& mss, Error* error) = 0; virtual void DoMemoryState(StateWrapper& sw, System::MemorySaveState& mss) = 0; void HandleUpdateDisplayCommand(const GPUBackendUpdateDisplayCommand* cmd); void HandleSubmitFrameCommand(const GPUBackendFramePresentationParameters* cmd); GPUPresenter& m_presenter; GSVector4i m_clamped_drawing_area = {}; static Counters s_counters; static Stats s_stats; private: static void ReleaseQueuedFrame(); }; namespace Host { /// Called at the end of the frame, before presentation. void FrameDoneOnGPUThread(GPUBackend* gpu_backend, u32 frame_number); } // namespace Host