Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/util/d3d12_device.h
4214 views
1
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.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 D3D12SwapChain;
41
42
class D3D12Device final : public GPUDevice
43
{
44
public:
45
friend D3D12Texture;
46
friend D3D12DownloadTexture;
47
48
template<typename T>
49
using ComPtr = Microsoft::WRL::ComPtr<T>;
50
51
enum : u32
52
{
53
NUM_COMMAND_LISTS = 3,
54
55
/// Start/End timestamp queries.
56
NUM_TIMESTAMP_QUERIES_PER_CMDLIST = 2,
57
};
58
59
public:
60
D3D12Device();
61
~D3D12Device() override;
62
63
std::string GetDriverInfo() const override;
64
65
void FlushCommands() override;
66
void WaitForGPUIdle() override;
67
68
std::unique_ptr<GPUSwapChain> CreateSwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode,
69
bool allow_present_throttle,
70
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
71
std::optional<bool> exclusive_fullscreen_control,
72
Error* error) override;
73
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
74
GPUTexture::Type type, GPUTexture::Format format, GPUTexture::Flags flags,
75
const void* data = nullptr, u32 data_stride = 0,
76
Error* error = nullptr) override;
77
std::unique_ptr<GPUSampler> CreateSampler(const GPUSampler::Config& config, Error* error = nullptr) override;
78
std::unique_ptr<GPUTextureBuffer> CreateTextureBuffer(GPUTextureBuffer::Format format, u32 size_in_elements,
79
Error* error = nullptr) override;
80
81
std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
82
Error* error = nullptr) override;
83
std::unique_ptr<GPUDownloadTexture> CreateDownloadTexture(u32 width, u32 height, GPUTexture::Format format,
84
void* memory, size_t memory_size, u32 memory_stride,
85
Error* error = nullptr) override;
86
87
bool SupportsTextureFormat(GPUTexture::Format format) const override;
88
void CopyTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src,
89
u32 src_x, u32 src_y, u32 src_layer, u32 src_level, u32 width, u32 height) override;
90
void ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u32 dst_layer, u32 dst_level, GPUTexture* src,
91
u32 src_x, u32 src_y, u32 width, u32 height) override;
92
void ClearRenderTarget(GPUTexture* t, u32 c) override;
93
void ClearDepth(GPUTexture* t, float d) override;
94
void InvalidateRenderTarget(GPUTexture* t) override;
95
96
std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, std::span<const u8> data,
97
Error* error) override;
98
std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, GPUShaderLanguage language,
99
std::string_view source, const char* entry_point,
100
DynamicHeapArray<u8>* out_binary, Error* error) override;
101
std::unique_ptr<GPUPipeline> CreatePipeline(const GPUPipeline::GraphicsConfig& config, Error* error) override;
102
std::unique_ptr<GPUPipeline> CreatePipeline(const GPUPipeline::ComputeConfig& config, Error* error) override;
103
104
#ifdef ENABLE_GPU_OBJECT_NAMES
105
void PushDebugGroup(const char* name) override;
106
void PopDebugGroup() override;
107
void InsertDebugMessage(const char* msg) override;
108
#endif
109
110
void MapVertexBuffer(u32 vertex_size, u32 vertex_count, void** map_ptr, u32* map_space,
111
u32* map_base_vertex) override;
112
void UnmapVertexBuffer(u32 vertex_size, u32 vertex_count) override;
113
void MapIndexBuffer(u32 index_count, DrawIndex** map_ptr, u32* map_space, u32* map_base_index) override;
114
void UnmapIndexBuffer(u32 used_index_count) override;
115
void PushUniformBuffer(const void* data, u32 data_size) override;
116
void* MapUniformBuffer(u32 size) override;
117
void UnmapUniformBuffer(u32 size) override;
118
void SetRenderTargets(GPUTexture* const* rts, u32 num_rts, GPUTexture* ds,
119
GPUPipeline::RenderPassFlag flags = GPUPipeline::NoRenderPassFlags) override;
120
void SetPipeline(GPUPipeline* pipeline) override;
121
void SetTextureSampler(u32 slot, GPUTexture* texture, GPUSampler* sampler) override;
122
void SetTextureBuffer(u32 slot, GPUTextureBuffer* buffer) override;
123
void SetViewport(const GSVector4i rc) override;
124
void SetScissor(const GSVector4i rc) override;
125
void Draw(u32 vertex_count, u32 base_vertex) override;
126
void DrawIndexed(u32 index_count, u32 base_index, u32 base_vertex) override;
127
void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) override;
128
void Dispatch(u32 threads_x, u32 threads_y, u32 threads_z, u32 group_size_x, u32 group_size_y,
129
u32 group_size_z) override;
130
131
bool SetGPUTimingEnabled(bool enabled) override;
132
float GetAndResetAccumulatedGPUTime() override;
133
134
PresentResult BeginPresent(GPUSwapChain* swap_chain, u32 clear_color) override;
135
void EndPresent(GPUSwapChain* swap_chain, bool explicit_present, u64 present_time) override;
136
void SubmitPresent(GPUSwapChain* swap_chain) override;
137
138
// Global state accessors
139
ALWAYS_INLINE static D3D12Device& GetInstance() { return *static_cast<D3D12Device*>(g_gpu_device.get()); }
140
ALWAYS_INLINE IDXGIAdapter1* GetAdapter() const { return m_adapter.Get(); }
141
ALWAYS_INLINE ID3D12Device1* GetDevice() const { return m_device.Get(); }
142
ALWAYS_INLINE ID3D12CommandQueue* GetCommandQueue() const { return m_command_queue.Get(); }
143
ALWAYS_INLINE IDXGIFactory5* GetDXGIFactory() { return m_dxgi_factory.Get(); }
144
ALWAYS_INLINE D3D12MA::Allocator* GetAllocator() const { return m_allocator.Get(); }
145
146
void WaitForAllFences();
147
148
// Descriptor manager access.
149
D3D12DescriptorHeapManager& GetDescriptorHeapManager() { return m_descriptor_heap_manager; }
150
D3D12DescriptorHeapManager& GetRTVHeapManager() { return m_rtv_heap_manager; }
151
D3D12DescriptorHeapManager& GetDSVHeapManager() { return m_dsv_heap_manager; }
152
D3D12DescriptorHeapManager& GetSamplerHeapManager() { return m_sampler_heap_manager; }
153
const D3D12DescriptorHandle& GetNullSRVDescriptor() const { return m_null_srv_descriptor; }
154
155
// These command buffers are allocated per-frame. They are valid until the command buffer
156
// is submitted, after that you should call these functions again.
157
ALWAYS_INLINE ID3D12GraphicsCommandList4* GetCommandList() const
158
{
159
return m_command_lists[m_current_command_list].command_lists[1].Get();
160
}
161
ALWAYS_INLINE D3D12StreamBuffer& GetTextureUploadBuffer() { return m_texture_upload_buffer; }
162
ID3D12GraphicsCommandList4* GetInitCommandList();
163
164
// Root signature access.
165
ComPtr<ID3D12RootSignature> CreateRootSignature(const D3D12_ROOT_SIGNATURE_DESC* desc, Error* error);
166
167
/// Fence value for current command list.
168
u64 GetCurrentFenceValue() const { return m_current_fence_value; }
169
170
/// Last "completed" fence.
171
u64 GetCompletedFenceValue() const { return m_completed_fence_value; }
172
173
// Schedule a d3d12 resource for destruction later on. This will occur when the command buffer
174
// is next re-used, and the GPU has finished working with the specified resource.
175
void DeferObjectDestruction(ComPtr<ID3D12Object> resource);
176
void DeferResourceDestruction(ComPtr<D3D12MA::Allocation> allocation, ComPtr<ID3D12Resource> resource);
177
void DeferDescriptorDestruction(D3D12DescriptorHeapManager& heap, D3D12DescriptorHandle* descriptor);
178
179
// Wait for a fence to be completed.
180
// Also invokes callbacks for completion.
181
void WaitForFence(u64 fence_counter);
182
183
// Ends a render pass if we're currently in one.
184
// When Bind() is next called, the pass will be restarted.
185
void BeginRenderPass();
186
void EndRenderPass();
187
bool InRenderPass();
188
189
/// Ends any render pass, executes the command buffer, and invalidates cached state.
190
void SubmitCommandList(bool wait_for_completion);
191
void SubmitCommandList(bool wait_for_completion, const std::string_view reason);
192
void SubmitCommandListAndRestartRenderPass(const std::string_view reason);
193
194
void UnbindPipeline(D3D12Pipeline* pl);
195
void UnbindTexture(D3D12Texture* tex);
196
void UnbindTextureBuffer(D3D12TextureBuffer* buf);
197
198
void RenderTextureMipmap(D3D12Texture* texture, u32 dst_level, u32 dst_width, u32 dst_height, u32 src_level,
199
u32 src_width, u32 src_height);
200
201
protected:
202
bool CreateDeviceAndMainSwapChain(std::string_view adapter, CreateFlags create_flags, const WindowInfo& wi,
203
GPUVSyncMode vsync_mode, bool allow_present_throttle,
204
const ExclusiveFullscreenMode* exclusive_fullscreen_mode,
205
std::optional<bool> exclusive_fullscreen_control, Error* error) override;
206
void DestroyDevice() override;
207
208
bool ReadPipelineCache(DynamicHeapArray<u8> data, Error* error) override;
209
bool CreatePipelineCache(const std::string& path, Error* error) override;
210
bool GetPipelineCacheData(DynamicHeapArray<u8>* data, Error* error) override;
211
212
private:
213
enum DIRTY_FLAG : u32
214
{
215
DIRTY_FLAG_INITIAL = (1 << 0),
216
DIRTY_FLAG_PIPELINE_LAYOUT = (1 << 1),
217
DIRTY_FLAG_CONSTANT_BUFFER = (1 << 2),
218
DIRTY_FLAG_TEXTURES = (1 << 3),
219
DIRTY_FLAG_SAMPLERS = (1 << 3),
220
DIRTY_FLAG_RT_UAVS = (1 << 4),
221
222
LAYOUT_DEPENDENT_DIRTY_STATE = DIRTY_FLAG_PIPELINE_LAYOUT | DIRTY_FLAG_CONSTANT_BUFFER | DIRTY_FLAG_TEXTURES |
223
DIRTY_FLAG_SAMPLERS | DIRTY_FLAG_RT_UAVS,
224
ALL_DIRTY_STATE = DIRTY_FLAG_INITIAL | LAYOUT_DEPENDENT_DIRTY_STATE,
225
};
226
227
struct CommandList
228
{
229
// [0] - Init (upload) command buffer, [1] - draw command buffer
230
std::array<ComPtr<ID3D12CommandAllocator>, 2> command_allocators;
231
std::array<ComPtr<ID3D12GraphicsCommandList4>, 2> command_lists;
232
D3D12DescriptorAllocator descriptor_allocator;
233
D3D12GroupedSamplerAllocator<MAX_TEXTURE_SAMPLERS> sampler_allocator;
234
u64 fence_counter = 0;
235
bool init_list_used = false;
236
bool needs_fence_wait = false;
237
bool has_timestamp_query = false;
238
};
239
240
struct PIPELINE_CACHE_HEADER
241
{
242
u64 adapter_luid;
243
u32 render_api_version;
244
u32 unused;
245
};
246
static_assert(sizeof(PIPELINE_CACHE_HEADER) == 16);
247
248
void GetPipelineCacheHeader(PIPELINE_CACHE_HEADER* hdr);
249
void SetFeatures(D3D_FEATURE_LEVEL feature_level, CreateFlags create_flags);
250
251
bool CreateCommandLists(Error* error);
252
void DestroyCommandLists();
253
bool CreateRootSignatures(Error* error);
254
void DestroyRootSignatures();
255
bool CreateBuffers(Error* error);
256
void DestroyBuffers();
257
bool CreateDescriptorHeaps(Error* error);
258
void DestroyDescriptorHeaps();
259
bool CreateTimestampQuery();
260
void DestroyTimestampQuery();
261
void DestroyDeferredObjects(u64 fence_value);
262
263
void RenderBlankFrame(D3D12SwapChain* swap_chain);
264
void MoveToNextCommandList();
265
266
bool CreateSRVDescriptor(ID3D12Resource* resource, u32 layers, u32 levels, u32 samples, DXGI_FORMAT format,
267
D3D12DescriptorHandle* dh, Error* error);
268
bool CreateRTVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format, D3D12DescriptorHandle* dh,
269
Error* error);
270
bool CreateDSVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format, D3D12DescriptorHandle* dh,
271
Error* error);
272
bool CreateUAVDescriptor(ID3D12Resource* resource, u32 samples, DXGI_FORMAT format, D3D12DescriptorHandle* dh,
273
Error* error);
274
275
bool IsRenderTargetBound(const GPUTexture* tex) const;
276
277
/// Set dirty flags on everything to force re-bind at next draw time.
278
void InvalidateCachedState();
279
void SetVertexBuffer(ID3D12GraphicsCommandList4* cmdlist);
280
void SetViewport(ID3D12GraphicsCommandList4* cmdlist);
281
void SetScissor(ID3D12GraphicsCommandList4* cmdlist);
282
283
/// Applies any changed state.
284
ID3D12RootSignature* GetCurrentRootSignature() const;
285
void SetInitialPipelineState();
286
void PreDrawCheck();
287
void PreDispatchCheck();
288
289
bool IsUsingROVRootSignature() const;
290
bool IsUsingComputeRootSignature() const;
291
void UpdateRootSignature();
292
template<GPUPipeline::Layout layout>
293
bool UpdateParametersForLayout(u32 dirty);
294
bool UpdateRootParameters(u32 dirty);
295
296
ComPtr<IDXGIAdapter1> m_adapter;
297
ComPtr<ID3D12Device1> m_device;
298
ComPtr<ID3D12CommandQueue> m_command_queue;
299
ComPtr<D3D12MA::Allocator> m_allocator;
300
301
ComPtr<ID3D12Fence> m_fence;
302
HANDLE m_fence_event = {};
303
u64 m_current_fence_value = 0;
304
u64 m_completed_fence_value = 0;
305
306
std::array<CommandList, NUM_COMMAND_LISTS> m_command_lists;
307
u32 m_current_command_list = NUM_COMMAND_LISTS - 1;
308
bool m_device_was_lost = false;
309
310
ComPtr<IDXGIFactory5> m_dxgi_factory;
311
312
D3D12DescriptorHeapManager m_descriptor_heap_manager;
313
D3D12DescriptorHeapManager m_rtv_heap_manager;
314
D3D12DescriptorHeapManager m_dsv_heap_manager;
315
D3D12DescriptorHeapManager m_sampler_heap_manager;
316
D3D12DescriptorHandle m_null_srv_descriptor;
317
D3D12DescriptorHandle m_null_uav_descriptor;
318
319
ComPtr<ID3D12QueryHeap> m_timestamp_query_heap;
320
ComPtr<ID3D12Resource> m_timestamp_query_buffer;
321
ComPtr<D3D12MA::Allocation> m_timestamp_query_allocation;
322
double m_timestamp_frequency = 0.0;
323
float m_accumulated_gpu_time = 0.0f;
324
325
std::deque<std::pair<u64, std::pair<D3D12MA::Allocation*, ID3D12Object*>>> m_cleanup_resources;
326
std::deque<std::pair<u64, std::pair<D3D12DescriptorHeapManager*, D3D12DescriptorHandle>>> m_cleanup_descriptors;
327
328
DimensionalArray<ComPtr<ID3D12RootSignature>, static_cast<u8>(GPUPipeline::Layout::MaxCount), 2> m_root_signatures =
329
{};
330
331
D3D12StreamBuffer m_vertex_buffer;
332
D3D12StreamBuffer m_index_buffer;
333
D3D12StreamBuffer m_uniform_buffer;
334
D3D12StreamBuffer m_texture_upload_buffer;
335
336
u32 m_uniform_buffer_position = 0;
337
bool m_in_render_pass = false;
338
339
ComPtr<ID3D12PipelineLibrary> m_pipeline_library;
340
341
// Which bindings/state has to be updated before the next draw.
342
u32 m_dirty_flags = ALL_DIRTY_STATE;
343
344
D3D12Pipeline* m_current_pipeline = nullptr;
345
D3D12_PRIMITIVE_TOPOLOGY m_current_topology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED;
346
u8 m_num_current_render_targets = 0;
347
GPUPipeline::RenderPassFlag m_current_render_pass_flags = GPUPipeline::NoRenderPassFlags;
348
std::array<D3D12Texture*, MAX_RENDER_TARGETS> m_current_render_targets = {};
349
D3D12Texture* m_current_depth_target = nullptr;
350
u32 m_current_vertex_stride = 0;
351
u32 m_current_blend_constant = 0;
352
GPUPipeline::Layout m_current_pipeline_layout = GPUPipeline::Layout::SingleTextureAndPushConstants;
353
354
std::array<D3D12Texture*, MAX_TEXTURE_SAMPLERS> m_current_textures = {};
355
std::array<D3D12DescriptorHandle, MAX_TEXTURE_SAMPLERS> m_current_samplers = {};
356
D3D12TextureBuffer* m_current_texture_buffer = nullptr;
357
GSVector4i m_current_viewport = GSVector4i::cxpr(0, 0, 1, 1);
358
GSVector4i m_current_scissor = {};
359
360
D3D12SwapChain* m_current_swap_chain = nullptr;
361
362
std::array<ComPtr<ID3D12PipelineState>, static_cast<size_t>(GPUTexture::Format::MaxCount)> m_mipmap_render_pipelines =
363
{};
364
};
365
366
class D3D12SwapChain : public GPUSwapChain
367
{
368
public:
369
template<typename T>
370
using ComPtr = Microsoft::WRL::ComPtr<T>;
371
372
friend D3D12Device;
373
374
using BufferPair = std::pair<ComPtr<ID3D12Resource>, D3D12DescriptorHandle>;
375
376
D3D12SwapChain(const WindowInfo& wi, GPUVSyncMode vsync_mode, bool allow_present_throttle,
377
const GPUDevice::ExclusiveFullscreenMode* fullscreen_mode);
378
~D3D12SwapChain() override;
379
380
ALWAYS_INLINE IDXGISwapChain1* GetSwapChain() const { return m_swap_chain.Get(); }
381
ALWAYS_INLINE const BufferPair& GetCurrentBuffer() const { return m_swap_chain_buffers[m_current_swap_chain_buffer]; }
382
ALWAYS_INLINE bool IsUsingAllowTearing() const { return m_using_allow_tearing; }
383
384
void AdvanceBuffer()
385
{
386
m_current_swap_chain_buffer = ((m_current_swap_chain_buffer + 1) % static_cast<u32>(m_swap_chain_buffers.size()));
387
}
388
bool ResizeBuffers(u32 new_width, u32 new_height, float new_scale, Error* error) override;
389
bool SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle, Error* error) override;
390
bool IsExclusiveFullscreen() const override;
391
392
private:
393
static u32 GetNewBufferCount(GPUVSyncMode vsync_mode);
394
395
bool InitializeExclusiveFullscreenMode(const GPUDevice::ExclusiveFullscreenMode* mode);
396
397
bool CreateSwapChain(D3D12Device& dev, Error* error);
398
bool CreateRTV(D3D12Device& dev, Error* error);
399
400
void DestroySwapChain();
401
void DestroyRTVs();
402
403
ComPtr<IDXGISwapChain1> m_swap_chain;
404
std::vector<BufferPair> m_swap_chain_buffers;
405
u32 m_current_swap_chain_buffer = 0;
406
bool m_using_allow_tearing = false;
407
408
ComPtr<IDXGIOutput> m_fullscreen_output;
409
std::optional<DXGI_MODE_DESC> m_fullscreen_mode;
410
};
411
412