DXIL Debugger support for direct heap access Constant Buffers

This commit is contained in:
Jake Turner
2026-04-14 13:21:48 +01:00
parent 2361be9f36
commit 35437065a1
4 changed files with 160 additions and 36 deletions
@@ -1297,6 +1297,85 @@ SRVInfo D3D12APIWrapper::FetchSRV(const BindingSlot &slot)
return srvData;
}
// Called from any thread
bool D3D12APIWrapper::IsCBVCached(const BindingSlot &slot) const
{
SCOPED_READLOCK(m_CBVsLock);
return m_CBVBuffers.find(slot) != m_CBVBuffers.end();
}
// Called from any thread
void D3D12APIWrapper::GetCBV(const BindingSlot &slot)
{
{
SCOPED_READLOCK(m_CBVsLock);
auto it = m_CBVBuffers.find(slot);
if(it != m_CBVBuffers.end())
return;
}
FetchCBV(slot);
}
// Must be called from the replay manager thread (the debugger thread)
void D3D12APIWrapper::FetchCBV(const BindingSlot &slot)
{
CHECK_DEVICE_THREAD();
const HeapDescriptorType heapType = slot.heapType;
const uint32_t descriptorIndex = slot.descriptorIndex;
const D3D12Descriptor resDescriptor =
D3D12ShaderDebug::FindDescriptor(m_Device, heapType, descriptorIndex);
bytebuf data;
D3D12ResourceManager *rm = m_Device->GetResourceManager();
ResourceId cbvId = WrappedID3D12Resource::GetResIDFromAddr(resDescriptor.GetCBV().BufferLocation);
ID3D12Resource *pResource = rm->GetResAs<ID3D12Resource>(cbvId);
if(pResource)
m_Device->GetDebugManager()->GetBufferData(pResource, 0, 0, data);
{
SCOPED_WRITELOCK(m_CBVsLock);
auto bufferIt = m_CBVBuffers.insert(std::make_pair(slot, data));
RDCASSERT(bufferIt.second);
}
}
// Called from any thread
// Resource must be cached
ShaderValue D3D12APIWrapper::CBVLoad(const BindingSlot &slot, uint32_t regIndex) const
{
ShaderValue result;
result.u32v[0] = 0;
result.u32v[1] = 0;
result.u32v[2] = 0;
result.u32v[3] = 0;
SCOPED_READLOCK(m_CBVsLock);
auto it = m_CBVBuffers.find(slot);
if(it == m_CBVBuffers.end())
{
RDCERR("CBV Load slot %u space %u desc Index %u no cached data", slot.shaderRegister,
slot.registerSpace, slot.descriptorIndex);
return result;
}
const bytebuf &cbufferData = it->second;
const uint32_t bufferSize = (uint32_t)cbufferData.size();
const uint32_t maxIndex = AlignUp16(bufferSize) / 16;
RDCASSERTMSG("Out of bounds cbuffer load", regIndex < maxIndex, regIndex, maxIndex);
if(regIndex < maxIndex)
{
const uint32_t dataOffset = regIndex * 16;
const uint32_t byteWidth = 4;
const byte *base = cbufferData.data() + dataOffset;
const uint32_t *data = (const uint32_t *)base;
const uint32_t numComps = RDCMIN(4U, (bufferSize - dataOffset) / byteWidth);
for(uint32_t c = 0; c < numComps; c++)
result.u32v[c] = data[c];
}
return result;
}
// Called from any thread
// Resource must be cached
ShaderValue D3D12APIWrapper::TypedUAVLoad(const BindingSlot &slot, const DXILDebug::ViewFmt &fmt,
@@ -43,6 +43,7 @@ public:
void FetchConstantBufferData(const D3D12RenderState::RootSignature &rootsig);
ShaderValue CBVLoad(const BindingSlot &slot, uint32_t dataOffset) const override;
ShaderValue TypedUAVLoad(const BindingSlot &slot, const DXILDebug::ViewFmt &fmt,
uint64_t dataOffset) const override;
ShaderValue TypedSRVLoad(const BindingSlot &slot, const DXILDebug::ViewFmt &fmt,
@@ -51,6 +52,7 @@ public:
const ShaderValue &value) override;
bool TypedSRVStore(const BindingSlot &slot, const DXILDebug::ViewFmt &fmt, uint64_t dataOffset,
const ShaderValue &value) override;
void GetCBV(const BindingSlot &slot) override;
UAVInfo GetUAV(const BindingSlot &slot) override;
SRVInfo GetSRV(const BindingSlot &slot) override;
@@ -75,6 +77,7 @@ public:
ShaderDirectAccess GetShaderDirectAccess(DescriptorType type,
const DXDebug::BindingSlot &slot) override;
bool IsCBVCached(const DXDebug::BindingSlot &slot) const override;
bool IsSRVCached(const DXDebug::BindingSlot &slot) const override;
bool IsUAVCached(const DXDebug::BindingSlot &slot) const override;
bool IsResourceInfoCached(const DXDebug::BindingSlot &slot, uint32_t mipLevel) override;
@@ -138,6 +141,8 @@ private:
SRVInfo FetchSRV(const BindingSlot &slot);
SRVInfo FetchSRV(const D3D12Descriptor *resDescriptor, const BindingSlot &slot);
void FetchCBV(const BindingSlot &slot);
UAVInfo FetchUAV(const BindingSlot &slot);
UAVInfo FetchUAV(const D3D12Descriptor *resDescriptor, const BindingSlot &slot);
@@ -177,6 +182,8 @@ private:
}
};
mutable Threading::RWLock m_CBVsLock;
std::map<BindingSlot, bytebuf> m_CBVBuffers;
mutable Threading::RWLock m_UAVsLock;
std::map<BindingSlot, UAVInfo> m_UAVInfos;
std::map<BindingSlot, bytebuf> m_UAVBuffers;
+70 -36
View File
@@ -2351,7 +2351,6 @@ bool ThreadState::ExecuteInstruction(const rdcarray<ThreadState> &workgroup)
SetStepNeedsDeviceThread();
break;
}
RDCASSERT(m_DirectHeapAccessBindings.count(resultId) == 0);
m_DirectHeapAccessBindings[resultId] = resRefInfo;
// Default to unannotated handle
@@ -2377,7 +2376,6 @@ bool ThreadState::ExecuteInstruction(const rdcarray<ThreadState> &workgroup)
// Update m_DirectHeapAccessBindings for the annotated handle
// to use the data from the source resource
RDCASSERT(m_DirectHeapAccessBindings.count(baseResourceId) > 0);
RDCASSERT(m_DirectHeapAccessBindings.count(resultId) == 0);
m_DirectHeapAccessBindings[resultId] = m_DirectHeapAccessBindings.at(baseResourceId);
}
else
@@ -2431,16 +2429,19 @@ bool ThreadState::ExecuteInstruction(const rdcarray<ThreadState> &workgroup)
}
else if(resKind == ResourceKind::CBuffer)
{
// Create the cbuffer handle reference for the annotated handle
auto it = m_ConstantBlockHandles.find(baseResourceId);
if(it != m_ConstantBlockHandles.end())
if(!resource.IsDirectAccess())
{
m_ConstantBlockHandles[resultId] = it->second;
}
else
{
RDCERR("Annotated handle resName:%s %s has no cbuffer handle reference %u",
resName.c_str(), baseResource.c_str(), baseResourceId);
// Create the cbuffer handle reference for the annotated handle
auto it = m_ConstantBlockHandles.find(baseResourceId);
if(it != m_ConstantBlockHandles.end())
{
m_ConstantBlockHandles[resultId] = it->second;
}
else
{
RDCERR("Annotated handle resName:%s %s has no cbuffer handle reference %u",
resName.c_str(), baseResource.c_str(), baseResourceId);
}
}
}
// Store the annotate properties for the result
@@ -2648,42 +2649,63 @@ bool ThreadState::ExecuteInstruction(const rdcarray<ThreadState> &workgroup)
uint32_t regIndex = arg.value.u32v[0];
RDCASSERT(m_Live[handleId]);
const ShaderVariable &handleVar = m_Variables[handleId];
ShaderVariable handleVar;
bool annotatedHandle = false;
ResourceReferenceInfo resRefInfo = GetResource(handleId, annotatedHandle, handleVar);
if(!resRefInfo.Valid())
{
RDCERR("Invalid cbuffer resource %u", handleId);
break;
}
result.value.u32v[0] = 0;
result.value.u32v[1] = 0;
result.value.u32v[2] = 0;
result.value.u32v[3] = 0;
auto constantBlockRefIt = m_ConstantBlockHandles.find(handleId);
if(constantBlockRefIt != m_ConstantBlockHandles.end())
if(handleVar.IsDirectAccess())
{
const ConstantBlockReference &constantBlockRef = constantBlockRefIt->second;
auto it = m_GlobalState.constantBlocksDatas.find(constantBlockRef);
if(it != m_GlobalState.constantBlocksDatas.end())
const BindingSlot &slot = resRefInfo.binding;
if(m_Debugger.LoadCBVData(slot, regIndex, result.value) == DeviceOpResult::NeedsDevice)
{
const bytebuf &cbufferData = it->second;
const uint32_t bufferSize = (uint32_t)cbufferData.size();
const uint32_t maxIndex = AlignUp16(bufferSize) / 16;
RDCASSERTMSG("Out of bounds cbuffer load", regIndex < maxIndex, regIndex, maxIndex);
if(regIndex < maxIndex)
{
const uint32_t dataOffset = regIndex * 16;
const uint32_t byteWidth = 4;
const byte *base = cbufferData.data() + dataOffset;
const uint32_t *data = (const uint32_t *)base;
const uint32_t numComps = RDCMIN(4U, (bufferSize - dataOffset) / byteWidth);
for(uint32_t c = 0; c < numComps; c++)
result.value.u32v[c] = data[c];
}
}
else
{
RDCERR("Failed to find data for constant block data for %s", handleVar.name.c_str());
SetStepNeedsDeviceThread();
break;
}
}
else
{
RDCERR("Failed to find data for cbuffer %s", handleVar.name.c_str());
auto constantBlockRefIt = m_ConstantBlockHandles.find(handleId);
if(constantBlockRefIt != m_ConstantBlockHandles.end())
{
const ConstantBlockReference &constantBlockRef = constantBlockRefIt->second;
auto it = m_GlobalState.constantBlocksDatas.find(constantBlockRef);
if(it != m_GlobalState.constantBlocksDatas.end())
{
const bytebuf &cbufferData = it->second;
const uint32_t bufferSize = (uint32_t)cbufferData.size();
const uint32_t maxIndex = AlignUp16(bufferSize) / 16;
RDCASSERTMSG("Out of bounds cbuffer load", regIndex < maxIndex, regIndex, maxIndex);
if(regIndex < maxIndex)
{
const uint32_t dataOffset = regIndex * 16;
const uint32_t byteWidth = 4;
const byte *base = cbufferData.data() + dataOffset;
const uint32_t *data = (const uint32_t *)base;
const uint32_t numComps = RDCMIN(4U, (bufferSize - dataOffset) / byteWidth);
for(uint32_t c = 0; c < numComps; c++)
result.value.u32v[c] = data[c];
}
}
else
{
RDCERR("Failed to find data for constant block data for %s",
handleVar.name.c_str());
}
}
else
{
RDCERR("Failed to find data for cbuffer %s", handleVar.name.c_str());
}
}
// DXIL will create a vector of a single type with total size of 16-bytes
@@ -10452,6 +10474,18 @@ bool Debugger::TypedResourceStore(DXIL::ResourceClass resClass, const BindingSlo
return false;
}
// Called from any thread
DeviceOpResult Debugger::LoadCBVData(const BindingSlot &slot, uint32_t regIndex, ShaderValue &value)
{
if(!IsDeviceThread() && !m_ApiWrapper->IsCBVCached(slot))
return DeviceOpResult::NeedsDevice;
m_ApiWrapper->GetCBV(slot);
value = m_ApiWrapper->CBVLoad(slot, regIndex);
return DeviceOpResult::Succeeded;
}
// Called from any thread
DeviceOpResult Debugger::GetUAV(const BindingSlot &slot, UAVInfo &uavInfo) const
{
@@ -299,6 +299,7 @@ class DebugAPIWrapper
public:
virtual ~DebugAPIWrapper() {}
virtual ShaderValue CBVLoad(const BindingSlot &slot, uint32_t regIndex) const = 0;
virtual ShaderValue TypedUAVLoad(const BindingSlot &slot, const DXILDebug::ViewFmt &fmt,
uint64_t dataOffset) const = 0;
virtual ShaderValue TypedSRVLoad(const BindingSlot &slot, const DXILDebug::ViewFmt &fmt,
@@ -309,6 +310,7 @@ public:
uint64_t dataOffset, const ShaderValue &value) = 0;
// These will fetch the data on demand.
virtual void GetCBV(const BindingSlot &slot) = 0;
virtual UAVInfo GetUAV(const BindingSlot &slot) = 0;
virtual SRVInfo GetSRV(const BindingSlot &slot) = 0;
@@ -332,6 +334,7 @@ public:
virtual ShaderDirectAccess GetShaderDirectAccess(DescriptorType type,
const DXDebug::BindingSlot &slot) = 0;
virtual bool IsCBVCached(const DXDebug::BindingSlot &slot) const = 0;
virtual bool IsSRVCached(const DXDebug::BindingSlot &slot) const = 0;
virtual bool IsUAVCached(const DXDebug::BindingSlot &slot) const = 0;
virtual bool IsResourceInfoCached(const DXDebug::BindingSlot &slot, uint32_t mipLevel) = 0;
@@ -932,6 +935,7 @@ public:
bool TypedResourceStore(DXIL::ResourceClass resClass, const BindingSlot &slot,
const DXILDebug::ViewFmt &fmt, uint64_t dataOffset, ShaderValue &value);
DeviceOpResult LoadCBVData(const BindingSlot &slot, uint32_t regIndex, ShaderValue &value);
DeviceOpResult GetUAV(const BindingSlot &slot, UAVInfo &uavInfo) const;
DeviceOpResult GetSRV(const BindingSlot &slot, SRVInfo &srvInfo) const;