From f006e672915d6de99e907e41c6f1b380f75d7740 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 17 Nov 2016 14:24:12 +0100 Subject: [PATCH] Add quad overdraw overlay to D3D12 * Becaused D3D12 has a limitation against binding multiple descriptor heaps of the same type, we have to reserve some space in every user heap to be able to patch in our own descriptors for use. --- renderdoc/driver/d3d12/d3d12_debug.cpp | 409 ++++++++++++++++++- renderdoc/driver/d3d12/d3d12_debug.h | 8 + renderdoc/driver/d3d12/d3d12_device_wrap.cpp | 6 + 3 files changed, 417 insertions(+), 6 deletions(-) diff --git a/renderdoc/driver/d3d12/d3d12_debug.cpp b/renderdoc/driver/d3d12/d3d12_debug.cpp index 15e2ed65d..7ab4998c4 100644 --- a/renderdoc/driver/d3d12/d3d12_debug.cpp +++ b/renderdoc/driver/d3d12/d3d12_debug.cpp @@ -407,6 +407,35 @@ D3D12DebugManager::D3D12DebugManager(WrappedID3D12Device *wrapper) SAFE_RELEASE(root); + rootSig.clear(); + + // 0: CBV + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV; + param.Descriptor.ShaderRegister = 0; + param.Descriptor.Flags = D3D12_ROOT_DESCRIPTOR_FLAG_NONE; + + rootSig.push_back(param); + + srvrange.NumDescriptors = 1; + + // 1: SRV + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.DescriptorTable.NumDescriptorRanges = 1; + param.DescriptorTable.pDescriptorRanges = &srvrange; + + rootSig.push_back(param); + + root = MakeRootSig(rootSig); + + RDCASSERT(root); + + hr = m_WrappedDevice->CreateRootSignature(0, root->GetBufferPointer(), root->GetBufferSize(), + __uuidof(ID3D12RootSignature), + (void **)&m_QuadResolveRootSig); + + SAFE_RELEASE(root); + RenderDoc::Inst().SetProgress(DebugManagerInit, 0.6f); D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeDesc; @@ -421,6 +450,7 @@ D3D12DebugManager::D3D12DebugManager(WrappedID3D12Device *wrapper) ID3DBlob *TexDisplayPS = NULL; ID3DBlob *CheckerboardPS = NULL; ID3DBlob *OutlinePS = NULL; + ID3DBlob *QOResolvePS = NULL; GetShaderBlob(displayhlsl.c_str(), "RENDERDOC_DebugVS", D3DCOMPILE_WARNINGS_ARE_ERRORS, "vs_5_0", &GenericVS); @@ -432,12 +462,15 @@ D3D12DebugManager::D3D12DebugManager(WrappedID3D12Device *wrapper) "ps_5_0", &CheckerboardPS); GetShaderBlob(displayhlsl.c_str(), "RENDERDOC_OutlinePS", D3DCOMPILE_WARNINGS_ARE_ERRORS, "ps_5_0", &OutlinePS); + GetShaderBlob(displayhlsl.c_str(), "RENDERDOC_QOResolvePS", D3DCOMPILE_WARNINGS_ARE_ERRORS, + "ps_5_0", &QOResolvePS); RDCASSERT(GenericVS); RDCASSERT(FullscreenVS); RDCASSERT(TexDisplayPS); RDCASSERT(CheckerboardPS); RDCASSERT(OutlinePS); + RDCASSERT(QOResolvePS); pipeDesc.pRootSignature = m_TexDisplayRootSig; pipeDesc.VS.BytecodeLength = GenericVS->GetBufferSize(); @@ -522,6 +555,24 @@ D3D12DebugManager::D3D12DebugManager(WrappedID3D12Device *wrapper) RDCERR("Couldn't create m_OutlinePipe! 0x%08x", hr); } + GetShaderBlob(displayhlsl.c_str(), "RENDERDOC_QuadOverdrawPS", D3DCOMPILE_WARNINGS_ARE_ERRORS, + "ps_5_0", &m_QuadOverdrawWritePS); + + pipeDesc.BlendState.RenderTarget[0].BlendEnable = FALSE; + + pipeDesc.pRootSignature = m_QuadResolveRootSig; + + pipeDesc.PS.BytecodeLength = QOResolvePS->GetBufferSize(); + pipeDesc.PS.pShaderBytecode = QOResolvePS->GetBufferPointer(); + + hr = m_WrappedDevice->CreateGraphicsPipelineState(&pipeDesc, __uuidof(ID3D12PipelineState), + (void **)&m_QuadResolvePipe); + + if(FAILED(hr)) + { + RDCERR("Couldn't create m_QuadResolvePipe! 0x%08x", hr); + } + m_OverlayRenderTex = NULL; m_OverlayResourceId = ResourceId(); @@ -613,6 +664,7 @@ D3D12DebugManager::D3D12DebugManager(WrappedID3D12Device *wrapper) SAFE_RELEASE(GenericVS); SAFE_RELEASE(TexDisplayPS); SAFE_RELEASE(OutlinePS); + SAFE_RELEASE(QOResolvePS); SAFE_RELEASE(CheckerboardPS); { @@ -1029,6 +1081,10 @@ D3D12DebugManager::~D3D12DebugManager() SAFE_RELEASE(m_CheckerboardPipe); SAFE_RELEASE(m_OutlinePipe); + SAFE_RELEASE(m_QuadOverdrawWritePS); + SAFE_RELEASE(m_QuadResolveRootSig); + SAFE_RELEASE(m_QuadResolvePipe); + SAFE_RELEASE(m_PickPixelTex); SAFE_RELEASE(m_HistogramRootSig); @@ -3169,6 +3225,223 @@ bool D3D12DebugManager::GetHistogram(ResourceId texid, uint32_t sliceFace, uint3 return true; } +struct QuadOverdrawCallback : public D3D12DrawcallCallback +{ + QuadOverdrawCallback(WrappedID3D12Device *dev, const vector &events, PortableHandle uav) + : m_pDevice(dev), m_pDebug(dev->GetDebugManager()), m_Events(events), m_UAV(uav) + { + m_pDevice->GetQueue()->GetCommandData()->m_DrawcallCallback = this; + } + ~QuadOverdrawCallback() { m_pDevice->GetQueue()->GetCommandData()->m_DrawcallCallback = NULL; } + void PreDraw(uint32_t eid, ID3D12GraphicsCommandList *cmd) + { + if(std::find(m_Events.begin(), m_Events.end(), eid) == m_Events.end()) + return; + + // we customise the pipeline to disable framebuffer writes, but perform normal testing + // and substitute our quad calculation fragment shader that writes to a storage image + // that is bound in a new root signature element. + + D3D12RenderState &rs = m_pDevice->GetQueue()->GetCommandData()->m_RenderState; + m_PrevState = rs; + + // check cache first + CachedPipeline cache = m_PipelineCache[rs.pipe]; + + // if we don't get a hit, create a modified pipeline + if(cache.pipe == NULL) + { + HRESULT hr = S_OK; + + WrappedID3D12RootSignature *sig = + m_pDevice->GetResourceManager()->GetCurrentAs( + rs.graphics.rootsig); + + // need to be able to add a descriptor table with our UAV without hitting the 64 DWORD limit + RDCASSERT(sig->sig.dwordLength < 64); + + D3D12RootSignature modsig = sig->sig; + + // make sure no other UAV tables overlap. We can't remove elements entirely because then the + // root signature indices wouldn't match up as expected. + // Instead move them into an unused space. + for(size_t i = 0; i < modsig.params.size(); i++) + { + if(modsig.params[i].ShaderVisibility == D3D12_SHADER_VISIBILITY_PIXEL) + { + if(modsig.params[i].ParameterType == D3D12_ROOT_PARAMETER_TYPE_UAV) + { + modsig.params[i].Descriptor.RegisterSpace = modsig.numSpaces; + } + else if(modsig.params[i].ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE) + { + for(size_t r = 0; r < modsig.params[i].ranges.size(); r++) + { + modsig.params[i].ranges[r].RegisterSpace = modsig.numSpaces; + } + } + } + } + + D3D12_DESCRIPTOR_RANGE1 range; + range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; + range.NumDescriptors = 1; + range.BaseShaderRegister = 0; + range.RegisterSpace = 0; + range.Flags = D3D12_DESCRIPTOR_RANGE_FLAG_NONE; + range.OffsetInDescriptorsFromTableStart = 0; + + modsig.params.push_back(D3D12RootSignatureParameter()); + D3D12RootSignatureParameter ¶m = modsig.params.back(); + param.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; + param.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + param.DescriptorTable.NumDescriptorRanges = 1; + param.DescriptorTable.pDescriptorRanges = ⦥ + + cache.sigElem = uint32_t(modsig.params.size() - 1); + + std::vector params; + params.resize(modsig.params.size()); + for(size_t i = 0; i < params.size(); i++) + params[i] = modsig.params[i]; + + ID3DBlob *root = m_pDebug->MakeRootSig(params, modsig.Flags, (UINT)modsig.samplers.size(), + modsig.samplers.empty() ? NULL : &modsig.samplers[0]); + + hr = m_pDevice->CreateRootSignature(0, root->GetBufferPointer(), root->GetBufferSize(), + __uuidof(ID3D12RootSignature), (void **)&cache.sig); + RDCASSERTEQUAL(hr, S_OK); + + SAFE_RELEASE(root); + + WrappedID3D12PipelineState *origPSO = + m_pDevice->GetResourceManager()->GetCurrentAs(rs.pipe); + + RDCASSERT(origPSO->IsGraphics()); + + D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeDesc = origPSO->GetGraphicsDesc(); + + for(size_t i = 0; i < ARRAY_COUNT(pipeDesc.BlendState.RenderTarget); i++) + pipeDesc.BlendState.RenderTarget[i].RenderTargetWriteMask = 0; + + // disable depth/stencil writes + pipeDesc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO; + pipeDesc.DepthStencilState.FrontFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + pipeDesc.DepthStencilState.BackFace.StencilFunc = D3D12_COMPARISON_FUNC_ALWAYS; + pipeDesc.DepthStencilState.StencilWriteMask = 0; + + pipeDesc.PS.BytecodeLength = m_pDebug->GetOverdrawWritePS()->GetBufferSize(); + pipeDesc.PS.pShaderBytecode = m_pDebug->GetOverdrawWritePS()->GetBufferPointer(); + + pipeDesc.pRootSignature = cache.sig; + + hr = m_pDevice->CreateGraphicsPipelineState(&pipeDesc, __uuidof(ID3D12PipelineState), + (void **)&cache.pipe); + RDCASSERTEQUAL(hr, S_OK); + + m_PipelineCache[rs.pipe] = cache; + } + + // modify state for first draw call + rs.pipe = GetResID(cache.pipe); + rs.graphics.rootsig = GetResID(cache.sig); + + if(rs.graphics.sigelems.size() <= cache.sigElem) + rs.graphics.sigelems.resize(cache.sigElem + 1); + + PortableHandle uav = m_UAV; + + // if a CBV_SRV_UAV heap is already set, we need to copy our descriptor in + // if we haven't already. Otherwise we can set our own heap. + for(size_t i = 0; i < rs.heaps.size(); i++) + { + WrappedID3D12DescriptorHeap *h = + m_pDevice->GetResourceManager()->GetCurrentAs(rs.heaps[i]); + if(h->GetDesc().Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) + { + // use the last descriptor + D3D12_CPU_DESCRIPTOR_HANDLE dst = h->GetCPUDescriptorHandleForHeapStart(); + dst.ptr += (h->GetDesc().NumDescriptors - 1) * sizeof(D3D12Descriptor); + + if(m_CopiedHeaps.find(rs.heaps[i]) == m_CopiedHeaps.end()) + { + WrappedID3D12DescriptorHeap *h2 = + m_pDevice->GetResourceManager()->GetCurrentAs(m_UAV.heap); + D3D12_CPU_DESCRIPTOR_HANDLE src = h2->GetCPUDescriptorHandleForHeapStart(); + src.ptr += m_UAV.index * sizeof(D3D12Descriptor); + + // can't do a copy because the src heap is CPU write-only (shader visible). So instead, + // create directly + D3D12Descriptor *srcDesc = (D3D12Descriptor *)src.ptr; + srcDesc->Create(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_pDevice, dst); + + m_CopiedHeaps.insert(rs.heaps[i]); + } + + uav = ToPortableHandle(dst); + + break; + } + } + + if(uav.heap == m_UAV.heap) + rs.heaps.push_back(m_UAV.heap); + + rs.graphics.sigelems[cache.sigElem] = + D3D12RenderState::SignatureElement(eRootTable, uav.heap, uav.index); + + // as we're changing the root signature, we need to reapply all elements, + // so just apply all state + if(cmd) + rs.ApplyState(cmd); + } + + bool PostDraw(uint32_t eid, ID3D12GraphicsCommandList *cmd) + { + if(std::find(m_Events.begin(), m_Events.end(), eid) == m_Events.end()) + return false; + + // restore the render state and go ahead with the real draw + m_pDevice->GetQueue()->GetCommandData()->m_RenderState = m_PrevState; + + RDCASSERT(cmd); + m_pDevice->GetQueue()->GetCommandData()->m_RenderState.ApplyState(cmd); + + return true; + } + + void PostRedraw(uint32_t eid, ID3D12GraphicsCommandList *cmd) + { + // nothing to do + } + + // Dispatches don't rasterize, so do nothing + void PreDispatch(uint32_t eid, ID3D12GraphicsCommandList *cmd) {} + bool PostDispatch(uint32_t eid, ID3D12GraphicsCommandList *cmd) { return false; } + void PostRedispatch(uint32_t eid, ID3D12GraphicsCommandList *cmd) {} + bool RecordAllCmds() { return false; } + void AliasEvent(uint32_t primary, uint32_t alias) + { + // don't care + } + + WrappedID3D12Device *m_pDevice; + D3D12DebugManager *m_pDebug; + const vector &m_Events; + PortableHandle m_UAV; + + // cache modified pipelines + struct CachedPipeline + { + ID3D12RootSignature *sig; + uint32_t sigElem; + ID3D12PipelineState *pipe; + }; + map m_PipelineCache; + std::set m_CopiedHeaps; + D3D12RenderState m_PrevState; +}; + ResourceId D3D12DebugManager::RenderOverlay(ResourceId texid, FormatComponentType typeHint, TextureDisplayOverlay overlay, uint32_t eventID, const vector &passEvents) @@ -3243,21 +3516,21 @@ ResourceId D3D12DebugManager::RenderOverlay(ResourceId texid, FormatComponentTyp D3D12Descriptor *dsView = DescriptorFromPortableHandle(m_WrappedDevice->GetResourceManager(), rs.dsv); - D3D12_DEPTH_STENCIL_VIEW_DESC dsViewDesc; - RDCEraseEl(dsViewDesc); + D3D12_RESOURCE_DESC depthTexDesc = {}; + D3D12_DEPTH_STENCIL_VIEW_DESC dsViewDesc = {}; if(dsView) { ID3D12Resource *realDepth = dsView->nonsamp.resource; dsViewDesc = dsView->nonsamp.dsv; - D3D12_RESOURCE_DESC desc = realDepth->GetDesc(); - desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; - desc.Alignment = 0; + depthTexDesc = realDepth->GetDesc(); + depthTexDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; + depthTexDesc.Alignment = 0; HRESULT hr = S_OK; - hr = m_WrappedDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &desc, + hr = m_WrappedDevice->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &depthTexDesc, D3D12_RESOURCE_STATE_COPY_DEST, NULL, __uuidof(ID3D12Resource), (void **)&renderDepth); if(FAILED(hr)) @@ -3715,6 +3988,130 @@ ResourceId D3D12DebugManager::RenderOverlay(ResourceId texid, FormatComponentTyp } else if(overlay == eTexOverlay_QuadOverdrawPass || overlay == eTexOverlay_QuadOverdrawDraw) { + SCOPED_TIMER("Quad Overdraw"); + + vector events = passEvents; + + if(overlay == eTexOverlay_QuadOverdrawDraw) + events.clear(); + + events.push_back(eventID); + + if(!events.empty()) + { + if(overlay == eTexOverlay_QuadOverdrawPass) + { + list->Close(); + m_WrappedDevice->ReplayLog(0, events[0], eReplay_WithoutDraw); + list = m_WrappedDevice->GetNewList(); + } + + uint32_t width = uint32_t(resourceDesc.Width >> 1); + uint32_t height = resourceDesc.Height >> 1; + + D3D12_RESOURCE_DESC uavTexDesc = {}; + uavTexDesc.Alignment = 0; + uavTexDesc.DepthOrArraySize = 4; + uavTexDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + uavTexDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + uavTexDesc.Format = DXGI_FORMAT_R32_UINT; + uavTexDesc.Height = height; + uavTexDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + uavTexDesc.MipLevels = 1; + uavTexDesc.SampleDesc.Count = 1; + uavTexDesc.SampleDesc.Quality = 0; + uavTexDesc.Width = width; + + ID3D12Resource *overdrawTex = NULL; + HRESULT hr = m_WrappedDevice->CreateCommittedResource( + &heapProps, D3D12_HEAP_FLAG_NONE, &uavTexDesc, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, + NULL, __uuidof(ID3D12Resource), (void **)&overdrawTex); + if(FAILED(hr)) + { + RDCERR("Failed to create overdrawTex %08x", hr); + list->Close(); + list = NULL; + return m_OverlayResourceId; + } + + m_WrappedDevice->CreateShaderResourceView(overdrawTex, NULL, GetCPUHandle(OVERDRAW_SRV)); + m_WrappedDevice->CreateUnorderedAccessView(overdrawTex, NULL, NULL, GetCPUHandle(OVERDRAW_UAV)); + + UINT zeroes[4] = {0, 0, 0, 0}; + list->ClearUnorderedAccessViewUint(GetGPUHandle(OVERDRAW_UAV), GetCPUHandle(OVERDRAW_UAV), + overdrawTex, zeroes, 0, NULL); + list->Close(); + list = NULL; + +#if ENABLED(SINGLE_FLUSH_VALIDATE) + m_WrappedDevice->ExecuteLists(); + m_WrappedDevice->FlushLists(); +#endif + + m_WrappedDevice->ReplayLog(0, events[0], eReplay_WithoutDraw); + + // declare callback struct here + QuadOverdrawCallback cb(m_WrappedDevice, events, ToPortableHandle(GetCPUHandle(OVERDRAW_UAV))); + + m_WrappedDevice->ReplayLog(events.front(), events.back(), eReplay_Full); + + // resolve pass + { + list = m_WrappedDevice->GetNewList(); + + D3D12_RESOURCE_BARRIER overdrawBarriers[2] = {}; + + // make sure UAV work is done then prepare for reading in PS + overdrawBarriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; + overdrawBarriers[0].UAV.pResource = overdrawTex; + overdrawBarriers[1].Transition.pResource = overdrawTex; + overdrawBarriers[1].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + overdrawBarriers[1].Transition.StateBefore = D3D12_RESOURCE_STATE_UNORDERED_ACCESS; + overdrawBarriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + + // prepare tex resource for copying + list->ResourceBarrier(2, overdrawBarriers); + + list->OMSetRenderTargets(1, &rtv, TRUE, NULL); + + list->RSSetViewports(1, &rs.views[0]); + + D3D12_RECT scissor = {0, 0, 16384, 16384}; + list->RSSetScissorRects(1, &scissor); + + list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + list->SetPipelineState(m_QuadResolvePipe); + + list->SetGraphicsRootSignature(m_QuadResolveRootSig); + + list->SetDescriptorHeaps(1, &cbvsrvuavHeap); + + FillBuffer(m_GenericVSCbuffer, &overdrawRamp[0].x, sizeof(overdrawRamp)); + + list->SetGraphicsRootConstantBufferView(0, m_GenericVSCbuffer->GetGPUVirtualAddress()); + list->SetGraphicsRootDescriptorTable(1, GetGPUHandle(OVERDRAW_SRV)); + + list->DrawInstanced(3, 1, 0, 0); + + list->Close(); + list = NULL; + } + + m_WrappedDevice->ExecuteLists(); + m_WrappedDevice->FlushLists(); + + for(auto it = cb.m_PipelineCache.begin(); it != cb.m_PipelineCache.end(); ++it) + { + SAFE_RELEASE(it->second.pipe); + SAFE_RELEASE(it->second.sig); + } + + SAFE_RELEASE(overdrawTex); + } + + if(overlay == eTexOverlay_QuadOverdrawPass) + m_WrappedDevice->ReplayLog(0, eventID, eReplay_WithoutDraw); } else if(overlay == eTexOverlay_Depth || overlay == eTexOverlay_Stencil) { diff --git a/renderdoc/driver/d3d12/d3d12_debug.h b/renderdoc/driver/d3d12/d3d12_debug.h index 34c439a68..89ca680df 100644 --- a/renderdoc/driver/d3d12/d3d12_debug.h +++ b/renderdoc/driver/d3d12/d3d12_debug.h @@ -106,6 +106,7 @@ public: UINT NumStaticSamplers = 0, D3D12_STATIC_SAMPLER_DESC *StaticSamplers = NULL); + ID3DBlob *GetOverdrawWritePS() { return m_QuadOverdrawWritePS; } private: struct OutputWindow { @@ -146,6 +147,9 @@ private: MINMAX_TILE_UAVS = MINMAX_TILE_SRVS + 3, MINMAX_RESULT_UAVS = MINMAX_TILE_UAVS + 3, HISTOGRAM_UAV = MINMAX_RESULT_UAVS + 3, + + OVERDRAW_SRV, + OVERDRAW_UAV, }; enum RTVSlot @@ -228,6 +232,10 @@ private: ID3D12PipelineState *m_CheckerboardPipe; ID3D12PipelineState *m_OutlinePipe; + ID3DBlob *m_QuadOverdrawWritePS; + ID3D12RootSignature *m_QuadResolveRootSig; + ID3D12PipelineState *m_QuadResolvePipe; + ID3D12Resource *m_PickPixelTex; D3D12_CPU_DESCRIPTOR_HANDLE m_PickPixelRTV; diff --git a/renderdoc/driver/d3d12/d3d12_device_wrap.cpp b/renderdoc/driver/d3d12/d3d12_device_wrap.cpp index eda40497b..fdd42ad48 100644 --- a/renderdoc/driver/d3d12/d3d12_device_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_device_wrap.cpp @@ -444,6 +444,12 @@ bool WrappedID3D12Device::Serialise_CreateDescriptorHeap( if(m_State == READING) { + // inflate the heap so we can insert our own descriptors at the end + // while patching, because DX12 has a stupid limitation to not be able + // to set multiple descriptor heaps at once of the same type + if(Descriptor.Type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) + Descriptor.NumDescriptors += 16; + ID3D12DescriptorHeap *ret = NULL; HRESULT hr = m_pDevice->CreateDescriptorHeap(&Descriptor, guid, (void **)&ret);