Pass subgroup size and index through to DXIL instruction execute

This commit is contained in:
baldurk
2025-03-19 18:31:56 +00:00
parent 9bdb5bc134
commit b565693e15
6 changed files with 165 additions and 12 deletions
@@ -2357,6 +2357,7 @@ ShaderDebugTrace *D3D12Replay::DebugVertex(uint32_t eventId, uint32_t vertid, ui
const rdcarray<DXIL::EntryPointInterface::Signature> &dxilInputs =
debugger->GetDXILEntryPointInputs();
globalState.subgroupSize = buf->subgroupSize;
for(uint32_t t = 0; t < buf->subgroupSize; t++)
{
DXDebug::VSLaneData *lane = (DXDebug::VSLaneData *)(initialData.data() + laneDataOffset +
@@ -2369,6 +2370,7 @@ ShaderDebugTrace *D3D12Replay::DebugVertex(uint32_t eventId, uint32_t vertid, ui
if(lane->active)
RDCASSERTEQUAL(lane->laneIndex, t);
workgroupProperties[t][DXILDebug::ThreadProperty::Active] = lane->active;
workgroupProperties[t][DXILDebug::ThreadProperty::SubgroupIdx] = t;
rdcarray<DXILDebug::InputData> inputDatas;
for(int i = 0; i < fetcher.inputs.count(); i++)
@@ -2404,6 +2406,8 @@ ShaderDebugTrace *D3D12Replay::DebugVertex(uint32_t eventId, uint32_t vertid, ui
data += inputElement.numwords * sizeof(uint32_t);
}
state.m_Builtins[ShaderBuiltin::IndexInSubgroup] = ShaderVariable(rdcstr(), t, 0U, 0U, 0U);
for(const DXILDebug::InputData &input : inputDatas)
{
int32_t *rawout = NULL;
@@ -3159,6 +3163,7 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
DXILDebug::FetchConstantBufferData(m_pDevice, dxbc->GetDXILByteCode(), rs.graphics, refl,
globalState, ret->sourceVars);
globalState.subgroupSize = hit->subgroupSize;
for(uint32_t q = 0; q < hit->subgroupSize; q++)
{
DXDebug::PSLaneData *lane = (DXDebug::PSLaneData *)data;
@@ -3170,6 +3175,7 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
workgroupProperties[q][DXILDebug::ThreadProperty::Helper] = lane->isHelper;
workgroupProperties[q][DXILDebug::ThreadProperty::QuadLane] = lane->quadLane;
workgroupProperties[q][DXILDebug::ThreadProperty::QuadId] = lane->quadId;
workgroupProperties[q][DXILDebug::ThreadProperty::SubgroupIdx] = q;
data += sizeof(DXDebug::PSLaneData);
@@ -3212,6 +3218,7 @@ ShaderDebugTrace *D3D12Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
data += inputElement.numwords * sizeof(uint32_t);
}
state.m_Builtins[ShaderBuiltin::IndexInSubgroup] = ShaderVariable(rdcstr(), q, 0U, 0U, 0U);
state.m_Builtins[ShaderBuiltin::PrimitiveIndex] =
ShaderVariable(rdcstr(), lane->primitive, 0U, 0U, 0U);
state.m_Builtins[ShaderBuiltin::MSAACoverage] =
@@ -3433,6 +3440,7 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId,
};
uint32_t numThreads = 1;
uint32_t subgroupSize = 1;
uint32_t activeLaneIndex = 0;
rdcflatmap<ShaderBuiltin, ShaderVariable> globalBuiltins;
@@ -3594,6 +3602,7 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId,
else
activeLaneIndex = buf->laneIndex;
subgroupSize = buf->subgroupSize;
for(uint32_t t = 0; t < buf->subgroupSize; t++)
{
DXDebug::CSLaneData *value = (DXDebug::CSLaneData *)(initialData.data() + laneDataOffset +
@@ -3616,6 +3625,7 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId,
activeLaneIndex = lane;
workgroupProperties[lane][DXILDebug::ThreadProperty::Active] = value->active;
workgroupProperties[lane][DXILDebug::ThreadProperty::SubgroupIdx] = t;
RDCASSERT(value->active);
threadBuiltins[lane][ShaderBuiltin::DispatchThreadIndex] =
@@ -3629,6 +3639,8 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId,
value->threadid[2] * threadDim[0] * threadDim[1] +
value->threadid[1] * threadDim[0] + value->threadid[0],
0U, 0U, 0U);
threadBuiltins[lane][ShaderBuiltin::IndexInSubgroup] =
ShaderVariable(rdcstr(), value->laneIndex, 0U, 0U, 0U);
}
if(activeLaneIndex == ~0U)
@@ -3658,6 +3670,9 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId,
groupid[1] * threadDim[1] + ty);
RDCASSERTEQUAL(thread_builtins[ShaderBuiltin::DispatchThreadIndex].value.u32v[2],
groupid[2] * threadDim[2] + tz);
RDCASSERTEQUAL(thread_builtins[ShaderBuiltin::IndexInSubgroup].value.u32v[0],
i % buf->subgroupSize);
}
else
{
@@ -3668,7 +3683,12 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId,
ShaderVariable(rdcstr(), tx, ty, tz, 0U);
thread_builtins[ShaderBuiltin::GroupFlatIndex] = ShaderVariable(
rdcstr(), tz * threadDim[0] * threadDim[1] + ty * threadDim[0] + tx, 0U, 0U, 0U);
// tightly wrap subgroups, this is likely not how the GPU actually assigns them
thread_builtins[ShaderBuiltin::IndexInSubgroup] =
ShaderVariable(rdcstr(), i % buf->subgroupSize, 0U, 0U, 0U);
workgroupProperties[i][DXILDebug::ThreadProperty::Active] = 1;
workgroupProperties[i][DXILDebug::ThreadProperty::SubgroupIdx] =
i % buf->subgroupSize;
}
i++;
@@ -3751,6 +3771,7 @@ ShaderDebugTrace *D3D12Replay::DebugThread(uint32_t eventId,
DXILDebug::GlobalState &globalState = debugger->GetGlobalState();
globalState.builtins.swap(globalBuiltins);
globalState.subgroupSize = subgroupSize;
for(uint32_t i = 0; i < threadBuiltins.size(); i++)
debugger->GetLane(i).m_Builtins.swap(threadBuiltins[i]);
@@ -620,6 +620,33 @@ enum class AtomicBinOpCode : uint32_t
Invalid // Must be last.
};
// WaveOp / WavePrefixOp
enum class WaveOpCode : uint32_t
{
Sum = 0,
Product = 1,
Min = 2,
Max = 3,
};
// WaveBitOp
enum class WaveBitOpCode : uint32_t
{
And = 0,
Or = 1,
Xor = 2,
};
// WaveMultiPrefixOp
enum class WaveMultiPrefixOpCode : uint32_t
{
Sum = 0,
And = 1,
Or = 2,
Xor = 3,
Product = 4,
};
enum class QuadOpKind : uint32_t
{
ReadAcrossX = 0, // returns the value from the other lane in the quad in the
+93 -7
View File
@@ -1655,7 +1655,8 @@ bool IsNopInstruction(const Instruction &inst)
}
bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper,
const rdcarray<ThreadState> &workgroup)
const rdcarray<ThreadState> &workgroup,
const rdcarray<bool> &activeMask)
{
m_CurrentInstruction = m_FunctionInfo->function->instructions[m_FunctionInstructionIdx];
const Instruction &inst = *m_CurrentInstruction;
@@ -3529,6 +3530,92 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper,
result.value.u64v[0] = a.value.u64v[0];
break;
}
// Wave/Subgroup Operations
case DXOp::WaveGetLaneIndex:
{
// SV_PrimitiveID
result.value.u32v[0] = m_SubgroupIdx;
break;
}
case DXOp::WaveActiveOp:
{
// WaveActiveOp(value,op,sop)
ShaderVariable arg;
RDCASSERT(GetShaderVariable(inst.args[2], opCode, dxOpCode, arg));
WaveOpCode waveOpCode = (WaveOpCode)arg.value.u32v[0];
RDCASSERT(GetShaderVariable(inst.args[3], opCode, dxOpCode, arg));
bool isUnsigned = (arg.value.u32v[0] != 0);
// determine active lane indices in our subgroup
rdcarray<uint32_t> activeLanes;
const uint32_t firstLaneInSub = m_WorkgroupIndex - m_SubgroupIdx;
for(uint32_t lane = firstLaneInSub; lane < firstLaneInSub + m_GlobalState.subgroupSize;
lane++)
{
// wave operations exclude helpers
if(activeMask[lane])
{
if(!m_GlobalState.waveOpsIncludeHelpers && workgroup[lane - firstLaneInSub].m_Helper)
continue;
activeLanes.push_back(lane - firstLaneInSub);
}
}
ShaderVariable accum;
RDCASSERT(GetShaderVariable(inst.args[1], opCode, dxOpCode, accum));
// set the identity
switch(waveOpCode)
{
default:
RDCERR("Unhandled wave opcode");
accum.value = {};
break;
case WaveOpCode::Sum: accum.value = {}; break;
}
for(uint32_t lane : activeLanes)
{
ShaderVariable x;
RDCASSERT(workgroup[lane].GetShaderVariable(inst.args[1], opCode, dxOpCode, x));
switch(waveOpCode)
{
default: RDCERR("Unhandled wave opcode"); break;
case WaveOpCode::Sum:
{
for(uint8_t c = 0; c < x.columns; c++)
{
if(isUnsigned)
{
#undef _IMPL
#define _IMPL(I, S, U) comp<U>(accum, c) = comp<U>(accum, c) + comp<U>(x, c)
IMPL_FOR_INT_TYPES_FOR_TYPE(_IMPL, x.type);
}
else
{
#undef _IMPL
#define _IMPL(I, S, U) comp<S>(accum, c) = comp<S>(accum, c) + comp<S>(x, c)
IMPL_FOR_INT_TYPES_FOR_TYPE(_IMPL, x.type);
#undef _IMPL
#define _IMPL(T) comp<T>(accum, c) = comp<T>(accum, c) + comp<T>(x, c)
IMPL_FOR_FLOAT_TYPES_FOR_TYPE(_IMPL, x.type);
}
}
break;
}
}
}
result = accum;
break;
}
// Quad Operations
case DXOp::QuadReadLaneAt:
case DXOp::QuadOp:
@@ -3818,7 +3905,6 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper,
// Wave/Subgroup Operations
case DXOp::WaveIsFirstLane:
case DXOp::WaveGetLaneIndex:
case DXOp::WaveGetLaneCount:
case DXOp::WaveAnyTrue:
case DXOp::WaveAllTrue:
@@ -3826,7 +3912,6 @@ bool ThreadState::ExecuteInstruction(DebugAPIWrapper *apiWrapper,
case DXOp::WaveActiveBallot:
case DXOp::WaveReadLaneAt:
case DXOp::WaveReadLaneFirst:
case DXOp::WaveActiveOp:
case DXOp::WaveActiveBit:
case DXOp::WavePrefixOp:
case DXOp::WaveAllBitCount:
@@ -5321,7 +5406,7 @@ void ThreadState::StepOverNopInstructions()
}
void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper,
const rdcarray<ThreadState> &workgroup)
const rdcarray<ThreadState> &workgroup, const rdcarray<bool> &activeMask)
{
m_State = state;
@@ -5359,7 +5444,7 @@ void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper,
}
}
}
ExecuteInstruction(apiWrapper, workgroup);
ExecuteInstruction(apiWrapper, workgroup, activeMask);
m_State = NULL;
}
@@ -8360,6 +8445,7 @@ void Debugger::InitialiseWorkgroup(const rdcarray<ThreadProperties> &workgroupPr
}
lane.m_Dead = workgroupProperties[i][ThreadProperty::Active] == 0;
lane.m_SubgroupIdx = workgroupProperties[i][ThreadProperty::SubgroupIdx];
}
// find quad neighbours
@@ -8486,12 +8572,12 @@ rdcarray<ShaderDebugState> Debugger::ContinueDebug(DebugAPIWrapper *apiWrapper)
{
hasDebugState = true;
state.stepIndex = m_Steps;
thread.StepNext(&state, apiWrapper, m_Workgroup);
thread.StepNext(&state, apiWrapper, m_Workgroup, activeMask);
m_Steps++;
}
else
{
thread.StepNext(NULL, apiWrapper, m_Workgroup);
thread.StepNext(NULL, apiWrapper, m_Workgroup, activeMask);
}
}
}
+8 -2
View File
@@ -222,13 +222,14 @@ struct ThreadState
void EnterFunction(const DXIL::Function *function, const rdcarray<DXIL::Value *> &args);
void EnterEntryPoint(const DXIL::Function *function, ShaderDebugState *state);
void StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper,
const rdcarray<ThreadState> &workgroup);
const rdcarray<ThreadState> &workgroup, const rdcarray<bool> &activeMask);
void StepOverNopInstructions();
bool Finished() const;
bool InUniformBlock() const;
bool ExecuteInstruction(DebugAPIWrapper *apiWrapper, const rdcarray<ThreadState> &workgroup);
bool ExecuteInstruction(DebugAPIWrapper *apiWrapper, const rdcarray<ThreadState> &workgroup,
const rdcarray<bool> &activeMask);
void MarkResourceAccess(const rdcstr &name, const ResourceReferenceInfo &resRefInfo,
bool directAccess, const ShaderDirectAccess &access,
@@ -347,6 +348,8 @@ struct ThreadState
rdcfixedarray<uint32_t, 4> m_QuadNeighbours = {~0U, ~0U, ~0U, ~0U};
// index in the workgroup
uint32_t m_WorkgroupIndex = ~0U;
// index in the subgroup
uint32_t m_SubgroupIdx = ~0U;
bool m_Dead = false;
bool m_Ended = false;
bool m_Helper = false;
@@ -357,6 +360,8 @@ struct GlobalState
GlobalState() = default;
~GlobalState();
BuiltinInputs builtins;
uint32_t subgroupSize = 1;
bool waveOpsIncludeHelpers = false;
struct ViewFmt
{
@@ -518,6 +523,7 @@ enum class ThreadProperty : uint32_t
QuadId,
QuadLane,
Active,
SubgroupIdx,
Count,
};
@@ -28,6 +28,8 @@
#include "dxil_bytecode.h"
#include "dxil_common.h"
RDOC_EXTERN_CONFIG(bool, D3D_Hack_EnableGroups);
namespace DXIL
{
enum class ResourcesTag
@@ -1868,6 +1870,12 @@ rdcstr Program::GetDebugStatus()
"Only supported when debugging pixel shaders dx.op call `%s` %s",
callFunc->name.c_str(), ToStr(dxOpCode).c_str());
continue;
case DXOp::WaveGetLaneIndex:
case DXOp::WaveActiveOp:
if(!D3D_Hack_EnableGroups())
return StringFormat::Fmt("Unsupported dx.op call `%s` %s", callFunc->name.c_str(),
ToStr(dxOpCode).c_str());
continue;
case DXOp::TempRegLoad:
case DXOp::TempRegStore:
case DXOp::MinPrecXRegLoad:
@@ -1889,7 +1897,6 @@ rdcstr Program::GetDebugStatus()
case DXOp::OutputControlPointID:
case DXOp::CycleCounterLegacy:
case DXOp::WaveIsFirstLane:
case DXOp::WaveGetLaneIndex:
case DXOp::WaveGetLaneCount:
case DXOp::WaveAnyTrue:
case DXOp::WaveAllTrue:
@@ -1897,7 +1904,6 @@ rdcstr Program::GetDebugStatus()
case DXOp::WaveActiveBallot:
case DXOp::WaveReadLaneAt:
case DXOp::WaveReadLaneFirst:
case DXOp::WaveActiveOp:
case DXOp::WaveActiveBit:
case DXOp::WavePrefixOp:
case DXOp::WaveAllBitCount:
+8 -1
View File
@@ -6154,6 +6154,11 @@ ShaderDebugTrace *VulkanReplay::DebugComputeCommon(ShaderStage stage, uint32_t e
groupid[1] * threadDim[1] + ty);
RDCASSERTEQUAL(thread_builtins[ShaderBuiltin::DispatchThreadIndex].value.u32v[2],
groupid[2] * threadDim[2] + tz);
RDCASSERTEQUAL(thread_builtins[ShaderBuiltin::IndexInSubgroup].value.u32v[0],
i % winner->subgroupSize);
RDCASSERTEQUAL(thread_builtins[ShaderBuiltin::SubgroupIndexInWorkgroup].value.u32v[0],
i / winner->subgroupSize);
}
else
{
@@ -6165,8 +6170,10 @@ ShaderDebugTrace *VulkanReplay::DebugComputeCommon(ShaderStage stage, uint32_t e
thread_builtins[ShaderBuiltin::GroupFlatIndex] = ShaderVariable(
rdcstr(), tz * threadDim[0] * threadDim[1] + ty * threadDim[0] + tx, 0U, 0U, 0U);
// tightly wrap subgroups, this is likely not how the GPU actually assigns them
thread_builtins[ShaderBuiltin::SubgroupIndexInWorkgroup] =
thread_builtins[ShaderBuiltin::IndexInSubgroup] =
ShaderVariable(rdcstr(), i % winner->subgroupSize, 0U, 0U, 0U);
thread_builtins[ShaderBuiltin::SubgroupIndexInWorkgroup] =
ShaderVariable(rdcstr(), i / winner->subgroupSize, 0U, 0U, 0U);
apiWrapper->thread_props[i][(size_t)rdcspv::ThreadProperty::Active] = 1;
}