mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-06 01:50:38 +00:00
Handle indexes and partial source mapping in SPIR-V debug info
This commit is contained in:
@@ -250,6 +250,17 @@ enum class DebugScope
|
||||
Block,
|
||||
};
|
||||
|
||||
struct TypeData
|
||||
{
|
||||
VarType type = VarType::Unknown;
|
||||
uint32_t vecSize = 0, matSize = 0;
|
||||
bool colMajorMat = false;
|
||||
|
||||
Id baseType;
|
||||
uint32_t arrayDimension = 0;
|
||||
rdcarray<rdcpair<rdcstr, Id>> structMembers;
|
||||
};
|
||||
|
||||
struct ScopeData
|
||||
{
|
||||
DebugScope type;
|
||||
@@ -274,11 +285,48 @@ struct LocalData
|
||||
{
|
||||
rdcstr name;
|
||||
ScopeData *scope;
|
||||
|
||||
Id curId;
|
||||
TypeData *type;
|
||||
};
|
||||
|
||||
typedef rdcpair<Id, Id> LocalMapping;
|
||||
struct LocalMapping
|
||||
{
|
||||
bool operator<(const LocalMapping &o) const
|
||||
{
|
||||
if(sourceVar != o.sourceVar)
|
||||
return sourceVar < o.sourceVar;
|
||||
if(indexes != o.indexes)
|
||||
return indexes < o.indexes;
|
||||
return debugVar < o.debugVar;
|
||||
}
|
||||
|
||||
bool isSourceSupersetOf(const LocalMapping &o) const
|
||||
{
|
||||
// this mapping is a superset of the other if:
|
||||
|
||||
// it's the same source var
|
||||
if(sourceVar != o.sourceVar)
|
||||
return false;
|
||||
|
||||
// it contains the same or fewer indices
|
||||
if(o.indexes.size() < indexes.size())
|
||||
return false;
|
||||
|
||||
// the common prefix of indexes is identical
|
||||
for(size_t i = 0; i < indexes.size(); i++)
|
||||
if(indexes[i] != o.indexes[i])
|
||||
return false;
|
||||
|
||||
// if all those conditions are true, we either map to the same index (indexes size is the same -
|
||||
// likely case) or else we cover a whole sub-tree where the other only covers a leaf (other has
|
||||
// more indexes - unlikely but possible)
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t offset;
|
||||
Id sourceVar;
|
||||
Id debugVar;
|
||||
rdcarray<uint32_t> indexes;
|
||||
};
|
||||
|
||||
class Debugger : public Processor, public ShaderDebugger
|
||||
{
|
||||
@@ -406,6 +454,7 @@ private:
|
||||
{
|
||||
bool valid = false;
|
||||
|
||||
SparseIdMap<TypeData> types;
|
||||
SparseIdMap<ScopeData> scopes;
|
||||
SparseIdMap<InlineData> inlined;
|
||||
ScopeData *curScope = NULL;
|
||||
@@ -422,7 +471,11 @@ private:
|
||||
std::map<size_t, ScopeData *> lineScope;
|
||||
std::map<size_t, InlineData *> lineInline;
|
||||
std::map<size_t, LocalMapping> localMappings;
|
||||
|
||||
rdcarray<LocalMapping> activeLocalMappings;
|
||||
} m_DebugInfo;
|
||||
|
||||
const ScopeData *GetScope(size_t offset) const;
|
||||
};
|
||||
|
||||
// this does a 'safe' value assignment, by doing parallel depth-first iteration of both variables
|
||||
|
||||
@@ -1347,66 +1347,238 @@ rdcarray<ShaderDebugState> Debugger::ContinueDebug()
|
||||
|
||||
const LocalMapping &mapping = it->second;
|
||||
|
||||
if(mapping.second == Id())
|
||||
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
|
||||
ScopeData *scope = m_DebugInfo.lineScope[startOffs];
|
||||
const ScopeData *scope = GetScope(startOffs);
|
||||
while(scope)
|
||||
{
|
||||
if(scope->end <= endOffs)
|
||||
{
|
||||
for(Id id : scope->locals)
|
||||
m_DebugInfo.locals[id].curId = Id();
|
||||
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)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise this is indicating the new current Id of a local
|
||||
m_DebugInfo.locals[mapping.first].curId = mapping.second;
|
||||
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();)
|
||||
{
|
||||
if(m_DebugInfo.activeLocalMappings[i].sourceVar != mapping.sourceVar)
|
||||
break;
|
||||
|
||||
if(mapping.isSourceSupersetOf(m_DebugInfo.activeLocalMappings[i]))
|
||||
{
|
||||
m_DebugInfo.activeLocalMappings.erase(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
// now go from the innermost scope to the outermost. At each scope add its locals
|
||||
ScopeData *scope = m_DebugInfo.lineScope[endOffs];
|
||||
while(scope)
|
||||
const ScopeData *scope = GetScope(endOffs);
|
||||
// only add locals when in a scope
|
||||
if(scope)
|
||||
{
|
||||
for(Id id : scope->locals)
|
||||
// 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(),
|
||||
[](const LocalMapping &a, const LocalMapping &b) { return a.offset < b.offset; });
|
||||
|
||||
for(const LocalMapping &mapping : sorted)
|
||||
{
|
||||
const LocalData &l = m_DebugInfo.locals[id];
|
||||
const LocalData &l = m_DebugInfo.locals[mapping.sourceVar];
|
||||
|
||||
if(l.curId != Id())
|
||||
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())
|
||||
{
|
||||
ShaderVariable var = ReadFromPointer(thread.ids[l.curId]);
|
||||
SourceVariableMapping sourceVar;
|
||||
|
||||
// don't map locals to IDs that don't exist yet
|
||||
if(!var.name.empty())
|
||||
const TypeData *typeWalk = l.type;
|
||||
|
||||
sourceVar.name = l.name;
|
||||
sourceVar.offset = 0;
|
||||
sourceVar.rows = 1U;
|
||||
sourceVar.columns = 1U;
|
||||
|
||||
if(typeWalk->matSize != 0)
|
||||
{
|
||||
SourceVariableMapping sourceVar;
|
||||
const TypeData &vec = m_DebugInfo.types[typeWalk->baseType];
|
||||
const TypeData &scalar = m_DebugInfo.types[vec.baseType];
|
||||
|
||||
sourceVar.name = l.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));
|
||||
sourceVar.type = scalar.type;
|
||||
|
||||
state.sourceVars.push_back(sourceVar);
|
||||
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
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
RDCASSERT(indexes.empty());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
scope = scope->parent;
|
||||
}
|
||||
|
||||
// append any inlined functions to the top of the stack
|
||||
@@ -1417,7 +1589,7 @@ rdcarray<ShaderDebugState> Debugger::ContinueDebug()
|
||||
// start with the current scope, it refers to the *inlined* function
|
||||
if(inlined)
|
||||
{
|
||||
scope = m_DebugInfo.lineScope[endOffs];
|
||||
scope = GetScope(endOffs);
|
||||
// find the function parent of the current scope
|
||||
while(scope && scope->parent && scope->type == DebugScope::Block)
|
||||
scope = scope->parent;
|
||||
@@ -1443,17 +1615,17 @@ rdcarray<ShaderDebugState> Debugger::ContinueDebug()
|
||||
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];
|
||||
});
|
||||
}
|
||||
|
||||
// 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(state);
|
||||
|
||||
steps++;
|
||||
@@ -2553,6 +2725,14 @@ bool Debugger::IsDebugExtInstSet(Id id) const
|
||||
return knownExtSet[ExtSet_ShaderDbg] == id;
|
||||
}
|
||||
|
||||
const ScopeData *Debugger::GetScope(size_t offset) const
|
||||
{
|
||||
auto it = m_DebugInfo.lineScope.find(offset);
|
||||
if(it == m_DebugInfo.lineScope.end())
|
||||
return NULL;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void Debugger::PreParse(uint32_t maxId)
|
||||
{
|
||||
Processor::PreParse(maxId);
|
||||
@@ -2587,26 +2767,22 @@ void Debugger::PostParse()
|
||||
|
||||
// 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] = {Id(), Id()};
|
||||
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.second == Id())
|
||||
if(it->second.debugVar == Id())
|
||||
continue;
|
||||
|
||||
const LocalData &l = m_DebugInfo.locals[it->second.first];
|
||||
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.second;
|
||||
Id id = it->second.debugVar;
|
||||
|
||||
idDeathOffset[id] = RDCMAX(l.scope->end + 1, RDCMAX(it->first + 1, idDeathOffset[id]));
|
||||
}
|
||||
|
||||
// reset current Ids
|
||||
for(auto it = m_DebugInfo.locals.begin(); it != m_DebugInfo.locals.end(); ++it)
|
||||
it->second.curId = Id();
|
||||
}
|
||||
|
||||
memberNames.clear();
|
||||
@@ -2654,184 +2830,286 @@ void Debugger::RegisterOp(Iter it)
|
||||
// the types are identical just with different accessors
|
||||
OpShaderDbg &dbg = (OpShaderDbg &)extinst;
|
||||
|
||||
if(dbg.inst == ShaderDbg::Source)
|
||||
switch(dbg.inst)
|
||||
{
|
||||
int32_t fileIndex = (int32_t)m_DebugInfo.sources.size();
|
||||
|
||||
m_DebugInfo.sources[dbg.result] = fileIndex;
|
||||
m_DebugInfo.filenames[dbg.result] = strings[dbg.arg<Id>(0)];
|
||||
}
|
||||
else if(dbg.inst == ShaderDbg::CompilationUnit)
|
||||
{
|
||||
m_DebugInfo.scopes[dbg.result] = {
|
||||
DebugScope::CompilationUnit,
|
||||
NULL,
|
||||
1,
|
||||
1,
|
||||
m_DebugInfo.sources[dbg.arg<Id>(2)],
|
||||
0,
|
||||
m_DebugInfo.filenames[dbg.arg<Id>(2)],
|
||||
};
|
||||
}
|
||||
else if(dbg.inst == ShaderDbg::Function)
|
||||
{
|
||||
rdcstr name = strings[dbg.arg<Id>(0)];
|
||||
// ignore arg 1 type
|
||||
// don't use arg 2 source - assume the parent is in the same file so it's redundant
|
||||
uint32_t line = EvaluateConstant(dbg.arg<Id>(3), {}).value.u32v[0];
|
||||
uint32_t column = EvaluateConstant(dbg.arg<Id>(4), {}).value.u32v[0];
|
||||
ScopeData *parent = &m_DebugInfo.scopes[dbg.arg<Id>(5)];
|
||||
// ignore arg 6 linkage name
|
||||
// ignore arg 7 flags
|
||||
// ignore arg 8 scope line
|
||||
// ignore arg 9 (optional) declaration
|
||||
|
||||
m_DebugInfo.scopes[dbg.result] = {
|
||||
DebugScope::Function, parent, line, column, parent->fileIndex, 0, name,
|
||||
};
|
||||
}
|
||||
else if(dbg.inst == ShaderDbg::TypeComposite)
|
||||
{
|
||||
rdcstr name = strings[dbg.arg<Id>(0)];
|
||||
uint32_t tag = EvaluateConstant(dbg.arg<Id>(1), {}).value.u32v[0];
|
||||
const rdcstr tagString[3] = {
|
||||
"class ", "struct ", "union ",
|
||||
};
|
||||
|
||||
// don't use arg 2 source - assume the parent is in the same file so it's redundant
|
||||
uint32_t line = EvaluateConstant(dbg.arg<Id>(3), {}).value.u32v[0];
|
||||
uint32_t column = EvaluateConstant(dbg.arg<Id>(4), {}).value.u32v[0];
|
||||
ScopeData *parent = &m_DebugInfo.scopes[dbg.arg<Id>(5)];
|
||||
// ignore arg 6 linkage name
|
||||
// ignore arg 7 size
|
||||
// ignore arg 8 flags
|
||||
// ignore arg 9... members
|
||||
|
||||
name = tagString[tag % 3] + name;
|
||||
|
||||
m_DebugInfo.scopes[dbg.result] = {
|
||||
DebugScope::Composite, parent, line, column, parent->fileIndex, 0, name,
|
||||
};
|
||||
}
|
||||
else if(dbg.inst == ShaderDbg::LexicalBlock)
|
||||
{
|
||||
// don't use arg 0 source - assume the parent is in the same file so it's redundant
|
||||
uint32_t line = EvaluateConstant(dbg.arg<Id>(1), {}).value.u32v[0];
|
||||
uint32_t column = EvaluateConstant(dbg.arg<Id>(2), {}).value.u32v[0];
|
||||
ScopeData *parent = &m_DebugInfo.scopes[dbg.arg<Id>(3)];
|
||||
|
||||
rdcstr name;
|
||||
if(dbg.params.count() >= 5)
|
||||
case ShaderDbg::Source:
|
||||
{
|
||||
name = strings[dbg.arg<Id>(4)];
|
||||
if(name.isEmpty())
|
||||
name = "anonymous_scope";
|
||||
int32_t fileIndex = (int32_t)m_DebugInfo.sources.size();
|
||||
|
||||
m_DebugInfo.sources[dbg.result] = fileIndex;
|
||||
m_DebugInfo.filenames[dbg.result] = strings[dbg.arg<Id>(0)];
|
||||
break;
|
||||
}
|
||||
else
|
||||
case ShaderDbg::CompilationUnit:
|
||||
{
|
||||
name = parent->name + ":" + ToStr(line);
|
||||
m_DebugInfo.scopes[dbg.result] = {
|
||||
DebugScope::CompilationUnit,
|
||||
NULL,
|
||||
1,
|
||||
1,
|
||||
m_DebugInfo.sources[dbg.arg<Id>(2)],
|
||||
0,
|
||||
m_DebugInfo.filenames[dbg.arg<Id>(2)],
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
m_DebugInfo.scopes[dbg.result] = {
|
||||
DebugScope::Block, parent, line, column, parent->fileIndex, 0, name,
|
||||
};
|
||||
}
|
||||
else if(dbg.inst == ShaderDbg::Scope)
|
||||
{
|
||||
if(m_DebugInfo.curScope)
|
||||
m_DebugInfo.curScope->end = it.offs();
|
||||
|
||||
m_DebugInfo.curScope = &m_DebugInfo.scopes[dbg.arg<Id>(0)];
|
||||
|
||||
if(dbg.params.size() >= 2)
|
||||
m_DebugInfo.curInline = &m_DebugInfo.inlined[dbg.arg<Id>(1)];
|
||||
else
|
||||
m_DebugInfo.curInline = NULL;
|
||||
}
|
||||
else if(dbg.inst == ShaderDbg::NoScope)
|
||||
{
|
||||
// don't want to set curScope to NULL until after this instruction. That way flood-fill of
|
||||
// scopes in PostParse() can find this instruction in a scope.
|
||||
leaveScope = true;
|
||||
}
|
||||
else if(dbg.inst == ShaderDbg::GlobalVariable)
|
||||
{
|
||||
// copy the name string to the variable string only if it's empty. If it has a name already,
|
||||
// we prefer that. If the variable is DebugInfoNone then we don't care about it's name.
|
||||
if(strings[dbg.arg<Id>(7)].empty())
|
||||
strings[dbg.arg<Id>(7)] = strings[dbg.arg<Id>(0)];
|
||||
|
||||
OpVariable var(GetID(dbg.arg<Id>(7)));
|
||||
|
||||
if(var.storageClass == StorageClass::Private ||
|
||||
var.storageClass == StorageClass::Workgroup || var.storageClass == StorageClass::Output)
|
||||
case ShaderDbg::Function:
|
||||
{
|
||||
m_DebugInfo.globals.push_back(var.result);
|
||||
rdcstr name = strings[dbg.arg<Id>(0)];
|
||||
// ignore arg 1 type
|
||||
// don't use arg 2 source - assume the parent is in the same file so it's redundant
|
||||
uint32_t line = EvaluateConstant(dbg.arg<Id>(3), {}).value.u32v[0];
|
||||
uint32_t column = EvaluateConstant(dbg.arg<Id>(4), {}).value.u32v[0];
|
||||
ScopeData *parent = &m_DebugInfo.scopes[dbg.arg<Id>(5)];
|
||||
// ignore arg 6 linkage name
|
||||
// ignore arg 7 flags
|
||||
// ignore arg 8 scope line
|
||||
// ignore arg 9 (optional) declaration
|
||||
|
||||
m_DebugInfo.scopes[dbg.result] = {
|
||||
DebugScope::Function, parent, line, column, parent->fileIndex, 0, name,
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(dbg.inst == ShaderDbg::LocalVariable)
|
||||
{
|
||||
m_DebugInfo.locals[dbg.result] = {
|
||||
strings[dbg.arg<Id>(0)], &m_DebugInfo.scopes[dbg.arg<Id>(5)],
|
||||
};
|
||||
|
||||
m_DebugInfo.scopes[dbg.arg<Id>(5)].locals.push_back(dbg.result);
|
||||
}
|
||||
else if(dbg.inst == ShaderDbg::Declare || dbg.inst == ShaderDbg::Value)
|
||||
{
|
||||
Id id = dbg.arg<Id>(1);
|
||||
|
||||
m_DebugInfo.localMappings[it.offs()] = {dbg.arg<Id>(0), dbg.arg<Id>(1)};
|
||||
|
||||
LocalData &local = m_DebugInfo.locals[dbg.arg<Id>(0)];
|
||||
|
||||
// keep the previous raw ID alive at least until this location starts
|
||||
if(local.curId != Id())
|
||||
idDeathOffset[local.curId] = RDCMAX(it.offs() + 1, idDeathOffset[local.curId]);
|
||||
|
||||
local.curId = id;
|
||||
|
||||
if(constants.find(id) != constants.end() && !m_DebugInfo.constants.contains(id))
|
||||
m_DebugInfo.constants.push_back(id);
|
||||
|
||||
OpShaderDbg expr(GetID(dbg.arg<Id>(2)));
|
||||
|
||||
// don't support expressions yet
|
||||
RDCASSERT(expr.params.empty());
|
||||
|
||||
// don't support indexes yet for values
|
||||
RDCASSERT(dbg.params.size() == 3);
|
||||
}
|
||||
else if(dbg.inst == ShaderDbg::InlinedAt)
|
||||
{
|
||||
// ignore arg 0 the line number
|
||||
ScopeData *scope = &m_DebugInfo.scopes[dbg.arg<Id>(1)];
|
||||
|
||||
if(dbg.params.count() >= 3)
|
||||
m_DebugInfo.inlined[dbg.result] = {scope, &m_DebugInfo.inlined[dbg.arg<Id>(2)]};
|
||||
else
|
||||
m_DebugInfo.inlined[dbg.result] = {scope, NULL};
|
||||
}
|
||||
else if(dbg.inst == ShaderDbg::InlinedVariable)
|
||||
{
|
||||
// TODO handle inlined variables
|
||||
}
|
||||
else if(dbg.inst == ShaderDbg::Line)
|
||||
{
|
||||
m_CurLineCol.lineStart = EvaluateConstant(dbg.arg<Id>(1), {}).value.u32v[0];
|
||||
m_CurLineCol.lineEnd = EvaluateConstant(dbg.arg<Id>(2), {}).value.u32v[0];
|
||||
if(Vulkan_Debug_UseDebugColumnInformation())
|
||||
case ShaderDbg::TypeBasic:
|
||||
{
|
||||
m_CurLineCol.colStart = EvaluateConstant(dbg.arg<Id>(3), {}).value.u32v[0];
|
||||
m_CurLineCol.colEnd = EvaluateConstant(dbg.arg<Id>(4), {}).value.u32v[0];
|
||||
uint32_t byteSize = EvaluateConstant(dbg.arg<Id>(1), {}).value.u32v[0];
|
||||
uint32_t encoding = EvaluateConstant(dbg.arg<Id>(2), {}).value.u32v[0];
|
||||
switch(encoding)
|
||||
{
|
||||
case 2: m_DebugInfo.types[dbg.result].type = VarType::Bool; break;
|
||||
case 3:
|
||||
if(byteSize == 64)
|
||||
m_DebugInfo.types[dbg.result].type = VarType::Double;
|
||||
else if(byteSize == 32)
|
||||
m_DebugInfo.types[dbg.result].type = VarType::Float;
|
||||
else if(byteSize == 16)
|
||||
m_DebugInfo.types[dbg.result].type = VarType::Half;
|
||||
break;
|
||||
case 4:
|
||||
if(byteSize == 64)
|
||||
m_DebugInfo.types[dbg.result].type = VarType::SLong;
|
||||
else if(byteSize == 32)
|
||||
m_DebugInfo.types[dbg.result].type = VarType::SInt;
|
||||
else if(byteSize == 16)
|
||||
m_DebugInfo.types[dbg.result].type = VarType::SShort;
|
||||
else if(byteSize == 8)
|
||||
m_DebugInfo.types[dbg.result].type = VarType::SByte;
|
||||
break;
|
||||
case 5: m_DebugInfo.types[dbg.result].type = VarType::SByte; break;
|
||||
case 6:
|
||||
if(byteSize == 64)
|
||||
m_DebugInfo.types[dbg.result].type = VarType::ULong;
|
||||
else if(byteSize == 32)
|
||||
m_DebugInfo.types[dbg.result].type = VarType::UInt;
|
||||
else if(byteSize == 16)
|
||||
m_DebugInfo.types[dbg.result].type = VarType::UShort;
|
||||
else if(byteSize == 8)
|
||||
m_DebugInfo.types[dbg.result].type = VarType::UByte;
|
||||
break;
|
||||
case 7: m_DebugInfo.types[dbg.result].type = VarType::UByte; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::TypePointer:
|
||||
{
|
||||
m_DebugInfo.types[dbg.result].baseType = dbg.arg<Id>(0);
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::TypeVector:
|
||||
{
|
||||
m_DebugInfo.types[dbg.result].baseType = dbg.arg<Id>(0);
|
||||
m_DebugInfo.types[dbg.result].vecSize = EvaluateConstant(dbg.arg<Id>(1), {}).value.u32v[0];
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::TypeMatrix:
|
||||
{
|
||||
m_DebugInfo.types[dbg.result].baseType = dbg.arg<Id>(0);
|
||||
m_DebugInfo.types[dbg.result].matSize = EvaluateConstant(dbg.arg<Id>(1), {}).value.u32v[0];
|
||||
m_DebugInfo.types[dbg.result].colMajorMat =
|
||||
EvaluateConstant(dbg.arg<Id>(2), {}).value.u32v[0] != 0;
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::TypeArray:
|
||||
{
|
||||
m_DebugInfo.types[dbg.result].baseType = dbg.arg<Id>(0);
|
||||
m_DebugInfo.types[dbg.result].arrayDimension = (uint32_t)dbg.params.size() - 1;
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::TypeComposite:
|
||||
{
|
||||
rdcstr name = strings[dbg.arg<Id>(0)];
|
||||
uint32_t tag = EvaluateConstant(dbg.arg<Id>(1), {}).value.u32v[0];
|
||||
const rdcstr tagString[3] = {
|
||||
"class ", "struct ", "union ",
|
||||
};
|
||||
|
||||
// find file index by filename matching, this would be nice to improve as it's brittle
|
||||
m_CurLineCol.fileIndex = m_DebugInfo.sources[dbg.arg<Id>(0)];
|
||||
}
|
||||
else if(dbg.inst == ShaderDbg::NoLine)
|
||||
{
|
||||
m_CurLineCol = LineColumnInfo();
|
||||
// don't use arg 2 source - assume the parent is in the same file so it's redundant
|
||||
uint32_t line = EvaluateConstant(dbg.arg<Id>(3), {}).value.u32v[0];
|
||||
uint32_t column = EvaluateConstant(dbg.arg<Id>(4), {}).value.u32v[0];
|
||||
ScopeData *parent = &m_DebugInfo.scopes[dbg.arg<Id>(5)];
|
||||
// ignore arg 6 linkage name
|
||||
// ignore arg 7 size
|
||||
// ignore arg 8 flags
|
||||
|
||||
for(uint32_t i = 9; i < dbg.params.size(); i++)
|
||||
{
|
||||
OpShaderDbg member(GetID(dbg.arg<Id>(i)));
|
||||
|
||||
m_DebugInfo.types[dbg.result].structMembers.push_back(
|
||||
{strings[member.arg<Id>(0)], member.arg<Id>(1)});
|
||||
}
|
||||
|
||||
name = tagString[tag % 3] + name;
|
||||
|
||||
m_DebugInfo.scopes[dbg.result] = {
|
||||
DebugScope::Composite, parent, line, column, parent->fileIndex, 0, name,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::LexicalBlock:
|
||||
{
|
||||
// don't use arg 0 source - assume the parent is in the same file so it's redundant
|
||||
uint32_t line = EvaluateConstant(dbg.arg<Id>(1), {}).value.u32v[0];
|
||||
uint32_t column = EvaluateConstant(dbg.arg<Id>(2), {}).value.u32v[0];
|
||||
ScopeData *parent = &m_DebugInfo.scopes[dbg.arg<Id>(3)];
|
||||
|
||||
rdcstr name;
|
||||
if(dbg.params.count() >= 5)
|
||||
{
|
||||
name = strings[dbg.arg<Id>(4)];
|
||||
if(name.isEmpty())
|
||||
name = "anonymous_scope";
|
||||
}
|
||||
else
|
||||
{
|
||||
name = parent->name + ":" + ToStr(line);
|
||||
}
|
||||
|
||||
m_DebugInfo.scopes[dbg.result] = {
|
||||
DebugScope::Block, parent, line, column, parent->fileIndex, 0, name,
|
||||
};
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::Scope:
|
||||
{
|
||||
if(m_DebugInfo.curScope)
|
||||
m_DebugInfo.curScope->end = it.offs();
|
||||
|
||||
m_DebugInfo.curScope = &m_DebugInfo.scopes[dbg.arg<Id>(0)];
|
||||
|
||||
if(dbg.params.size() >= 2)
|
||||
m_DebugInfo.curInline = &m_DebugInfo.inlined[dbg.arg<Id>(1)];
|
||||
else
|
||||
m_DebugInfo.curInline = NULL;
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::NoScope:
|
||||
{
|
||||
// don't want to set curScope to NULL until after this instruction. That way flood-fill of
|
||||
// scopes in PostParse() can find this instruction in a scope.
|
||||
leaveScope = true;
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::GlobalVariable:
|
||||
{
|
||||
// copy the name string to the variable string only if it's empty. If it has a name
|
||||
// already,
|
||||
// we prefer that. If the variable is DebugInfoNone then we don't care about it's name.
|
||||
if(strings[dbg.arg<Id>(7)].empty())
|
||||
strings[dbg.arg<Id>(7)] = strings[dbg.arg<Id>(0)];
|
||||
|
||||
OpVariable var(GetID(dbg.arg<Id>(7)));
|
||||
|
||||
if(var.storageClass == StorageClass::Private ||
|
||||
var.storageClass == StorageClass::Workgroup || var.storageClass == StorageClass::Output)
|
||||
{
|
||||
m_DebugInfo.globals.push_back(var.result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::LocalVariable:
|
||||
{
|
||||
m_DebugInfo.locals[dbg.result] = {
|
||||
strings[dbg.arg<Id>(0)], &m_DebugInfo.scopes[dbg.arg<Id>(5)],
|
||||
&m_DebugInfo.types[dbg.arg<Id>(1)],
|
||||
};
|
||||
|
||||
m_DebugInfo.scopes[dbg.arg<Id>(5)].locals.push_back(dbg.result);
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::Declare:
|
||||
case ShaderDbg::Value:
|
||||
{
|
||||
Id id = dbg.arg<Id>(1);
|
||||
|
||||
LocalMapping &mapping = m_DebugInfo.localMappings[it.offs()];
|
||||
|
||||
mapping = {it.offs(), dbg.arg<Id>(0), id};
|
||||
|
||||
if(constants.find(id) != constants.end() && !m_DebugInfo.constants.contains(id))
|
||||
m_DebugInfo.constants.push_back(id);
|
||||
|
||||
mapping.indexes.resize(dbg.params.size() - 3);
|
||||
for(uint32_t i = 0; i < mapping.indexes.size(); i++)
|
||||
{
|
||||
size_t idx = mapping.indexes.size() - 1 - i;
|
||||
mapping.indexes[idx] = EvaluateConstant(dbg.arg<Id>(i + 3), {}).value.u32v[0];
|
||||
}
|
||||
|
||||
{
|
||||
// don't support expressions, only allow for a single 'deref' which is used for
|
||||
// variables to 'deref' into the pointed value
|
||||
OpShaderDbg expr(GetID(dbg.arg<Id>(2)));
|
||||
|
||||
for(uint32_t i = 0; i < expr.params.size(); i++)
|
||||
{
|
||||
OpShaderDbg op(GetID(expr.arg<Id>(i)));
|
||||
|
||||
if(op.params.size() > 1 || EvaluateConstant(op.arg<Id>(0), {}).value.u32v[0] != 0)
|
||||
{
|
||||
RDCERR("Only deref expressions supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::InlinedAt:
|
||||
{
|
||||
// ignore arg 0 the line number
|
||||
ScopeData *scope = &m_DebugInfo.scopes[dbg.arg<Id>(1)];
|
||||
|
||||
if(dbg.params.count() >= 3)
|
||||
m_DebugInfo.inlined[dbg.result] = {scope, &m_DebugInfo.inlined[dbg.arg<Id>(2)]};
|
||||
else
|
||||
m_DebugInfo.inlined[dbg.result] = {scope, NULL};
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::InlinedVariable:
|
||||
{
|
||||
// TODO handle inlined variables
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::Line:
|
||||
{
|
||||
m_CurLineCol.lineStart = EvaluateConstant(dbg.arg<Id>(1), {}).value.u32v[0];
|
||||
m_CurLineCol.lineEnd = EvaluateConstant(dbg.arg<Id>(2), {}).value.u32v[0];
|
||||
if(Vulkan_Debug_UseDebugColumnInformation())
|
||||
{
|
||||
m_CurLineCol.colStart = EvaluateConstant(dbg.arg<Id>(3), {}).value.u32v[0];
|
||||
m_CurLineCol.colEnd = EvaluateConstant(dbg.arg<Id>(4), {}).value.u32v[0];
|
||||
}
|
||||
|
||||
// find file index by filename matching, this would be nice to improve as it's brittle
|
||||
m_CurLineCol.fileIndex = m_DebugInfo.sources[dbg.arg<Id>(0)];
|
||||
break;
|
||||
}
|
||||
case ShaderDbg::NoLine:
|
||||
{
|
||||
m_CurLineCol = LineColumnInfo();
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user