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 };