duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

gpu_device.h (30364B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
      3 
      4 #pragma once
      5 
      6 #include "gpu_shader_cache.h"
      7 #include "gpu_texture.h"
      8 #include "window_info.h"
      9 
     10 #include "common/bitfield.h"
     11 #include "common/gsvector.h"
     12 #include "common/heap_array.h"
     13 #include "common/small_string.h"
     14 #include "common/types.h"
     15 
     16 #include <cstring>
     17 #include <deque>
     18 #include <memory>
     19 #include <optional>
     20 #include <span>
     21 #include <string>
     22 #include <string_view>
     23 #include <tuple>
     24 #include <vector>
     25 
     26 class Error;
     27 
     28 enum class RenderAPI : u32
     29 {
     30   None,
     31   D3D11,
     32   D3D12,
     33   Vulkan,
     34   OpenGL,
     35   OpenGLES,
     36   Metal
     37 };
     38 
     39 enum class GPUVSyncMode : u8
     40 {
     41   Disabled,
     42   FIFO,
     43   Mailbox,
     44   Count
     45 };
     46 
     47 class GPUSampler
     48 {
     49 public:
     50   enum class Filter
     51   {
     52     Nearest,
     53     Linear,
     54 
     55     MaxCount
     56   };
     57 
     58   enum class AddressMode
     59   {
     60     Repeat,
     61     ClampToEdge,
     62     ClampToBorder,
     63     MirrorRepeat,
     64 
     65     MaxCount
     66   };
     67 
     68   union Config
     69   {
     70     static constexpr u8 LOD_MAX = 15;
     71 
     72     BitField<u64, Filter, 0, 1> min_filter;
     73     BitField<u64, Filter, 1, 1> mag_filter;
     74     BitField<u64, Filter, 2, 1> mip_filter;
     75     BitField<u64, AddressMode, 3, 2> address_u;
     76     BitField<u64, AddressMode, 5, 2> address_v;
     77     BitField<u64, AddressMode, 7, 2> address_w;
     78     BitField<u64, u8, 9, 5> anisotropy;
     79     BitField<u64, u8, 14, 4> min_lod;
     80     BitField<u64, u8, 18, 4> max_lod;
     81     BitField<u64, u32, 32, 32> border_color;
     82     u64 key;
     83 
     84     // clang-format off
     85     ALWAYS_INLINE float GetBorderRed() const { return static_cast<float>(border_color.GetValue() & 0xFF) / 255.0f; }
     86     ALWAYS_INLINE float GetBorderGreen() const { return static_cast<float>((border_color.GetValue() >> 8) & 0xFF) / 255.0f; }
     87     ALWAYS_INLINE float GetBorderBlue() const { return static_cast<float>((border_color.GetValue() >> 16) & 0xFF) / 255.0f; }
     88     ALWAYS_INLINE float GetBorderAlpha() const { return static_cast<float>((border_color.GetValue() >> 24) & 0xFF) / 255.0f; }
     89     // clang-format on
     90     ALWAYS_INLINE std::array<float, 4> GetBorderFloatColor() const
     91     {
     92       return std::array<float, 4>{GetBorderRed(), GetBorderGreen(), GetBorderBlue(), GetBorderAlpha()};
     93     }
     94   };
     95 
     96   GPUSampler();
     97   virtual ~GPUSampler();
     98 
     99   virtual void SetDebugName(std::string_view name) = 0;
    100 
    101   static Config GetNearestConfig();
    102   static Config GetLinearConfig();
    103 };
    104 
    105 enum class GPUShaderStage : u8
    106 {
    107   Vertex,
    108   Fragment,
    109   Geometry,
    110   Compute,
    111 
    112   MaxCount
    113 };
    114 
    115 enum class GPUShaderLanguage : u8
    116 {
    117   None,
    118   HLSL,
    119   GLSL,
    120   GLSLES,
    121   GLSLVK,
    122   MSL,
    123   SPV,
    124   Count
    125 };
    126 
    127 class GPUShader
    128 {
    129 public:
    130   GPUShader(GPUShaderStage stage);
    131   virtual ~GPUShader();
    132 
    133   static const char* GetStageName(GPUShaderStage stage);
    134 
    135   ALWAYS_INLINE GPUShaderStage GetStage() const { return m_stage; }
    136 
    137   virtual void SetDebugName(std::string_view name) = 0;
    138 
    139 protected:
    140   GPUShaderStage m_stage;
    141 };
    142 
    143 class GPUPipeline
    144 {
    145 public:
    146   enum class Layout : u8
    147   {
    148     // 1 streamed UBO, 1 texture in PS.
    149     SingleTextureAndUBO,
    150 
    151     // 128 byte UBO via push constants, 1 texture.
    152     SingleTextureAndPushConstants,
    153 
    154     // 128 byte UBO via push constants, 1 texture buffer/SSBO.
    155     SingleTextureBufferAndPushConstants,
    156 
    157     // Multiple textures, 1 streamed UBO.
    158     MultiTextureAndUBO,
    159 
    160     // Multiple textures, 128 byte UBO via push constants.
    161     MultiTextureAndPushConstants,
    162 
    163     MaxCount
    164   };
    165 
    166   enum RenderPassFlag : u8
    167   {
    168     NoRenderPassFlags = 0,
    169     ColorFeedbackLoop = (1 << 0),
    170     SampleDepthBuffer = (1 << 1),
    171     BindRenderTargetsAsImages = (1 << 2),
    172   };
    173 
    174   enum class Primitive : u8
    175   {
    176     Points,
    177     Lines,
    178     Triangles,
    179     TriangleStrips,
    180 
    181     MaxCount
    182   };
    183 
    184   union VertexAttribute
    185   {
    186     static constexpr u32 MaxAttributes = 16;
    187 
    188     enum class Semantic : u8
    189     {
    190       Position,
    191       TexCoord,
    192       Color,
    193 
    194       MaxCount
    195     };
    196 
    197     enum class Type : u8
    198     {
    199       Float,
    200       UInt8,
    201       SInt8,
    202       UNorm8,
    203       UInt16,
    204       SInt16,
    205       UNorm16,
    206       UInt32,
    207       SInt32,
    208 
    209       MaxCount
    210     };
    211 
    212     BitField<u32, u8, 0, 4> index;
    213     BitField<u32, Semantic, 4, 2> semantic;
    214     BitField<u32, u8, 6, 2> semantic_index;
    215     BitField<u32, Type, 8, 4> type;
    216     BitField<u32, u8, 12, 3> components;
    217     BitField<u32, u16, 16, 16> offset;
    218 
    219     u32 key;
    220 
    221     // clang-format off
    222     ALWAYS_INLINE VertexAttribute() = default;
    223     ALWAYS_INLINE constexpr VertexAttribute(const VertexAttribute& rhs) : key(rhs.key) {}
    224     ALWAYS_INLINE VertexAttribute& operator=(const VertexAttribute& rhs) { key = rhs.key; return *this; }
    225     ALWAYS_INLINE bool operator==(const VertexAttribute& rhs) const { return key == rhs.key; }
    226     ALWAYS_INLINE bool operator!=(const VertexAttribute& rhs) const { return key != rhs.key; }
    227     ALWAYS_INLINE bool operator<(const VertexAttribute& rhs) const { return key < rhs.key; }
    228     // clang-format on
    229 
    230     static constexpr VertexAttribute Make(u8 index, Semantic semantic, u8 semantic_index, Type type, u8 components,
    231                                           u16 offset)
    232     {
    233       // Nasty :/ can't access an inactive element of a union here..
    234       return VertexAttribute((static_cast<u32>(index) & 0xf) | ((static_cast<u32>(semantic) & 0x3) << 4) |
    235                              ((static_cast<u32>(semantic_index) & 0x3) << 6) | ((static_cast<u32>(type) & 0xf) << 8) |
    236                              ((static_cast<u32>(components) & 0x7) << 12) |
    237                              ((static_cast<u32>(offset) & 0xffff) << 16));
    238     }
    239 
    240   private:
    241     ALWAYS_INLINE constexpr VertexAttribute(u32 key_) : key(key_) {}
    242   };
    243 
    244   struct InputLayout
    245   {
    246     std::span<const VertexAttribute> vertex_attributes;
    247     u32 vertex_stride;
    248 
    249     bool operator==(const InputLayout& rhs) const;
    250     bool operator!=(const InputLayout& rhs) const;
    251   };
    252 
    253   struct InputLayoutHash
    254   {
    255     size_t operator()(const InputLayout& il) const;
    256   };
    257 
    258   enum class CullMode : u8
    259   {
    260     None,
    261     Front,
    262     Back,
    263 
    264     MaxCount
    265   };
    266 
    267   enum class DepthFunc : u8
    268   {
    269     Never,
    270     Always,
    271     Less,
    272     LessEqual,
    273     Greater,
    274     GreaterEqual,
    275     Equal,
    276 
    277     MaxCount
    278   };
    279 
    280   enum class BlendFunc : u8
    281   {
    282     Zero,
    283     One,
    284     SrcColor,
    285     InvSrcColor,
    286     DstColor,
    287     InvDstColor,
    288     SrcAlpha,
    289     InvSrcAlpha,
    290     SrcAlpha1,
    291     InvSrcAlpha1,
    292     DstAlpha,
    293     InvDstAlpha,
    294     ConstantColor,
    295     InvConstantColor,
    296 
    297     MaxCount
    298   };
    299 
    300   enum class BlendOp : u8
    301   {
    302     Add,
    303     Subtract,
    304     ReverseSubtract,
    305     Min,
    306     Max,
    307 
    308     MaxCount
    309   };
    310 
    311   // TODO: purge this?
    312   union RasterizationState
    313   {
    314     BitField<u8, CullMode, 0, 2> cull_mode;
    315     u8 key;
    316 
    317     // clang-format off
    318     ALWAYS_INLINE RasterizationState() = default;
    319     ALWAYS_INLINE RasterizationState(const RasterizationState& rhs) : key(rhs.key) {}
    320     ALWAYS_INLINE RasterizationState& operator=(const RasterizationState& rhs) { key = rhs.key; return *this; }
    321     ALWAYS_INLINE bool operator==(const RasterizationState& rhs) const { return key == rhs.key; }
    322     ALWAYS_INLINE bool operator!=(const RasterizationState& rhs) const { return key != rhs.key; }
    323     ALWAYS_INLINE bool operator<(const RasterizationState& rhs) const { return key < rhs.key; }
    324     // clang-format on
    325 
    326     static RasterizationState GetNoCullState();
    327   };
    328 
    329   union DepthState
    330   {
    331     BitField<u8, DepthFunc, 0, 3> depth_test;
    332     BitField<u8, bool, 4, 1> depth_write;
    333     u8 key;
    334 
    335     // clang-format off
    336     ALWAYS_INLINE DepthState() = default;
    337     ALWAYS_INLINE DepthState(const DepthState& rhs) : key(rhs.key) {}
    338     ALWAYS_INLINE DepthState& operator=(const DepthState& rhs) { key = rhs.key; return *this; }
    339     ALWAYS_INLINE bool operator==(const DepthState& rhs) const { return key == rhs.key; }
    340     ALWAYS_INLINE bool operator!=(const DepthState& rhs) const { return key != rhs.key; }
    341     ALWAYS_INLINE bool operator<(const DepthState& rhs) const { return key < rhs.key; }
    342     // clang-format on
    343 
    344     static DepthState GetNoTestsState();
    345     static DepthState GetAlwaysWriteState();
    346   };
    347 
    348   union BlendState
    349   {
    350     BitField<u64, bool, 0, 1> enable;
    351     BitField<u64, BlendFunc, 1, 4> src_blend;
    352     BitField<u64, BlendFunc, 5, 4> src_alpha_blend;
    353     BitField<u64, BlendFunc, 9, 4> dst_blend;
    354     BitField<u64, BlendFunc, 13, 4> dst_alpha_blend;
    355     BitField<u64, BlendOp, 17, 3> blend_op;
    356     BitField<u64, BlendOp, 20, 3> alpha_blend_op;
    357     BitField<u64, bool, 24, 1> write_r;
    358     BitField<u64, bool, 25, 1> write_g;
    359     BitField<u64, bool, 26, 1> write_b;
    360     BitField<u64, bool, 27, 1> write_a;
    361     BitField<u64, u8, 24, 4> write_mask;
    362     BitField<u64, u32, 32, 32> constant;
    363 
    364     BitField<u64, u16, 1, 16> blend_factors;
    365     BitField<u64, u8, 17, 6> blend_ops;
    366 
    367     u64 key;
    368 
    369     // clang-format off
    370     ALWAYS_INLINE BlendState() = default;
    371     ALWAYS_INLINE BlendState(const BlendState& rhs) : key(rhs.key) {}
    372     ALWAYS_INLINE BlendState& operator=(const BlendState& rhs) { key = rhs.key; return *this; }
    373     ALWAYS_INLINE bool operator==(const BlendState& rhs) const { return key == rhs.key; }
    374     ALWAYS_INLINE bool operator!=(const BlendState& rhs) const { return key != rhs.key; }
    375     ALWAYS_INLINE bool operator<(const BlendState& rhs) const { return key < rhs.key; }
    376     // clang-format on
    377 
    378     // clang-format off
    379     ALWAYS_INLINE float GetConstantRed() const { return static_cast<float>(constant.GetValue() & 0xFF) / 255.0f; }
    380     ALWAYS_INLINE float GetConstantGreen() const { return static_cast<float>((constant.GetValue() >> 8) & 0xFF) / 255.0f; }
    381     ALWAYS_INLINE float GetConstantBlue() const { return static_cast<float>((constant.GetValue() >> 16) & 0xFF) / 255.0f; }
    382     ALWAYS_INLINE float GetConstantAlpha() const { return static_cast<float>((constant.GetValue() >> 24) & 0xFF) / 255.0f; }
    383     // clang-format on
    384     ALWAYS_INLINE std::array<float, 4> GetConstantFloatColor() const
    385     {
    386       return std::array<float, 4>{GetConstantRed(), GetConstantGreen(), GetConstantBlue(), GetConstantAlpha()};
    387     }
    388 
    389     static BlendState GetNoBlendingState();
    390     static BlendState GetAlphaBlendingState();
    391   };
    392 
    393   struct GraphicsConfig
    394   {
    395     Layout layout;
    396 
    397     Primitive primitive;
    398     InputLayout input_layout;
    399 
    400     RasterizationState rasterization;
    401     DepthState depth;
    402     BlendState blend;
    403 
    404     GPUShader* vertex_shader;
    405     GPUShader* geometry_shader;
    406     GPUShader* fragment_shader;
    407 
    408     GPUTexture::Format color_formats[4];
    409     GPUTexture::Format depth_format;
    410     u8 samples;
    411     bool per_sample_shading;
    412     RenderPassFlag render_pass_flags;
    413 
    414     void SetTargetFormats(GPUTexture::Format color_format,
    415                           GPUTexture::Format depth_format_ = GPUTexture::Format::Unknown);
    416     u32 GetRenderTargetCount() const;
    417   };
    418 
    419   GPUPipeline();
    420   virtual ~GPUPipeline();
    421 
    422   virtual void SetDebugName(std::string_view name) = 0;
    423 };
    424 
    425 class GPUTextureBuffer
    426 {
    427 public:
    428   enum class Format
    429   {
    430     R16UI,
    431 
    432     MaxCount
    433   };
    434 
    435   GPUTextureBuffer(Format format, u32 size_in_elements);
    436   virtual ~GPUTextureBuffer();
    437 
    438   static u32 GetElementSize(Format format);
    439 
    440   ALWAYS_INLINE Format GetFormat() const { return m_format; }
    441   ALWAYS_INLINE u32 GetSizeInElements() const { return m_size_in_elements; }
    442   ALWAYS_INLINE u32 GetSizeInBytes() const { return m_size_in_elements * GetElementSize(m_format); }
    443   ALWAYS_INLINE u32 GetCurrentPosition() const { return m_current_position; }
    444 
    445   virtual void* Map(u32 required_elements) = 0;
    446   virtual void Unmap(u32 used_elements) = 0;
    447 
    448   virtual void SetDebugName(std::string_view name) = 0;
    449 
    450 protected:
    451   Format m_format;
    452   u32 m_size_in_elements;
    453   u32 m_current_position = 0;
    454 };
    455 
    456 class GPUDevice
    457 {
    458 public:
    459   friend GPUTexture;
    460 
    461   // TODO: drop virtuals
    462   // TODO: gpu crash handling on present
    463   using DrawIndex = u16;
    464 
    465   enum FeatureMask : u32
    466   {
    467     FEATURE_MASK_DUAL_SOURCE_BLEND = (1 << 0),
    468     FEATURE_MASK_FEEDBACK_LOOPS = (1 << 1),
    469     FEATURE_MASK_FRAMEBUFFER_FETCH = (1 << 2),
    470     FEATURE_MASK_TEXTURE_BUFFERS = (1 << 3),
    471     FEATURE_MASK_GEOMETRY_SHADERS = (1 << 4),
    472     FEATURE_MASK_TEXTURE_COPY_TO_SELF = (1 << 5),
    473     FEATURE_MASK_MEMORY_IMPORT = (1 << 6),
    474     FEATURE_MASK_RASTER_ORDER_VIEWS = (1 << 7),
    475   };
    476 
    477   enum class DrawBarrier : u32
    478   {
    479     None,
    480     One,
    481     Full
    482   };
    483 
    484   struct Features
    485   {
    486     bool dual_source_blend : 1;
    487     bool framebuffer_fetch : 1;
    488     bool per_sample_shading : 1;
    489     bool noperspective_interpolation : 1;
    490     bool texture_copy_to_self : 1;
    491     bool supports_texture_buffers : 1;
    492     bool texture_buffers_emulated_with_ssbo : 1;
    493     bool feedback_loops : 1;
    494     bool geometry_shaders : 1;
    495     bool partial_msaa_resolve : 1;
    496     bool memory_import : 1;
    497     bool explicit_present : 1;
    498     bool gpu_timing : 1;
    499     bool shader_cache : 1;
    500     bool pipeline_cache : 1;
    501     bool prefer_unused_textures : 1;
    502     bool raster_order_views : 1;
    503   };
    504 
    505   struct Statistics
    506   {
    507     size_t buffer_streamed;
    508     u32 num_draws;
    509     u32 num_barriers;
    510     u32 num_render_passes;
    511     u32 num_copies;
    512     u32 num_downloads;
    513     u32 num_uploads;
    514   };
    515 
    516   struct AdapterInfo
    517   {
    518     std::string name;
    519     std::vector<std::string> fullscreen_modes;
    520     u32 max_texture_size;
    521     u32 max_multisamples;
    522     bool supports_sample_shading;
    523   };
    524   using AdapterInfoList = std::vector<AdapterInfo>;
    525 
    526   struct PooledTextureDeleter
    527   {
    528     void operator()(GPUTexture* const tex);
    529   };
    530 
    531   static constexpr u32 MAX_TEXTURE_SAMPLERS = 8;
    532   static constexpr u32 MIN_TEXEL_BUFFER_ELEMENTS = 4 * 1024 * 512;
    533   static constexpr u32 MAX_RENDER_TARGETS = 4;
    534   static constexpr u32 MAX_IMAGE_RENDER_TARGETS = 2;
    535   static constexpr u32 DEFAULT_CLEAR_COLOR = 0xFF000000u;
    536   static constexpr u32 PIPELINE_CACHE_HASH_SIZE = 20;
    537   static_assert(sizeof(GPUPipeline::GraphicsConfig::color_formats) == sizeof(GPUTexture::Format) * MAX_RENDER_TARGETS);
    538 
    539   GPUDevice();
    540   virtual ~GPUDevice();
    541 
    542   /// Returns the default/preferred API for the system.
    543   static RenderAPI GetPreferredAPI();
    544 
    545   /// Returns a string representing the specified API.
    546   static const char* RenderAPIToString(RenderAPI api);
    547 
    548   /// Returns a string representing the specified language.
    549   static const char* ShaderLanguageToString(GPUShaderLanguage language);
    550 
    551   /// Returns a new device for the specified API.
    552   static std::unique_ptr<GPUDevice> CreateDeviceForAPI(RenderAPI api);
    553 
    554   /// Returns true if the render API is the same (e.g. GLES and GL).
    555   static bool IsSameRenderAPI(RenderAPI lhs, RenderAPI rhs);
    556 
    557   /// Returns a list of adapters for the given API.
    558   static AdapterInfoList GetAdapterListForAPI(RenderAPI api);
    559 
    560   /// Parses a fullscreen mode into its components (width * height @ refresh hz)
    561   static bool GetRequestedExclusiveFullscreenMode(u32* width, u32* height, float* refresh_rate);
    562 
    563   /// Converts a fullscreen mode to a string.
    564   static std::string GetFullscreenModeString(u32 width, u32 height, float refresh_rate);
    565 
    566   /// Returns the directory bad shaders are saved to.
    567   static std::string GetShaderDumpPath(std::string_view name);
    568 
    569   /// Dumps out a shader that failed compilation.
    570   static void DumpBadShader(std::string_view code, std::string_view errors);
    571 
    572   /// Converts a RGBA8 value to 4 floating-point values.
    573   static std::array<float, 4> RGBA8ToFloat(u32 rgba);
    574 
    575   /// Returns the number of texture bindings for a given pipeline layout.
    576   static constexpr u32 GetActiveTexturesForLayout(GPUPipeline::Layout layout)
    577   {
    578     constexpr std::array<u8, static_cast<u8>(GPUPipeline::Layout::MaxCount)> counts = {
    579       1,                    // SingleTextureAndUBO
    580       1,                    // SingleTextureAndPushConstants
    581       0,                    // SingleTextureBufferAndPushConstants
    582       MAX_TEXTURE_SAMPLERS, // MultiTextureAndUBO
    583       MAX_TEXTURE_SAMPLERS, // MultiTextureAndPushConstants
    584     };
    585 
    586     return counts[static_cast<u8>(layout)];
    587   }
    588 
    589   ALWAYS_INLINE const Features& GetFeatures() const { return m_features; }
    590   ALWAYS_INLINE u32 GetMaxTextureSize() const { return m_max_texture_size; }
    591   ALWAYS_INLINE u32 GetMaxMultisamples() const { return m_max_multisamples; }
    592 
    593   ALWAYS_INLINE const WindowInfo& GetWindowInfo() const { return m_window_info; }
    594   ALWAYS_INLINE s32 GetWindowWidth() const { return static_cast<s32>(m_window_info.surface_width); }
    595   ALWAYS_INLINE s32 GetWindowHeight() const { return static_cast<s32>(m_window_info.surface_height); }
    596   ALWAYS_INLINE float GetWindowScale() const { return m_window_info.surface_scale; }
    597   ALWAYS_INLINE GPUTexture::Format GetWindowFormat() const { return m_window_info.surface_format; }
    598 
    599   ALWAYS_INLINE GPUSampler* GetLinearSampler() const { return m_linear_sampler.get(); }
    600   ALWAYS_INLINE GPUSampler* GetNearestSampler() const { return m_nearest_sampler.get(); }
    601 
    602   ALWAYS_INLINE bool IsGPUTimingEnabled() const { return m_gpu_timing_enabled; }
    603 
    604   virtual RenderAPI GetRenderAPI() const = 0;
    605 
    606   bool Create(std::string_view adapter, std::string_view shader_cache_path, u32 shader_cache_version, bool debug_device,
    607               GPUVSyncMode vsync, bool allow_present_throttle, bool threaded_presentation,
    608               std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features, Error* error);
    609   void Destroy();
    610 
    611   virtual bool HasSurface() const = 0;
    612   virtual void DestroySurface() = 0;
    613   virtual bool UpdateWindow() = 0;
    614 
    615   virtual bool SupportsExclusiveFullscreen() const;
    616 
    617   /// Call when the window size changes externally to recreate any resources.
    618   virtual void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) = 0;
    619 
    620   virtual std::string GetDriverInfo() const = 0;
    621 
    622   // Executes current command buffer, waits for its completion, and destroys all pending resources.
    623   virtual void ExecuteAndWaitForGPUIdle() = 0;
    624 
    625   virtual std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
    626                                                     GPUTexture::Type type, GPUTexture::Format format,
    627                                                     const void* data = nullptr, u32 data_stride = 0) = 0;
    628   virtual std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config) = 0;
    629   virtual std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format,
    630                                                                 u32 size_in_elements) = 0;
    631 
    632   // Texture pooling.
    633   std::unique_ptr<GPUTexture> FetchTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
    634                                            GPUTexture::Type type, GPUTexture::Format format, const void* data = nullptr,
    635                                            u32 data_stride = 0);
    636   std::unique_ptr<GPUTexture, PooledTextureDeleter>
    637   FetchAutoRecycleTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples, GPUTexture::Type type,
    638                           GPUTexture::Format format, const void* data = nullptr, u32 data_stride = 0,
    639                           bool dynamic = false);
    640   void RecycleTexture(std::unique_ptr<GPUTexture> texture);
    641   void PurgeTexturePool();
    642 
    643   virtual std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height,
    644                                                                     GPUTexture::Format format) = 0;
    645   virtual std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
    646                                                                     void* memory, size_t memory_size,
    647                                                                     u32 memory_stride) = 0;
    648 
    649   virtual void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src,
    650                                  u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width, u32 height) = 0;
    651   virtual void ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level,
    652                                     GPUTexture* src, u32 src_x, u32 src_y, u32 width, u32 height) = 0;
    653   virtual void ClearRenderTarget(GPUTexture* t, u32 c);
    654   virtual void ClearDepth(GPUTexture* t, float d);
    655   virtual void InvalidateRenderTarget(GPUTexture* t);
    656 
    657   /// Shader abstraction.
    658   std::unique_ptr<GPUShader> CreateShader(GPUShaderStage stage, GPUShaderLanguage language, std::string_view source,
    659                                           Error* error = nullptr, const char* entry_point = "main");
    660   virtual std::unique_ptr<GPUPipeline> CreatePipeline(const GPUPipeline::GraphicsConfig& config,
    661                                                       Error* error = nullptr) = 0;
    662 
    663   /// Debug messaging.
    664   virtual void PushDebugGroup(const char* name) = 0;
    665   virtual void PopDebugGroup() = 0;
    666   virtual void InsertDebugMessage(const char* msg) = 0;
    667 
    668   /// Vertex/index buffer abstraction.
    669   virtual void MapVertexBuffer(u32 vertex_size, u32 vertex_count, void** map_ptr, u32* map_space,
    670                                u32* map_base_vertex) = 0;
    671   virtual void UnmapVertexBuffer(u32 vertex_size, u32 vertex_count) = 0;
    672   virtual void MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index) = 0;
    673   virtual void UnmapIndexBuffer(u32 used_size) = 0;
    674 
    675   void UploadVertexBuffer(const void* vertices, u32 vertex_size, u32 vertex_count, u32* base_vertex);
    676   void UploadIndexBuffer(const DrawIndex* indices, u32 index_count, u32* base_index);
    677 
    678   /// Uniform buffer abstraction.
    679   virtual void PushUniformBuffer(const void* data, u32 data_size) = 0;
    680   virtual void* MapUniformBuffer(u32 size) = 0;
    681   virtual void UnmapUniformBuffer(u32 size) = 0;
    682   void UploadUniformBuffer(const void* data, u32 data_size);
    683 
    684   /// Drawing setup abstraction.
    685   virtual void SetRenderTargets(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds,
    686                                 GPUPipeline::RenderPassFlag flags = GPUPipeline::NoRenderPassFlags) = 0;
    687   virtual void SetPipeline(GPUPipeline* pipeline) = 0;
    688   virtual void SetTextureSampler(u32 slot, GPUTexture* texture, GPUSampler* sampler) = 0;
    689   virtual void SetTextureBuffer(u32 slot, GPUTextureBuffer* buffer) = 0;
    690   virtual void SetViewport(const GSVector4i rc) = 0;
    691   virtual void SetScissor(const GSVector4i rc) = 0;
    692   void SetRenderTarget(GPUTexture* rt, GPUTexture* ds = nullptr,
    693                        GPUPipeline::RenderPassFlag flags = GPUPipeline::NoRenderPassFlags);
    694   void SetViewport(s32 x, s32 y, s32 width, s32 height);
    695   void SetScissor(s32 x, s32 y, s32 width, s32 height);
    696   void SetViewportAndScissor(s32 x, s32 y, s32 width, s32 height);
    697   void SetViewportAndScissor(const GSVector4i rc);
    698 
    699   // Drawing abstraction.
    700   virtual void Draw(u32 vertex_count, u32 base_vertex) = 0;
    701   virtual void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) = 0;
    702   virtual void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) = 0;
    703 
    704   /// Returns false if the window was completely occluded.
    705   virtual bool BeginPresent(bool skip_present, u32 clear_color = DEFAULT_CLEAR_COLOR) = 0;
    706   virtual void EndPresent(bool explicit_submit) = 0;
    707   virtual void SubmitPresent() = 0;
    708 
    709   /// Renders ImGui screen elements. Call before EndPresent().
    710   void RenderImGui();
    711 
    712   ALWAYS_INLINE GPUVSyncMode GetVSyncMode() const { return m_vsync_mode; }
    713   ALWAYS_INLINE bool IsVSyncModeBlocking() const { return (m_vsync_mode == GPUVSyncMode::FIFO); }
    714   virtual void SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle) = 0;
    715 
    716   ALWAYS_INLINE bool IsDebugDevice() const { return m_debug_device; }
    717   ALWAYS_INLINE size_t GetVRAMUsage() const { return s_total_vram_usage; }
    718 
    719   bool UpdateImGuiFontTexture();
    720   bool UsesLowerLeftOrigin() const;
    721   static GSVector4i FlipToLowerLeft(GSVector4i rc, s32 target_height);
    722   bool ResizeTexture(std::unique_ptr<GPUTexture>* tex, u32 new_width, u32 new_height, GPUTexture::Type type,
    723                      GPUTexture::Format format, bool preserve = true);
    724   bool ShouldSkipPresentingFrame();
    725   void ThrottlePresentation();
    726 
    727   virtual bool SupportsTextureFormat(GPUTexture::Format format) const = 0;
    728 
    729   /// Enables/disables GPU frame timing.
    730   virtual bool SetGPUTimingEnabled(bool enabled);
    731 
    732   /// Returns the amount of GPU time utilized since the last time this method was called.
    733   virtual float GetAndResetAccumulatedGPUTime();
    734 
    735   ALWAYS_INLINE static Statistics& GetStatistics() { return s_stats; }
    736   static void ResetStatistics();
    737 
    738 protected:
    739   virtual bool CreateDevice(std::string_view adapter, bool threaded_presentation,
    740                             std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features,
    741                             Error* error) = 0;
    742   virtual void DestroyDevice() = 0;
    743 
    744   std::string GetShaderCacheBaseName(std::string_view type) const;
    745   virtual bool OpenPipelineCache(const std::string& filename);
    746   virtual bool ReadPipelineCache(std::optional<DynamicHeapArray<u8>> data);
    747   virtual bool GetPipelineCacheData(DynamicHeapArray<u8>* data);
    748 
    749   virtual std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
    750                                                             Error* error) = 0;
    751   virtual std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
    752                                                             std::string_view source, const char* entry_point,
    753                                                             DynamicHeapArray<u8>* out_binary, Error* error) = 0;
    754 
    755   bool AcquireWindow(bool recreate_window);
    756 
    757   void TrimTexturePool();
    758 
    759   bool CompileGLSLShaderToVulkanSpv(GPUShaderStage stage, GPUShaderLanguage source_language, std::string_view source,
    760                                     const char* entry_point, bool optimization, bool nonsemantic_debug_info,
    761                                     DynamicHeapArray<u8>* out_binary, Error* error);
    762   bool TranslateVulkanSpvToLanguage(const std::span<const u8> spirv, GPUShaderStage stage,
    763                                     GPUShaderLanguage target_language, u32 target_version, std::string* output,
    764                                     Error* error);
    765   std::unique_ptr<GPUShader> TranspileAndCreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage source_language,
    766                                                                 std::string_view source, const char* entry_point,
    767                                                                 GPUShaderLanguage target_language, u32 target_version,
    768                                                                 DynamicHeapArray<u8>* out_binary, Error* error);
    769 
    770   Features m_features = {};
    771   u32 m_max_texture_size = 0;
    772   u32 m_max_multisamples = 0;
    773 
    774   WindowInfo m_window_info;
    775   u64 m_last_frame_displayed_time = 0;
    776 
    777   GPUShaderCache m_shader_cache;
    778 
    779   std::unique_ptr<GPUSampler> m_nearest_sampler;
    780   std::unique_ptr<GPUSampler> m_linear_sampler;
    781 
    782 private:
    783   static constexpr u32 MAX_TEXTURE_POOL_SIZE = 125;
    784   static constexpr u32 MAX_TARGET_POOL_SIZE = 50;
    785   static constexpr u32 POOL_PURGE_DELAY = 300;
    786 
    787   struct TexturePoolKey
    788   {
    789     u16 width;
    790     u16 height;
    791     u8 layers;
    792     u8 levels;
    793     u8 samples;
    794     GPUTexture::Type type;
    795     GPUTexture::Format format;
    796     u8 pad;
    797 
    798     ALWAYS_INLINE bool operator==(const TexturePoolKey& rhs) const
    799     {
    800       return std::memcmp(this, &rhs, sizeof(TexturePoolKey)) == 0;
    801     }
    802     ALWAYS_INLINE bool operator!=(const TexturePoolKey& rhs) const
    803     {
    804       return std::memcmp(this, &rhs, sizeof(TexturePoolKey)) != 0;
    805     }
    806   };
    807   struct TexturePoolEntry
    808   {
    809     std::unique_ptr<GPUTexture> texture;
    810     u32 use_counter;
    811     TexturePoolKey key;
    812   };
    813 
    814   using TexturePool = std::deque<TexturePoolEntry>;
    815 
    816 #ifdef __APPLE__
    817   // We have to define these in the base class, because they're in Objective C++.
    818   static std::unique_ptr<GPUDevice> WrapNewMetalDevice();
    819   static AdapterInfoList WrapGetMetalAdapterList();
    820 #endif
    821 
    822   void OpenShaderCache(std::string_view base_path, u32 version);
    823   void CloseShaderCache();
    824   bool CreateResources(Error* error);
    825   void DestroyResources();
    826 
    827   static bool IsTexturePoolType(GPUTexture::Type type);
    828 
    829   static size_t s_total_vram_usage;
    830 
    831   std::unique_ptr<GPUPipeline> m_imgui_pipeline;
    832   std::unique_ptr<GPUTexture> m_imgui_font_texture;
    833 
    834   TexturePool m_texture_pool;
    835   TexturePool m_target_pool;
    836   size_t m_pool_vram_usage = 0;
    837   u32 m_texture_pool_counter = 0;
    838 
    839 protected:
    840   static Statistics s_stats;
    841 
    842   GPUVSyncMode m_vsync_mode = GPUVSyncMode::Disabled;
    843   bool m_allow_present_throttle = false;
    844   bool m_gpu_timing_enabled = false;
    845   bool m_debug_device = false;
    846 };
    847 
    848 extern std::unique_ptr<GPUDevice> g_gpu_device;
    849 
    850 ALWAYS_INLINE void GPUDevice::PooledTextureDeleter::operator()(GPUTexture* const tex)
    851 {
    852   g_gpu_device->RecycleTexture(std::unique_ptr<GPUTexture>(tex));
    853 }
    854 
    855 namespace Host {
    856 /// Called when the core is creating a render device.
    857 /// This could also be fullscreen transition.
    858 std::optional<WindowInfo> AcquireRenderWindow(bool recreate_window);
    859 
    860 /// Called when the core is finished with a render window.
    861 void ReleaseRenderWindow();
    862 
    863 /// Returns true if the hosting application is currently fullscreen.
    864 bool IsFullscreen();
    865 
    866 /// Alters fullscreen state of hosting application.
    867 void SetFullscreen(bool enabled);
    868 } // namespace Host
    869 
    870 // Macros for debug messages.
    871 #ifdef _DEBUG
    872 struct GLAutoPop
    873 {
    874   GLAutoPop(int dummy) {}
    875   ~GLAutoPop() { g_gpu_device->PopDebugGroup(); }
    876 };
    877 
    878 #define GL_SCOPE(name) GLAutoPop gl_auto_pop((g_gpu_device->PushDebugGroup(name), 0))
    879 #define GL_PUSH(name) g_gpu_device->PushDebugGroup(name)
    880 #define GL_POP() g_gpu_device->PopDebugGroup()
    881 #define GL_INS(msg) g_gpu_device->InsertDebugMessage(msg)
    882 #define GL_OBJECT_NAME(obj, name) (obj)->SetDebugName(name)
    883 
    884 #define GL_SCOPE_FMT(...)                                                                                              \
    885   GLAutoPop gl_auto_pop((g_gpu_device->PushDebugGroup(SmallString::from_format(__VA_ARGS__)), 0))
    886 #define GL_PUSH_FMT(...) g_gpu_device->PushDebugGroup(SmallString::from_format(__VA_ARGS__))
    887 #define GL_INS_FMT(...) g_gpu_device->InsertDebugMessage(SmallString::from_format(__VA_ARGS__))
    888 #define GL_OBJECT_NAME_FMT(obj, ...) (obj)->SetDebugName(SmallString::from_format(__VA_ARGS__))
    889 #else
    890 #define GL_SCOPE(name) (void)0
    891 #define GL_PUSH(name) (void)0
    892 #define GL_POP() (void)0
    893 #define GL_INS(msg) (void)0
    894 #define GL_OBJECT_NAME(obj, name) (void)0
    895 
    896 #define GL_SCOPE_FMT(...) (void)0
    897 #define GL_PUSH_FMT(...) (void)0
    898 #define GL_INS_FMT(...) (void)0
    899 #define GL_OBJECT_NAME_FMT(obj, ...) (void)0
    900 #endif