mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-22 22:46:41 +00:00
Store source vars data per-instruction rather than per-state
* Since the source vars data doesn't change for a given instruction, we can pre- calculate it and save time on re-calculating per-state. * Note callstack *can* change per-state on SPIR-V where the same instruction can be reached by different flow paths, so the callstack remains part of the per- state data.
This commit is contained in:
@@ -373,6 +373,7 @@ TEMPLATE_ARRAY_INSTANTIATE(rdcarray, PixelModification)
|
||||
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ResourceDescription)
|
||||
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ResourceId)
|
||||
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, LineColumnInfo)
|
||||
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, InstructionSourceInfo)
|
||||
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderCompileFlag)
|
||||
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderConstant)
|
||||
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderDebugState)
|
||||
|
||||
@@ -631,9 +631,11 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR
|
||||
bool hasLineInfo = false;
|
||||
LineColumnInfo prevLine;
|
||||
|
||||
for(uint32_t inst = 0; inst < m_Trace->lineInfo.size(); inst++)
|
||||
for(uint32_t inst = 0; inst < m_Trace->instInfo.size(); inst++)
|
||||
{
|
||||
LineColumnInfo line = m_Trace->lineInfo[inst];
|
||||
const InstructionSourceInfo &instInfo = m_Trace->instInfo[inst];
|
||||
|
||||
LineColumnInfo line = instInfo.lineInfo;
|
||||
|
||||
int disasmLine = (int)line.disassemblyLine;
|
||||
if(disasmLine > 0 && disasmLine >= m_AsmLine2Inst.size())
|
||||
@@ -930,7 +932,7 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR
|
||||
{
|
||||
applyForwardsChange();
|
||||
|
||||
if(m_Trace->lineInfo[GetCurrentState().nextInstruction].fileIndex >= 0)
|
||||
if(GetCurrentInstInfo().lineInfo.fileIndex >= 0)
|
||||
break;
|
||||
|
||||
if(IsLastState())
|
||||
@@ -2029,7 +2031,7 @@ bool ShaderViewer::step(bool forward, StepMode mode)
|
||||
|
||||
if(isSourceDebugging())
|
||||
{
|
||||
LineColumnInfo oldLine = m_Trace->lineInfo[GetCurrentState().nextInstruction];
|
||||
LineColumnInfo oldLine = GetCurrentInstInfo().lineInfo;
|
||||
rdcarray<rdcstr> oldStack = GetCurrentState().callstack;
|
||||
|
||||
do
|
||||
@@ -2040,7 +2042,7 @@ bool ShaderViewer::step(bool forward, StepMode mode)
|
||||
else
|
||||
applyBackwardsChange();
|
||||
|
||||
LineColumnInfo curLine = m_Trace->lineInfo[GetCurrentState().nextInstruction];
|
||||
LineColumnInfo curLine = GetCurrentLineInfo();
|
||||
|
||||
// break out if we hit a breakpoint, no matter what
|
||||
if(m_Breakpoints.contains({-1, curLine.disassemblyLine}) ||
|
||||
@@ -2121,7 +2123,7 @@ bool ShaderViewer::step(bool forward, StepMode mode)
|
||||
|
||||
} while(true);
|
||||
|
||||
oldLine = m_Trace->lineInfo[GetCurrentState().nextInstruction];
|
||||
oldLine = GetCurrentLineInfo();
|
||||
oldStack = GetCurrentState().callstack;
|
||||
|
||||
if(!forward)
|
||||
@@ -2144,7 +2146,7 @@ bool ShaderViewer::step(bool forward, StepMode mode)
|
||||
{
|
||||
if((oldStack == GetPreviousState().callstack ||
|
||||
oldStack.size() > GetPreviousState().callstack.size()) &&
|
||||
!oldLine.SourceEqual(m_Trace->lineInfo[GetPreviousState().nextInstruction]))
|
||||
!oldLine.SourceEqual(GetPreviousInstInfo().lineInfo))
|
||||
{
|
||||
// if we hit this case, it means we jumped to an instruction in the same call which maps
|
||||
// to a different line (perhaps through a loop or branch), or we lost a function in the
|
||||
@@ -2155,7 +2157,7 @@ bool ShaderViewer::step(bool forward, StepMode mode)
|
||||
|
||||
applyBackwardsChange();
|
||||
|
||||
LineColumnInfo curLine = m_Trace->lineInfo[GetCurrentState().nextInstruction];
|
||||
LineColumnInfo curLine = GetCurrentLineInfo();
|
||||
|
||||
// still need to check for instruction-level breakpoints
|
||||
if(m_Breakpoints.contains({-1, curLine.disassemblyLine}))
|
||||
@@ -2164,12 +2166,11 @@ bool ShaderViewer::step(bool forward, StepMode mode)
|
||||
}
|
||||
else
|
||||
{
|
||||
while(!IsFirstState() &&
|
||||
m_Trace->lineInfo[GetPreviousState().nextInstruction].SourceEqual(oldLine))
|
||||
while(!IsFirstState() && GetPreviousInstInfo().lineInfo.SourceEqual(oldLine))
|
||||
{
|
||||
applyBackwardsChange();
|
||||
|
||||
LineColumnInfo curLine = m_Trace->lineInfo[GetCurrentState().nextInstruction];
|
||||
LineColumnInfo curLine = GetCurrentLineInfo();
|
||||
|
||||
// still need to check for instruction-level breakpoints
|
||||
if(m_Breakpoints.contains({-1, curLine.disassemblyLine}))
|
||||
@@ -2262,6 +2263,33 @@ const ShaderDebugState &ShaderViewer::GetNextState() const
|
||||
return m_States.back();
|
||||
}
|
||||
|
||||
const InstructionSourceInfo &ShaderViewer::GetPreviousInstInfo() const
|
||||
{
|
||||
return GetInstInfo(GetPreviousState().nextInstruction);
|
||||
}
|
||||
|
||||
const InstructionSourceInfo &ShaderViewer::GetCurrentInstInfo() const
|
||||
{
|
||||
return GetInstInfo(GetCurrentState().nextInstruction);
|
||||
}
|
||||
|
||||
const InstructionSourceInfo &ShaderViewer::GetNextInstInfo() const
|
||||
{
|
||||
return GetInstInfo(GetNextState().nextInstruction);
|
||||
}
|
||||
|
||||
const InstructionSourceInfo &ShaderViewer::GetInstInfo(uint32_t instruction) const
|
||||
{
|
||||
InstructionSourceInfo search;
|
||||
search.instruction = instruction;
|
||||
return *std::lower_bound(m_Trace->instInfo.begin(), m_Trace->instInfo.end(), search);
|
||||
}
|
||||
|
||||
const LineColumnInfo &ShaderViewer::GetCurrentLineInfo() const
|
||||
{
|
||||
return GetCurrentInstInfo().lineInfo;
|
||||
}
|
||||
|
||||
void ShaderViewer::runTo(uint32_t runToInstruction, bool forward, ShaderEvents condition)
|
||||
{
|
||||
rdcarray<uint32_t> insts = {runToInstruction};
|
||||
@@ -2275,7 +2303,7 @@ void ShaderViewer::runTo(const rdcarray<uint32_t> &runToInstructions, bool forwa
|
||||
return;
|
||||
|
||||
bool firstStep = true;
|
||||
LineColumnInfo oldLine = m_Trace->lineInfo[GetCurrentState().nextInstruction];
|
||||
LineColumnInfo oldLine = GetCurrentLineInfo();
|
||||
|
||||
// this is effectively infinite as we break out before moving to next/previous state if that would
|
||||
// be first/last
|
||||
@@ -2290,7 +2318,7 @@ void ShaderViewer::runTo(const rdcarray<uint32_t> &runToInstructions, bool forwa
|
||||
break;
|
||||
|
||||
// or breakpoint
|
||||
LineColumnInfo curLine = m_Trace->lineInfo[GetCurrentState().nextInstruction];
|
||||
LineColumnInfo curLine = GetCurrentLineInfo();
|
||||
if(!firstStep && (m_Breakpoints.contains({-1, curLine.disassemblyLine}) ||
|
||||
m_Breakpoints.contains({curLine.fileIndex, curLine.lineStart})))
|
||||
break;
|
||||
@@ -2362,7 +2390,7 @@ void ShaderViewer::runToResourceAccess(bool forward, VarType type, const Bindpoi
|
||||
break;
|
||||
|
||||
// or breakpoint
|
||||
LineColumnInfo curLine = m_Trace->lineInfo[GetCurrentState().nextInstruction];
|
||||
LineColumnInfo curLine = GetCurrentLineInfo();
|
||||
if(m_Breakpoints.contains({-1, curLine.disassemblyLine}) ||
|
||||
m_Breakpoints.contains({curLine.fileIndex, curLine.lineStart}))
|
||||
break;
|
||||
@@ -2731,9 +2759,9 @@ QString ShaderViewer::getRegNames(const RDTreeWidgetItem *item, uint32_t swizzle
|
||||
mapping = m_Trace->sourceVars[tag.sourceVarIdx];
|
||||
}
|
||||
else if(!tag.globalSourceVar && tag.sourceVarIdx >= 0 &&
|
||||
tag.sourceVarIdx < GetCurrentState().sourceVars.count())
|
||||
tag.sourceVarIdx < GetCurrentInstInfo().sourceVars.count())
|
||||
{
|
||||
mapping = GetCurrentState().sourceVars[tag.sourceVarIdx];
|
||||
mapping = GetCurrentInstInfo().sourceVars[tag.sourceVarIdx];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2991,9 +3019,9 @@ const RDTreeWidgetItem *ShaderViewer::evaluateVar(const RDTreeWidgetItem *item,
|
||||
mapping = m_Trace->sourceVars[tag.sourceVarIdx];
|
||||
}
|
||||
else if(!tag.globalSourceVar && tag.sourceVarIdx >= 0 &&
|
||||
tag.sourceVarIdx < GetCurrentState().sourceVars.count())
|
||||
tag.sourceVarIdx < GetCurrentInstInfo().sourceVars.count())
|
||||
{
|
||||
mapping = GetCurrentState().sourceVars[tag.sourceVarIdx];
|
||||
mapping = GetCurrentInstInfo().sourceVars[tag.sourceVarIdx];
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3515,9 +3543,8 @@ void ShaderViewer::updateDebugState()
|
||||
for(const rdcstr &s : state.callstack)
|
||||
ui->callstack->insertItem(0, s);
|
||||
|
||||
if(state.nextInstruction < m_Trace->lineInfo.size())
|
||||
{
|
||||
LineColumnInfo lineInfo = m_Trace->lineInfo[state.nextInstruction];
|
||||
LineColumnInfo lineInfo = GetInstInfo(state.nextInstruction).lineInfo;
|
||||
|
||||
// highlight the current line
|
||||
{
|
||||
@@ -3538,7 +3565,7 @@ void ShaderViewer::updateDebugState()
|
||||
// last state which did.
|
||||
for(int stateLookbackIdx = (int)m_CurrentStateIdx; stateLookbackIdx > 0; stateLookbackIdx--)
|
||||
{
|
||||
lineInfo = m_Trace->lineInfo[m_States[stateLookbackIdx].nextInstruction];
|
||||
lineInfo = GetInstInfo(m_States[stateLookbackIdx].nextInstruction).lineInfo;
|
||||
|
||||
if(lineInfo.fileIndex >= 0 && lineInfo.fileIndex < m_FileScintillas.count())
|
||||
break;
|
||||
@@ -3916,21 +3943,22 @@ void ShaderViewer::updateDebugState()
|
||||
|
||||
RDTreeWidgetItem fakeroot;
|
||||
|
||||
const rdcarray<SourceVariableMapping> &sourceVars = state.sourceVars;
|
||||
|
||||
for(size_t lidx = 0; lidx < sourceVars.size(); lidx++)
|
||||
for(int globalVarIdx = 0; globalVarIdx < m_Trace->sourceVars.count(); globalVarIdx++)
|
||||
{
|
||||
int32_t localVarIdx = int32_t(sourceVars.size() - 1 - lidx);
|
||||
const SourceVariableMapping &sourceVar = m_Trace->sourceVars[globalVarIdx];
|
||||
|
||||
// iterate in reverse order, so newest locals tend to end up on top
|
||||
const SourceVariableMapping &l = sourceVars[localVarIdx];
|
||||
if(!sourceVar.variables.empty() && sourceVar.variables[0].type != DebugVariableType::Variable)
|
||||
continue;
|
||||
|
||||
if(sourceVar.rows == 0 || sourceVar.columns == 0)
|
||||
continue;
|
||||
|
||||
bool modified = false;
|
||||
|
||||
// don't list any modified variables on the first step when they all come into existance
|
||||
if(l.variables[0].type == DebugVariableType::Variable && !IsFirstState())
|
||||
if(sourceVar.variables[0].type == DebugVariableType::Variable && !IsFirstState())
|
||||
{
|
||||
for(const DebugVariableReference &v : l.variables)
|
||||
for(const DebugVariableReference &v : sourceVar.variables)
|
||||
{
|
||||
rdcstr base = v.name;
|
||||
int offs = base.find_first_of("[.");
|
||||
@@ -3952,6 +3980,59 @@ void ShaderViewer::updateDebugState()
|
||||
}
|
||||
}
|
||||
|
||||
fakeroot.addChild(makeSourceVariableNode(sourceVar, globalVarIdx, -1, modified));
|
||||
}
|
||||
|
||||
const rdcarray<SourceVariableMapping> &sourceVars = GetCurrentInstInfo().sourceVars;
|
||||
|
||||
for(size_t lidx = 0; lidx < sourceVars.size(); lidx++)
|
||||
{
|
||||
int32_t localVarIdx = int32_t(sourceVars.size() - 1 - lidx);
|
||||
|
||||
// iterate in reverse order, so newest locals tend to end up on top
|
||||
const SourceVariableMapping &l = sourceVars[localVarIdx];
|
||||
|
||||
bool modified = false;
|
||||
bool hasNonError = false;
|
||||
|
||||
for(const DebugVariableReference &v : l.variables)
|
||||
{
|
||||
hasNonError |= (GetDebugVariable(v) != NULL);
|
||||
|
||||
// don't list any modified variables on the first step when they all come into existance
|
||||
if(IsFirstState())
|
||||
{
|
||||
if(hasNonError)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
rdcstr base = v.name;
|
||||
int offs = base.find_first_of("[.");
|
||||
if(offs > 0)
|
||||
base = v.name.substr(0, offs);
|
||||
|
||||
for(const ShaderVariableChange &c : GetCurrentState().changes)
|
||||
{
|
||||
if(c.before.name == v.name || c.after.name == v.name || c.before.name == base ||
|
||||
c.after.name == base)
|
||||
{
|
||||
modified = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(modified && hasNonError)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// don't display source variables that map to non-existant debug variables. This can happen
|
||||
// when flow control means those debug variables were never created, but they would be mapped
|
||||
// at this point.
|
||||
if(!hasNonError)
|
||||
continue;
|
||||
|
||||
RDTreeWidgetItem *node = makeSourceVariableNode(l, -1, localVarIdx, modified);
|
||||
|
||||
fakeroot.addChild(node);
|
||||
@@ -4952,7 +5033,7 @@ void ShaderViewer::ToggleBreakpointOnInstruction(int32_t instruction)
|
||||
|
||||
if(instruction >= 0)
|
||||
{
|
||||
LineColumnInfo &instLine = m_Trace->lineInfo[instruction];
|
||||
const LineColumnInfo &instLine = GetInstInfo(instruction).lineInfo;
|
||||
sourceBreakpoint = {instLine.fileIndex, instLine.lineStart};
|
||||
disasmBreakpoints.push_back({-1, instLine.disassemblyLine});
|
||||
}
|
||||
@@ -4988,7 +5069,7 @@ void ShaderViewer::ToggleBreakpointOnInstruction(int32_t instruction)
|
||||
{
|
||||
sourceBreakpoint = {scintillaIndex, (uint32_t)i};
|
||||
for(uint32_t inst : it.value())
|
||||
disasmBreakpoints.push_back({-1, m_Trace->lineInfo[inst].disassemblyLine});
|
||||
disasmBreakpoints.push_back({-1, GetInstInfo(inst).lineInfo.disassemblyLine});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -354,6 +354,12 @@ private:
|
||||
const ShaderDebugState &GetCurrentState() const;
|
||||
const ShaderDebugState &GetNextState() const;
|
||||
|
||||
const InstructionSourceInfo &GetPreviousInstInfo() const;
|
||||
const InstructionSourceInfo &GetCurrentInstInfo() const;
|
||||
const InstructionSourceInfo &GetNextInstInfo() const;
|
||||
const InstructionSourceInfo &GetInstInfo(uint32_t instruction) const;
|
||||
const LineColumnInfo &GetCurrentLineInfo() const;
|
||||
|
||||
void updateDebugState();
|
||||
void markWatchStale(RDTreeWidgetItem *item);
|
||||
bool updateWatchVariable(RDTreeWidgetItem *watchItem, const RDTreeWidgetItem *varItem,
|
||||
|
||||
@@ -617,6 +617,37 @@ treated as covering the code.
|
||||
};
|
||||
DECLARE_REFLECTION_STRUCT(LineColumnInfo);
|
||||
|
||||
struct InstructionSourceInfo
|
||||
{
|
||||
DOCUMENT("");
|
||||
InstructionSourceInfo() = default;
|
||||
InstructionSourceInfo(const InstructionSourceInfo &) = default;
|
||||
InstructionSourceInfo &operator=(const InstructionSourceInfo &) = default;
|
||||
|
||||
bool operator==(const InstructionSourceInfo &o) const { return instruction == o.instruction; }
|
||||
bool operator<(const InstructionSourceInfo &o) const { return instruction < o.instruction; }
|
||||
DOCUMENT("The instruction that this information is for.");
|
||||
uint32_t instruction;
|
||||
|
||||
DOCUMENT(R"(The source location that this instruction corresponds to
|
||||
|
||||
:type: LineColumnInfo
|
||||
)");
|
||||
LineColumnInfo lineInfo;
|
||||
|
||||
DOCUMENT(R"(An optional mapping of which high-level source variables map to which debug variables
|
||||
and including extra type information.
|
||||
|
||||
This list contains source variable mapping that is only valid at this instruction, and is fully
|
||||
complete & redundant including all previous source variables that are still valid at this
|
||||
instruction.
|
||||
|
||||
:type: List[SourceVariableMapping]
|
||||
)");
|
||||
rdcarray<SourceVariableMapping> sourceVars;
|
||||
};
|
||||
DECLARE_REFLECTION_STRUCT(InstructionSourceInfo);
|
||||
|
||||
DOCUMENT("This stores the before and after state of a :class:`ShaderVariable`.");
|
||||
struct ShaderVariableChange
|
||||
{
|
||||
@@ -675,7 +706,7 @@ struct ShaderDebugState
|
||||
bool operator==(const ShaderDebugState &o) const
|
||||
{
|
||||
return nextInstruction == o.nextInstruction && flags == o.flags && changes == o.changes &&
|
||||
sourceVars == o.sourceVars && stepIndex == o.stepIndex;
|
||||
stepIndex == o.stepIndex;
|
||||
}
|
||||
bool operator<(const ShaderDebugState &o) const
|
||||
{
|
||||
@@ -687,8 +718,6 @@ struct ShaderDebugState
|
||||
return stepIndex < o.stepIndex;
|
||||
if(!(changes == o.changes))
|
||||
return changes < o.changes;
|
||||
if(!(sourceVars == o.sourceVars))
|
||||
return sourceVars < o.sourceVars;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -713,18 +742,7 @@ backwards using the information.
|
||||
)");
|
||||
rdcarray<ShaderVariableChange> changes;
|
||||
|
||||
DOCUMENT(R"(An optional mapping of which high-level source variables map to which debug variables
|
||||
and including extra type information.
|
||||
|
||||
This list contains source variable mapping that is only valid at this state - it is not valid at any
|
||||
other state where the lifetime of the source variable may have run out, or it may now be stored in
|
||||
a different debug variable.
|
||||
|
||||
:type: List[SourceVariableMapping]
|
||||
)");
|
||||
rdcarray<SourceVariableMapping> sourceVars;
|
||||
|
||||
DOCUMENT(R"(The function names in the current callstack at this line.
|
||||
DOCUMENT(R"(The function names in the current callstack at this instruction.
|
||||
|
||||
The oldest/outer function is first in the list, the newest/inner function is last.
|
||||
|
||||
@@ -831,12 +849,21 @@ If this is ``None`` then the trace is invalid.
|
||||
)");
|
||||
ShaderDebugger *debugger = NULL;
|
||||
|
||||
DOCUMENT(R"(An array of the same size as the number of instructions in the shader, with a mapping
|
||||
to source lines.
|
||||
DOCUMENT(R"(An array of the same size as the number of instructions in the shader, with
|
||||
per-instruction information such as source line mapping, and source variables.
|
||||
|
||||
:type: List[LineColumnInfo]
|
||||
.. warning::
|
||||
|
||||
This array is *not* indexed by instruction. Since it is common for adjacent instructions to have
|
||||
effectively identical source information, this array only stores unique information ordered by
|
||||
instruction. On some internal representations this may be one entry per instruction, and on others
|
||||
it may be sparse and require a binary lookup to locate the corresponding information for an
|
||||
instruction. If no direct match is found, the lower bound match is valid (i.e. the data for
|
||||
instruction A before the data for instruction B is valid for all instructions in range ``[A, B)``.
|
||||
|
||||
:type: List[InstructionSourceInfo]
|
||||
)");
|
||||
rdcarray<LineColumnInfo> lineInfo;
|
||||
rdcarray<InstructionSourceInfo> instInfo;
|
||||
};
|
||||
|
||||
DECLARE_REFLECTION_STRUCT(ShaderDebugTrace);
|
||||
|
||||
@@ -593,13 +593,13 @@ void DXBCContainer::FillTraceLineInfo(ShaderDebugTrace &trace) const
|
||||
{
|
||||
if(m_DXBCByteCode)
|
||||
{
|
||||
trace.lineInfo.resize(m_DXBCByteCode->GetNumInstructions());
|
||||
trace.instInfo.resize(m_DXBCByteCode->GetNumInstructions());
|
||||
for(size_t i = 0; i < m_DXBCByteCode->GetNumInstructions(); i++)
|
||||
{
|
||||
const DXBCBytecode::Operation &op = m_DXBCByteCode->GetInstruction(i);
|
||||
|
||||
if(m_DebugInfo)
|
||||
m_DebugInfo->GetLineInfo(i, op.offset, trace.lineInfo[i]);
|
||||
m_DebugInfo->GetLineInfo(i, op.offset, trace.instInfo[i].lineInfo);
|
||||
|
||||
// we add some number of lines for the header we added with shader hash, debug name, etc on
|
||||
// top of what the bytecode disassembler did
|
||||
@@ -615,38 +615,14 @@ void DXBCContainer::FillTraceLineInfo(ShaderDebugTrace &trace) const
|
||||
extraLines += (uint32_t)Bits::CountOnes((uint32_t)m_GlobalFlags) + 2;
|
||||
|
||||
if(op.line > 0)
|
||||
trace.lineInfo[i].disassemblyLine = extraLines + op.line;
|
||||
trace.instInfo[i].lineInfo.disassemblyLine = extraLines + op.line;
|
||||
|
||||
if(m_DebugInfo)
|
||||
m_DebugInfo->GetLocals(this, i, op.offset, trace.instInfo[i].sourceVars);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DXBCContainer::FillStateInstructionInfo(ShaderDebugState &state) const
|
||||
{
|
||||
uint32_t instruction = state.nextInstruction;
|
||||
|
||||
uintptr_t offset = 0;
|
||||
|
||||
state.sourceVars.clear();
|
||||
|
||||
if(m_DXBCByteCode)
|
||||
{
|
||||
if(instruction < m_DXBCByteCode->GetNumInstructions())
|
||||
offset = m_DXBCByteCode->GetInstruction(instruction).offset;
|
||||
|
||||
if(m_DebugInfo)
|
||||
m_DebugInfo->GetLocals(this, instruction, offset, state.sourceVars);
|
||||
}
|
||||
|
||||
if(m_DebugInfo)
|
||||
{
|
||||
m_DebugInfo->GetCallstack(instruction, offset, state.callstack);
|
||||
}
|
||||
else
|
||||
{
|
||||
state.callstack.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void DXBCContainer::StripChunk(bytebuf &ByteCode, uint32_t fourcc)
|
||||
{
|
||||
FileHeader *header = (FileHeader *)ByteCode.data();
|
||||
|
||||
@@ -187,7 +187,6 @@ public:
|
||||
|
||||
const rdcstr &GetDisassembly();
|
||||
void FillTraceLineInfo(ShaderDebugTrace &trace) const;
|
||||
void FillStateInstructionInfo(ShaderDebugState &state) const;
|
||||
|
||||
static void StripChunk(bytebuf &ByteCode, uint32_t fourcc);
|
||||
static void ReplaceChunk(bytebuf &ByteCode, uint32_t fourcc, const byte *replacement, size_t size);
|
||||
|
||||
@@ -1151,6 +1151,7 @@ ThreadState::ThreadState(int workgroupIdx, GlobalState &globalState, const DXBC:
|
||||
nextInstruction = 0;
|
||||
reflection = dxbc->GetReflection();
|
||||
program = dxbc->GetDXBCByteCode();
|
||||
debug = dxbc->GetDebugInfo();
|
||||
RDCEraseEl(semantics);
|
||||
|
||||
program->SetupRegisterFile(variables);
|
||||
@@ -1952,6 +1953,9 @@ void ThreadState::StepNext(ShaderDebugState *state, DebugAPIWrapper *apiWrapper,
|
||||
|
||||
const Operation &op = program->GetInstruction((size_t)nextInstruction);
|
||||
|
||||
if(state && debug)
|
||||
debug->GetCallstack(nextInstruction, op.offset, state->callstack);
|
||||
|
||||
apiWrapper->SetCurrentInstruction(nextInstruction);
|
||||
nextInstruction++;
|
||||
|
||||
@@ -5647,7 +5651,6 @@ rdcarray<ShaderDebugState> InterpretDebugger::ContinueDebug(DXBCDebug::DebugAPIW
|
||||
|
||||
for(const ShaderVariable &v : active.variables)
|
||||
initial.changes.push_back({ShaderVariable(), v});
|
||||
dxbc->FillStateInstructionInfo(initial);
|
||||
|
||||
ret.push_back(std::move(initial));
|
||||
|
||||
@@ -5685,7 +5688,6 @@ rdcarray<ShaderDebugState> InterpretDebugger::ContinueDebug(DXBCDebug::DebugAPIW
|
||||
workgroup[i].StepNext(&state, apiWrapper, oldworkgroup);
|
||||
state.stepIndex = steps;
|
||||
state.nextInstruction = workgroup[i].nextInstruction;
|
||||
dxbc->FillStateInstructionInfo(state);
|
||||
ret.push_back(std::move(state));
|
||||
|
||||
steps++;
|
||||
|
||||
@@ -323,6 +323,7 @@ private:
|
||||
|
||||
const DXBC::Reflection *reflection;
|
||||
const DXBCBytecode::Program *program;
|
||||
const DXBC::IDebugInfo *debug;
|
||||
|
||||
rdcarray<BindpointIndex> m_accessedSRVs;
|
||||
rdcarray<BindpointIndex> m_accessedUAVs;
|
||||
|
||||
@@ -125,12 +125,10 @@ void ThreadState::EnterFunction(const rdcarray<Id> &arguments)
|
||||
// process the outgoing scope
|
||||
ProcessScopeChange(live, {});
|
||||
callstack.back()->live = live;
|
||||
callstack.back()->sourceVars = sourceVars;
|
||||
}
|
||||
|
||||
// start with just globals
|
||||
live = debugger.GetLiveGlobals();
|
||||
sourceVars = debugger.GetGlobalSourceVars();
|
||||
|
||||
callstack.push_back(frame);
|
||||
|
||||
@@ -371,8 +369,6 @@ void ThreadState::SetDst(Id id, const ShaderVariable &val)
|
||||
change.before = prev;
|
||||
change.after = debugger.GetPointerValue(ids[id]);
|
||||
m_State->changes.push_back(change);
|
||||
|
||||
debugger.AddSourceVars(sourceVars, change.after, id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,7 +557,6 @@ bool ThreadState::ReferencePointer(Id id)
|
||||
{
|
||||
if(!frame->localsUsed.contains(id))
|
||||
{
|
||||
debugger.AddSourceVars(sourceVars, frame->locals[i], id);
|
||||
frame->localsUsed.push_back(id);
|
||||
firstLocalWrite = true;
|
||||
}
|
||||
@@ -3051,7 +3046,6 @@ void ThreadState::StepNext(ShaderDebugState *state, const rdcarray<ThreadState>
|
||||
|
||||
// restore the live list from the calling frame
|
||||
live = callstack.back()->live;
|
||||
sourceVars = callstack.back()->sourceVars;
|
||||
}
|
||||
|
||||
delete exitingFrame;
|
||||
|
||||
@@ -148,7 +148,6 @@ struct StackFrame
|
||||
|
||||
// the thread's live list before the function was entered
|
||||
rdcarray<Id> live;
|
||||
rdcarray<SourceVariableMapping> sourceVars;
|
||||
|
||||
// the last block we were in and the current block, for OpPhis
|
||||
Id lastBlock, curBlock;
|
||||
@@ -219,8 +218,6 @@ struct ThreadState
|
||||
|
||||
std::map<Id, uint32_t> lastWrite;
|
||||
|
||||
rdcarray<SourceVariableMapping> sourceVars;
|
||||
|
||||
// index in the pixel quad
|
||||
uint32_t workgroupIndex;
|
||||
bool helperInvocation;
|
||||
@@ -261,33 +258,6 @@ struct TypeData
|
||||
rdcarray<rdcpair<rdcstr, Id>> structMembers;
|
||||
};
|
||||
|
||||
struct ScopeData
|
||||
{
|
||||
DebugScope type;
|
||||
ScopeData *parent;
|
||||
uint32_t line;
|
||||
uint32_t column;
|
||||
int32_t fileIndex;
|
||||
size_t end;
|
||||
|
||||
rdcstr name;
|
||||
|
||||
rdcarray<Id> locals;
|
||||
};
|
||||
|
||||
struct InlineData
|
||||
{
|
||||
ScopeData *scope;
|
||||
InlineData *parent;
|
||||
};
|
||||
|
||||
struct LocalData
|
||||
{
|
||||
rdcstr name;
|
||||
ScopeData *scope;
|
||||
TypeData *type;
|
||||
};
|
||||
|
||||
struct LocalMapping
|
||||
{
|
||||
bool operator<(const LocalMapping &o) const
|
||||
@@ -322,13 +292,42 @@ struct LocalMapping
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t stepIndex;
|
||||
uint32_t instIndex;
|
||||
Id sourceVar;
|
||||
Id debugVar;
|
||||
bool isDeclare;
|
||||
rdcarray<uint32_t> indexes;
|
||||
};
|
||||
|
||||
struct ScopeData
|
||||
{
|
||||
DebugScope type;
|
||||
ScopeData *parent;
|
||||
uint32_t line;
|
||||
uint32_t column;
|
||||
int32_t fileIndex;
|
||||
size_t end;
|
||||
|
||||
rdcstr name;
|
||||
|
||||
rdcarray<Id> locals;
|
||||
|
||||
rdcarray<LocalMapping> localMappings;
|
||||
};
|
||||
|
||||
struct InlineData
|
||||
{
|
||||
ScopeData *scope;
|
||||
InlineData *parent;
|
||||
};
|
||||
|
||||
struct LocalData
|
||||
{
|
||||
rdcstr name;
|
||||
ScopeData *scope;
|
||||
TypeData *type;
|
||||
};
|
||||
|
||||
Id ParseRawName(const rdcstr &name);
|
||||
rdcstr GetRawName(Id id);
|
||||
|
||||
@@ -345,8 +344,6 @@ public:
|
||||
|
||||
rdcarray<ShaderDebugState> ContinueDebug();
|
||||
|
||||
void ApplyDebugSourceVars(size_t startOffs, ThreadState &thread, ShaderDebugState &state);
|
||||
|
||||
Iter GetIterForInstruction(uint32_t inst);
|
||||
uint32_t GetInstructionForIter(Iter it);
|
||||
uint32_t GetInstructionForFunction(Id id);
|
||||
@@ -358,7 +355,6 @@ public:
|
||||
bool HasDebugInfo() const { return m_DebugInfo.valid; }
|
||||
bool InDebugScope(uint32_t inst) const;
|
||||
rdcstr GetHumanName(Id id);
|
||||
void AddSourceVars(rdcarray<SourceVariableMapping> &sourceVars, const ShaderVariable &var, Id id);
|
||||
void AllocateVariable(Id id, Id typeId, ShaderVariable &outVar);
|
||||
|
||||
ShaderVariable ReadFromPointer(const ShaderVariable &v) const;
|
||||
@@ -377,7 +373,6 @@ public:
|
||||
uint32_t GetNumInstructions() { return (uint32_t)instructionOffsets.size(); }
|
||||
GlobalState GetGlobal() { return global; }
|
||||
const rdcarray<Id> &GetLiveGlobals() { return liveGlobals; }
|
||||
const rdcarray<SourceVariableMapping> &GetGlobalSourceVars();
|
||||
ThreadState &GetActiveLane() { return workgroup[activeLaneIndex]; }
|
||||
const ThreadState &GetActiveLane() const { return workgroup[activeLaneIndex]; }
|
||||
private:
|
||||
@@ -397,6 +392,9 @@ private:
|
||||
|
||||
void MakeSignatureNames(const rdcarray<SPIRVInterfaceAccess> &sigList, rdcarray<rdcstr> &sigNames);
|
||||
|
||||
void FillDebugSourceVars(rdcarray<InstructionSourceInfo> &instInfo);
|
||||
void FillDefaultSourceVars(rdcarray<InstructionSourceInfo> &instInfo);
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// debug data
|
||||
|
||||
@@ -426,17 +424,16 @@ private:
|
||||
rdcarray<MemberName> memberNames;
|
||||
std::map<ShaderEntryPoint, Id> entryLookup;
|
||||
|
||||
DenseIdMap<size_t> idDeathOffset;
|
||||
DenseIdMap<rdcpair<size_t, size_t>> idLiveRange;
|
||||
|
||||
SparseIdMap<size_t> m_Files;
|
||||
LineColumnInfo m_CurLineCol;
|
||||
std::map<size_t, LineColumnInfo> m_LineColInfo;
|
||||
rdcarray<InstructionSourceInfo> m_InstInfo;
|
||||
|
||||
SparseIdMap<uint32_t> labelInstruction;
|
||||
|
||||
// the live mutable global variables, to initialise a stack frame's live list
|
||||
rdcarray<Id> liveGlobals;
|
||||
rdcarray<SourceVariableMapping> globalSourceVars;
|
||||
|
||||
struct Function
|
||||
{
|
||||
@@ -464,6 +461,8 @@ private:
|
||||
ScopeData *curScope = NULL;
|
||||
InlineData *curInline = NULL;
|
||||
|
||||
rdcarray<LocalMapping> scopelessMappings;
|
||||
|
||||
rdcarray<Id> globals;
|
||||
rdcarray<Id> constants;
|
||||
|
||||
@@ -474,7 +473,6 @@ private:
|
||||
|
||||
std::map<size_t, ScopeData *> lineScope;
|
||||
std::map<size_t, InlineData *> lineInline;
|
||||
std::map<size_t, LocalMapping> localMappings;
|
||||
|
||||
rdcarray<LocalMapping> activeLocalMappings;
|
||||
} m_DebugInfo;
|
||||
|
||||
@@ -818,9 +818,10 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s
|
||||
sourceVar.variables.push_back(DebugVariableReference(
|
||||
isInput ? DebugVariableType::Input : DebugVariableType::Variable, debugVarName, x));
|
||||
|
||||
ret->sourceVars.push_back(sourceVar);
|
||||
if(!isInput && addSource)
|
||||
globalSourceVars.push_back(sourceVar);
|
||||
if(isInput)
|
||||
ret->sourceVars.push_back(sourceVar);
|
||||
else if(addSource)
|
||||
ret->sourceVars.push_back(sourceVar);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1206,7 +1207,7 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s
|
||||
sourceVar.variables.push_back(
|
||||
DebugVariableReference(DebugVariableType::Variable, var.name, x));
|
||||
|
||||
globalSourceVars.push_back(sourceVar);
|
||||
ret->sourceVars.push_back(sourceVar);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1236,20 +1237,22 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s
|
||||
p.Set(*this, global, lane);
|
||||
}
|
||||
|
||||
ret->lineInfo.resize(instructionOffsets.size());
|
||||
for(size_t i = 0; i < instructionOffsets.size(); i++)
|
||||
// this contains all the accumulated line number information. Add in our disassembly mapping
|
||||
ret->instInfo = m_InstInfo;
|
||||
for(size_t i = 0; i < m_InstInfo.size(); i++)
|
||||
{
|
||||
ret->lineInfo[i] = m_LineColInfo[instructionOffsets[i]];
|
||||
|
||||
{
|
||||
auto it = instructionLines.find(instructionOffsets[i]);
|
||||
if(it != instructionLines.end())
|
||||
ret->lineInfo[i].disassemblyLine = it->second;
|
||||
else
|
||||
ret->lineInfo[i].disassemblyLine = 0;
|
||||
}
|
||||
auto it = instructionLines.find(instructionOffsets[m_InstInfo[i].instruction]);
|
||||
if(it != instructionLines.end())
|
||||
ret->instInfo[i].lineInfo.disassemblyLine = it->second;
|
||||
else
|
||||
ret->instInfo[i].lineInfo.disassemblyLine = 0;
|
||||
}
|
||||
|
||||
if(m_DebugInfo.valid)
|
||||
FillDebugSourceVars(ret->instInfo);
|
||||
else
|
||||
FillDefaultSourceVars(ret->instInfo);
|
||||
|
||||
ret->constantBlocks = global.constantBlocks;
|
||||
ret->readOnlyResources = global.readOnlyResources;
|
||||
ret->readWriteResources = global.readWriteResources;
|
||||
@@ -1292,261 +1295,365 @@ ShaderDebugTrace *Debugger::BeginDebug(DebugAPIWrapper *api, const ShaderStage s
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Debugger::ApplyDebugSourceVars(size_t startOffs, ThreadState &thread, ShaderDebugState &state)
|
||||
void Debugger::FillDebugSourceVars(rdcarray<InstructionSourceInfo> &instInfo)
|
||||
{
|
||||
size_t endOffs = instructionOffsets[thread.nextInstruction - 1];
|
||||
|
||||
// apply any local mapping changes
|
||||
for(auto it = m_DebugInfo.localMappings.lower_bound(startOffs);
|
||||
it != m_DebugInfo.localMappings.end(); ++it)
|
||||
for(InstructionSourceInfo &i : instInfo)
|
||||
{
|
||||
// if we've gone too far, end
|
||||
if(it->first > endOffs)
|
||||
break;
|
||||
size_t offs = instructionOffsets[i.instruction];
|
||||
|
||||
LocalMapping mapping = it->second;
|
||||
const ScopeData *scope = GetScope(offs);
|
||||
|
||||
if(mapping.debugVar == Id())
|
||||
{
|
||||
// if the Ids are empty, this is a scope exiting. Potentially multiple scopes at
|
||||
// once. The only scopes that could have exited are the ones that we were previously
|
||||
// in, so start from the scope of the previous instruction and walk up its parents.
|
||||
// Set curId to empty on all the locals of any scope that's exited
|
||||
const ScopeData *scope = GetScope(startOffs);
|
||||
while(scope)
|
||||
{
|
||||
if(scope->end <= endOffs)
|
||||
{
|
||||
m_DebugInfo.activeLocalMappings.removeIf(
|
||||
[scope](const LocalMapping &l) { return scope->locals.contains(l.sourceVar); });
|
||||
}
|
||||
|
||||
scope = scope->parent;
|
||||
}
|
||||
}
|
||||
else if(thread.ids[mapping.debugVar].type == VarType::Unknown)
|
||||
{
|
||||
if(!scope)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
|
||||
// track which mappings we've processed, so if the same variable has mappings in multiple scopes
|
||||
// we only pick the innermost.
|
||||
rdcarray<LocalMapping> processed;
|
||||
|
||||
while(scope)
|
||||
{
|
||||
LocalMapping search;
|
||||
search.sourceVar = mapping.sourceVar;
|
||||
|
||||
// find start of this sourceVar
|
||||
LocalMapping *pos = std::lower_bound(m_DebugInfo.activeLocalMappings.begin(),
|
||||
m_DebugInfo.activeLocalMappings.end(), search);
|
||||
|
||||
// look over all existing local mappings, and if any have been made redundant remove
|
||||
// them
|
||||
for(size_t i = (pos - m_DebugInfo.activeLocalMappings.begin());
|
||||
i < m_DebugInfo.activeLocalMappings.size();)
|
||||
for(size_t m = 0; m < scope->localMappings.size(); m++)
|
||||
{
|
||||
if(m_DebugInfo.activeLocalMappings[i].sourceVar != mapping.sourceVar)
|
||||
const LocalMapping &mapping = scope->localMappings[m];
|
||||
|
||||
// if this mapping is past the current instruction, stop here.
|
||||
if(mapping.instIndex > i.instruction)
|
||||
break;
|
||||
|
||||
if(mapping.isSourceSupersetOf(m_DebugInfo.activeLocalMappings[i]))
|
||||
// see if this mapping is superceded by a later mapping that is in scope for this
|
||||
// instruction. This is a bit inefficient but simple. The alternative would be to do record
|
||||
// start and end points for each mapping and update the end points, but this is simple and
|
||||
// should be limited since it's only per-scope
|
||||
bool supercede = false;
|
||||
for(size_t n = m + 1; n < scope->localMappings.size(); n++)
|
||||
{
|
||||
m_DebugInfo.activeLocalMappings.erase(i);
|
||||
continue;
|
||||
}
|
||||
const LocalMapping &laterMapping = scope->localMappings[n];
|
||||
|
||||
i++;
|
||||
}
|
||||
// if this mapping is past the current instruction, stop here.
|
||||
if(laterMapping.instIndex > i.instruction)
|
||||
break;
|
||||
|
||||
mapping.stepIndex = state.stepIndex;
|
||||
|
||||
// now add the new mapping in sorted order
|
||||
pos = std::lower_bound(m_DebugInfo.activeLocalMappings.begin(),
|
||||
m_DebugInfo.activeLocalMappings.end(), mapping);
|
||||
m_DebugInfo.activeLocalMappings.insert(pos - m_DebugInfo.activeLocalMappings.begin(), mapping);
|
||||
}
|
||||
}
|
||||
|
||||
// start with the global source vars
|
||||
state.sourceVars = globalSourceVars;
|
||||
|
||||
const ScopeData *scope = GetScope(endOffs);
|
||||
// only add locals when in a scope
|
||||
if(scope)
|
||||
{
|
||||
// get the function, only add locals that are in this function or a block child
|
||||
const ScopeData *func = scope;
|
||||
while(func && func->parent && func->type != DebugScope::Function)
|
||||
func = func->parent;
|
||||
|
||||
rdcarray<LocalMapping> sorted = m_DebugInfo.activeLocalMappings;
|
||||
std::sort(sorted.begin(), sorted.end(), [&thread](const LocalMapping &a, const LocalMapping &b) {
|
||||
size_t aStep = a.stepIndex;
|
||||
size_t bStep = b.stepIndex;
|
||||
|
||||
// declarations use the step index of the last write rather than the step index they
|
||||
// were added
|
||||
if(a.isDeclare && thread.lastWrite[a.debugVar] > 0)
|
||||
aStep = thread.lastWrite[a.debugVar];
|
||||
if(b.isDeclare && thread.lastWrite[b.debugVar] > 0)
|
||||
bStep = thread.lastWrite[b.debugVar];
|
||||
|
||||
return aStep < bStep;
|
||||
});
|
||||
|
||||
for(const LocalMapping &mapping : sorted)
|
||||
{
|
||||
const LocalData &l = m_DebugInfo.locals[mapping.sourceVar];
|
||||
|
||||
const ScopeData *lfunc = l.scope;
|
||||
while(lfunc && lfunc->parent && lfunc->type != DebugScope::Function)
|
||||
lfunc = lfunc->parent;
|
||||
|
||||
if(lfunc != func)
|
||||
continue;
|
||||
|
||||
// if it doesn't have indexes this is simple, set up a 1:1 map
|
||||
if(mapping.indexes.isEmpty())
|
||||
{
|
||||
SourceVariableMapping sourceVar;
|
||||
|
||||
const TypeData *typeWalk = l.type;
|
||||
|
||||
sourceVar.name = l.name;
|
||||
sourceVar.offset = 0;
|
||||
sourceVar.rows = 1U;
|
||||
sourceVar.columns = 1U;
|
||||
|
||||
if(typeWalk->matSize != 0)
|
||||
{
|
||||
const TypeData &vec = m_DebugInfo.types[typeWalk->baseType];
|
||||
const TypeData &scalar = m_DebugInfo.types[vec.baseType];
|
||||
|
||||
sourceVar.type = scalar.type;
|
||||
|
||||
if(typeWalk->colMajorMat)
|
||||
{
|
||||
sourceVar.rows = RDCMAX(1U, vec.vecSize);
|
||||
sourceVar.columns = RDCMAX(1U, typeWalk->matSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
sourceVar.rows = RDCMAX(1U, typeWalk->matSize);
|
||||
sourceVar.columns = RDCMAX(1U, vec.vecSize);
|
||||
}
|
||||
}
|
||||
else if(typeWalk->vecSize != 0)
|
||||
{
|
||||
const TypeData &scalar = m_DebugInfo.types[typeWalk->baseType];
|
||||
|
||||
sourceVar.type = scalar.type;
|
||||
sourceVar.columns = RDCMAX(1U, typeWalk->vecSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
while(typeWalk && typeWalk->baseType != Id() && typeWalk->type == VarType::Unknown)
|
||||
typeWalk = &m_DebugInfo.types[typeWalk->baseType];
|
||||
|
||||
sourceVar.type = typeWalk->type;
|
||||
if(sourceVar.type == VarType::Unknown)
|
||||
sourceVar.type = VarType::Struct;
|
||||
|
||||
ShaderVariable var = ReadFromPointer(thread.ids[mapping.debugVar]);
|
||||
|
||||
if(var.type != VarType::Struct)
|
||||
{
|
||||
sourceVar.rows = var.rows;
|
||||
sourceVar.columns = var.columns;
|
||||
}
|
||||
}
|
||||
|
||||
for(uint32_t x = 0; x < sourceVar.rows * sourceVar.columns; x++)
|
||||
sourceVar.variables.push_back(
|
||||
DebugVariableReference(DebugVariableType::Variable, GetRawName(mapping.debugVar), x));
|
||||
|
||||
state.sourceVars.push_back(sourceVar);
|
||||
}
|
||||
else
|
||||
{
|
||||
SourceVariableMapping sourceVar;
|
||||
|
||||
rdcarray<uint32_t> indexes = mapping.indexes;
|
||||
|
||||
const TypeData *typeWalk = l.type;
|
||||
|
||||
sourceVar.name = l.name;
|
||||
sourceVar.offset = 0;
|
||||
sourceVar.rows = 1U;
|
||||
sourceVar.columns = 1U;
|
||||
|
||||
while(!indexes.empty())
|
||||
{
|
||||
if(typeWalk->arrayDimension > 0)
|
||||
{
|
||||
uint32_t numIdxs = (uint32_t)indexes.size();
|
||||
for(size_t i = 0; i < RDCMIN(typeWalk->arrayDimension, numIdxs); i++)
|
||||
{
|
||||
sourceVar.name += StringFormat::Fmt("[%u]", indexes.back());
|
||||
indexes.pop_back();
|
||||
}
|
||||
|
||||
typeWalk = &m_DebugInfo.types[typeWalk->baseType];
|
||||
}
|
||||
else if(!typeWalk->structMembers.empty())
|
||||
{
|
||||
uint32_t idx = indexes.back();
|
||||
indexes.pop_back();
|
||||
|
||||
sourceVar.name += StringFormat::Fmt(".%s", typeWalk->structMembers[idx].first.c_str());
|
||||
|
||||
typeWalk = &m_DebugInfo.types[typeWalk->structMembers[idx].second];
|
||||
}
|
||||
else
|
||||
// if this mapping will supercede
|
||||
if(laterMapping.isSourceSupersetOf(mapping))
|
||||
{
|
||||
supercede = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RDCASSERT(indexes.empty());
|
||||
|
||||
if(typeWalk->matSize != 0)
|
||||
for(size_t n = 0; n < processed.size(); n++)
|
||||
{
|
||||
const TypeData &vec = m_DebugInfo.types[typeWalk->baseType];
|
||||
const TypeData &scalar = m_DebugInfo.types[vec.baseType];
|
||||
|
||||
sourceVar.type = scalar.type;
|
||||
|
||||
if(typeWalk->colMajorMat)
|
||||
if(processed[n].isSourceSupersetOf(mapping))
|
||||
{
|
||||
sourceVar.rows = RDCMAX(1U, vec.vecSize);
|
||||
sourceVar.columns = RDCMAX(1U, typeWalk->matSize);
|
||||
supercede = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// don't add the current mapping if it's going to be superceded.
|
||||
if(supercede)
|
||||
continue;
|
||||
|
||||
const LocalData &l = m_DebugInfo.locals[mapping.sourceVar];
|
||||
|
||||
// if it doesn't have indexes this is simple, set up a 1:1 map
|
||||
if(mapping.indexes.isEmpty())
|
||||
{
|
||||
SourceVariableMapping sourceVar;
|
||||
|
||||
const TypeData *typeWalk = l.type;
|
||||
|
||||
sourceVar.name = l.name;
|
||||
sourceVar.offset = 0;
|
||||
sourceVar.rows = 1U;
|
||||
sourceVar.columns = 1U;
|
||||
|
||||
// skip past any pointer types to get the 'real' type that we'll see
|
||||
while(typeWalk && typeWalk->baseType != Id() && typeWalk->type == VarType::GPUPointer)
|
||||
typeWalk = &m_DebugInfo.types[typeWalk->baseType];
|
||||
|
||||
if(typeWalk->matSize != 0)
|
||||
{
|
||||
const TypeData &vec = m_DebugInfo.types[typeWalk->baseType];
|
||||
const TypeData &scalar = m_DebugInfo.types[vec.baseType];
|
||||
|
||||
sourceVar.type = scalar.type;
|
||||
|
||||
if(typeWalk->colMajorMat)
|
||||
{
|
||||
sourceVar.rows = RDCMAX(1U, vec.vecSize);
|
||||
sourceVar.columns = RDCMAX(1U, typeWalk->matSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
sourceVar.rows = RDCMAX(1U, typeWalk->matSize);
|
||||
sourceVar.columns = RDCMAX(1U, vec.vecSize);
|
||||
}
|
||||
}
|
||||
else if(typeWalk->vecSize != 0)
|
||||
{
|
||||
const TypeData &scalar = m_DebugInfo.types[typeWalk->baseType];
|
||||
|
||||
sourceVar.type = scalar.type;
|
||||
sourceVar.columns = RDCMAX(1U, typeWalk->vecSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
sourceVar.rows = RDCMAX(1U, typeWalk->matSize);
|
||||
sourceVar.columns = RDCMAX(1U, vec.vecSize);
|
||||
}
|
||||
}
|
||||
else if(typeWalk->vecSize != 0)
|
||||
{
|
||||
const TypeData &scalar = m_DebugInfo.types[typeWalk->baseType];
|
||||
// walk down until we get to a scalar type, if we get there. This means arrays of basic
|
||||
// types will get the right type
|
||||
while(typeWalk && typeWalk->baseType != Id() && typeWalk->type == VarType::Unknown)
|
||||
typeWalk = &m_DebugInfo.types[typeWalk->baseType];
|
||||
|
||||
sourceVar.type = scalar.type;
|
||||
sourceVar.columns = RDCMAX(1U, typeWalk->vecSize);
|
||||
sourceVar.type = typeWalk->type;
|
||||
|
||||
// anything else we treat as a struct
|
||||
if(sourceVar.type == VarType::Unknown)
|
||||
sourceVar.type = VarType::Struct;
|
||||
}
|
||||
|
||||
for(uint32_t x = 0; x < sourceVar.rows * sourceVar.columns; x++)
|
||||
sourceVar.variables.push_back(DebugVariableReference(DebugVariableType::Variable,
|
||||
GetRawName(mapping.debugVar), x));
|
||||
|
||||
i.sourceVars.push_back(sourceVar);
|
||||
}
|
||||
else
|
||||
{
|
||||
while(typeWalk && typeWalk->baseType != Id() && typeWalk->type == VarType::Unknown)
|
||||
typeWalk = &m_DebugInfo.types[typeWalk->baseType];
|
||||
SourceVariableMapping sourceVar;
|
||||
|
||||
sourceVar.type = typeWalk->type;
|
||||
if(sourceVar.type == VarType::Unknown)
|
||||
sourceVar.type = VarType::Struct;
|
||||
rdcarray<uint32_t> indexes = mapping.indexes;
|
||||
|
||||
const TypeData *typeWalk = l.type;
|
||||
|
||||
sourceVar.name = l.name;
|
||||
sourceVar.offset = 0;
|
||||
sourceVar.rows = 1U;
|
||||
sourceVar.columns = 1U;
|
||||
|
||||
while(!indexes.empty())
|
||||
{
|
||||
if(typeWalk->arrayDimension > 0)
|
||||
{
|
||||
uint32_t numIdxs = (uint32_t)indexes.size();
|
||||
for(size_t a = 0; a < RDCMIN(typeWalk->arrayDimension, numIdxs); a++)
|
||||
{
|
||||
sourceVar.name += StringFormat::Fmt("[%u]", indexes.back());
|
||||
indexes.pop_back();
|
||||
}
|
||||
|
||||
typeWalk = &m_DebugInfo.types[typeWalk->baseType];
|
||||
}
|
||||
else if(!typeWalk->structMembers.empty())
|
||||
{
|
||||
uint32_t idx = indexes.back();
|
||||
indexes.pop_back();
|
||||
|
||||
sourceVar.name += StringFormat::Fmt(".%s", typeWalk->structMembers[idx].first.c_str());
|
||||
|
||||
typeWalk = &m_DebugInfo.types[typeWalk->structMembers[idx].second];
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const char swizzle[] = "xyzw";
|
||||
|
||||
if(typeWalk->matSize != 0)
|
||||
{
|
||||
const TypeData &vec = m_DebugInfo.types[typeWalk->baseType];
|
||||
const TypeData &scalar = m_DebugInfo.types[vec.baseType];
|
||||
|
||||
sourceVar.type = scalar.type;
|
||||
|
||||
if(typeWalk->colMajorMat)
|
||||
{
|
||||
sourceVar.rows = RDCMAX(1U, vec.vecSize);
|
||||
sourceVar.columns = RDCMAX(1U, typeWalk->matSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
sourceVar.rows = RDCMAX(1U, typeWalk->matSize);
|
||||
sourceVar.columns = RDCMAX(1U, vec.vecSize);
|
||||
}
|
||||
|
||||
// two remaining indices selects a scalar within the matrix
|
||||
if(indexes.size() == 2)
|
||||
{
|
||||
uint32_t col = indexes[0];
|
||||
uint32_t row = indexes[1];
|
||||
RDCASSERT(col < 4 && row < 4, col, row);
|
||||
sourceVar.name += StringFormat::Fmt(".row%u.%c", row, swizzle[RDCMIN(col, 3U)]);
|
||||
|
||||
sourceVar.variables.push_back(DebugVariableReference(
|
||||
DebugVariableType::Variable, GetRawName(mapping.debugVar), indexes[0]));
|
||||
}
|
||||
// one remaining index selects a column within the matrix. Since we display source vars
|
||||
// as row-major, this means adding 4 mappings
|
||||
else if(indexes.size() == 1)
|
||||
{
|
||||
uint32_t col = indexes[0];
|
||||
rdcstr name = sourceVar.name;
|
||||
for(uint32_t row = 0; row < sourceVar.rows; row++)
|
||||
{
|
||||
sourceVar.name = name + StringFormat::Fmt(".row%u.%c", row, swizzle[RDCMIN(col, 3U)]);
|
||||
sourceVar.variables.push_back(DebugVariableReference(
|
||||
DebugVariableType::Variable, GetRawName(mapping.debugVar), row));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCASSERT(indexes.empty(), indexes.size());
|
||||
for(uint32_t x = 0; x < sourceVar.rows * sourceVar.columns; x++)
|
||||
sourceVar.variables.push_back(DebugVariableReference(
|
||||
DebugVariableType::Variable, GetRawName(mapping.debugVar), x));
|
||||
}
|
||||
}
|
||||
else if(typeWalk->vecSize != 0)
|
||||
{
|
||||
const TypeData &scalar = m_DebugInfo.types[typeWalk->baseType];
|
||||
|
||||
sourceVar.type = scalar.type;
|
||||
sourceVar.columns = RDCMAX(1U, typeWalk->vecSize);
|
||||
|
||||
// remaining index selects a scalar within the vector
|
||||
if(indexes.size() == 1)
|
||||
{
|
||||
RDCASSERT(indexes[0] < 4, indexes[0]);
|
||||
sourceVar.name += StringFormat::Fmt(".%c", swizzle[RDCMIN(indexes[0], 3U)]);
|
||||
sourceVar.variables.push_back(DebugVariableReference(
|
||||
DebugVariableType::Variable, GetRawName(mapping.debugVar), 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
RDCASSERT(indexes.empty(), indexes.size());
|
||||
for(uint32_t x = 0; x < sourceVar.rows * sourceVar.columns; x++)
|
||||
sourceVar.variables.push_back(DebugVariableReference(
|
||||
DebugVariableType::Variable, GetRawName(mapping.debugVar), x));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// walk down until we get to a scalar type, if we get there. This means arrays of basic
|
||||
// types will get the right type
|
||||
while(typeWalk && typeWalk->baseType != Id() && typeWalk->type == VarType::Unknown)
|
||||
typeWalk = &m_DebugInfo.types[typeWalk->baseType];
|
||||
|
||||
sourceVar.type = typeWalk->type;
|
||||
|
||||
// anything else we treat as a struct
|
||||
if(sourceVar.type == VarType::Unknown)
|
||||
sourceVar.type = VarType::Struct;
|
||||
|
||||
sourceVar.variables.push_back(DebugVariableReference(DebugVariableType::Variable,
|
||||
GetRawName(mapping.debugVar), 0));
|
||||
}
|
||||
|
||||
i.sourceVars.push_back(sourceVar);
|
||||
}
|
||||
|
||||
processed.push_back(mapping);
|
||||
}
|
||||
|
||||
// if we reach a function scope, don't go up any further.
|
||||
if(scope->type == DebugScope::Function)
|
||||
break;
|
||||
|
||||
// move to the parent scope and apply the mappings there
|
||||
scope = scope->parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::FillDefaultSourceVars(rdcarray<InstructionSourceInfo> &instInfo)
|
||||
{
|
||||
rdcarray<SourceVariableMapping> sourceVars;
|
||||
rdcarray<Id> debugVars;
|
||||
|
||||
for(InstructionSourceInfo &i : instInfo)
|
||||
{
|
||||
// the source vars for this instruction are whatever we have currently, because when we're
|
||||
// looking up the source vars for instruction X we are effectively talking abotu the state just
|
||||
// before X executes, not just after.
|
||||
i.sourceVars = sourceVars;
|
||||
|
||||
// now update the sourcevars for after this instruction executed
|
||||
|
||||
size_t offs = instructionOffsets[i.instruction];
|
||||
|
||||
Iter it(m_SPIRV, offs);
|
||||
|
||||
OpDecoder opdata(it);
|
||||
|
||||
Id id = opdata.result;
|
||||
|
||||
// stores can bring their pointer into being, if it's the first write.
|
||||
if(opdata.op == Op::Store)
|
||||
id = OpStore(it).pointer;
|
||||
|
||||
// if this is the offset where the id's live range begins, try to add the source name for it if
|
||||
// one exists.
|
||||
if(id != Id() && idLiveRange[id].first == offs)
|
||||
{
|
||||
rdcstr name;
|
||||
|
||||
auto dyn = dynamicNames.find(id);
|
||||
if(dyn != dynamicNames.end())
|
||||
name = dyn->second;
|
||||
else
|
||||
name = strings[id];
|
||||
|
||||
if(!name.empty())
|
||||
{
|
||||
SourceVariableMapping sourceVar;
|
||||
|
||||
const DataType *type = &GetTypeForId(id);
|
||||
|
||||
while(type->type == DataType::PointerType || type->type == DataType::ArrayType)
|
||||
type = &GetType(type->InnerType());
|
||||
|
||||
sourceVar.name = name;
|
||||
sourceVar.offset = 0;
|
||||
if(type->type == DataType::MatrixType || type->type == DataType::VectorType ||
|
||||
type->type == DataType::ScalarType)
|
||||
sourceVar.type = type->scalar().Type();
|
||||
else if(type->type == DataType::StructType)
|
||||
sourceVar.type = VarType::Struct;
|
||||
else if(type->type == DataType::ImageType || type->type == DataType::SampledImageType ||
|
||||
type->type == DataType::SamplerType)
|
||||
sourceVar.type = VarType::ReadOnlyResource;
|
||||
sourceVar.rows = RDCMAX(1U, (uint32_t)type->matrix().count);
|
||||
sourceVar.columns = RDCMAX(1U, (uint32_t)type->vector().count);
|
||||
rdcstr rawName = GetRawName(id);
|
||||
for(uint32_t x = 0; x < sourceVar.rows * sourceVar.columns; x++)
|
||||
sourceVar.variables.push_back(
|
||||
DebugVariableReference(DebugVariableType::Variable, GetRawName(mapping.debugVar), x));
|
||||
DebugVariableReference(DebugVariableType::Variable, rawName, x));
|
||||
|
||||
state.sourceVars.push_back(sourceVar);
|
||||
sourceVars.push_back(sourceVar);
|
||||
debugVars.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
// see which vars have expired
|
||||
for(size_t d = 0; d < debugVars.size();)
|
||||
{
|
||||
if(offs > idLiveRange[debugVars[d]].second)
|
||||
{
|
||||
sourceVars.erase(d);
|
||||
debugVars.erase(d);
|
||||
continue;
|
||||
}
|
||||
|
||||
d++;
|
||||
}
|
||||
|
||||
// all variables/IDs are function-local
|
||||
if(opdata.op == Op::FunctionEnd)
|
||||
{
|
||||
sourceVars.clear();
|
||||
debugVars.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1569,15 +1676,9 @@ rdcarray<ShaderDebugState> Debugger::ContinueDebug()
|
||||
|
||||
if(lane == activeLaneIndex)
|
||||
{
|
||||
size_t beginOffs = instructionOffsets[thread.nextInstruction];
|
||||
|
||||
thread.EnterEntryPoint(&initial);
|
||||
thread.FillCallstack(initial);
|
||||
initial.nextInstruction = thread.nextInstruction;
|
||||
initial.sourceVars = thread.sourceVars;
|
||||
|
||||
if(m_DebugInfo.valid)
|
||||
ApplyDebugSourceVars(beginOffs, thread, initial);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1645,19 +1746,13 @@ rdcarray<ShaderDebugState> Debugger::ContinueDebug()
|
||||
for(size_t l = 0; l < thread.live.size();)
|
||||
{
|
||||
Id id = thread.live[l];
|
||||
if(idDeathOffset[id] < instOffs)
|
||||
if(idLiveRange[id].second < instOffs)
|
||||
{
|
||||
thread.live.erase(l);
|
||||
ShaderVariableChange change;
|
||||
change.before = GetPointerValue(thread.ids[id]);
|
||||
state.changes.push_back(change);
|
||||
|
||||
rdcstr name = GetRawName(id);
|
||||
|
||||
thread.sourceVars.removeIf([name](const SourceVariableMapping &var) {
|
||||
return var.variables[0].name.beginsWith(name);
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1684,8 +1779,6 @@ rdcarray<ShaderDebugState> Debugger::ContinueDebug()
|
||||
|
||||
if(m_DebugInfo.valid)
|
||||
{
|
||||
ApplyDebugSourceVars(instOffs, thread, state);
|
||||
|
||||
size_t endOffs = instructionOffsets[thread.nextInstruction - 1];
|
||||
|
||||
// append any inlined functions to the top of the stack
|
||||
@@ -1719,19 +1812,6 @@ rdcarray<ShaderDebugState> Debugger::ContinueDebug()
|
||||
inlined = inlined->parent;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
state.sourceVars = thread.sourceVars;
|
||||
|
||||
// sort sourceVars by last write to the underlying variable
|
||||
std::sort(state.sourceVars.begin(), state.sourceVars.end(),
|
||||
[&thread](const SourceVariableMapping &a, const SourceVariableMapping &b) {
|
||||
Id aId = ParseRawName(a.variables[0].name);
|
||||
Id bId = ParseRawName(b.variables[0].name);
|
||||
|
||||
return thread.lastWrite[aId] < thread.lastWrite[bId];
|
||||
});
|
||||
}
|
||||
|
||||
ret.push_back(std::move(state));
|
||||
|
||||
@@ -2336,42 +2416,6 @@ rdcstr Debugger::GetHumanName(Id id)
|
||||
return name;
|
||||
}
|
||||
|
||||
const rdcarray<SourceVariableMapping> &Debugger::GetGlobalSourceVars()
|
||||
{
|
||||
static const rdcarray<SourceVariableMapping> empty;
|
||||
return m_DebugInfo.valid ? empty : globalSourceVars;
|
||||
}
|
||||
|
||||
void Debugger::AddSourceVars(rdcarray<SourceVariableMapping> &sourceVars, const ShaderVariable &var,
|
||||
Id id)
|
||||
{
|
||||
if(m_DebugInfo.valid)
|
||||
return;
|
||||
|
||||
rdcstr name;
|
||||
|
||||
auto it = dynamicNames.find(id);
|
||||
if(it != dynamicNames.end())
|
||||
name = it->second;
|
||||
else
|
||||
name = strings[id];
|
||||
|
||||
if(!name.empty())
|
||||
{
|
||||
SourceVariableMapping sourceVar;
|
||||
|
||||
sourceVar.name = name;
|
||||
sourceVar.offset = 0;
|
||||
sourceVar.type = var.type;
|
||||
sourceVar.rows = RDCMAX(1U, (uint32_t)var.rows);
|
||||
sourceVar.columns = RDCMAX(1U, (uint32_t)var.columns);
|
||||
for(uint32_t x = 0; x < sourceVar.rows * sourceVar.columns; x++)
|
||||
sourceVar.variables.push_back(DebugVariableReference(DebugVariableType::Variable, var.name, x));
|
||||
|
||||
sourceVars.push_back(sourceVar);
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::CalcActiveMask(rdcarray<bool> &activeMask)
|
||||
{
|
||||
// one bool per workgroup thread
|
||||
@@ -2827,7 +2871,9 @@ void Debugger::PreParse(uint32_t maxId)
|
||||
Processor::PreParse(maxId);
|
||||
|
||||
strings.resize(idTypes.size());
|
||||
idDeathOffset.resize(idTypes.size());
|
||||
idLiveRange.resize(idTypes.size());
|
||||
|
||||
m_InstInfo.reserve(idTypes.size());
|
||||
}
|
||||
|
||||
void Debugger::PostParse()
|
||||
@@ -2839,40 +2885,35 @@ void Debugger::PostParse()
|
||||
|
||||
// global IDs never hit a death point
|
||||
for(const Variable &v : globals)
|
||||
idDeathOffset[v.id] = ~0U;
|
||||
idLiveRange[v.id].second = ~0U;
|
||||
|
||||
if(m_DebugInfo.valid)
|
||||
{
|
||||
// every scope's parent lasts at least as long as it
|
||||
for(auto it = m_DebugInfo.scopes.begin(); it != m_DebugInfo.scopes.end(); ++it)
|
||||
{
|
||||
ScopeData *scope = &it->second;
|
||||
|
||||
// keep every ID referenced by a local alive until the scope ends. We do this even if a source
|
||||
// variable maps to multiple debug variables and technically the earlier ones could be left to
|
||||
// die when superceeded by the later ones. This is simple and only means a little bloating of
|
||||
// debug variables in the UI (which generally won't be viewed directly anyway)
|
||||
for(LocalMapping &m : scope->localMappings)
|
||||
{
|
||||
Id id = m.debugVar;
|
||||
|
||||
if(id == Id())
|
||||
continue;
|
||||
|
||||
idLiveRange[id].second = RDCMAX(scope->end + 1, idLiveRange[id].second);
|
||||
}
|
||||
|
||||
// every scope's parent lasts at least as long as it
|
||||
while(scope->parent)
|
||||
{
|
||||
scope->parent->end = RDCMAX(scope->parent->end, scope->end);
|
||||
scope = scope->parent;
|
||||
}
|
||||
}
|
||||
|
||||
// add a dummy localMappings entry for each scope end
|
||||
for(auto it = m_DebugInfo.scopes.begin(); it != m_DebugInfo.scopes.end(); ++it)
|
||||
m_DebugInfo.localMappings[it->second.end] = {0, Id(), Id()};
|
||||
|
||||
for(auto it = m_DebugInfo.localMappings.begin(); it != m_DebugInfo.localMappings.end(); ++it)
|
||||
{
|
||||
if(it->second.debugVar == Id())
|
||||
continue;
|
||||
|
||||
const LocalData &l = m_DebugInfo.locals[it->second.sourceVar];
|
||||
if(l.scope == NULL)
|
||||
continue;
|
||||
|
||||
// keep last raw ID alive until the scope ends
|
||||
Id id = it->second.debugVar;
|
||||
|
||||
idDeathOffset[id] = RDCMAX(l.scope->end + 1, RDCMAX(it->first + 1, idDeathOffset[id]));
|
||||
}
|
||||
}
|
||||
|
||||
memberNames.clear();
|
||||
@@ -2888,10 +2929,15 @@ void Debugger::RegisterOp(Iter it)
|
||||
// since blocks always end with a terminator that doesn't consume IDs we're interested in
|
||||
// (variables) we'll always have one extra instruction to step to
|
||||
OpDecoder::ForEachID(it, [this, &it](Id id, bool result) {
|
||||
idDeathOffset[id] = RDCMAX(it.offs() + 1, idDeathOffset[id]);
|
||||
if(result)
|
||||
idLiveRange[id].first = it.offs();
|
||||
idLiveRange[id].second = RDCMAX(it.offs() + 1, idLiveRange[id].second);
|
||||
});
|
||||
|
||||
bool leaveScope = false;
|
||||
bool executable = curFunction != NULL;
|
||||
|
||||
const uint32_t curInstIndex = (uint32_t)instructionOffsets.size();
|
||||
|
||||
if(opdata.op == Op::ExtInst)
|
||||
{
|
||||
@@ -2903,7 +2949,7 @@ void Debugger::RegisterOp(Iter it)
|
||||
for(const uint32_t param : extinst.params)
|
||||
{
|
||||
Id id = Id::fromWord(param);
|
||||
idDeathOffset[id] = RDCMAX(it.offs() + 1, idDeathOffset[id]);
|
||||
idLiveRange[id].second = RDCMAX(it.offs() + 1, idLiveRange[id].second);
|
||||
}
|
||||
}
|
||||
else if(knownExtSet[ExtSet_Printf] == extinst.set)
|
||||
@@ -2912,7 +2958,7 @@ void Debugger::RegisterOp(Iter it)
|
||||
for(const uint32_t param : extinst.params)
|
||||
{
|
||||
Id id = Id::fromWord(param);
|
||||
idDeathOffset[id] = RDCMAX(it.offs() + 1, idDeathOffset[id]);
|
||||
idLiveRange[id].second = RDCMAX(it.offs() + 1, idLiveRange[id].second);
|
||||
}
|
||||
}
|
||||
else if(knownExtSet[ExtSet_ShaderDbg] == extinst.set)
|
||||
@@ -2920,6 +2966,9 @@ void Debugger::RegisterOp(Iter it)
|
||||
// the types are identical just with different accessors
|
||||
OpShaderDbg &dbg = (OpShaderDbg &)extinst;
|
||||
|
||||
if(dbg.inst != ShaderDbg::Value)
|
||||
executable = false;
|
||||
|
||||
switch(dbg.inst)
|
||||
{
|
||||
case ShaderDbg::Source:
|
||||
@@ -3004,6 +3053,7 @@ void Debugger::RegisterOp(Iter it)
|
||||
case ShaderDbg::TypePointer:
|
||||
{
|
||||
m_DebugInfo.types[dbg.result].baseType = dbg.arg<Id>(0);
|
||||
m_DebugInfo.types[dbg.result].type = VarType::GPUPointer;
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::TypeVector:
|
||||
@@ -3088,6 +3138,9 @@ void Debugger::RegisterOp(Iter it)
|
||||
|
||||
m_DebugInfo.curScope = &m_DebugInfo.scopes[dbg.arg<Id>(0)];
|
||||
|
||||
m_DebugInfo.curScope->localMappings.append(std::move(m_DebugInfo.scopelessMappings));
|
||||
m_DebugInfo.scopelessMappings.clear();
|
||||
|
||||
if(dbg.params.size() >= 2)
|
||||
m_DebugInfo.curInline = &m_DebugInfo.inlined[dbg.arg<Id>(1)];
|
||||
else
|
||||
@@ -3131,14 +3184,19 @@ void Debugger::RegisterOp(Iter it)
|
||||
case ShaderDbg::Declare:
|
||||
case ShaderDbg::Value:
|
||||
{
|
||||
Id id = dbg.arg<Id>(1);
|
||||
Id sourceVarId = dbg.arg<Id>(0);
|
||||
Id debugVarId = dbg.arg<Id>(1);
|
||||
|
||||
LocalMapping &mapping = m_DebugInfo.localMappings[it.offs()];
|
||||
rdcarray<LocalMapping> &mappings = m_DebugInfo.curScope
|
||||
? m_DebugInfo.curScope->localMappings
|
||||
: m_DebugInfo.scopelessMappings;
|
||||
|
||||
mapping = {0, dbg.arg<Id>(0), id, dbg.inst == ShaderDbg::Declare};
|
||||
mappings.push_back({curInstIndex, sourceVarId, debugVarId, dbg.inst == ShaderDbg::Declare});
|
||||
LocalMapping &mapping = mappings.back();
|
||||
|
||||
if(constants.find(id) != constants.end() && !m_DebugInfo.constants.contains(id))
|
||||
m_DebugInfo.constants.push_back(id);
|
||||
if(constants.find(debugVarId) != constants.end() &&
|
||||
!m_DebugInfo.constants.contains(debugVarId))
|
||||
m_DebugInfo.constants.push_back(debugVarId);
|
||||
|
||||
mapping.indexes.resize(dbg.params.size() - 3);
|
||||
for(uint32_t i = 0; i < mapping.indexes.size(); i++)
|
||||
@@ -3162,6 +3220,7 @@ void Debugger::RegisterOp(Iter it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::InlinedAt:
|
||||
@@ -3243,18 +3302,20 @@ void Debugger::RegisterOp(Iter it)
|
||||
if(!m_DebugInfo.valid)
|
||||
m_CurLineCol = LineColumnInfo();
|
||||
}
|
||||
else
|
||||
else if(executable)
|
||||
{
|
||||
// for debug info, only apply line info if we're in a scope. Otherwise the line info may not
|
||||
// apply to this instruction. This means OpPhi's will never be line mapped
|
||||
if(m_DebugInfo.valid)
|
||||
{
|
||||
if(m_DebugInfo.curScope)
|
||||
m_LineColInfo[it.offs()] = m_CurLineCol;
|
||||
m_InstInfo.push_back({curInstIndex, m_CurLineCol});
|
||||
else
|
||||
m_InstInfo.push_back({curInstIndex, LineColumnInfo()});
|
||||
}
|
||||
else
|
||||
{
|
||||
m_LineColInfo[it.offs()] = m_CurLineCol;
|
||||
m_InstInfo.push_back({curInstIndex, m_CurLineCol});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3354,13 +3415,11 @@ void Debugger::RegisterOp(Iter it)
|
||||
|
||||
if(opdata.op == Op::FunctionEnd)
|
||||
{
|
||||
// don't automatically kill function parameters and variables. They will be manually killed when
|
||||
// returning from a function's scope
|
||||
// allow function parameters and variables to live indefinitely
|
||||
for(const Id &id : curFunction->parameters)
|
||||
idDeathOffset[id] = ~0U;
|
||||
idLiveRange[id].second = ~0U;
|
||||
for(const Id &id : curFunction->variables)
|
||||
idDeathOffset[id] = ~0U;
|
||||
|
||||
idLiveRange[id].second = ~0U;
|
||||
curFunction = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -360,6 +360,16 @@ void DoSerialise(SerialiserType &ser, LineColumnInfo &el)
|
||||
SIZE_CHECK(24);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
void DoSerialise(SerialiserType &ser, InstructionSourceInfo &el)
|
||||
{
|
||||
SERIALISE_MEMBER(instruction);
|
||||
SERIALISE_MEMBER(lineInfo);
|
||||
SERIALISE_MEMBER(sourceVars);
|
||||
|
||||
SIZE_CHECK(56);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
void DoSerialise(SerialiserType &ser, ShaderVariableChange &el)
|
||||
{
|
||||
@@ -376,10 +386,9 @@ void DoSerialise(SerialiserType &ser, ShaderDebugState &el)
|
||||
SERIALISE_MEMBER(stepIndex);
|
||||
SERIALISE_MEMBER(flags);
|
||||
SERIALISE_MEMBER(changes);
|
||||
SERIALISE_MEMBER(sourceVars);
|
||||
SERIALISE_MEMBER(callstack);
|
||||
|
||||
SIZE_CHECK(88);
|
||||
SIZE_CHECK(64);
|
||||
}
|
||||
|
||||
template <typename SerialiserType>
|
||||
@@ -392,7 +401,7 @@ void DoSerialise(SerialiserType &ser, ShaderDebugTrace &el)
|
||||
SERIALISE_MEMBER(readOnlyResources);
|
||||
SERIALISE_MEMBER(readWriteResources);
|
||||
SERIALISE_MEMBER(sourceVars);
|
||||
SERIALISE_MEMBER(lineInfo);
|
||||
SERIALISE_MEMBER(instInfo);
|
||||
|
||||
// serialise the debugger pointer entirely opaquely, this is only used for replay proxying
|
||||
uint64_t debugger = 0;
|
||||
|
||||
Reference in New Issue
Block a user