Changed ExecutionPointPerId to be an rdcarray instead of std::map

DXIL Debugger optimisation
This commit is contained in:
Jake Turner
2025-10-04 09:04:14 +01:00
parent b70b79e897
commit 0809a6dd3f
2 changed files with 64 additions and 46 deletions
+61 -45
View File
@@ -6563,6 +6563,39 @@ void ThreadState::StepOverNopInstructions()
} while(true); } while(true);
} }
void ThreadState::RetireLiveIDs()
{
m_State->flags = ShaderEvents::NoEvent;
m_State->changes.clear();
// Remove variables which have gone out of scope
ExecPointReference current(m_Block, m_FunctionInstructionIdx);
for(uint32_t id = 0; id < m_Live.size(); ++id)
{
if(!m_Live[id])
continue;
// The fake output variable is always in scope
if(id == m_Output.id)
continue;
// Globals are always in scope
if(m_IsGlobal[id])
continue;
RDCASSERT(id < m_FunctionInfo->maxExecPointPerId.size());
const ExecPointReference maxPoint = m_FunctionInfo->maxExecPointPerId[id];
RDCASSERT(maxPoint.IsValid());
// Use control flow to determine if the current execution point is after the maximum point
if(current.IsAfter(maxPoint, m_FunctionInfo->controlFlow))
{
m_Live[id] = false;
ShaderVariableChange change;
change.before = m_Variables[id];
m_State->changes.push_back(change);
}
}
}
void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper, void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper,
const rdcarray<ThreadState> &workgroup, const rdcarray<bool> &activeMask) const rdcarray<ThreadState> &workgroup, const rdcarray<bool> &activeMask)
{ {
@@ -6578,33 +6611,7 @@ void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper,
{ {
m_State->flags = ShaderEvents::NoEvent; m_State->flags = ShaderEvents::NoEvent;
m_State->changes.clear(); m_State->changes.clear();
RetireLiveIDs();
// Remove variables which have gone out of scope
ExecPointReference current(m_Block, m_FunctionInstructionIdx);
for(uint32_t id = 0; id < m_Live.size(); ++id)
{
if(!m_Live[id])
continue;
// The fake output variable is always in scope
if(id == m_Output.id)
continue;
// Globals are always in scope
if(m_IsGlobal[id])
continue;
auto itRange = m_FunctionInfo->maxExecPointPerId.find(id);
RDCASSERT(itRange != m_FunctionInfo->maxExecPointPerId.end());
const ExecPointReference maxPoint = itRange->second;
// Use control flow to determine if the current execution point is after the maximum point
if(current.IsAfter(maxPoint, m_FunctionInfo->controlFlow))
{
m_Live[id] = false;
ShaderVariableChange change;
change.before = m_Variables[id];
m_State->changes.push_back(change);
}
}
} }
ExecuteInstruction(apiWrapper, workgroup, activeMask); ExecuteInstruction(apiWrapper, workgroup, activeMask);
@@ -8777,13 +8784,13 @@ ShaderDebugTrace *Debugger::BeginDebug(uint32_t eventId, const DXBC::DXBCContain
m_Stage = shaderStage; m_Stage = shaderStage;
uint32_t outputSSAId = m_Program->m_NextSSAId; uint32_t outputSSAId = m_Program->m_NextSSAId;
uint32_t nextSSAId = outputSSAId + 1; uint32_t maxSSAId = outputSSAId + 1;
ShaderDebugTrace *ret = new ShaderDebugTrace; ShaderDebugTrace *ret = new ShaderDebugTrace;
ret->stage = shaderStage; ret->stage = shaderStage;
for(uint32_t i = 0; i < threadsInWorkgroup; i++) for(uint32_t i = 0; i < threadsInWorkgroup; i++)
m_Workgroup.push_back(ThreadState(*this, m_GlobalState, nextSSAId)); m_Workgroup.push_back(ThreadState(*this, m_GlobalState, maxSSAId));
ThreadState &state = GetActiveLane(); ThreadState &state = GetActiveLane();
@@ -8900,7 +8907,7 @@ ShaderDebugTrace *Debugger::BeginDebug(uint32_t eventId, const DXBC::DXBCContain
sourceVar.variables.push_back(ref); sourceVar.variables.push_back(ref);
} }
m_LiveGlobals.resize(nextSSAId); m_LiveGlobals.resize(maxSSAId);
MemoryTracking &globalMemory = m_GlobalState.memory; MemoryTracking &globalMemory = m_GlobalState.memory;
for(const DXIL::GlobalVar *gv : m_Program->m_GlobalVars) for(const DXIL::GlobalVar *gv : m_Program->m_GlobalVars)
{ {
@@ -9066,6 +9073,8 @@ ShaderDebugTrace *Debugger::BeginDebug(uint32_t eventId, const DXBC::DXBCContain
FunctionInfo::ReferencedIds &ssaRefs = info.referencedIds; FunctionInfo::ReferencedIds &ssaRefs = info.referencedIds;
FunctionInfo::ExecutionPointPerId &ssaMaxExecPoints = info.maxExecPointPerId; FunctionInfo::ExecutionPointPerId &ssaMaxExecPoints = info.maxExecPointPerId;
ssaMaxExecPoints.resize(maxSSAId);
std::set<Id> ssaMaxRefs;
uint32_t curBlock = 0; uint32_t curBlock = 0;
info.instructionToBlock.resize(countInstructions); info.instructionToBlock.resize(countInstructions);
@@ -9087,12 +9096,17 @@ ShaderDebugTrace *Debugger::BeginDebug(uint32_t eventId, const DXBC::DXBCContain
RDCASSERTEQUAL(ssaRefs.count(resultId), 0); RDCASSERTEQUAL(ssaRefs.count(resultId), 0);
ssaRefs.insert(resultId); ssaRefs.insert(resultId);
auto it = ssaMaxExecPoints.find(resultId); auto it = ssaMaxRefs.find(resultId);
if(it == ssaMaxExecPoints.end()) if(it == ssaMaxRefs.end())
{
ssaMaxExecPoints[resultId] = current; ssaMaxExecPoints[resultId] = current;
ssaMaxRefs.insert(resultId);
}
else else
// If the result SSA has tracking then this access should be at a later execution point {
RDCASSERT(it->second.IsAfter(current, controlFlow)); // If the result SSA has tracking then that access should be after setting the result
RDCASSERT(current.IsAfter(ssaMaxExecPoints[resultId], controlFlow));
}
} }
// Track maximum execution point when an SSA is referenced as an argument // Track maximum execution point when an SSA is referenced as an argument
// Arguments to phi instructions are handled separately // Arguments to phi instructions are handled separately
@@ -9112,16 +9126,17 @@ ShaderDebugTrace *Debugger::BeginDebug(uint32_t eventId, const DXBC::DXBCContain
if(ssaRefs.count(argId) == 0) if(ssaRefs.count(argId) == 0)
ssaRefs.insert(argId); ssaRefs.insert(argId);
} }
auto it = ssaMaxExecPoints.find(argId); auto it = ssaMaxRefs.find(argId);
if(it == ssaMaxExecPoints.end()) if(it == ssaMaxRefs.end())
{ {
ssaMaxExecPoints[argId] = maxPoint; ssaMaxExecPoints[argId] = maxPoint;
ssaMaxRefs.insert(argId);
} }
else else
{ {
// Update the maximum execution point if access is later than the existing access // Update the maximum execution point if access is later than the existing access
if(maxPoint.IsAfter(it->second, controlFlow)) if(maxPoint.IsAfter(ssaMaxExecPoints[argId], controlFlow))
it->second = maxPoint; ssaMaxExecPoints[argId] = maxPoint;
} }
} }
if(inst.op == Operation::Branch || inst.op == Operation::Unreachable || if(inst.op == Operation::Branch || inst.op == Operation::Unreachable ||
@@ -9129,13 +9144,13 @@ ShaderDebugTrace *Debugger::BeginDebug(uint32_t eventId, const DXBC::DXBCContain
++curBlock; ++curBlock;
} }
// If these do not match in size that means there is a result SSA that is never read // If these do not match in size that means there is a result SSA that is never read
RDCASSERTEQUAL(ssaRefs.size(), ssaMaxExecPoints.size()); RDCASSERTEQUAL(ssaRefs.size(), ssaMaxRefs.size());
// Update any SSA max points which are inside a loop to the next uniform block // Update any SSA max points which are inside a loop to the next uniform block
// This covers the case of SSA IDs that are assigned to but never accessed // This covers the case of SSA IDs that are assigned to but never accessed
for(auto &it : ssaMaxExecPoints) for(Id id : ssaMaxRefs)
{ {
ExecPointReference &maxPoint = it.second; ExecPointReference &maxPoint = ssaMaxExecPoints[id];
uint32_t block = maxPoint.block; uint32_t block = maxPoint.block;
// If the current block is in a loop, set the execution point to the next uniform block // If the current block is in a loop, set the execution point to the next uniform block
if(loopBlocks.contains(block)) if(loopBlocks.contains(block))
@@ -9312,10 +9327,11 @@ ShaderDebugTrace *Debugger::BeginDebug(uint32_t eventId, const DXBC::DXBCContain
{ {
for(LocalMapping &localMapping : scope->localMappings) for(LocalMapping &localMapping : scope->localMappings)
{ {
auto it = info.maxExecPointPerId.find(localMapping.debugVarSSAId); if(localMapping.debugVarSSAId < info.maxExecPointPerId.size())
if(it != info.maxExecPointPerId.end())
{ {
const ExecPointReference &current = it->second; const ExecPointReference &current = info.maxExecPointPerId[localMapping.debugVarSSAId];
if(!current.IsValid())
continue;
uint32_t scopeEndInst = scope->maxInstruction + 1; uint32_t scopeEndInst = scope->maxInstruction + 1;
scopeEndInst = RDCMIN(scopeEndInst, (uint32_t)info.instructionToBlock.size() - 1); scopeEndInst = RDCMIN(scopeEndInst, (uint32_t)info.instructionToBlock.size() - 1);
const uint32_t scopeEndBlock = info.instructionToBlock[scopeEndInst]; const uint32_t scopeEndBlock = info.instructionToBlock[scopeEndInst];
@@ -9327,7 +9343,7 @@ ShaderDebugTrace *Debugger::BeginDebug(uint32_t eventId, const DXBC::DXBCContain
scopeEnd.instruction = info.function->blocks[nextUniformBlock]->startInstructionIdx + 1; scopeEnd.instruction = info.function->blocks[nextUniformBlock]->startInstructionIdx + 1;
} }
if(scopeEnd.IsAfter(current, controlFlow)) if(scopeEnd.IsAfter(current, controlFlow))
it->second = scopeEnd; info.maxExecPointPerId[localMapping.debugVarSSAId] = scopeEnd;
} }
} }
} }
+3 -1
View File
@@ -58,6 +58,7 @@ struct ExecPointReference
{ {
} }
bool IsAfter(const ExecPointReference &from, const DXIL::ControlFlow &controlFlow) const; bool IsAfter(const ExecPointReference &from, const DXIL::ControlFlow &controlFlow) const;
bool IsValid() const { return block != ~0U && instruction != ~0U; }
uint32_t block; uint32_t block;
uint32_t instruction; uint32_t instruction;
@@ -91,7 +92,7 @@ struct InputData
struct FunctionInfo struct FunctionInfo
{ {
typedef std::set<Id> ReferencedIds; typedef std::set<Id> ReferencedIds;
typedef std::map<Id, ExecPointReference> ExecutionPointPerId; typedef rdcarray<ExecPointReference> ExecutionPointPerId;
typedef std::map<uint32_t, ReferencedIds> PhiReferencedIdsPerBlock; typedef std::map<uint32_t, ReferencedIds> PhiReferencedIdsPerBlock;
typedef rdcarray<rdcstr> Callstack; typedef rdcarray<rdcstr> Callstack;
@@ -263,6 +264,7 @@ struct ThreadState
Id GetArgumentId(uint32_t i) const; Id GetArgumentId(uint32_t i) const;
ResourceReferenceInfo GetResource(Id handleId, bool &annotatedHandle); ResourceReferenceInfo GetResource(Id handleId, bool &annotatedHandle);
void FillCallstack(ShaderDebugState &state); void FillCallstack(ShaderDebugState &state);
void RetireLiveIDs();
bool GetShaderVariable(const DXIL::Value *dxilValue, DXIL::Operation op, DXIL::DXOp dxOpCode, bool GetShaderVariable(const DXIL::Value *dxilValue, DXIL::Operation op, DXIL::DXOp dxOpCode,
ShaderVariable &var, bool flushDenormInput = true) const ShaderVariable &var, bool flushDenormInput = true) const