Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/util/d3d12_descriptor_heap_manager.h
4223 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 "common/hash_combine.h"
7
#include "common/types.h"
8
#include "common/windows_headers.h"
9
10
#include <bitset>
11
#include <cstring>
12
#include <d3d12.h>
13
#include <unordered_map>
14
#include <vector>
15
#include <wrl/client.h>
16
17
class Error;
18
19
// This class provides an abstraction for D3D12 descriptor heaps.
20
struct D3D12DescriptorHandle final
21
{
22
enum : u32
23
{
24
INVALID_INDEX = 0xFFFFFFFF
25
};
26
27
D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle{};
28
D3D12_GPU_DESCRIPTOR_HANDLE gpu_handle{};
29
u32 index = INVALID_INDEX;
30
31
ALWAYS_INLINE operator bool() const { return index != INVALID_INDEX; }
32
33
ALWAYS_INLINE operator D3D12_CPU_DESCRIPTOR_HANDLE() const { return cpu_handle; }
34
ALWAYS_INLINE operator D3D12_GPU_DESCRIPTOR_HANDLE() const { return gpu_handle; }
35
36
ALWAYS_INLINE bool operator==(const D3D12DescriptorHandle& rhs) const { return (index == rhs.index); }
37
ALWAYS_INLINE bool operator!=(const D3D12DescriptorHandle& rhs) const { return (index != rhs.index); }
38
ALWAYS_INLINE bool operator<(const D3D12DescriptorHandle& rhs) const { return (index < rhs.index); }
39
ALWAYS_INLINE bool operator<=(const D3D12DescriptorHandle& rhs) const { return (index <= rhs.index); }
40
ALWAYS_INLINE bool operator>(const D3D12DescriptorHandle& rhs) const { return (index > rhs.index); }
41
ALWAYS_INLINE bool operator>=(const D3D12DescriptorHandle& rhs) const { return (index >= rhs.index); }
42
43
ALWAYS_INLINE void Clear()
44
{
45
cpu_handle = {};
46
gpu_handle = {};
47
index = INVALID_INDEX;
48
}
49
};
50
51
class D3D12DescriptorHeapManager final
52
{
53
public:
54
D3D12DescriptorHeapManager();
55
~D3D12DescriptorHeapManager();
56
57
ID3D12DescriptorHeap* GetDescriptorHeap() const { return m_descriptor_heap.Get(); }
58
u32 GetDescriptorIncrementSize() const { return m_descriptor_increment_size; }
59
60
bool Create(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 num_descriptors, bool shader_visible,
61
Error* error);
62
void Destroy();
63
64
bool Allocate(D3D12DescriptorHandle* handle);
65
void Free(D3D12DescriptorHandle* handle);
66
void Free(u32 index);
67
68
private:
69
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> m_descriptor_heap;
70
u32 m_num_descriptors = 0;
71
u32 m_descriptor_increment_size = 0;
72
bool m_shader_visible = false;
73
74
D3D12_CPU_DESCRIPTOR_HANDLE m_heap_base_cpu = {};
75
D3D12_GPU_DESCRIPTOR_HANDLE m_heap_base_gpu = {};
76
77
static constexpr u32 BITSET_SIZE = 1024;
78
using BitSetType = std::bitset<BITSET_SIZE>;
79
std::vector<BitSetType> m_free_slots = {};
80
};
81
82
class D3D12DescriptorAllocator
83
{
84
public:
85
D3D12DescriptorAllocator();
86
~D3D12DescriptorAllocator();
87
88
ALWAYS_INLINE ID3D12DescriptorHeap* GetDescriptorHeap() const { return m_descriptor_heap.Get(); }
89
ALWAYS_INLINE u32 GetDescriptorIncrementSize() const { return m_descriptor_increment_size; }
90
91
bool Create(ID3D12Device* device, D3D12_DESCRIPTOR_HEAP_TYPE type, u32 num_descriptors, Error* error);
92
void Destroy();
93
94
bool Allocate(u32 num_handles, D3D12DescriptorHandle* out_base_handle);
95
void Reset();
96
97
private:
98
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> m_descriptor_heap;
99
u32 m_descriptor_increment_size = 0;
100
u32 m_num_descriptors = 0;
101
u32 m_current_offset = 0;
102
103
D3D12_CPU_DESCRIPTOR_HANDLE m_heap_base_cpu = {};
104
D3D12_GPU_DESCRIPTOR_HANDLE m_heap_base_gpu = {};
105
};
106
107
template<u32 NumSamplers>
108
class D3D12GroupedSamplerAllocator : private D3D12DescriptorAllocator
109
{
110
struct Key
111
{
112
u32 idx[NumSamplers];
113
114
ALWAYS_INLINE bool operator==(const Key& rhs) const { return (std::memcmp(idx, rhs.idx, sizeof(idx)) == 0); }
115
ALWAYS_INLINE bool operator!=(const Key& rhs) const { return (std::memcmp(idx, rhs.idx, sizeof(idx)) != 0); }
116
};
117
118
struct KeyHash
119
{
120
ALWAYS_INLINE std::size_t operator()(const Key& key) const
121
{
122
size_t seed = 0;
123
for (u32 i : key.idx)
124
hash_combine(seed, i);
125
return seed;
126
}
127
};
128
129
public:
130
D3D12GroupedSamplerAllocator();
131
~D3D12GroupedSamplerAllocator();
132
133
using D3D12DescriptorAllocator::GetDescriptorHeap;
134
using D3D12DescriptorAllocator::GetDescriptorIncrementSize;
135
136
bool Create(ID3D12Device* device, u32 num_descriptors, Error* error);
137
void Destroy();
138
139
bool LookupSingle(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle, const D3D12DescriptorHandle& cpu_handle);
140
bool LookupGroup(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle, const D3D12DescriptorHandle* cpu_handles);
141
142
// Clears cache but doesn't reset allocator.
143
void InvalidateCache();
144
145
void Reset();
146
bool ShouldReset() const;
147
148
private:
149
std::unordered_map<Key, D3D12DescriptorHandle, KeyHash> m_groups;
150
};
151
152
template<u32 NumSamplers>
153
D3D12GroupedSamplerAllocator<NumSamplers>::D3D12GroupedSamplerAllocator() = default;
154
155
template<u32 NumSamplers>
156
D3D12GroupedSamplerAllocator<NumSamplers>::~D3D12GroupedSamplerAllocator() = default;
157
158
template<u32 NumSamplers>
159
bool D3D12GroupedSamplerAllocator<NumSamplers>::Create(ID3D12Device* device, u32 num_descriptors, Error* error)
160
{
161
return D3D12DescriptorAllocator::Create(device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, num_descriptors, error);
162
}
163
164
template<u32 NumSamplers>
165
void D3D12GroupedSamplerAllocator<NumSamplers>::Destroy()
166
{
167
D3D12DescriptorAllocator::Destroy();
168
}
169
170
template<u32 NumSamplers>
171
void D3D12GroupedSamplerAllocator<NumSamplers>::Reset()
172
{
173
m_groups.clear();
174
D3D12DescriptorAllocator::Reset();
175
}
176
177
template<u32 NumSamplers>
178
void D3D12GroupedSamplerAllocator<NumSamplers>::InvalidateCache()
179
{
180
m_groups.clear();
181
}
182
183
template<u32 NumSamplers>
184
bool D3D12GroupedSamplerAllocator<NumSamplers>::LookupSingle(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle,
185
const D3D12DescriptorHandle& cpu_handle)
186
{
187
Key key;
188
key.idx[0] = cpu_handle.index;
189
for (u32 i = 1; i < NumSamplers; i++)
190
key.idx[i] = 0;
191
192
auto it = m_groups.find(key);
193
if (it != m_groups.end())
194
{
195
*gpu_handle = it->second;
196
return true;
197
}
198
199
if (!Allocate(1, gpu_handle))
200
return false;
201
202
device->CopyDescriptorsSimple(1, *gpu_handle, cpu_handle, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
203
m_groups.emplace(key, *gpu_handle);
204
return true;
205
}
206
207
template<u32 NumSamplers>
208
bool D3D12GroupedSamplerAllocator<NumSamplers>::LookupGroup(ID3D12Device* device, D3D12DescriptorHandle* gpu_handle,
209
const D3D12DescriptorHandle* cpu_handles)
210
{
211
Key key;
212
for (u32 i = 0; i < NumSamplers; i++)
213
key.idx[i] = cpu_handles[i].index;
214
215
auto it = m_groups.find(key);
216
if (it != m_groups.end())
217
{
218
*gpu_handle = it->second;
219
return true;
220
}
221
222
if (!Allocate(NumSamplers, gpu_handle))
223
return false;
224
225
D3D12_CPU_DESCRIPTOR_HANDLE dst_handle = *gpu_handle;
226
UINT dst_size = NumSamplers;
227
D3D12_CPU_DESCRIPTOR_HANDLE src_handles[NumSamplers];
228
UINT src_sizes[NumSamplers];
229
for (u32 i = 0; i < NumSamplers; i++)
230
{
231
src_handles[i] = cpu_handles[i];
232
src_sizes[i] = 1;
233
}
234
device->CopyDescriptors(1, &dst_handle, &dst_size, NumSamplers, src_handles, src_sizes,
235
D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
236
237
m_groups.emplace(key, *gpu_handle);
238
return true;
239
}
240
241
template<u32 NumSamplers>
242
bool D3D12GroupedSamplerAllocator<NumSamplers>::ShouldReset() const
243
{
244
// We only reset the sampler heap if more than half of the descriptors are used.
245
// This saves descriptor copying when there isn't a large number of sampler configs per frame.
246
return m_groups.size() >= (D3D12_MAX_SHADER_VISIBLE_SAMPLER_HEAP_SIZE / 2);
247
}
248
249