// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: CC-BY-NC-ND-4.0 #pragma once #include "gpu_types.h" #include "common/align.h" #include #include #include class Error; enum class GPUVSyncMode : u8; class MediaCapture; class StateWrapper; class GPUBackend; namespace System { struct MemorySaveState; } #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4200) // warning C4200: nonstandard extension used: zero-sized array in struct/union #endif enum class GPUBackendCommandType : u8 { Wraparound, AsyncCall, AsyncBackendCall, Reconfigure, Shutdown, ClearVRAM, ClearDisplay, UpdateDisplay, SubmitFrame, BufferSwapped, LoadState, LoadMemoryState, SaveMemoryState, ReadVRAM, FillVRAM, UpdateVRAM, CopyVRAM, SetDrawingArea, UpdateCLUT, ClearCache, DrawPolygon, DrawPrecisePolygon, DrawRectangle, DrawLine, DrawPreciseLine, }; struct GPUThreadCommand { u32 size; GPUBackendCommandType type; static constexpr u32 AlignCommandSize(u32 size) { // Ensure size is a multiple of 8 (minimum data size) so we don't end up with an unaligned command. // NOTE: If we ever end up putting vectors in the command packets, this should be raised. constexpr u32 COMMAND_QUEUE_ALLOCATION_ALIGNMENT = 8; return Common::AlignUpPow2(size, COMMAND_QUEUE_ALLOCATION_ALIGNMENT); } }; struct GPUThreadReconfigureCommand : public GPUThreadCommand { Error* error_ptr; bool* out_result; std::string game_serial; std::optional renderer; std::optional fullscreen; std::optional start_fullscreen_ui; GPUVSyncMode vsync_mode; bool allow_present_throttle; bool force_recreate_device; bool upload_vram; }; struct GPUThreadAsyncCallCommand : public GPUThreadCommand { GPUThreadAsyncCallCommand(std::function func_) : func(std::move(func_)) {} std::function func; }; struct GPUThreadAsyncBackendCallCommand : public GPUThreadCommand { GPUThreadAsyncBackendCallCommand(std::function func_) : func(std::move(func_)) {} std::function func; }; struct GPUBackendLoadStateCommand : public GPUThreadCommand { u16 vram_data[VRAM_WIDTH * VRAM_HEIGHT]; u16 clut_data[GPU_CLUT_SIZE]; u32 texture_cache_state_version; u32 texture_cache_state_size; u8 texture_cache_state[0]; // texture_cache_state_size }; struct GPUBackendDoMemoryStateCommand : public GPUThreadCommand { System::MemorySaveState* memory_save_state; }; struct GPUBackendFramePresentationParameters { u32 frame_number; u32 internal_frame_number; u64 present_time; MediaCapture* media_capture; union { u8 bits; BitField allow_present_skip; BitField present_frame; BitField update_performance_counters; }; }; struct GPUBackendUpdateDisplayCommand : public GPUThreadCommand { u16 display_width; u16 display_height; u16 display_origin_left; u16 display_origin_top; u16 display_vram_left; u16 display_vram_top; u16 display_vram_width; u16 display_vram_height; float display_pixel_aspect_ratio; u16 X; // TODO: Can we get rid of this? bool interlaced_display_enabled : 1; bool interlaced_display_field : 1; bool interlaced_display_interleaved : 1; bool display_24bit : 1; bool display_disabled : 1; bool submit_frame : 1; bool : 2; GPUBackendFramePresentationParameters frame; }; // Only used for runahead. struct GPUBackendSubmitFrameCommand : public GPUThreadCommand { GPUBackendFramePresentationParameters frame; }; struct GPUBackendReadVRAMCommand : public GPUThreadCommand { u16 x; u16 y; u16 width; u16 height; }; struct GPUBackendFillVRAMCommand : public GPUThreadCommand { u16 x; u16 y; u16 width; u16 height; u32 color; bool interlaced_rendering; u8 active_line_lsb; }; struct GPUBackendUpdateVRAMCommand : public GPUThreadCommand { u16 x; u16 y; u16 width; u16 height; bool set_mask_while_drawing; bool check_mask_before_draw; u16 data[0]; }; struct GPUBackendCopyVRAMCommand : public GPUThreadCommand { u16 src_x; u16 src_y; u16 dst_x; u16 dst_y; u16 width; u16 height; bool set_mask_while_drawing; bool check_mask_before_draw; }; struct GPUBackendSetDrawingAreaCommand : public GPUThreadCommand { GPUDrawingArea new_area; }; struct GPUBackendUpdateCLUTCommand : public GPUThreadCommand { GPUTexturePaletteReg reg; bool clut_is_8bit; }; struct GPUBackendDrawCommand : public GPUThreadCommand { bool interlaced_rendering : 1; /// Returns 0 if the currently-displayed field is on an even line in VRAM, otherwise 1. bool active_line_lsb : 1; bool set_mask_while_drawing : 1; bool check_mask_before_draw : 1; bool texture_enable : 1; bool raw_texture_enable : 1; bool transparency_enable : 1; bool shading_enable : 1; bool quad_polygon : 1; bool dither_enable : 1; bool valid_w : 1; // only used for precise polygons // During transfer/render operations, if ((dst_pixel & mask_and) == 0) { pixel = src_pixel | mask_or } ALWAYS_INLINE u16 GetMaskAND() const { return check_mask_before_draw ? 0x8000 : 0x0000; } ALWAYS_INLINE u16 GetMaskOR() const { return set_mask_while_drawing ? 0x8000 : 0x0000; } u16 num_vertices; GPUDrawModeReg draw_mode; GPUTexturePaletteReg palette; GPUTextureWindow window; }; struct GPUBackendDrawPolygonCommand : public GPUBackendDrawCommand { struct Vertex { s32 x, y; union { struct { u8 r, g, b, a; }; u32 color; }; union { struct { u8 u, v; }; u16 texcoord; }; }; Vertex vertices[0]; }; struct GPUBackendDrawPrecisePolygonCommand : public GPUBackendDrawCommand { GPUBackendDrawCommand params; struct Vertex { float x, y, w; s32 native_x, native_y; u32 color; u16 texcoord; }; Vertex vertices[0]; }; struct GPUBackendDrawRectangleCommand : public GPUBackendDrawCommand { u16 width, height; u16 texcoord; s32 x, y; u32 color; }; struct GPUBackendDrawLineCommand : public GPUBackendDrawCommand { struct Vertex { s32 x, y; union { struct { u8 r, g, b, a; }; u32 color; }; ALWAYS_INLINE void Set(s32 x_, s32 y_, u32 color_) { x = x_; y = y_; color = color_; } }; Vertex vertices[0]; }; struct GPUBackendDrawPreciseLineCommand : public GPUBackendDrawCommand { struct Vertex { float x, y, w; s32 native_x, native_y; u32 color; }; Vertex vertices[0]; }; #ifdef _MSC_VER #pragma warning(pop) #endif