2 // Copyright (c) Microsoft. All rights reserved.
3 // This code is licensed under the MIT License (MIT).
4 // THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
5 // ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
6 // IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
7 // PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
9 // Developed by Minigraph
11 // Author: James Stanard
15 #include "DynamicDescriptorHeap.h"
16 #include "CommandContext.h"
17 #include "GraphicsCore.h"
18 #include "CommandListManager.h"
19 #include "RootSignature.h"
21 using namespace Graphics
;
24 // DynamicDescriptorHeap Implementation
27 std::mutex
DynamicDescriptorHeap::sm_Mutex
;
28 std::vector
<Microsoft::WRL::ComPtr
<ID3D12DescriptorHeap
>> DynamicDescriptorHeap::sm_DescriptorHeapPool
[2];
29 std::queue
<std::pair
<uint64_t, ID3D12DescriptorHeap
*>> DynamicDescriptorHeap::sm_RetiredDescriptorHeaps
[2];
30 std::queue
<ID3D12DescriptorHeap
*> DynamicDescriptorHeap::sm_AvailableDescriptorHeaps
[2];
32 ID3D12DescriptorHeap
* DynamicDescriptorHeap::RequestDescriptorHeap(D3D12_DESCRIPTOR_HEAP_TYPE HeapType
)
34 std::lock_guard
<std::mutex
> LockGuard(sm_Mutex
);
36 uint32_t idx
= HeapType
== D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER
? 1 : 0;
38 while (!sm_RetiredDescriptorHeaps
[idx
].empty() && g_CommandManager
.IsFenceComplete(sm_RetiredDescriptorHeaps
[idx
].front().first
))
40 sm_AvailableDescriptorHeaps
[idx
].push(sm_RetiredDescriptorHeaps
[idx
].front().second
);
41 sm_RetiredDescriptorHeaps
[idx
].pop();
44 if (!sm_AvailableDescriptorHeaps
[idx
].empty())
46 ID3D12DescriptorHeap
* HeapPtr
= sm_AvailableDescriptorHeaps
[idx
].front();
47 sm_AvailableDescriptorHeaps
[idx
].pop();
52 D3D12_DESCRIPTOR_HEAP_DESC HeapDesc
= {};
53 HeapDesc
.Type
= HeapType
;
54 HeapDesc
.NumDescriptors
= kNumDescriptorsPerHeap
;
55 HeapDesc
.Flags
= D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE
;
56 HeapDesc
.NodeMask
= 1;
57 Microsoft::WRL::ComPtr
<ID3D12DescriptorHeap
> HeapPtr
;
58 ASSERT_SUCCEEDED(g_Device
->CreateDescriptorHeap(&HeapDesc
, MY_IID_PPV_ARGS(&HeapPtr
)));
59 sm_DescriptorHeapPool
[idx
].emplace_back(HeapPtr
);
64 void DynamicDescriptorHeap::DiscardDescriptorHeaps( D3D12_DESCRIPTOR_HEAP_TYPE HeapType
, uint64_t FenceValue
, const std::vector
<ID3D12DescriptorHeap
*>& UsedHeaps
)
66 uint32_t idx
= HeapType
== D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER
? 1 : 0;
67 std::lock_guard
<std::mutex
> LockGuard(sm_Mutex
);
68 for (auto iter
= UsedHeaps
.begin(); iter
!= UsedHeaps
.end(); ++iter
)
69 sm_RetiredDescriptorHeaps
[idx
].push(std::make_pair(FenceValue
, *iter
));
72 void DynamicDescriptorHeap::RetireCurrentHeap( void )
74 // Don't retire unused heaps.
75 if (m_CurrentOffset
== 0)
77 ASSERT(m_CurrentHeapPtr
== nullptr);
81 ASSERT(m_CurrentHeapPtr
!= nullptr);
82 m_RetiredHeaps
.push_back(m_CurrentHeapPtr
);
83 m_CurrentHeapPtr
= nullptr;
87 void DynamicDescriptorHeap::RetireUsedHeaps( uint64_t fenceValue
)
89 DiscardDescriptorHeaps(m_DescriptorType
, fenceValue
, m_RetiredHeaps
);
90 m_RetiredHeaps
.clear();
93 DynamicDescriptorHeap::DynamicDescriptorHeap(CommandContext
& OwningContext
, D3D12_DESCRIPTOR_HEAP_TYPE HeapType
)
94 : m_OwningContext(OwningContext
), m_DescriptorType(HeapType
)
96 m_CurrentHeapPtr
= nullptr;
98 m_DescriptorSize
= Graphics::g_Device
->GetDescriptorHandleIncrementSize(HeapType
);
101 DynamicDescriptorHeap::~DynamicDescriptorHeap()
105 void DynamicDescriptorHeap::CleanupUsedHeaps( uint64_t fenceValue
)
108 RetireUsedHeaps(fenceValue
);
109 m_GraphicsHandleCache
.ClearCache();
110 m_ComputeHandleCache
.ClearCache();
113 inline ID3D12DescriptorHeap
* DynamicDescriptorHeap::GetHeapPointer()
115 if (m_CurrentHeapPtr
== nullptr)
117 ASSERT(m_CurrentOffset
== 0);
118 m_CurrentHeapPtr
= RequestDescriptorHeap(m_DescriptorType
);
119 m_FirstDescriptor
= DescriptorHandle(
120 m_CurrentHeapPtr
->GetCPUDescriptorHandleForHeapStart(),
121 m_CurrentHeapPtr
->GetGPUDescriptorHandleForHeapStart());
124 return m_CurrentHeapPtr
;
127 uint32_t DynamicDescriptorHeap::DescriptorHandleCache::ComputeStagedSize()
129 // Sum the maximum assigned offsets of stale descriptor tables to determine total needed space.
130 uint32_t NeededSpace
= 0;
131 unsigned long RootIndex
;
132 uint32_t StaleParams
= m_StaleRootParamsBitMap
;
133 while (_BitScanForward(&RootIndex
, StaleParams
))
135 StaleParams
^= (1 << RootIndex
);
137 unsigned long MaxSetHandle
;
138 ASSERT(TRUE
== _BitScanReverse(&MaxSetHandle
, m_RootDescriptorTable
[RootIndex
].AssignedHandlesBitMap
),
139 "Root entry marked as stale but has no stale descriptors");
141 NeededSpace
+= MaxSetHandle
+ 1;
146 void DynamicDescriptorHeap::DescriptorHandleCache::CopyAndBindStaleTables(
147 D3D12_DESCRIPTOR_HEAP_TYPE Type
, uint32_t DescriptorSize
,
148 DescriptorHandle DestHandleStart
, ID3D12GraphicsCommandList
* CmdList
,
149 void (STDMETHODCALLTYPE
ID3D12GraphicsCommandList::*SetFunc
)(UINT
, D3D12_GPU_DESCRIPTOR_HANDLE
))
151 uint32_t StaleParamCount
= 0;
152 uint32_t TableSize
[DescriptorHandleCache::kMaxNumDescriptorTables
];
153 uint32_t RootIndices
[DescriptorHandleCache::kMaxNumDescriptorTables
];
154 uint32_t NeededSpace
= 0;
155 unsigned long RootIndex
;
157 // Sum the maximum assigned offsets of stale descriptor tables to determine total needed space.
158 uint32_t StaleParams
= m_StaleRootParamsBitMap
;
159 while (_BitScanForward(&RootIndex
, StaleParams
))
161 RootIndices
[StaleParamCount
] = RootIndex
;
162 StaleParams
^= (1 << RootIndex
);
164 unsigned long MaxSetHandle
;
165 ASSERT(TRUE
== _BitScanReverse(&MaxSetHandle
, m_RootDescriptorTable
[RootIndex
].AssignedHandlesBitMap
),
166 "Root entry marked as stale but has no stale descriptors");
168 NeededSpace
+= MaxSetHandle
+ 1;
169 TableSize
[StaleParamCount
] = MaxSetHandle
+ 1;
174 ASSERT(StaleParamCount
<= DescriptorHandleCache::kMaxNumDescriptorTables
,
175 "We're only equipped to handle so many descriptor tables");
177 m_StaleRootParamsBitMap
= 0;
179 static const uint32_t kMaxDescriptorsPerCopy
= 16;
180 UINT NumDestDescriptorRanges
= 0;
181 D3D12_CPU_DESCRIPTOR_HANDLE pDestDescriptorRangeStarts
[kMaxDescriptorsPerCopy
];
182 UINT pDestDescriptorRangeSizes
[kMaxDescriptorsPerCopy
];
184 UINT NumSrcDescriptorRanges
= 0;
185 D3D12_CPU_DESCRIPTOR_HANDLE pSrcDescriptorRangeStarts
[kMaxDescriptorsPerCopy
];
186 UINT pSrcDescriptorRangeSizes
[kMaxDescriptorsPerCopy
];
188 for (uint32_t i
= 0; i
< StaleParamCount
; ++i
)
190 RootIndex
= RootIndices
[i
];
191 (CmdList
->*SetFunc
)(RootIndex
, DestHandleStart
.GetGpuHandle());
193 DescriptorTableCache
& RootDescTable
= m_RootDescriptorTable
[RootIndex
];
195 D3D12_CPU_DESCRIPTOR_HANDLE
* SrcHandles
= RootDescTable
.TableStart
;
196 uint64_t SetHandles
= (uint64_t)RootDescTable
.AssignedHandlesBitMap
;
197 D3D12_CPU_DESCRIPTOR_HANDLE CurDest
= DestHandleStart
.GetCpuHandle();
198 DestHandleStart
+= TableSize
[i
] * DescriptorSize
;
200 unsigned long SkipCount
;
201 while (_BitScanForward64(&SkipCount
, SetHandles
))
203 // Skip over unset descriptor handles
204 SetHandles
>>= SkipCount
;
205 SrcHandles
+= SkipCount
;
206 CurDest
.ptr
+= SkipCount
* DescriptorSize
;
208 unsigned long DescriptorCount
;
209 _BitScanForward64(&DescriptorCount
, ~SetHandles
);
210 SetHandles
>>= DescriptorCount
;
212 // If we run out of temp room, copy what we've got so far
213 if (NumSrcDescriptorRanges
+ DescriptorCount
> kMaxDescriptorsPerCopy
)
215 g_Device
->CopyDescriptors(
216 NumDestDescriptorRanges
, pDestDescriptorRangeStarts
, pDestDescriptorRangeSizes
,
217 NumSrcDescriptorRanges
, pSrcDescriptorRangeStarts
, pSrcDescriptorRangeSizes
,
220 NumSrcDescriptorRanges
= 0;
221 NumDestDescriptorRanges
= 0;
224 // Setup destination range
225 pDestDescriptorRangeStarts
[NumDestDescriptorRanges
] = CurDest
;
226 pDestDescriptorRangeSizes
[NumDestDescriptorRanges
] = DescriptorCount
;
227 ++NumDestDescriptorRanges
;
229 // Setup source ranges (one descriptor each because we don't assume they are contiguous)
230 for (uint32_t j
= 0; j
< DescriptorCount
; ++j
)
232 pSrcDescriptorRangeStarts
[NumSrcDescriptorRanges
] = SrcHandles
[j
];
233 pSrcDescriptorRangeSizes
[NumSrcDescriptorRanges
] = 1;
234 ++NumSrcDescriptorRanges
;
237 // Move the destination pointer forward by the number of descriptors we will copy
238 SrcHandles
+= DescriptorCount
;
239 CurDest
.ptr
+= DescriptorCount
* DescriptorSize
;
243 g_Device
->CopyDescriptors(
244 NumDestDescriptorRanges
, pDestDescriptorRangeStarts
, pDestDescriptorRangeSizes
,
245 NumSrcDescriptorRanges
, pSrcDescriptorRangeStarts
, pSrcDescriptorRangeSizes
,
249 void DynamicDescriptorHeap::CopyAndBindStagedTables( DescriptorHandleCache
& HandleCache
, ID3D12GraphicsCommandList
* CmdList
,
250 void (STDMETHODCALLTYPE
ID3D12GraphicsCommandList::*SetFunc
)(UINT
, D3D12_GPU_DESCRIPTOR_HANDLE
))
252 uint32_t NeededSize
= HandleCache
.ComputeStagedSize();
253 if (!HasSpace(NeededSize
))
257 NeededSize
= HandleCache
.ComputeStagedSize();
260 // This can trigger the creation of a new heap
261 m_OwningContext
.SetDescriptorHeap(m_DescriptorType
, GetHeapPointer());
262 HandleCache
.CopyAndBindStaleTables(m_DescriptorType
, m_DescriptorSize
, Allocate(NeededSize
), CmdList
, SetFunc
);
265 void DynamicDescriptorHeap::UnbindAllValid( void )
267 m_GraphicsHandleCache
.UnbindAllValid();
268 m_ComputeHandleCache
.UnbindAllValid();
271 D3D12_GPU_DESCRIPTOR_HANDLE
DynamicDescriptorHeap::UploadDirect( D3D12_CPU_DESCRIPTOR_HANDLE Handle
)
279 m_OwningContext
.SetDescriptorHeap(m_DescriptorType
, GetHeapPointer());
281 DescriptorHandle DestHandle
= m_FirstDescriptor
+ m_CurrentOffset
* m_DescriptorSize
;
282 m_CurrentOffset
+= 1;
284 g_Device
->CopyDescriptorsSimple(1, DestHandle
.GetCpuHandle(), Handle
, m_DescriptorType
);
286 return DestHandle
.GetGpuHandle();
289 void DynamicDescriptorHeap::DescriptorHandleCache::UnbindAllValid()
291 m_StaleRootParamsBitMap
= 0;
293 unsigned long TableParams
= m_RootDescriptorTablesBitMap
;
294 unsigned long RootIndex
;
295 while (_BitScanForward(&RootIndex
, TableParams
))
297 TableParams
^= (1 << RootIndex
);
298 if (m_RootDescriptorTable
[RootIndex
].AssignedHandlesBitMap
!= 0)
299 m_StaleRootParamsBitMap
|= (1 << RootIndex
);
303 void DynamicDescriptorHeap::DescriptorHandleCache::StageDescriptorHandles( UINT RootIndex
, UINT Offset
, UINT NumHandles
, const D3D12_CPU_DESCRIPTOR_HANDLE Handles
[] )
305 ASSERT(((1 << RootIndex
) & m_RootDescriptorTablesBitMap
) != 0, "Root parameter is not a CBV_SRV_UAV descriptor table");
306 ASSERT(Offset
+ NumHandles
<= m_RootDescriptorTable
[RootIndex
].TableSize
);
308 DescriptorTableCache
& TableCache
= m_RootDescriptorTable
[RootIndex
];
309 D3D12_CPU_DESCRIPTOR_HANDLE
* CopyDest
= TableCache
.TableStart
+ Offset
;
310 for (UINT i
= 0; i
< NumHandles
; ++i
)
311 CopyDest
[i
] = Handles
[i
];
312 TableCache
.AssignedHandlesBitMap
|= ((1 << NumHandles
) - 1) << Offset
;
313 m_StaleRootParamsBitMap
|= (1 << RootIndex
);
316 void DynamicDescriptorHeap::DescriptorHandleCache::ParseRootSignature( D3D12_DESCRIPTOR_HEAP_TYPE Type
, const RootSignature
& RootSig
)
318 UINT CurrentOffset
= 0;
320 ASSERT(RootSig
.m_NumParameters
<= 16, "Maybe we need to support something greater");
322 m_StaleRootParamsBitMap
= 0;
323 m_RootDescriptorTablesBitMap
= (Type
== D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER
?
324 RootSig
.m_SamplerTableBitMap
: RootSig
.m_DescriptorTableBitMap
);
326 unsigned long TableParams
= m_RootDescriptorTablesBitMap
;
327 unsigned long RootIndex
;
328 while (_BitScanForward(&RootIndex
, TableParams
))
330 TableParams
^= (1 << RootIndex
);
332 UINT TableSize
= RootSig
.m_DescriptorTableSize
[RootIndex
];
333 ASSERT(TableSize
> 0);
335 DescriptorTableCache
& RootDescriptorTable
= m_RootDescriptorTable
[RootIndex
];
336 RootDescriptorTable
.AssignedHandlesBitMap
= 0;
337 RootDescriptorTable
.TableStart
= m_HandleCache
+ CurrentOffset
;
338 RootDescriptorTable
.TableSize
= TableSize
;
340 CurrentOffset
+= TableSize
;
343 m_MaxCachedDescriptors
= CurrentOffset
;
345 ASSERT(m_MaxCachedDescriptors
<= kMaxNumDescriptors
, "Exceeded user-supplied maximum cache size");