DXIL Debugger BeginDebug

This commit is contained in:
Jake Turner
2024-09-13 16:40:17 +01:00
parent 14d0fdb964
commit 97c4899d66
@@ -3799,12 +3799,821 @@ ShaderDebugTrace *Debugger::BeginDebug(uint32_t eventId, const DXBC::DXBCContain
ShaderStage shaderStage = reflection.stage;
m_DXBC = dxbcContainer;
m_Program = m_DXBC->GetDXILByteCode();
m_EventId = eventId;
m_ActiveLaneIndex = activeLaneIndex;
m_Steps = 0;
// Ensure the DXIL reflection data is built
DXIL::Program *program = ((DXIL::Program *)m_Program);
program->BuildReflection();
ShaderDebugTrace *ret = new ShaderDebugTrace;
ret->stage = shaderStage;
uint32_t workgroupSize = shaderStage == ShaderStage::Pixel ? 4 : 1;
for(uint32_t i = 0; i < workgroupSize; i++)
m_Workgroups.push_back(ThreadState(i, *this, m_GlobalState));
// TODO: NEED TO POPULATE GROUPSHARED DATA
ThreadState &state = GetActiveLane();
// Create the storage layout for the constant buffers
// The constant buffer data and details are filled in outside of this method
size_t count = reflection.constantBlocks.size();
m_GlobalState.constantBlocks.resize(count);
for(uint32_t i = 0; i < count; i++)
{
const ConstantBlock &cbuffer = reflection.constantBlocks[i];
uint32_t bindCount = cbuffer.bindArraySize;
if(bindCount > 1)
{
// Create nested structure for constant buffer array
m_GlobalState.constantBlocks[i].members.resize(bindCount);
}
}
struct ResourceList
{
VarType varType;
DebugVariableType debugVarType;
DescriptorCategory category;
ResourceClass resourceClass;
const rdcarray<ShaderResource> &resources;
rdcarray<ShaderVariable> &dst;
};
// TODO: need to handle SRVs, UAVs, Samplers which are arrays
// Create the variables for SRVs and UAVs
ResourceList lists[] = {
{
VarType::ReadOnlyResource,
DebugVariableType::ReadOnlyResource,
DescriptorCategory::ReadOnlyResource,
ResourceClass::SRV,
reflection.readOnlyResources,
m_GlobalState.readOnlyResources,
},
{
VarType::ReadWriteResource,
DebugVariableType::ReadWriteResource,
DescriptorCategory::ReadWriteResource,
ResourceClass::UAV,
reflection.readWriteResources,
m_GlobalState.readWriteResources,
},
};
for(ResourceList &list : lists)
{
list.dst.reserve(list.resources.size());
for(uint32_t i = 0; i < list.resources.size(); i++)
{
const ShaderResource &res = list.resources[i];
// Fetch the resource name
BindingSlot slot(res.fixedBindNumber, res.fixedBindSetOrSpace);
rdcstr name = GetResourceReferenceName(m_Program, list.resourceClass, slot);
ShaderVariable shaderVar(name, 0U, 0U, 0U, 0U);
shaderVar.rows = 1;
shaderVar.columns = 1;
shaderVar.SetBindIndex(ShaderBindIndex(list.category, i, 0));
shaderVar.type = list.varType;
list.dst.push_back(shaderVar);
SourceVariableMapping sourceVar;
sourceVar.name = res.name;
sourceVar.type = list.varType;
sourceVar.rows = 1;
sourceVar.columns = 1;
sourceVar.offset = 0;
DebugVariableReference ref;
ref.type = list.debugVarType;
ref.name = shaderVar.name;
sourceVar.variables.push_back(ref);
ret->sourceVars.push_back(sourceVar);
}
}
// Create the variables for Samplers
count = reflection.samplers.size();
m_GlobalState.samplers.resize(count);
for(uint32_t i = 0; i < count; i++)
{
ShaderSampler sampler = reflection.samplers[i];
// Fetch the Sampler name
BindingSlot slot(sampler.fixedBindNumber, sampler.fixedBindSetOrSpace);
rdcstr name = GetResourceReferenceName(m_Program, ResourceClass::Sampler, slot);
ShaderVariable shaderVar(name, 0U, 0U, 0U, 0U);
shaderVar.rows = 1;
shaderVar.columns = 1;
shaderVar.SetBindIndex(ShaderBindIndex(DescriptorCategory::Sampler, i, 0));
shaderVar.type = VarType::Sampler;
m_GlobalState.samplers.push_back(shaderVar);
SourceVariableMapping sourceVar;
sourceVar.name = sampler.name;
sourceVar.type = VarType::Sampler;
sourceVar.rows = 1;
sourceVar.columns = 1;
sourceVar.offset = 0;
DebugVariableReference ref;
ref.type = DebugVariableType::Sampler;
ref.name = shaderVar.name;
sourceVar.variables.push_back(ref);
}
rdcstr entryPoint = reflection.entryPoint;
rdcstr entryFunction = m_Program->GetEntryFunction();
RDCASSERTEQUAL(entryPoint, entryFunction);
m_EntryPointFunction = NULL;
for(const Function *f : m_Program->m_Functions)
{
if(!f->external && (f->name == entryFunction))
{
m_EntryPointFunction = f;
break;
}
}
RDCASSERT(m_EntryPointFunction);
uint32_t globalOffset = 0;
// Generate helper data per function
// global instruction offset
// all SSA Ids referenced
// minimum and maximum instruction per SSA reference
for(const Function *f : m_Program->m_Functions)
{
if(!f->external)
{
FunctionInfo &info = m_FunctionInfos[f];
info.function = f;
info.globalInstructionOffset = globalOffset;
uint32_t countInstructions = (uint32_t)f->instructions.size();
globalOffset += countInstructions;
ReferencedIds &ssaRefs = info.referencedIds;
InstructionRangePerId &ssaRange = info.rangePerId;
for(uint32_t i = 0; i < countInstructions; ++i)
{
const Instruction &inst = *(f->instructions[i]);
if(DXIL::IsDXCNop(inst) || DXIL::IsLLVMDebugCall(inst))
continue;
// Allow the variable to live for one instruction longer
const uint32_t maxInst = i + 1;
{
Id resultId = inst.slot;
if(resultId != DXIL_INVALID_ID)
{
// The result SSA should not have been referenced before
RDCASSERTEQUAL(ssaRefs.count(resultId), 0);
ssaRefs.insert(resultId);
// For assignment track maximum and minimum (as current instruction plus one)
auto itRange = ssaRange.find(resultId);
if(itRange == ssaRange.end())
{
ssaRange[resultId] = {i + 1, maxInst};
}
else
{
itRange->second.min = RDCMIN(i + 1, itRange->second.min);
itRange->second.max = RDCMAX(maxInst, itRange->second.max);
}
// Stack allocations last until the end of the function
if(inst.op == Operation::Alloca)
itRange->second.max = countInstructions;
}
}
// Track min and max when SSA is referenced
bool isPhiNode = (inst.op == Operation::Phi);
for(uint32_t a = 0; a < inst.args.size(); ++a)
{
DXIL::Value *arg = inst.args[a];
if(DXIL::IsSSA(arg))
{
Id argId = GetSSAId(arg);
if(!isPhiNode)
{
// For non phi-nodes the argument SSA should already exist as the result of a previous operation
RDCASSERTEQUAL(ssaRefs.count(argId), 1);
}
auto itRange = ssaRange.find(argId);
if(itRange == ssaRange.end())
{
ssaRange[argId] = {i, maxInst};
}
else
{
itRange->second.min = RDCMIN(i, itRange->second.min);
itRange->second.max = RDCMAX(maxInst, itRange->second.max);
}
}
}
}
// If these do not match in size that means there is a result SSA that is never read
RDCASSERTEQUAL(ssaRefs.size(), ssaRange.size());
}
}
// Parse LLVM debug data
for(const Function *f : m_Program->m_Functions)
{
if(!f->external)
{
const FunctionInfo &info = m_FunctionInfos[f];
uint32_t countInstructions = (uint32_t)f->instructions.size();
uint32_t activeInstructionIndex = 0;
for(uint32_t i = 0; i < countInstructions; ++i)
{
uint32_t instructionIndex = i + info.globalInstructionOffset;
const Instruction &inst = *(f->instructions[i]);
if(!DXIL::IsLLVMDebugCall(inst))
{
// Include DebugLoc data for building up the list of scopes
uint32_t dbgLoc = inst.debugLoc;
if(dbgLoc != ~0U)
{
const DebugLocation &debugLoc = m_Program->m_DebugLocations[dbgLoc];
size_t scopeIndex = AddScopedDebugData(debugLoc.scope, instructionIndex);
ScopedDebugData &scope = m_DebugInfo.scopedDebugDatas[scopeIndex];
scope.minInstruction = RDCMIN(scope.minInstruction, instructionIndex);
scope.maxInstruction = RDCMAX(scope.maxInstruction, instructionIndex);
}
activeInstructionIndex = instructionIndex;
continue;
}
const Function *dbgFunc = inst.getFuncCall();
switch(dbgFunc->llvmDbgOp)
{
case LLVMDbgOp::Declare: ParseDbgOpDeclare(inst, activeInstructionIndex); break;
case LLVMDbgOp::Value: ParseDbgOpValue(inst, activeInstructionIndex); break;
case LLVMDbgOp::Unknown:
RDCASSERT("Unsupported LLVM debug operation", dbgFunc->llvmDbgOp);
break;
};
}
}
}
// Sort the scopes by instruction index
std::sort(m_DebugInfo.scopedDebugDatas.begin(), m_DebugInfo.scopedDebugDatas.end(),
[](const ScopedDebugData &a, const ScopedDebugData &b) { return a < b; });
// Track current active scope, previous scope
// For each instruction
for(const Function *f : m_Program->m_Functions)
{
if(!f->external)
{
const FunctionInfo &info = m_FunctionInfos[f];
uint32_t countInstructions = (uint32_t)f->instructions.size();
for(uint32_t i = 0; i < countInstructions; ++i)
{
uint32_t instructionIndex = i + info.globalInstructionOffset;
DXIL::Program::LocalSourceVariable localSrcVar;
localSrcVar.startInst = instructionIndex;
localSrcVar.endInst = instructionIndex;
// - find which scope it belongs
size_t scopeIndex = FindScopedDebugDataIndex(instructionIndex);
// track which mappings we've processed, so if the same variable has mappings in multiple
// scopes we only pick the innermost.
rdcarray<LocalMapping> processed;
rdcarray<rdcstr> sourceVars;
// capture the scopes upwards (from child to parent)
rdcarray<size_t> scopeIndexes;
while(scopeIndex < m_DebugInfo.scopedDebugDatas.size())
{
const ScopedDebugData &scope = m_DebugInfo.scopedDebugDatas[scopeIndex];
scopeIndexes.push_back(scopeIndex);
// if we reach a function scope, don't go up any further.
if(scope.md->dwarf->type == DIBase::Type::Subprogram)
break;
scopeIndex = scope.parentIndex;
}
// Iterate over the scopes downwards (parent->child)
for(size_t s = 0; s < scopeIndexes.size(); ++s)
{
scopeIndex = scopeIndexes[scopeIndexes.size() - 1 - s];
const ScopedDebugData &scope = m_DebugInfo.scopedDebugDatas[scopeIndex];
for(size_t m = 0; m < scope.localMappings.size(); m++)
{
const LocalMapping &mapping = scope.localMappings[m];
// if this mapping is past the current instruction, stop here.
if(mapping.instIndex > instructionIndex)
break;
// see if this mapping is superceded by a later mapping in this 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++)
{
const LocalMapping &laterMapping = scope.localMappings[n];
// if this mapping is past the current instruction, stop here.
if(laterMapping.instIndex > instructionIndex)
break;
// if this mapping will supercede and starts later
if(laterMapping.isSourceSupersetOf(mapping) &&
laterMapping.instIndex > mapping.instIndex)
{
supercede = true;
break;
}
}
// don't add the current mapping if it's going to be superceded by something later
if(supercede)
continue;
processed.push_back(mapping);
rdcstr sourceVarName = mapping.sourceVarName;
if(!sourceVars.contains(mapping.sourceVarName))
sourceVars.push_back(mapping.sourceVarName);
}
}
// Converting debug variable mappings to SourceVariableMapping is a two phase algorithm.
// Phase One
// For each source variable, repeatedly apply the debug variable mappings.
// This debug variable usage is tracked in a tree-like structure built using DebugVarNode
// elements.
// As each mapping is applied, the new mapping can fully or partially override the
// existing mapping. When an existing mapping is:
// - fully overridden: any sub-elements of that mapping are cleared
// i.e. assigning a vector, array, structure
// - partially overriden: the existing mapping is expanded into its sub-elements which are
// mapped to the current mapping and then the new mapping is set to its corresponding
// elements i.e. y-component in a vector, member in a structure, a single array element
// The DebugVarNode member "emitSourceVar" determines if the DebugVar mapping should be
// converted to a source variable mapping.
// Phase Two
// The DebugVarNode tree is walked to find the nodes which have "emitSourceVar" set to
// true and then those nodes are converted to SourceVariableMapping
struct DebugVarNode
{
rdcarray<DebugVarNode> children;
rdcstr debugVarSSAName;
rdcstr name;
rdcstr debugVarSuffix;
VarType type = VarType::Unknown;
uint32_t rows = 0;
uint32_t columns = 0;
uint32_t debugVarComponent = 0;
uint32_t offset = 0;
bool emitSourceVar = false;
};
::std::map<rdcstr, DebugVarNode> roots;
// Phase One: generate the DebugVarNode tree by repeatedly applying debug variables
// updating existing mappings with later mappings
for(size_t sv = 0; sv < sourceVars.size(); ++sv)
{
rdcstr sourceVarName = sourceVars[sv];
const DXIL::DILocalVariable *variable = m_DebugInfo.locals[sourceVarName].variable;
// Convert processed mappings into a usage map
for(size_t m = 0; m < processed.size(); ++m)
{
const LocalMapping &mapping = processed[m];
if(mapping.sourceVarName != sourceVarName)
continue;
DebugVarNode *usage = &roots[sourceVarName];
if(usage->name.isEmpty())
{
usage->name = sourceVarName;
usage->rows = 1U;
usage->columns = 1U;
}
const DXIL::Metadata *typeMD = variable->type;
const TypeData *typeWalk = &m_DebugInfo.types[typeMD];
// if the mapping is the entire variable
if((mapping.byteOffset == 0 && mapping.countBytes == 0))
{
uint32_t rows = 1;
uint32_t columns = 1;
// TODO: is it worth considering GPU pointers for DXIL
// skip past any pointer types to get the 'real' type that we'll see
while(typeWalk && typeWalk->baseType != NULL && typeWalk->type == VarType::GPUPointer)
typeWalk = &m_DebugInfo.types[typeWalk->baseType];
const size_t arrayDimension = typeWalk->arrayDimensions.size();
if(arrayDimension > 0)
{
// 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];
usage->type = typeWalk->type;
}
else if(!typeWalk->structMembers.empty())
{
usage->type = typeWalk->type;
}
if(typeWalk->matSize != 0)
{
const TypeData &vec = m_DebugInfo.types[typeWalk->baseType];
const TypeData &scalar = m_DebugInfo.types[vec.baseType];
usage->type = scalar.type;
if(typeWalk->colMajorMat)
{
rows = RDCMAX(1U, vec.vecSize);
columns = RDCMAX(1U, typeWalk->matSize);
}
else
{
columns = RDCMAX(1U, vec.vecSize);
rows = RDCMAX(1U, typeWalk->matSize);
}
}
else if(typeWalk->vecSize != 0)
{
const TypeData &scalar = m_DebugInfo.types[typeWalk->baseType];
usage->type = scalar.type;
columns = RDCMAX(1U, typeWalk->vecSize);
}
else
{
const TypeData &scalar = m_DebugInfo.types[typeWalk->baseType];
usage->type = scalar.type;
columns = 1U;
}
usage->debugVarSSAName = mapping.ssaIdName;
// Remove any child mappings : this mapping covers everything
usage->children.clear();
usage->emitSourceVar = true;
usage->rows = rows;
usage->columns = columns;
}
else
{
uint64_t byteOffset = mapping.byteOffset;
uint64_t bytesRemaining = mapping.countBytes;
// walk any aggregate types
while(bytesRemaining)
{
bytesRemaining = 0;
RDCERR("Aggregate types not handled yet %u %u", byteOffset, bytesRemaining);
}
}
}
}
// Phase Two: walk the DebugVarNode tree and convert "emitSourceVar = true" nodes to a SourceVariableMapping
for(size_t sv = 0; sv < sourceVars.size(); ++sv)
{
rdcstr sourceVarName = sourceVars[sv];
DebugVarNode *usage = &roots[sourceVarName];
rdcarray<const DebugVarNode *> nodesToProcess;
rdcarray<const DebugVarNode *> sourceVarNodes;
nodesToProcess.push_back(usage);
while(!nodesToProcess.isEmpty())
{
const DebugVarNode *n = nodesToProcess.back();
nodesToProcess.pop_back();
if(n->emitSourceVar)
{
sourceVarNodes.push_back(n);
}
else
{
for(size_t x = 0; x < n->children.size(); ++x)
{
const DebugVarNode *child = &n->children[x];
nodesToProcess.push_back(child);
}
}
}
for(size_t x = 0; x < sourceVarNodes.size(); ++x)
{
const DebugVarNode *n = sourceVarNodes[x];
SourceVariableMapping sourceVar;
sourceVar.name = n->name;
sourceVar.type = n->type;
sourceVar.signatureIndex = -1;
sourceVar.offset = n->offset;
sourceVar.variables.clear();
// unknown is treated as a struct
if(sourceVar.type == VarType::Unknown)
sourceVar.type = VarType::Struct;
if(n->children.empty())
{
RDCASSERTNOTEQUAL(n->rows * n->columns, 0);
for(uint32_t c = 0; c < n->rows * n->columns; ++c)
{
sourceVar.variables.push_back(DebugVariableReference(
DebugVariableType::Variable, n->debugVarSSAName + n->debugVarSuffix, c));
}
}
else
{
RDCASSERTEQUAL(n->rows * n->columns, (uint32_t)n->children.count());
for(int32_t c = 0; c < n->children.count(); ++c)
sourceVar.variables.push_back(DebugVariableReference(
DebugVariableType::Variable,
n->children[c].debugVarSSAName + n->children[c].debugVarSuffix,
n->children[c].debugVarComponent));
}
localSrcVar.sourceVars.push_back(sourceVar);
}
}
program->m_Locals.push_back(localSrcVar);
}
}
}
// Add inputs to the shader trace
const rdcarray<SigParameter> &inParams = dxbcContainer->GetReflection()->InputSig;
// TODO: compute this from DXIL
const bool inputCoverage = false;
const uint32_t countInParams = (uint32_t)inParams.size();
if(countInParams || inputCoverage)
{
// Make fake ShaderVariable struct to hold all the inputs
ShaderVariable &inStruct = state.m_Input;
inStruct.name = DXIL_FAKE_INPUT_STRUCT_NAME;
inStruct.rows = 1;
inStruct.columns = 1;
inStruct.type = VarType::Struct;
inStruct.members.resize(countInParams + (inputCoverage ? 1 : 0));
for(uint32_t sigIdx = 0; sigIdx < countInParams; sigIdx++)
{
const SigParameter &sig = inParams[sigIdx];
ShaderVariable v;
v.name = sig.semanticIdxName;
v.rows = 1;
v.columns = (uint8_t)sig.compCount;
v.type = sig.varType;
ShaderVariable &dst = inStruct.members[sigIdx];
// if the variable hasn't been initialised, just assign. If it has, we're in a situation
// where two input parameters are assigned to the same variable overlapping, so just update
// the number of columns to the max of both. The source mapping (either from debug info or
// our own below) will handle distinguishing better.
if(dst.name.empty())
dst = v;
else
dst.columns = RDCMAX(dst.columns, v.columns);
SourceVariableMapping inputMapping;
inputMapping.name = v.name;
inputMapping.type = v.type;
inputMapping.rows = 1;
inputMapping.columns = sig.compCount;
inputMapping.signatureIndex = sigIdx;
inputMapping.variables.reserve(sig.compCount);
for(uint32_t c = 0; c < 4; c++)
{
if(sig.regChannelMask & (1 << c))
{
DebugVariableReference ref;
ref.type = DebugVariableType::Input;
ref.name = inStruct.name + "." + v.name;
ref.component = c;
inputMapping.variables.push_back(ref);
}
}
// ret->sourceVars.push_back(inputMapping);
// Put the coverage mask at the end
if(inputCoverage)
{
// TODO
inStruct.members.back() = ShaderVariable("TODO_COVERAGE", 0U, 0U, 0U, 0U);
inStruct.members.back().columns = 1;
// TODO: handle the input of system values
if(false)
{
SourceVariableMapping sourcemap;
sourcemap.name = "SV_Coverage";
sourcemap.type = VarType::UInt;
sourcemap.rows = 1;
sourcemap.columns = 1;
// no corresponding signature element for this - maybe we should generate one?
sourcemap.signatureIndex = -1;
DebugVariableReference ref;
ref.type = DebugVariableType::Input;
ref.name = inStruct.members.back().name;
sourcemap.variables.push_back(ref);
}
}
}
// Make a single source variable mapping for the whole input struct
SourceVariableMapping inputMapping;
inputMapping.name = inStruct.name;
inputMapping.type = VarType::Struct;
inputMapping.rows = 1;
inputMapping.columns = 1;
inputMapping.variables.resize(1);
inputMapping.variables.push_back(DebugVariableReference(DebugVariableType::Input, inStruct.name));
ret->sourceVars.push_back(inputMapping);
}
const rdcarray<SigParameter> &outParams = dxbcContainer->GetReflection()->OutputSig;
uint32_t countOutputs = (uint32_t)outParams.size();
// Make fake ShaderVariable struct to hold all the outputs
ShaderVariable &outStruct = state.m_Output;
outStruct.name = DXIL_FAKE_OUTPUT_STRUCT_NAME;
outStruct.rows = 1;
outStruct.columns = 1;
outStruct.type = VarType::Struct;
outStruct.members.resize(countOutputs);
state.m_OutputSSAId = m_Program->m_NextSSAId;
for(uint32_t sigIdx = 0; sigIdx < countOutputs; sigIdx++)
{
const SigParameter &sig = outParams[sigIdx];
// TODO: ShaderBuiltin::DepthOutput, ShaderBuiltin::DepthOutputLessEqual,
// ShaderBuiltin::DepthOutputGreaterEqual, ShaderBuiltin::MSAACoverage,
// ShaderBuiltin::StencilReference
ShaderVariable v;
v.name = sig.semanticIdxName;
v.rows = 1;
v.columns = (uint8_t)sig.compCount;
v.type = sig.varType;
ShaderVariable &dst = outStruct.members[sigIdx];
// if the variable hasn't been initialised, just assign. If it has, we're in a situation where
// two input parameters are assigned to the same variable overlapping, so just update the
// number of columns to the max of both. The source mapping (either from debug info or our own
// below) will handle distinguishing better.
if(dst.name.empty())
dst = v;
else
dst.columns = RDCMAX(dst.columns, v.columns);
SourceVariableMapping outputMapping;
outputMapping.name = v.name;
outputMapping.type = v.type;
outputMapping.rows = 1;
outputMapping.columns = sig.compCount;
outputMapping.signatureIndex = sigIdx;
outputMapping.variables.reserve(sig.compCount);
for(uint32_t c = 0; c < 4; c++)
{
if(sig.regChannelMask & (1 << c))
{
DebugVariableReference ref;
ref.type = DebugVariableType::Variable;
ref.name = outStruct.name + "." + v.name;
ref.component = c;
outputMapping.variables.push_back(ref);
}
}
ret->sourceVars.push_back(outputMapping);
// TODO: handle the output of system values
if(false)
{
SourceVariableMapping sourcemap;
if(sig.systemValue == ShaderBuiltin::DepthOutput)
{
sourcemap.name = "SV_Depth";
sourcemap.type = VarType::Float;
}
else if(sig.systemValue == ShaderBuiltin::DepthOutputLessEqual)
{
sourcemap.name = "SV_DepthLessEqual";
sourcemap.type = VarType::Float;
}
else if(sig.systemValue == ShaderBuiltin::DepthOutputGreaterEqual)
{
sourcemap.name = "SV_DepthGreaterEqual";
sourcemap.type = VarType::Float;
}
else if(sig.systemValue == ShaderBuiltin::MSAACoverage)
{
sourcemap.name = "SV_Coverage";
sourcemap.type = VarType::UInt;
}
else if(sig.systemValue == ShaderBuiltin::StencilReference)
{
sourcemap.name = "SV_StencilRef";
sourcemap.type = VarType::UInt;
}
// all these variables are 1 scalar component
sourcemap.rows = 1;
sourcemap.columns = 1;
sourcemap.signatureIndex = sigIdx;
DebugVariableReference ref;
ref.type = DebugVariableType::Variable;
ref.name = v.name;
sourcemap.variables.push_back(ref);
ret->sourceVars.push_back(sourcemap);
}
}
if(0)
{
// Make a single source variable mapping for the whole output struct
SourceVariableMapping outputMapping;
outputMapping.name = state.m_Output.name;
outputMapping.type = VarType::Struct;
outputMapping.rows = 1;
outputMapping.columns = 1;
outputMapping.variables.resize(1);
outputMapping.variables[0].name = state.m_Output.name;
outputMapping.variables[0].type = DebugVariableType::Variable;
ret->sourceVars.push_back(outputMapping);
}
// Global source variable mappings valid for lifetime of the debug session
// ret->sourceVars.push_back(sourceMapping)
// Per instruction all source variable mappings at this instruction (cumulative and complete)
// InstructionSourceInfo
// {
// uint32_t instruction;
// LineColumnInfo lineInfo;
// {
// uint32_t disassemblyLine = 0;
// int32_t fileIndex = -1;
// uint32_t lineStart = 0;
// uint32_t lineEnd = 0;
// uint32_t colStart = 0;
// uint32_t colEnd = 0;
// }
// rdcarray<SourceVariableMapping> sourceVars;
// {
// rdcstr name;
// VarType type = VarType::Unknown;
// uint32_t rows = 0;
// uint32_t columns = 0;
// uint32_t offset;
// int32_t signatureIndex = -1;
// rdcarray<DebugVariableReference> variables;
// {
// rdcstr name;
// DebugVariableType type = DebugVariableType::Undefined;
// uint32_t component = 0;
// }
// }
// }
// ret->instInfo.push_back(InstructionSourceInfo())
ret->inputs = {state.m_Input};
ret->inputs.append(state.m_Input.members);
ret->constantBlocks = m_GlobalState.constantBlocks;
ret->readOnlyResources = m_GlobalState.readOnlyResources;
ret->readWriteResources = m_GlobalState.readWriteResources;
ret->samplers = m_GlobalState.samplers;
ret->debugger = this;
// Add the output struct to the global state
if(countOutputs)
m_GlobalState.globals.push_back(state.m_Output);
return ret;
}