Support for DXIL Quad Ops in Compute Shader Debugging

linear layout (4x1x1) : for 1D workgroup (Nx1x1) otherwise quad layout (2x2x1)
This commit is contained in:
Jake Turner
2025-12-11 15:39:19 +13:00
parent dc61249328
commit 95702ce6f7
3 changed files with 109 additions and 10 deletions
+104 -1
View File
@@ -3566,9 +3566,14 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId,
refl.dispatchThreadsDimension[2],
};
bool hasQuadScope = (dxbc->GetThreadScope() & DXBC::ThreadScope::Quad) ? true : false;
bool hasSubgroupScoope = (dxbc->GetThreadScope() & DXBC::ThreadScope::Subgroup) ? true : false;
uint32_t subgroupSize = 1;
uint32_t activeLaneIndex = ~0U;
uint32_t numThreads = 1;
if(hasQuadScope)
numThreads = RDCMAX(numThreads, 4U);
DXILDebug::D3D12APIWrapper *apiWrapper = new DXILDebug::D3D12APIWrapper(
m_pDevice, dxbc->GetDXILByteCode(), refl, eventId, dxbc->GetReflection()->InputSig);
@@ -3577,8 +3582,40 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId,
rdcarray<DXILDebug::ThreadProperties> workgroupProperties;
rdcarray<rdcflatmap<ShaderBuiltin, ShaderVariable>> threadsBuiltins;
const uint32_t quadIdOffset = 10000;
uint32_t countQuadX = ~0U;
uint32_t countQuadY = ~0U;
uint32_t quadW = ~0U;
uint32_t quadH = ~0U;
if(hasQuadScope)
{
if((threadDim[1] == 1) && (threadDim[2] == 1))
{
// linear: 4x1x1
quadW = 4;
quadH = 1;
}
else
{
// quad: 2x2x1
quadW = 2;
quadH = 2;
}
countQuadX = threadDim[0] / quadW;
countQuadY = threadDim[1] / quadH;
}
if(hasQuadScope)
{
RDCASSERTEQUAL(threadDim[0], countQuadX * quadW);
RDCASSERTEQUAL(threadDim[1], countQuadY * quadH);
}
// hard case - with subgroups we want the actual layout so read that from the GPU
if(dxbc->GetThreadScope() & DXBC::ThreadScope::Subgroup)
if(hasSubgroupScoope)
{
DXDebug::InputFetcherConfig cfg;
DXDebug::InputFetcher fetcher;
@@ -3721,6 +3758,9 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId,
subgroupSize = buf->subgroupSize;
numThreads = wholeWorkgroup ? threadDim[0] * threadDim[1] * threadDim[2] : subgroupSize;
if(hasQuadScope)
RDCASSERT(numThreads >= 4);
workgroupProperties.resize(numThreads);
threadsBuiltins.resize(numThreads);
@@ -3836,6 +3876,20 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId,
const uint32_t groupFlatIndex = laneData.threadid[2] * threadDim[0] * threadDim[1] +
laneData.threadid[1] * threadDim[0] + laneData.threadid[0];
uint32_t quadId = ~0U;
uint32_t quadLaneIndex = ~0U;
if(hasQuadScope)
{
uint32_t quadX = (laneData.threadid[0] / quadW);
uint32_t quadY = (laneData.threadid[1] / quadH);
uint32_t quadZ = laneData.threadid[2];
quadId = quadX + (quadY * countQuadX) + (quadZ * countQuadY * countQuadX);
quadLaneIndex = (laneData.threadid[0] % quadW) + (laneData.threadid[1] % quadH) * 2;
workgroupProperties[lane][DXILDebug::ThreadProperty::QuadLane] = quadLaneIndex;
workgroupProperties[lane][DXILDebug::ThreadProperty::QuadId] = quadId + quadIdOffset;
}
workgroupProperties[lane][DXILDebug::ThreadProperty::Active] = laneData.active;
workgroupProperties[lane][DXILDebug::ThreadProperty::SubgroupIdx] = laneData.laneIndex;
// The simulation requires
@@ -3893,6 +3947,19 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId,
rdcstr(), tz * threadDim[0] * threadDim[1] + ty * threadDim[0] + tx, 0U, 0U, 0U);
workgroupProperties[i][DXILDebug::ThreadProperty::Active] = 1;
if(hasQuadScope)
{
uint32_t quadX = (tx / quadW);
uint32_t quadY = (ty / quadH);
uint32_t quadZ = tz;
uint32_t quadId =
quadIdOffset + quadX + (quadY * countQuadX) + (quadZ * countQuadY * countQuadX);
uint32_t quadLaneIndex = (tx % quadW) + (ty % quadH) * 2;
workgroupProperties[i][DXILDebug::ThreadProperty::QuadLane] = quadLaneIndex;
workgroupProperties[i][DXILDebug::ThreadProperty::QuadId] = quadId;
}
if(rdcfixedarray<uint32_t, 3>({tx, ty, tz}) == threadid)
activeLaneIndex = i;
@@ -3901,6 +3968,42 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId,
}
}
}
else if(hasQuadScope)
{
// need to simulate the whole quad, do not readback from the GPU like we do with subgroups
// the quad is guaranteed to be in the same subgroup
// We lay things out in linear or quad order
RDCASSERTEQUAL(numThreads, 4U);
uint32_t txMin = (threadid[0] / quadW) * quadW;
uint32_t tyMin = (threadid[1] / quadH) * quadH;
uint32_t tz = threadid[2];
uint32_t quadZ = tz;
for(uint32_t i = 0; i < 4U; ++i)
{
uint32_t tx = txMin + (i % quadW);
uint32_t ty = tyMin + (i / quadW);
rdcflatmap<ShaderBuiltin, ShaderVariable> &thread_builtins = threadsBuiltins[i];
thread_builtins[ShaderBuiltin::DispatchThreadIndex] =
ShaderVariable(rdcstr(), groupid[0] * threadDim[0] + tx, groupid[1] * threadDim[1] + ty,
groupid[2] * threadDim[2] + tz, 0U);
thread_builtins[ShaderBuiltin::GroupThreadIndex] = ShaderVariable(rdcstr(), tx, ty, tz, 0U);
thread_builtins[ShaderBuiltin::GroupFlatIndex] = ShaderVariable(
rdcstr(), tz * threadDim[0] * threadDim[1] + ty * threadDim[0] + tx, 0U, 0U, 0U);
workgroupProperties[i][DXILDebug::ThreadProperty::Active] = 1;
uint32_t quadX = (tx / quadW);
uint32_t quadY = (ty / quadH);
uint32_t quadId =
quadIdOffset + quadX + (quadY * countQuadX) + (quadZ * countQuadY * countQuadX);
uint32_t quadLaneIndex = (tx % quadW) + (ty % quadH) * 2;
workgroupProperties[i][DXILDebug::ThreadProperty::QuadLane] = quadLaneIndex;
workgroupProperties[i][DXILDebug::ThreadProperty::QuadId] = quadId;
if(rdcfixedarray<uint32_t, 3>({tx, ty, tz}) == threadid)
activeLaneIndex = i;
}
}
else
{
workgroupProperties.resize(numThreads);
@@ -10032,6 +10032,11 @@ void Debugger::InitialiseWorkgroup()
lane.SetQuadLaneIndex(workgroupProperties[i][ThreadProperty::QuadLane]);
lane.SetQuadId(workgroupProperties[i][ThreadProperty::QuadId]);
}
if(m_Stage == ShaderStage::Compute)
{
lane.SetQuadLaneIndex(workgroupProperties[i][ThreadProperty::QuadLane]);
lane.SetQuadId(workgroupProperties[i][ThreadProperty::QuadId]);
}
lane.SetDead(workgroupProperties[i][ThreadProperty::Active] == 0);
lane.SetSubgroupIdx(workgroupProperties[i][ThreadProperty::SubgroupIdx]);
@@ -1860,15 +1860,6 @@ rdcstr Program::GetDebugStatus()
RDCASSERT(dxOpCode < DXOp::NumOpCodes, dxOpCode, DXOp::NumOpCodes);
switch(dxOpCode)
{
case DXOp::QuadReadLaneAt:
case DXOp::QuadOp:
// Only supported on pixel shaders
if(m_Type != DXBC::ShaderType::Pixel)
return StringFormat::Fmt(
"Only supported when debugging pixel shaders dx.op call `%s` %s",
callFunc->name.c_str(), ToStr(dxOpCode).c_str());
continue;
// Implement when required
case DXOp::CBufferLoad:
// loads single value from byte offset in constant buffer, 8-byte alignment on the offset