Spirv debugger support for queue and run of Simulation on Device Thread

This commit is contained in:
Jake Turner
2025-09-24 14:33:01 +01:00
parent 6e28ff1ca8
commit 35fdf44c20
3 changed files with 75 additions and 0 deletions
@@ -5142,3 +5142,16 @@ rdcstr DoStringise(const rdcspv::StepThreadMode &el)
}
END_ENUM_STRINGISE();
};
template <>
rdcstr DoStringise(const rdcspv::DeviceOpResult &el)
{
BEGIN_ENUM_STRINGISE(rdcspv::DeviceOpResult)
{
STRINGISE_ENUM_CLASS(Unknown)
STRINGISE_ENUM_CLASS(Succeeded)
STRINGISE_ENUM_CLASS(Failed)
STRINGISE_ENUM_CLASS(NeedsDevice)
}
END_ENUM_STRINGISE();
};
@@ -58,6 +58,14 @@ enum class ThreadProperty : uint32_t
ITERABLE_OPERATORS(ThreadProperty);
enum class DeviceOpResult : uint32_t
{
Unknown,
Succeeded,
Failed,
NeedsDevice,
};
inline void AtomicStore(int32_t *var, int32_t newVal)
{
int32_t oldVal = *var;
@@ -372,6 +380,7 @@ struct ThreadState
AtomicStore(&atomic_isSimulationStepActive, 1);
AtomicStore(&atomic_stepNeedsGpuSampleGatherOp, 0);
AtomicStore(&atomic_stepNeedsGpuMathOp, 0);
AtomicStore(&atomic_stepNeedsDeviceThread, 0);
}
void SetStepNeedsGpuSampleGatherOp()
{
@@ -388,6 +397,12 @@ struct ThreadState
SetPendingResultStatus(PendingResultStatus::Pending);
}
bool StepNeedsGpuMathOp() const { return (AtomicLoad(&atomic_stepNeedsGpuMathOp) == 1); }
void SetStepNeedsDeviceThread()
{
AtomicStore(&atomic_stepNeedsDeviceThread, 1);
SetPendingResultStatus(PendingResultStatus::Pending);
}
bool StepNeedsDeviceThread() const { return (AtomicLoad(&atomic_stepNeedsDeviceThread) == 1); }
const GpuMathOperation &GetQueuedGpuMathOp() const
{
RDCASSERT(AtomicLoad(&atomic_stepNeedsGpuMathOp));
@@ -455,6 +470,7 @@ private:
int32_t atomic_pendingResultStatus = (int32_t)PendingResultStatus::Unknown;
int32_t atomic_stepNeedsGpuSampleGatherOp = 0;
int32_t atomic_stepNeedsGpuMathOp = 0;
int32_t atomic_stepNeedsDeviceThread = 0;
int32_t atomic_isSimulationStepActive = 0;
};
@@ -757,6 +773,9 @@ private:
void ClampScalars(const ShaderVariable &var, uint8_t &scalar0) const;
void ClampScalars(const ShaderVariable &var, uint8_t &scalar0, uint8_t &scalar1) const;
void QueueDeviceThreadStep(uint32_t lane);
void ProcessQueuedDeviceThreadSteps();
void QueueJob(uint32_t lane, rdcarray<ShaderDebugState> *ret);
void StepThread(uint32_t lane, StepThreadMode stepMode, rdcarray<ShaderDebugState> *ret);
void InternalStepThread(uint32_t lane, rdcarray<ShaderDebugState> *ret);
@@ -772,6 +791,7 @@ private:
mutable rdcarray<DebugMessage> queuedDebugMessages;
rdcarray<bool> queuedGpuMathOps;
rdcarray<bool> queuedGpuSampleGatherOps;
rdcarray<bool> queuedDeviceThreadSteps;
rdcarray<ShaderDebugState> *shaderChangesReturn;
bool retireIDs = true;
@@ -792,3 +812,4 @@ void AssignValue(ShaderVariable &dst, const ShaderVariable &src);
DECLARE_REFLECTION_ENUM(rdcspv::ThreadState::PendingResultStatus);
DECLARE_REFLECTION_ENUM(rdcspv::StepThreadMode);
DECLARE_REFLECTION_ENUM(rdcspv::DeviceOpResult);
@@ -1024,12 +1024,14 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s
stage = shaderStage;
apiWrapper = api;
queuedDeviceThreadSteps.resize(threadsInWorkgroup);
queuedGpuMathOps.resize(threadsInWorkgroup);
queuedGpuSampleGatherOps.resize(threadsInWorkgroup);
pendingLanes.resize(threadsInWorkgroup);
for(uint32_t i = 0; i < threadsInWorkgroup; i++)
{
workgroup.push_back(ThreadState(*this, global));
queuedDeviceThreadSteps[i] = false;
queuedGpuMathOps[i] = false;
queuedGpuSampleGatherOps[i] = false;
pendingLanes[i] = false;
@@ -2693,6 +2695,7 @@ rdcarray<ShaderDebugState> Debugger::ContinueDebug()
do
{
ProcessQueuedDebugMessages();
ProcessQueuedDeviceThreadSteps();
// Convert the simulation threads queued operations into pending operations i.e. GPU commands
ProcessQueuedOps();
// Sync any pending GPU operations and set the results to the pending threads
@@ -4669,6 +4672,8 @@ void Debugger::StepThread(uint32_t lane, StepThreadMode stepMode, rdcarray<Shade
break;
else if(thread.StepNeedsGpuMathOp())
break;
else if(thread.StepNeedsDeviceThread())
break;
if(isActiveThread)
curActiveSteps++;
@@ -4695,6 +4700,12 @@ void Debugger::StepThread(uint32_t lane, StepThreadMode stepMode, rdcarray<Shade
QueueGpuMathOp(lane);
return;
}
if(thread.StepNeedsDeviceThread())
{
RDCASSERT(!simulateStep);
QueueDeviceThreadStep(lane);
return;
}
RDCASSERT(!thread.IsPendingResultPending());
thread.SetSimulationStepCompleted();
}
@@ -4744,6 +4755,8 @@ void Debugger::InternalStepThread(uint32_t lane, rdcarray<ShaderDebugState> *ret
return;
if(thread.StepNeedsGpuMathOp())
return;
if(thread.StepNeedsDeviceThread())
return;
if(!thread.IsPendingResultPending())
{
@@ -4821,6 +4834,8 @@ void Debugger::InternalStepThread(uint32_t lane, rdcarray<ShaderDebugState> *ret
return;
if(thread.StepNeedsGpuMathOp())
return;
if(thread.StepNeedsDeviceThread())
return;
}
}
@@ -4856,6 +4871,32 @@ void Debugger::AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSou
queuedDebugMessages.push_back({c, sv, src, d});
}
// Can be called from any thread
void Debugger::QueueDeviceThreadStep(uint32_t lane)
{
ThreadState &thread = workgroup[lane];
RDCASSERT(thread.IsSimulationStepActive());
thread.SetStepQueued();
RDCASSERT(!queuedDeviceThreadSteps[lane]);
queuedDeviceThreadSteps[lane] = true;
}
// Must be called from the replay manager thread (the debugger thread)
void Debugger::ProcessQueuedDeviceThreadSteps()
{
CHECK_DEBUGGER_THREAD();
for(uint32_t lane = 0; lane < queuedDeviceThreadSteps.size(); ++lane)
{
if(queuedDeviceThreadSteps[lane])
{
queuedDeviceThreadSteps[lane] = false;
ThreadState &thread = workgroup[lane];
thread.SetPendingResultUnknown();
RDCASSERT(thread.IsSimulationStepActive());
StepThread(lane, StepThreadMode::QUEUE_MULTIPLE_STEPS, shaderChangesReturn);
}
}
}
}; // namespace rdcspv
#if ENABLED(ENABLE_UNIT_TESTS)