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