diff --git a/renderdoc/driver/d3d12/d3d12_command_list.h b/renderdoc/driver/d3d12/d3d12_command_list.h index 7f5e53ed4..3f26f618f 100644 --- a/renderdoc/driver/d3d12/d3d12_command_list.h +++ b/renderdoc/driver/d3d12/d3d12_command_list.h @@ -176,6 +176,14 @@ private: D3D12ResourceRecord *m_ListRecord; D3D12ResourceRecord *m_CreationRecord; + // for ray dispatching we need to patch the tables on the GPU which requires pushing & popping + // compute pipeline and root signature, so we track it here during capture + D3D12RenderState m_CaptureComputeState; + + // the ray dispatches which have happened on this list, this keeps a reference on the buffers until + // the list is reset, and each time the list is submitted the queue takes an additional reference + rdcarray m_RayDispatches; + CaptureState &m_State; WrappedID3D12DebugCommandList m_WrappedDebug; @@ -226,6 +234,8 @@ public: ID3D12Resource *pArgumentBuffer, UINT64 ArgumentBufferOffset, uint32_t argumentsReplayed); + void AddRayDispatches(rdcarray &dispatches); + void SetAMDMarkerInterface(IAmdExtD3DCommandListMarker *marker) { m_AMDMarkers = marker; } void SetCommandData(D3D12CommandData *cmd) { m_Cmd = cmd; } void SetInitParams(REFIID riid, UINT nodeMask, D3D12_COMMAND_LIST_TYPE type) diff --git a/renderdoc/driver/d3d12/d3d12_command_list4_wrap.cpp b/renderdoc/driver/d3d12/d3d12_command_list4_wrap.cpp index de3bf1238..854908c23 100644 --- a/renderdoc/driver/d3d12/d3d12_command_list4_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_command_list4_wrap.cpp @@ -929,6 +929,9 @@ bool WrappedID3D12GraphicsCommandList::Serialise_BuildRaytracingAccelerationStru RDCERR("TLAS Buffer isn't patched"); return false; } + + // Switch back to previous state + bakedCmdInfo.state.ApplyState(m_pDevice, (ID3D12GraphicsCommandListX *)pCommandList); } dxrCmd->BuildRaytracingAccelerationStructure(&AccStructDesc, NumPostbuildInfoDescs, @@ -1202,6 +1205,8 @@ void WrappedID3D12GraphicsCommandList::SetPipelineState1(_In_ ID3D12StateObject m_ListRecord->AddChunk(scope.Get(m_ListRecord->cmdInfo->alloc)); m_ListRecord->MarkResourceFrameReferenced(GetResID(pStateObject), eFrameRef_Read); + + m_CaptureComputeState.stateobj = GetResID(pStateObject); } } @@ -1271,7 +1276,20 @@ bool WrappedID3D12GraphicsCommandList::Serialise_DispatchRays(SerialiserType &se void WrappedID3D12GraphicsCommandList::DispatchRays(_In_ const D3D12_DISPATCH_RAYS_DESC *pDesc) { - SERIALISE_TIME_CALL(m_pList4->DispatchRays(pDesc)); + // this call will copy the specified buffers containing shader records and patch them. We get a + // reference to the lookup buffer used as well as a reference to the scratch buffer containing the + // patched shader records. + PatchedRayDispatch patchedDispatch = + GetResourceManager()->GetRaytracingResourceAndUtilHandler()->PatchRayDispatch(m_pList4, *pDesc); + + // restore state that would have been mutated by the patching process + m_pList4->SetComputeRootSignature(Unwrap(GetResourceManager()->GetCurrentAs( + m_CaptureComputeState.compute.rootsig))); + m_pList4->SetPipelineState1( + Unwrap(GetResourceManager()->GetCurrentAs(m_CaptureComputeState.stateobj))); + m_CaptureComputeState.ApplyComputeRootElementsUnwrapped(m_pList); + + SERIALISE_TIME_CALL(m_pList4->DispatchRays(&patchedDispatch.desc)); if(IsCaptureMode(m_State)) { @@ -1296,6 +1314,10 @@ void WrappedID3D12GraphicsCommandList::DispatchRays(_In_ const D3D12_DISPATCH_RA if(pDesc->HitGroupTable.SizeInBytes > 0) m_ListRecord->MarkResourceFrameReferenced( WrappedID3D12Resource::GetResIDFromAddr(pDesc->HitGroupTable.StartAddress), eFrameRef_Read); + + // during capture track the ray dispatches so the memory can be freed dynamically. On replay we + // free all the memory at the end of each replay + m_RayDispatches.push_back(patchedDispatch.resources); } } diff --git a/renderdoc/driver/d3d12/d3d12_command_list_wrap.cpp b/renderdoc/driver/d3d12/d3d12_command_list_wrap.cpp index b8b60e79f..1ddb7b285 100644 --- a/renderdoc/driver/d3d12/d3d12_command_list_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_command_list_wrap.cpp @@ -386,6 +386,18 @@ HRESULT WrappedID3D12GraphicsCommandList::ResetInternal(ID3D12CommandAllocator * m_ListRecord->DeleteChunks(); m_ListRecord->ContainsExecuteIndirect = false; + // release the 'persistent' reference on all these buffers immediately. If this list was never + // submitted, this immediately frees the buffer. If it was submitted those submissions will be + // holding references until their fences are appropriately signalled. + for(PatchedRayDispatch::Resources &r : m_RayDispatches) + { + r.lookupBuffer->Release(); + r.patchScratchBuffer->Release(); + } + m_RayDispatches.clear(); + + m_CaptureComputeState.m_ResourceManager = GetResourceManager(); + // free any baked commands. if(m_ListRecord->bakedCommands) m_ListRecord->bakedCommands->Delete(GetResourceManager()); @@ -1651,6 +1663,7 @@ void WrappedID3D12GraphicsCommandList::SetComputeRootSignature(ID3D12RootSignatu // store this so we can look up how many descriptors a given slot references, etc m_CurCompRootSig = GetWrapped(pRootSignature); + m_CaptureComputeState.compute.rootsig = GetResID(pRootSignature); } } @@ -1722,6 +1735,14 @@ void WrappedID3D12GraphicsCommandList::SetComputeRootDescriptorTable( m_ListRecord->MarkResourceFrameReferenced(GetWrapped(BaseDescriptor)->GetHeapResourceId(), eFrameRef_Read); + { + m_CaptureComputeState.compute.sigelems.resize_for_index(RootParameterIndex); + m_CaptureComputeState.compute.sigelems[RootParameterIndex] = + D3D12RenderState::SignatureElement(eRootTable, + GetWrapped(BaseDescriptor)->GetHeapResourceId(), + (UINT64)GetWrapped(BaseDescriptor)->GetHeapIndex()); + } + rdcarray &ranges = GetWrapped(m_CurCompRootSig)->sig.Parameters[RootParameterIndex].ranges; @@ -1826,6 +1847,12 @@ void WrappedID3D12GraphicsCommandList::SetComputeRoot32BitConstant(UINT RootPara Serialise_SetComputeRoot32BitConstant(ser, RootParameterIndex, SrcData, DestOffsetIn32BitValues); m_ListRecord->AddChunk(scope.Get(m_ListRecord->cmdInfo->alloc)); + + { + m_CaptureComputeState.compute.sigelems.resize_for_index(RootParameterIndex); + m_CaptureComputeState.compute.sigelems[RootParameterIndex].SetConstant( + DestOffsetIn32BitValues, SrcData); + } } } @@ -1909,6 +1936,12 @@ void WrappedID3D12GraphicsCommandList::SetComputeRoot32BitConstants(UINT RootPar DestOffsetIn32BitValues); m_ListRecord->AddChunk(scope.Get(m_ListRecord->cmdInfo->alloc)); + + { + m_CaptureComputeState.compute.sigelems.resize_for_index(RootParameterIndex); + m_CaptureComputeState.compute.sigelems[RootParameterIndex].SetConstants( + Num32BitValuesToSet, pValidSrcData, DestOffsetIn32BitValues); + } } } @@ -1988,6 +2021,12 @@ void WrappedID3D12GraphicsCommandList::SetComputeRootConstantBufferView( m_ListRecord->AddChunk(scope.Get(m_ListRecord->cmdInfo->alloc)); m_ListRecord->MarkResourceFrameReferenced(id, eFrameRef_Read); + + { + m_CaptureComputeState.compute.sigelems.resize_for_index(RootParameterIndex); + m_CaptureComputeState.compute.sigelems[RootParameterIndex] = + D3D12RenderState::SignatureElement(eRootCBV, id, offs); + } } } @@ -2067,6 +2106,12 @@ void WrappedID3D12GraphicsCommandList::SetComputeRootShaderResourceView( m_ListRecord->AddChunk(scope.Get(m_ListRecord->cmdInfo->alloc)); m_ListRecord->MarkResourceFrameReferenced(id, eFrameRef_Read); + + { + m_CaptureComputeState.compute.sigelems.resize_for_index(RootParameterIndex); + m_CaptureComputeState.compute.sigelems[RootParameterIndex] = + D3D12RenderState::SignatureElement(eRootSRV, id, offs); + } } } @@ -2146,6 +2191,12 @@ void WrappedID3D12GraphicsCommandList::SetComputeRootUnorderedAccessView( m_ListRecord->AddChunk(scope.Get(m_ListRecord->cmdInfo->alloc)); m_ListRecord->MarkResourceFrameReferenced(id, eFrameRef_Read); + + { + m_CaptureComputeState.compute.sigelems.resize_for_index(RootParameterIndex); + m_CaptureComputeState.compute.sigelems[RootParameterIndex] = + D3D12RenderState::SignatureElement(eRootUAV, id, offs); + } } } diff --git a/renderdoc/driver/d3d12/d3d12_command_queue.h b/renderdoc/driver/d3d12/d3d12_command_queue.h index 0b50803fc..97c53b216 100644 --- a/renderdoc/driver/d3d12/d3d12_command_queue.h +++ b/renderdoc/driver/d3d12/d3d12_command_queue.h @@ -171,6 +171,11 @@ class WrappedID3D12CommandQueue : public ID3D12CommandQueue, CaptureState &m_State; + // tracking ray dispatches that are pending during capture, to free them once the execution is finished + ID3D12Fence *m_RayFence = NULL; + UINT64 m_RayFenceValue = 1; + rdcarray m_RayDispatchesPending; + bool m_MarkedActive = false; WrappedID3D12DebugCommandQueue m_WrappedDebug; @@ -226,6 +231,8 @@ public: return m_SparseBindResources.find(id) != m_SparseBindResources.end(); } + void CheckAndFreeRayDispatches(); + RDResult ReplayLog(CaptureState readType, uint32_t startEventID, uint32_t endEventID, bool partial); void SetFrameReader(StreamReader *reader) { m_FrameReader = reader; } D3D12CommandData *GetCommandData() { return &m_Cmd; } diff --git a/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp b/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp index 7e969a92b..955e3f72e 100644 --- a/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp +++ b/renderdoc/driver/d3d12/d3d12_command_queue_wrap.cpp @@ -753,6 +753,8 @@ void WrappedID3D12CommandQueue::ExecuteCommandListsInternal(UINT NumCommandLists if(IsCaptureMode(m_State)) { + rdcarray rayDispatches; + if(!InFrameCaptureBoundary) m_pDevice->GetCapTransitionLock().ReadLock(); @@ -772,6 +774,8 @@ void WrappedID3D12CommandQueue::ExecuteCommandListsInternal(UINT NumCommandLists m_pDevice->ApplyBarriers(record->bakedCommands->cmdInfo->barriers); + wrapped->AddRayDispatches(rayDispatches); + // need to lock the whole section of code, not just the check on // m_State, as we also need to make sure we don't check the state, // start marking dirty resources then while we're doing so the @@ -882,6 +886,27 @@ void WrappedID3D12CommandQueue::ExecuteCommandListsInternal(UINT NumCommandLists record->cmdInfo->dirtied.clear(); } + if(!rayDispatches.empty()) + { + // if we don't have a fence for this queue tracking, create it now + if(!m_RayFence) + { + // create this unwrapped so that it doesn't get recorded into captures + m_pDevice->GetReal()->CreateFence(0, D3D12_FENCE_FLAG_NONE, __uuidof(ID3D12Fence), + (void **)&m_RayFence); + m_RayFence->SetName(L"Queue Ray Fence"); + } + + for(PatchedRayDispatch::Resources &ray : rayDispatches) + ray.fenceValue = m_RayFenceValue; + + m_RayDispatchesPending.append(rayDispatches); + + HRESULT hr = m_pReal->Signal(m_RayFence, m_RayFenceValue++); + m_pDevice->CheckHRESULT(hr); + RDCASSERTEQUAL(hr, S_OK); + } + if(capframe) { rdcarray maps = m_pDevice->GetMaps(); diff --git a/renderdoc/driver/d3d12/d3d12_commands.cpp b/renderdoc/driver/d3d12/d3d12_commands.cpp index 8a932b5e9..5fa3b68ec 100644 --- a/renderdoc/driver/d3d12/d3d12_commands.cpp +++ b/renderdoc/driver/d3d12/d3d12_commands.cpp @@ -525,6 +525,8 @@ WrappedID3D12CommandQueue::~WrappedID3D12CommandQueue() { SAFE_DELETE(m_FrameReader); + SAFE_RELEASE(m_RayFence); + if(m_CreationRecord) m_CreationRecord->Delete(m_pDevice->GetResourceManager()); @@ -629,6 +631,25 @@ HRESULT STDMETHODCALLTYPE WrappedID3D12CommandQueue::QueryInterface(REFIID riid, return RefCounter12::QueryInterface("ID3D12CommandQueue", riid, ppvObject); } +void WrappedID3D12CommandQueue::CheckAndFreeRayDispatches() +{ + UINT64 signalled = 0; + if(m_RayFence) + signalled = m_RayFence->GetCompletedValue(); + + for(PatchedRayDispatch::Resources &ray : m_RayDispatchesPending) + { + if(signalled >= ray.fenceValue) + { + SAFE_RELEASE(ray.patchScratchBuffer); + SAFE_RELEASE(ray.lookupBuffer); + } + } + + m_RayDispatchesPending.removeIf( + [](const PatchedRayDispatch::Resources &ray) { return ray.lookupBuffer == NULL; }); +} + void WrappedID3D12CommandQueue::ClearAfterCapture() { // delete cmd buffers now - had to keep them alive until after serialiser flush. @@ -1407,6 +1428,17 @@ WriteSerialiser &WrappedID3D12GraphicsCommandList::GetThreadSerialiser() return m_pDevice->GetThreadSerialiser(); } +void WrappedID3D12GraphicsCommandList::AddRayDispatches(rdcarray &dispatches) +{ + dispatches.reserve(dispatches.size() + m_RayDispatches.size()); + for(const PatchedRayDispatch::Resources &r : m_RayDispatches) + { + dispatches.push_back(r); + r.lookupBuffer->AddRef(); + r.patchScratchBuffer->AddRef(); + } +} + rdcstr WrappedID3D12GraphicsCommandList::GetChunkName(uint32_t idx) { if((SystemChunk)idx < SystemChunk::FirstDriverChunk) diff --git a/renderdoc/driver/d3d12/d3d12_manager.cpp b/renderdoc/driver/d3d12/d3d12_manager.cpp index 8d264ed1d..9f0287c0e 100644 --- a/renderdoc/driver/d3d12/d3d12_manager.cpp +++ b/renderdoc/driver/d3d12/d3d12_manager.cpp @@ -779,6 +779,12 @@ void D3D12RaytracingResourceAndUtilHandler::ResizeSerialisationBuffer(UINT64 siz } } +PatchedRayDispatch D3D12RaytracingResourceAndUtilHandler::PatchRayDispatch( + ID3D12GraphicsCommandList4 *unwrappedCmd, const D3D12_DISPATCH_RAYS_DESC &desc) +{ + return {}; +} + void D3D12RaytracingResourceAndUtilHandler::InitReplayBlasPatchingResources() { // Root Signature diff --git a/renderdoc/driver/d3d12/d3d12_manager.h b/renderdoc/driver/d3d12/d3d12_manager.h index 5f3736501..9e3052537 100644 --- a/renderdoc/driver/d3d12/d3d12_manager.h +++ b/renderdoc/driver/d3d12/d3d12_manager.h @@ -1045,6 +1045,29 @@ struct D3D12AccStructPatchInfo ID3D12PipelineState *m_pipeline; }; +struct PatchedRayDispatch +{ + struct Resources + { + // the lookup buffer + D3D12GpuBuffer *lookupBuffer; + // the scratch buffer used for patching's fence. + D3D12GpuBuffer *patchScratchBuffer; + + // for convenience, when these resources are referenced in a queue they get a fence value to + // indicate when they're safe to release. This values are unset when returned from patching or + // referenced in the list and is set in each queue's copy of the references. + UINT64 fenceValue = 0; + }; + + Resources resources; + + // the patched dispatch descriptor + D3D12_DISPATCH_RAYS_DESC desc = {}; +}; + +struct D3D12ShaderExportDatabase; + class D3D12RaytracingResourceAndUtilHandler { public: @@ -1074,6 +1097,9 @@ public: void RegisterExportDatabase(D3D12ShaderExportDatabase *db); void UnregisterExportDatabase(D3D12ShaderExportDatabase *db); + PatchedRayDispatch PatchRayDispatch(ID3D12GraphicsCommandList4 *unwrappedCmd, + const D3D12_DISPATCH_RAYS_DESC &desc); + void ResizeSerialisationBuffer(UINT64 size); // buffer in UAV state for emitting AS queries to, CPU accessible/mappable diff --git a/renderdoc/driver/d3d12/d3d12_state.cpp b/renderdoc/driver/d3d12/d3d12_state.cpp index 618b1e6de..d6911f638 100644 --- a/renderdoc/driver/d3d12/d3d12_state.cpp +++ b/renderdoc/driver/d3d12/d3d12_state.cpp @@ -347,7 +347,7 @@ void D3D12RenderState::ApplyState(WrappedID3D12Device *dev, ID3D12GraphicsComman } } -void D3D12RenderState::ApplyDescriptorHeaps(ID3D12GraphicsCommandListX *cmd) const +void D3D12RenderState::ApplyDescriptorHeaps(ID3D12GraphicsCommandList *cmd) const { rdcarray descHeaps; descHeaps.resize(heaps.size()); @@ -359,7 +359,7 @@ void D3D12RenderState::ApplyDescriptorHeaps(ID3D12GraphicsCommandListX *cmd) con cmd->SetDescriptorHeaps((UINT)descHeaps.size(), &descHeaps[0]); } -void D3D12RenderState::ApplyComputeRootElements(ID3D12GraphicsCommandListX *cmd) const +void D3D12RenderState::ApplyComputeRootElements(ID3D12GraphicsCommandList *cmd) const { for(size_t i = 0; i < compute.sigelems.size(); i++) { @@ -367,7 +367,7 @@ void D3D12RenderState::ApplyComputeRootElements(ID3D12GraphicsCommandListX *cmd) // and is probably just from stale bindings that aren't going to be used if(compute.sigelems[i].type != eRootTable || heaps.contains(compute.sigelems[i].id)) { - compute.sigelems[i].SetToCompute(GetResourceManager(), cmd, (UINT)i); + compute.sigelems[i].SetToCompute(GetResourceManager(), cmd, (UINT)i, false); } else { @@ -377,7 +377,7 @@ void D3D12RenderState::ApplyComputeRootElements(ID3D12GraphicsCommandListX *cmd) } } -void D3D12RenderState::ApplyGraphicsRootElements(ID3D12GraphicsCommandListX *cmd) const +void D3D12RenderState::ApplyGraphicsRootElements(ID3D12GraphicsCommandList *cmd) const { for(size_t i = 0; i < graphics.sigelems.size(); i++) { @@ -385,7 +385,43 @@ void D3D12RenderState::ApplyGraphicsRootElements(ID3D12GraphicsCommandListX *cmd // and is probably just from stale bindings that aren't going to be used if(graphics.sigelems[i].type != eRootTable || heaps.contains(graphics.sigelems[i].id)) { - graphics.sigelems[i].SetToGraphics(GetResourceManager(), cmd, (UINT)i); + graphics.sigelems[i].SetToGraphics(GetResourceManager(), cmd, (UINT)i, false); + } + else + { + RDCDEBUG("Skipping setting possibly stale graphics root table referring to heap %s", + ToStr(graphics.sigelems[i].id).c_str()); + } + } +} + +void D3D12RenderState::ApplyComputeRootElementsUnwrapped(ID3D12GraphicsCommandList *cmd) const +{ + for(size_t i = 0; i < compute.sigelems.size(); i++) + { + // just don't set tables that aren't in the descriptor heaps, since it's invalid and can crash + // and is probably just from stale bindings that aren't going to be used + if(compute.sigelems[i].type != eRootTable || heaps.contains(compute.sigelems[i].id)) + { + compute.sigelems[i].SetToCompute(GetResourceManager(), cmd, (UINT)i, true); + } + else + { + RDCDEBUG("Skipping setting possibly stale compute root table referring to heap %s", + ToStr(compute.sigelems[i].id).c_str()); + } + } +} + +void D3D12RenderState::ApplyGraphicsRootElementsUnwrapped(ID3D12GraphicsCommandList *cmd) const +{ + for(size_t i = 0; i < graphics.sigelems.size(); i++) + { + // just don't set tables that aren't in the descriptor heaps, since it's invalid and can crash + // and is probably just from stale bindings that aren't going to be used + if(graphics.sigelems[i].type != eRootTable || heaps.contains(graphics.sigelems[i].id)) + { + graphics.sigelems[i].SetToGraphics(GetResourceManager(), cmd, (UINT)i, true); } else { diff --git a/renderdoc/driver/d3d12/d3d12_state.h b/renderdoc/driver/d3d12/d3d12_state.h index 7029499cd..b3c6a553e 100644 --- a/renderdoc/driver/d3d12/d3d12_state.h +++ b/renderdoc/driver/d3d12/d3d12_state.h @@ -47,9 +47,11 @@ struct D3D12RenderState D3D12RenderState &operator=(const D3D12RenderState &o) = default; void ApplyState(WrappedID3D12Device *dev, ID3D12GraphicsCommandListX *list) const; - void ApplyDescriptorHeaps(ID3D12GraphicsCommandListX *list) const; - void ApplyComputeRootElements(ID3D12GraphicsCommandListX *cmd) const; - void ApplyGraphicsRootElements(ID3D12GraphicsCommandListX *cmd) const; + void ApplyDescriptorHeaps(ID3D12GraphicsCommandList *list) const; + void ApplyComputeRootElements(ID3D12GraphicsCommandList *cmd) const; + void ApplyGraphicsRootElements(ID3D12GraphicsCommandList *cmd) const; + void ApplyComputeRootElementsUnwrapped(ID3D12GraphicsCommandList *cmd) const; + void ApplyGraphicsRootElementsUnwrapped(ID3D12GraphicsCommandList *cmd) const; rdcarray views; rdcarray scissors; @@ -93,7 +95,8 @@ struct D3D12RenderState memcpy(&constants[offs], vals, numVals * sizeof(UINT)); } - void SetToGraphics(D3D12ResourceManager *rm, ID3D12GraphicsCommandList *cmd, UINT slot) const + void SetToGraphics(D3D12ResourceManager *rm, ID3D12GraphicsCommandList *cmd, UINT slot, + bool unwrapped) const { if(type == eRootConst) { @@ -104,7 +107,10 @@ struct D3D12RenderState D3D12_GPU_DESCRIPTOR_HANDLE handle = rm->GetCurrentAs(id)->GetGPUDescriptorHandleForHeapStart(); handle.ptr += sizeof(D3D12Descriptor) * offset; - cmd->SetGraphicsRootDescriptorTable(slot, handle); + if(unwrapped) + cmd->SetGraphicsRootDescriptorTable(slot, Unwrap(handle)); + else + cmd->SetGraphicsRootDescriptorTable(slot, handle); } else if(type == eRootCBV) { @@ -123,7 +129,8 @@ struct D3D12RenderState } } - void SetToCompute(D3D12ResourceManager *rm, ID3D12GraphicsCommandList *cmd, UINT slot) const + void SetToCompute(D3D12ResourceManager *rm, ID3D12GraphicsCommandList *cmd, UINT slot, + bool unwrapped) const { if(type == eRootConst) { @@ -134,7 +141,10 @@ struct D3D12RenderState D3D12_GPU_DESCRIPTOR_HANDLE handle = rm->GetCurrentAs(id)->GetGPUDescriptorHandleForHeapStart(); handle.ptr += sizeof(D3D12Descriptor) * offset; - cmd->SetComputeRootDescriptorTable(slot, handle); + if(unwrapped) + cmd->SetComputeRootDescriptorTable(slot, Unwrap(handle)); + else + cmd->SetComputeRootDescriptorTable(slot, handle); } else if(type == eRootCBV) {