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

d3d12_device.h (15099B)


      1 // SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
      2 // SPDX-License-Identifier: (GPL-3.0 OR PolyForm-Strict-1.0.0)
      3 
      4 #pragma once
      5 
      6 #include "d3d12_descriptor_heap_manager.h"
      7 #include "d3d12_stream_buffer.h"
      8 #include "gpu_device.h"
      9 #include "gpu_texture.h"
     10 
     11 #include "common/dimensional_array.h"
     12 #include "common/windows_headers.h"
     13 
     14 #include <array>
     15 #include <atomic>
     16 #include <condition_variable>
     17 #include <d3d12.h>
     18 #include <deque>
     19 #include <dxgi1_5.h>
     20 #include <functional>
     21 #include <memory>
     22 #include <mutex>
     23 #include <string>
     24 #include <thread>
     25 #include <unordered_map>
     26 #include <vector>
     27 #include <wrl/client.h>
     28 
     29 class D3D12Framebuffer;
     30 class D3D12Pipeline;
     31 class D3D12SwapChain;
     32 class D3D12Texture;
     33 class D3D12TextureBuffer;
     34 class D3D12DownloadTexture;
     35 
     36 namespace D3D12MA {
     37 class Allocator;
     38 }
     39 
     40 class D3D12Device final : public GPUDevice
     41 {
     42 public:
     43   friend D3D12Texture;
     44   friend D3D12DownloadTexture;
     45 
     46   template<typename T>
     47   using ComPtr = Microsoft::WRL::ComPtr<T>;
     48 
     49   enum : u32
     50   {
     51     NUM_COMMAND_LISTS = 3,
     52 
     53     /// Start/End timestamp queries.
     54     NUM_TIMESTAMP_QUERIES_PER_CMDLIST = 2,
     55   };
     56 
     57 public:
     58   D3D12Device();
     59   ~D3D12Device() override;
     60 
     61   RenderAPI GetRenderAPI() const override;
     62 
     63   bool HasSurface() const override;
     64 
     65   bool UpdateWindow() override;
     66   void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
     67 
     68   void DestroySurface() override;
     69 
     70   std::string GetDriverInfo() const override;
     71 
     72   void ExecuteAndWaitForGPUIdle() override;
     73 
     74   std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
     75                                             GPUTexture::Type type, GPUTexture::Format format,
     76                                             const void* data = nullptr, u32 data_stride = 0) override;
     77   std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config) override;
     78   std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements) override;
     79 
     80   std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format) override;
     81   std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
     82                                                             void* memory, size_t memory_size,
     83                                                             u32 memory_stride) override;
     84 
     85   bool SupportsTextureFormat(GPUTexture::Format format) const override;
     86   void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src,
     87                          u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width, u32 height) override;
     88   void ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src,
     89                             u32 src_x, u32 src_y, u32 width, u32 height) override;
     90   void ClearRenderTarget(GPUTexture* t, u32 c) override;
     91   void ClearDepth(GPUTexture* t, float d) override;
     92   void InvalidateRenderTarget(GPUTexture* t) override;
     93 
     94   std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
     95                                                     Error* error) override;
     96   std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
     97                                                     std::string_view source, const char* entry_point,
     98                                                     DynamicHeapArray<u8>* out_binary, Error* error) override;
     99   std::unique_ptr<GPUPipeline> CreatePipeline(const GPUPipeline::GraphicsConfig& config, Error* error) override;
    100 
    101   void PushDebugGroup(const char* name) override;
    102   void PopDebugGroup() override;
    103   void InsertDebugMessage(const char* msg) override;
    104 
    105   void MapVertexBuffer(u32 vertex_size, u32 vertex_count, void** map_ptr, u32* map_space,
    106                        u32* map_base_vertex) override;
    107   void UnmapVertexBuffer(u32 vertex_size, u32 vertex_count) override;
    108   void MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index) override;
    109   void UnmapIndexBuffer(u32 used_index_count) override;
    110   void PushUniformBuffer(const void* data, u32 data_size) override;
    111   void* MapUniformBuffer(u32 size) override;
    112   void UnmapUniformBuffer(u32 size) override;
    113   void SetRenderTargets(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds,
    114                         GPUPipeline::RenderPassFlag flags = GPUPipeline::NoRenderPassFlags) override;
    115   void SetPipeline(GPUPipeline* pipeline) override;
    116   void SetTextureSampler(u32 slot, GPUTexture* texture, GPUSampler* sampler) override;
    117   void SetTextureBuffer(u32 slot, GPUTextureBuffer* buffer) override;
    118   void SetViewport(const GSVector4i rc) override;
    119   void SetScissor(const GSVector4i rc) override;
    120   void Draw(u32 vertex_count, u32 base_vertex) override;
    121   void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
    122   void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
    123 
    124   void SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle) override;
    125 
    126   bool SetGPUTimingEnabled(bool enabled) override;
    127   float GetAndResetAccumulatedGPUTime() override;
    128 
    129   bool BeginPresent(bool skip_present, u32 clear_color) override;
    130   void EndPresent(bool explicit_present) override;
    131   void SubmitPresent() override;
    132 
    133   // Global state accessors
    134   ALWAYS_INLINE static D3D12Device& GetInstance() { return *static_cast<D3D12Device*>(g_gpu_device.get()); }
    135   ALWAYS_INLINE IDXGIAdapter1* GetAdapter() const { return m_adapter.Get(); }
    136   ALWAYS_INLINE ID3D12Device1* GetDevice() const { return m_device.Get(); }
    137   ALWAYS_INLINE ID3D12CommandQueue* GetCommandQueue() const { return m_command_queue.Get(); }
    138   ALWAYS_INLINE D3D12MA::Allocator* GetAllocator() const { return m_allocator.Get(); }
    139 
    140   void WaitForGPUIdle();
    141 
    142   // Descriptor manager access.
    143   D3D12DescriptorHeapManager& GetDescriptorHeapManager() { return m_descriptor_heap_manager; }
    144   D3D12DescriptorHeapManager& GetRTVHeapManager() { return m_rtv_heap_manager; }
    145   D3D12DescriptorHeapManager& GetDSVHeapManager() { return m_dsv_heap_manager; }
    146   D3D12DescriptorHeapManager& GetSamplerHeapManager() { return m_sampler_heap_manager; }
    147   const D3D12DescriptorHandle& GetNullSRVDescriptor() const { return m_null_srv_descriptor; }
    148 
    149   // These command buffers are allocated per-frame. They are valid until the command buffer
    150   // is submitted, after that you should call these functions again.
    151   ALWAYS_INLINE ID3D12GraphicsCommandList4* GetCommandList() const
    152   {
    153     return m_command_lists[m_current_command_list].command_lists[1].Get();
    154   }
    155   ALWAYS_INLINE D3D12StreamBuffer& GetTextureUploadBuffer() { return m_texture_upload_buffer; }
    156   ID3D12GraphicsCommandList4* GetInitCommandList();
    157 
    158   // Root signature access.
    159   ComPtr<ID3DBlob> SerializeRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc, Error* error);
    160   ComPtr<ID3D12RootSignature> CreateRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc, Error* error);
    161 
    162   /// Fence value for current command list.
    163   u64 GetCurrentFenceValue() const { return m_current_fence_value; }
    164 
    165   /// Last "completed" fence.
    166   u64 GetCompletedFenceValue() const { return m_completed_fence_value; }
    167 
    168   // Schedule a d3d12 resource for destruction later on. This will occur when the command buffer
    169   // is next re-used, and the GPU has finished working with the specified resource.
    170   void DeferObjectDestruction(ComPtr<ID3D12Object> resource);
    171   void DeferResourceDestruction(ComPtr<D3D12MA::Allocation> allocation, ComPtr<ID3D12Resource> resource);
    172   void DeferDescriptorDestruction(D3D12DescriptorHeapManager& heap, D3D12DescriptorHandle* descriptor);
    173 
    174   // Wait for a fence to be completed.
    175   // Also invokes callbacks for completion.
    176   void WaitForFence(u64 fence_counter);
    177 
    178   /// Ends any render pass, executes the command buffer, and invalidates cached state.
    179   void SubmitCommandList(bool wait_for_completion);
    180   void SubmitCommandList(bool wait_for_completion, const std::string_view reason);
    181   void SubmitCommandListAndRestartRenderPass(const std::string_view reason);
    182 
    183   void UnbindPipeline(D3D12Pipeline* pl);
    184   void UnbindTexture(D3D12Texture* tex);
    185   void UnbindTextureBuffer(D3D12TextureBuffer* buf);
    186 
    187 protected:
    188   bool CreateDevice(std::string_view adapter, bool threaded_presentation,
    189                     std::optional<bool> exclusive_fullscreen_control, FeatureMask disabled_features,
    190                     Error* error) override;
    191   void DestroyDevice() override;
    192 
    193   bool ReadPipelineCache(std::optional<DynamicHeapArray<u8>> data) override;
    194   bool GetPipelineCacheData(DynamicHeapArray<u8>* data) override;
    195 
    196 private:
    197   enum DIRTY_FLAG : u32
    198   {
    199     DIRTY_FLAG_INITIAL = (1 << 0),
    200     DIRTY_FLAG_PIPELINE_LAYOUT = (1 << 1),
    201     DIRTY_FLAG_CONSTANT_BUFFER = (1 << 2),
    202     DIRTY_FLAG_TEXTURES = (1 << 3),
    203     DIRTY_FLAG_SAMPLERS = (1 << 3),
    204     DIRTY_FLAG_RT_UAVS = (1 << 4),
    205 
    206     LAYOUT_DEPENDENT_DIRTY_STATE = DIRTY_FLAG_PIPELINE_LAYOUT | DIRTY_FLAG_CONSTANT_BUFFER | DIRTY_FLAG_TEXTURES |
    207                                    DIRTY_FLAG_SAMPLERS | DIRTY_FLAG_RT_UAVS,
    208     ALL_DIRTY_STATE = DIRTY_FLAG_INITIAL | (LAYOUT_DEPENDENT_DIRTY_STATE & ~DIRTY_FLAG_RT_UAVS),
    209   };
    210 
    211   struct CommandList
    212   {
    213     // [0] - Init (upload) command buffer, [1] - draw command buffer
    214     std::array<ComPtr<ID3D12CommandAllocator>, 2> command_allocators;
    215     std::array<ComPtr<ID3D12GraphicsCommandList4>, 2> command_lists;
    216     D3D12DescriptorAllocator descriptor_allocator;
    217     D3D12GroupedSamplerAllocator<MAX_TEXTURE_SAMPLERS> sampler_allocator;
    218     u64 fence_counter = 0;
    219     bool init_list_used = false;
    220     bool needs_fence_wait = false;
    221     bool has_timestamp_query = false;
    222   };
    223 
    224   using SamplerMap = std::unordered_map<u64, D3D12DescriptorHandle>;
    225 
    226   void SetFeatures(FeatureMask disabled_features);
    227 
    228   u32 GetSwapChainBufferCount() const;
    229   bool CreateSwapChain(Error* error);
    230   bool CreateSwapChainRTV(Error* error);
    231   void DestroySwapChainRTVs();
    232   void DestroySwapChain();
    233 
    234   bool CreateCommandLists(Error* error);
    235   void DestroyCommandLists();
    236   bool CreateRootSignatures(Error* error);
    237   void DestroyRootSignatures();
    238   bool CreateBuffers(Error* error);
    239   void DestroyBuffers();
    240   bool CreateDescriptorHeaps(Error* error);
    241   void DestroyDescriptorHeaps();
    242   bool CreateTimestampQuery();
    243   void DestroyTimestampQuery();
    244   D3D12DescriptorHandle GetSampler(const GPUSampler::Config& config);
    245   void DestroySamplers();
    246   void DestroyDeferredObjects(u64 fence_value);
    247 
    248   void RenderBlankFrame();
    249   void MoveToNextCommandList();
    250 
    251   bool CreateSRVDescriptor(ID3D12Resource* resource, u32 layers, u32 levels, u32 samples, DXGI_FORMAT format,
    252                            D3D12DescriptorHandle* dh);
    253   bool CreateRTVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
    254   bool CreateDSVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
    255   bool CreateUAVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format, D3D12DescriptorHandle* dh);
    256 
    257   bool IsRenderTargetBound(const GPUTexture* tex) const;
    258 
    259   /// Set dirty flags on everything to force re-bind at next draw time.
    260   void InvalidateCachedState();
    261   void SetVertexBuffer(ID3D12GraphicsCommandList4* cmdlist);
    262   void SetViewport(ID3D12GraphicsCommandList4* cmdlist);
    263   void SetScissor(ID3D12GraphicsCommandList4* cmdlist);
    264 
    265   /// Applies any changed state.
    266   ID3D12RootSignature* GetCurrentRootSignature() const;
    267   void SetInitialPipelineState();
    268   void PreDrawCheck();
    269 
    270   bool IsUsingROVRootSignature() const;
    271   void UpdateRootSignature();
    272   template<GPUPipeline::Layout layout>
    273   bool UpdateParametersForLayout(u32 dirty);
    274   bool UpdateRootParameters(u32 dirty);
    275 
    276   // Ends a render pass if we're currently in one.
    277   // When Bind() is next called, the pass will be restarted.
    278   void BeginRenderPass();
    279   void BeginSwapChainRenderPass(u32 clear_color);
    280   void EndRenderPass();
    281   bool InRenderPass();
    282 
    283   ComPtr<IDXGIAdapter1> m_adapter;
    284   ComPtr<ID3D12Device1> m_device;
    285   ComPtr<ID3D12CommandQueue> m_command_queue;
    286   ComPtr<D3D12MA::Allocator> m_allocator;
    287 
    288   ComPtr<ID3D12Fence> m_fence;
    289   HANDLE m_fence_event = {};
    290   u64 m_current_fence_value = 0;
    291   u64 m_completed_fence_value = 0;
    292 
    293   std::array<CommandList, NUM_COMMAND_LISTS> m_command_lists;
    294   u32 m_current_command_list = NUM_COMMAND_LISTS - 1;
    295   D3D_FEATURE_LEVEL m_feature_level = D3D_FEATURE_LEVEL_11_0;
    296 
    297   ComPtr<IDXGIFactory5> m_dxgi_factory;
    298   ComPtr<IDXGISwapChain1> m_swap_chain;
    299   std::vector<std::pair<ComPtr<ID3D12Resource>, D3D12DescriptorHandle>> m_swap_chain_buffers;
    300   u32 m_current_swap_chain_buffer = 0;
    301   bool m_allow_tearing_supported = false;
    302   bool m_using_allow_tearing = false;
    303   bool m_is_exclusive_fullscreen = false;
    304 
    305   D3D12DescriptorHeapManager m_descriptor_heap_manager;
    306   D3D12DescriptorHeapManager m_rtv_heap_manager;
    307   D3D12DescriptorHeapManager m_dsv_heap_manager;
    308   D3D12DescriptorHeapManager m_sampler_heap_manager;
    309   D3D12DescriptorHandle m_null_srv_descriptor;
    310   D3D12DescriptorHandle m_null_uav_descriptor;
    311   D3D12DescriptorHandle m_point_sampler;
    312 
    313   ComPtr<ID3D12QueryHeap> m_timestamp_query_heap;
    314   ComPtr<ID3D12Resource> m_timestamp_query_buffer;
    315   ComPtr<D3D12MA::Allocation> m_timestamp_query_allocation;
    316   double m_timestamp_frequency = 0.0;
    317   float m_accumulated_gpu_time = 0.0f;
    318 
    319   std::deque<std::pair<u64, std::pair<D3D12MA::Allocation*, ID3D12Object*>>> m_cleanup_resources;
    320   std::deque<std::pair<u64, std::pair<D3D12DescriptorHeapManager*, D3D12DescriptorHandle>>> m_cleanup_descriptors;
    321 
    322   DimensionalArray<ComPtr<ID3D12RootSignature>, static_cast<u8>(GPUPipeline::Layout::MaxCount), 2> m_root_signatures =
    323     {};
    324 
    325   D3D12StreamBuffer m_vertex_buffer;
    326   D3D12StreamBuffer m_index_buffer;
    327   D3D12StreamBuffer m_uniform_buffer;
    328   D3D12StreamBuffer m_texture_upload_buffer;
    329 
    330   u32 m_uniform_buffer_position = 0;
    331   bool m_in_render_pass = false;
    332 
    333   SamplerMap m_sampler_map;
    334   ComPtr<ID3D12PipelineLibrary> m_pipeline_library;
    335 
    336   // Which bindings/state has to be updated before the next draw.
    337   u32 m_dirty_flags = ALL_DIRTY_STATE;
    338 
    339   D3D12Pipeline* m_current_pipeline = nullptr;
    340   D3D12_PRIMITIVE_TOPOLOGY m_current_topology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
    341   u8 m_num_current_render_targets = 0;
    342   GPUPipeline::RenderPassFlag m_current_render_pass_flags = GPUPipeline::NoRenderPassFlags;
    343   std::array<D3D12Texture*, MAX_RENDER_TARGETS> m_current_render_targets = {};
    344   D3D12Texture* m_current_depth_target = nullptr;
    345   u32 m_current_vertex_stride = 0;
    346   u32 m_current_blend_constant = 0;
    347   GPUPipeline::Layout m_current_pipeline_layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
    348 
    349   std::array<D3D12Texture*, MAX_TEXTURE_SAMPLERS> m_current_textures = {};
    350   std::array<D3D12DescriptorHandle, MAX_TEXTURE_SAMPLERS> m_current_samplers = {};
    351   D3D12TextureBuffer* m_current_texture_buffer = nullptr;
    352   GSVector4i m_current_viewport = GSVector4i::cxpr(0, 0, 1, 1);
    353   GSVector4i m_current_scissor = {};
    354 };