diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index d3870a95d..676a07f65 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -419,7 +419,7 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR ui->registers->header()->setSectionResizeMode(1, QHeaderView::Interactive); ui->registers->header()->setSectionResizeMode(2, QHeaderView::Stretch); - ui->locals->setColumns({tr("Name"), tr("Register"), tr("Type"), tr("Value")}); + ui->locals->setColumns({tr("Name"), tr("Register(s)"), tr("Type"), tr("Value")}); ui->locals->header()->setSectionResizeMode(0, QHeaderView::Interactive); ui->locals->header()->setSectionResizeMode(1, QHeaderView::Interactive); ui->locals->header()->setSectionResizeMode(2, QHeaderView::Interactive); @@ -1090,7 +1090,7 @@ bool ShaderViewer::stepBack() const ShaderDebugState &oldstate = m_Trace->states[CurrentStep()]; LineColumnInfo oldLine = - m_Trace->lineInfo[qMax(m_Trace->lineInfo.size() - 1, (size_t)oldstate.nextInstruction)]; + m_Trace->lineInfo[qMin(m_Trace->lineInfo.size() - 1, (size_t)oldstate.nextInstruction)]; while(CurrentStep() < m_Trace->states.count()) { @@ -1617,105 +1617,118 @@ void ShaderViewer::updateDebugging() { ui->locals->clear(); + const QString xyzw = lit("xyzw"); + for(size_t lidx = 0; lidx < state.locals.size(); lidx++) { // iterate in reverse order, so newest locals tend to end up on top const LocalVariableMapping &l = state.locals[state.locals.size() - 1 - lidx]; - const ShaderVariable *var = NULL; - - switch(l.registerType) - { - case RegisterType::Input: - continue; // skip inputs, they are immutable - case RegisterType::Temporary: - if(l.registerIndex < state.registers.size()) - var = &state.registers[l.registerIndex]; - break; - case RegisterType::IndexedTemporary: - if(l.registerIndex < state.indexableTemps.size()) - var = &state.indexableTemps[l.registerIndex]; - break; - case RegisterType::Output: - if(l.registerIndex < state.outputs.size()) - var = &state.outputs[l.registerIndex]; - break; - } QString localName = l.localName; - QString regName = lit("-"), typeName = lit("-"); - QString value = tr(""); + QString regNames, typeName; + QString value; - if(var) + if(l.type == VarType::UInt) + typeName = lit("uint"); + else if(l.type == VarType::Int) + typeName = lit("int"); + else if(l.type == VarType::Float) + typeName = lit("float"); + else if(l.type == VarType::Double) + typeName = lit("double"); + + if(l.registers[0].type == RegisterType::IndexedTemporary) { - value.clear(); + typeName += lit("[]"); - regName = var->name; - - if(l.variableType == VarType::UInt) - typeName = lit("uint"); - else if(l.variableType == VarType::Int) - typeName = lit("int"); - else if(l.variableType == VarType::Float) - typeName = lit("float"); - else if(l.variableType == VarType::Double) - typeName = lit("double"); - - if(l.registerType == RegisterType::IndexedTemporary) - { - typeName += lit("[]"); - - regName = QFormatStr("x%1").arg(l.registerIndex); - } + regNames = QFormatStr("x%1").arg(l.registers[0].index); + } + else + { + if(l.rows > 1) + typeName += QFormatStr("%1x%1").arg(l.rows).arg(l.columns); else + typeName += QString::number(l.columns); + + for(uint32_t i = 0; i < l.regCount; i++) { - for(int i = 1; i < 4; i++) + const RegisterRange &r = l.registers[i]; + const ShaderVariable *var = NULL; + + if(!value.isEmpty()) + value += lit(", "); + if(!regNames.isEmpty()) + regNames += lit(", "); + + switch(r.type) { - if(i == 3 || l.variableSwizzle[i] == -1) - { - typeName += QString::number(i); + case RegisterType::Undefined: + regNames += lit("-"); + value += lit("?"); + continue; + case RegisterType::Input: + if(r.index < m_Trace->inputs.size()) + var = &m_Trace->inputs[r.index]; + break; + case RegisterType::Temporary: + if(r.index < state.registers.size()) + var = &state.registers[r.index]; + break; + case RegisterType::IndexedTemporary: + qCritical() << "unexpected indexed temporary"; + break; + case RegisterType::Output: + if(r.index < state.outputs.size()) + var = &state.outputs[r.index]; break; - } } - regName += lit("."); - localName += lit("."); - - QString swizzle = lit("xyzw"); - - for(uint32_t i = 0; i < 4; i++) + if(var) { - if(l.variableSwizzle[i] != -1) + // if the previous register was the same, just append our component + if(i > 0 && r.type == l.registers[i - 1].type && r.index == l.registers[i - 1].index) { - int8_t vs = l.variableSwizzle[i]; - int8_t rs = l.registerSwizzle[i]; - - localName += swizzle[vs]; - regName += swizzle[rs]; - if(!value.isEmpty()) - value += lit(", "); - - if(l.variableType == VarType::UInt) - value += Formatter::Format(var->value.uv[rs]); - else if(l.variableType == VarType::Int) - value += Formatter::Format(var->value.iv[rs]); - else if(l.variableType == VarType::Float) - value += Formatter::Format(var->value.fv[rs]); - else if(l.variableType == VarType::Double) - value += Formatter::Format(var->value.dv[rs]); + // remove the auto-appended ", " - there must be one because this isn't the first + // register + regNames.chop(2); + regNames += xyzw[r.component]; } + else + { + regNames += QFormatStr("%1.%2").arg(var->name).arg(xyzw[r.component]); + } + + if(l.type == VarType::UInt) + value += Formatter::Format(var->value.uv[r.component]); + else if(l.type == VarType::Int) + value += Formatter::Format(var->value.iv[r.component]); + else if(l.type == VarType::Float) + value += Formatter::Format(var->value.fv[r.component]); + else if(l.type == VarType::Double) + value += Formatter::Format(var->value.dv[r.component]); + } + else + { + regNames += lit(""); + value += lit(""); } } } - RDTreeWidgetItem *node = new RDTreeWidgetItem({localName, regName, typeName, value}); + RDTreeWidgetItem *node = new RDTreeWidgetItem({localName, regNames, typeName, value}); - if(l.registerType == RegisterType::IndexedTemporary) + if(l.registers[0].type == RegisterType::IndexedTemporary) { - for(int t = 0; t < var->members.count(); t++) + const ShaderVariable *var = NULL; + + if(l.registers[0].index < state.indexableTemps.size()) + var = &state.indexableTemps[l.registers[0].index]; + + for(int t = 0; var && t < var->members.count(); t++) { node->addChild(new RDTreeWidgetItem({ - QFormatStr("%1[%2]").arg(localName).arg(t), QFormatStr("%1[%2]").arg(regName).arg(t), - typeName, RowString(var->members[t], 0, l.variableType), + QFormatStr("%1[%2]").arg(localName).arg(t), QFormatStr("%1[%2]").arg(regNames).arg(t), + typeName, RowString(var->members[t], 0, l.type), })); } } diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index e98d0dd97..af3de8264 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -97,6 +97,10 @@ DECLARE_REFLECTION_ENUM(SectionType); DOCUMENT(R"(Represents the type of register a local variable maps to. +.. data:: Undefined + + No defined register. + .. data:: Input An input register. @@ -115,6 +119,7 @@ DOCUMENT(R"(Represents the type of register a local variable maps to. )"); enum class RegisterType : uint32_t { + Undefined, Input, Temporary, IndexedTemporary, diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index 2fa224e0d..fb27de22e 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -226,66 +226,100 @@ struct ShaderVariable DECLARE_REFLECTION_STRUCT(ShaderVariable); -DOCUMENT(R"(Refers to a shader variable in a :class:`ShaderDebugState` as a high-level local -variable, with type information. Since locals don't always map directly this can change over time. +DOCUMENT( + "A particular component of a variable register that a high-level variable component maps to"); +struct RegisterRange +{ + DOCUMENT(""); + bool operator==(const RegisterRange &o) const + { + return type == o.type && index == o.index && component == o.component; + } + bool operator<(const RegisterRange &o) const + { + if(!(type == o.type)) + return type < o.type; + if(!(index == o.index)) + return index < o.index; + if(!(component == o.component)) + return component < o.component; + return false; + } -Locals can also be split and mapped to multiple registers, so a given high level variable may appear -several times with different subsections. + DOCUMENT("The :class:`RegisterType` of the register being mapped to."); + RegisterType type = RegisterType::Undefined; + + DOCUMENT("The index of the register within its type."); + uint16_t index = 0xFFFF; + + DOCUMENT("The component of the register."); + uint16_t component = 0; +}; + +DECLARE_REFLECTION_STRUCT(RegisterRange); + +DOCUMENT(R"(Maps the contents of a high-level local variable to one or more shader variables in a +:class:`ShaderDebugState`, with type information. + +A single high-level variable may be represented by multiple mappings but only along regular +boundaries, typically whole vectors. For example an array may have each element in a different +mapping, or a matrix may have a mapping per row. The properties such as :data:`rows` and +:data:`elements` reflect the *parent* object. + +Since locals don't always map directly this can change over time. )"); struct LocalVariableMapping { DOCUMENT(""); bool operator==(const LocalVariableMapping &o) const { - return localName == o.localName && variableType == o.variableType && - registerType == o.registerType && registerIndex == o.registerIndex && - registerSwizzle == o.registerSwizzle; + return localName == o.localName && type == o.type && builtin == o.builtin && rows == o.rows && + columns == o.columns && elements == o.elements && registers == o.registers; } bool operator<(const LocalVariableMapping &o) const { if(!(localName == o.localName)) return localName < o.localName; - if(!(variableType == o.variableType)) - return variableType < o.variableType; - if(!(registerType == o.registerType)) - return registerType < o.registerType; - if(!(registerIndex == o.registerIndex)) - return registerIndex < o.registerIndex; - for(int i = 0; i < 4; i++) - { - if(!(registerSwizzle[i] == o.registerSwizzle[i])) - return registerSwizzle[i] < o.registerSwizzle[i]; - } - for(int i = 0; i < 4; i++) - { - if(!(variableSwizzle[i] == o.variableSwizzle[i])) - return variableSwizzle[i] < o.variableSwizzle[i]; - } + if(!(type == o.type)) + return type < o.type; + if(!(builtin == o.builtin)) + return builtin < o.builtin; + if(!(rows == o.rows)) + return rows < o.rows; + if(!(columns == o.columns)) + return columns < o.columns; + if(!(elements == o.elements)) + return elements < o.elements; + if(!(registers == o.registers)) + return registers < o.registers; return false; } DOCUMENT("The name and member of this local variable that's being mapped from."); rdcstr localName; DOCUMENT("The variable type of the local being mapped from, if the register is untyped."); - VarType variableType = VarType::Unknown; + VarType type = VarType::Unknown; - DOCUMENT("The :class:`RegisterType` of the register being mapped to."); - RegisterType registerType = RegisterType::Temporary; + DOCUMENT("The shader builtin this variable corresponds to."); + ShaderBuiltin builtin = ShaderBuiltin::Undefined; - DOCUMENT("The index of the register within its type."); - uint32_t registerIndex = 0; + DOCUMENT("The number of rows in this variable - 1 for vectors, >1 for matrices."); + uint32_t rows; - DOCUMENT(R"(A swizzle mask - each element in the list is set to the component of the register to -map the variable component to. If an element is -1, there is no source component (i.e. not all 4 -components are used). This list will have the same number of elements as :data:`variableSwizzle`. + DOCUMENT("The number of columns in this variable."); + uint32_t columns; + + DOCUMENT("The number of array elements in this variable."); + uint32_t elements; + + DOCUMENT("The number of valid entries in :data:`registers`."); + uint32_t regCount; + + DOCUMENT(R"(The registers that the components of this variable map to. Multiple ranges could refer +to the same register if a contiguous range is mapped to - the mapping is component-by-component to +greatly simplify algorithms at the expense of a small amount of storage space. )"); - int8_t registerSwizzle[4] = {-1, -1, -1, -1}; - - DOCUMENT(R"(A swizzle mask - each element in the list is set to the component of the variable -being mapped from. If an element is -1, there is no source component (i.e. not all 4 -components are used). This list will have the same number of elements as :data:`registerSwizzle`. -)"); - int8_t variableSwizzle[4] = {-1, -1, -1, -1}; + RegisterRange registers[16]; }; DECLARE_REFLECTION_STRUCT(LocalVariableMapping); diff --git a/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp b/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp index e92a18255..03395aaa2 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp +++ b/renderdoc/driver/shaders/dxbc/dxbc_spdb.cpp @@ -189,30 +189,31 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) uint32_t byteSize; uint16_t vecSize; uint16_t matArrayStride; + LEAF_ENUM_e leafType; std::vector members; }; std::map typeInfo; // prepopulate with basic types - typeInfo[T_INT4] = {"int32_t", VarType::Int, 4, 1, 0, {}}; - typeInfo[T_INT2] = {"int16_t", VarType::Int, 2, 1, 0, {}}; - typeInfo[T_INT1] = {"int8_t", VarType::Int, 1, 1, 0, {}}; - typeInfo[T_LONG] = {"int32_t", VarType::Int, 4, 1, 0, {}}; - typeInfo[T_SHORT] = {"int16_t", VarType::Int, 2, 1, 0, {}}; - typeInfo[T_CHAR] = {"char", VarType::Int, 1, 1, 0, {}}; - typeInfo[T_BOOL32FF] = {"bool", VarType::UInt, 4, 1, 0, {}}; - typeInfo[T_UINT4] = {"uint32_t", VarType::UInt, 4, 1, 0, {}}; - typeInfo[T_UINT2] = {"uint16_t", VarType::UInt, 2, 1, 0, {}}; - typeInfo[T_UINT1] = {"uint8_t", VarType::UInt, 1, 1, 0, {}}; - typeInfo[T_ULONG] = {"uint32_t", VarType::UInt, 4, 1, 0, {}}; - typeInfo[T_USHORT] = {"uint16_t", VarType::UInt, 2, 1, 0, {}}; - typeInfo[T_UCHAR] = {"unsigned char", VarType::UInt, 1, 1, 0, {}}; - typeInfo[T_REAL16] = {"half", VarType::Float, 2, 1, 0, {}}; - typeInfo[T_REAL32] = {"float", VarType::Float, 4, 1, 0, {}}; + typeInfo[T_INT4] = {"int32_t", VarType::Int, 4, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_INT2] = {"int16_t", VarType::Int, 2, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_INT1] = {"int8_t", VarType::Int, 1, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_LONG] = {"int32_t", VarType::Int, 4, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_SHORT] = {"int16_t", VarType::Int, 2, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_CHAR] = {"char", VarType::Int, 1, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_BOOL32FF] = {"bool", VarType::UInt, 4, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_UINT4] = {"uint32_t", VarType::UInt, 4, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_UINT2] = {"uint16_t", VarType::UInt, 2, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_UINT1] = {"uint8_t", VarType::UInt, 1, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_ULONG] = {"uint32_t", VarType::UInt, 4, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_USHORT] = {"uint16_t", VarType::UInt, 2, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_UCHAR] = {"unsigned char", VarType::UInt, 1, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_REAL16] = {"half", VarType::Float, 2, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_REAL32] = {"float", VarType::Float, 4, 1, 0, LF_NUMERIC, {}}; // modern HLSL fake half - typeInfo[T_REAL32PP] = {"half", VarType::Float, 4, 1, 0, {}}; - typeInfo[T_REAL64] = {"double", VarType::Double, 8, 1, 0, {}}; + typeInfo[T_REAL32PP] = {"half", VarType::Float, 4, 1, 0, LF_NUMERIC, {}}; + typeInfo[T_REAL64] = {"double", VarType::Double, 8, 1, 0, LF_NUMERIC, {}}; if(streams.size() >= 3) { @@ -269,7 +270,9 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) vector->elemtype, vector->count, *bytelength); typeInfo[id] = { - name, typeInfo[vector->elemtype].baseType, *bytelength, (uint16_t)vector->count, 0, + name, typeInfo[vector->elemtype].baseType, + *bytelength, (uint16_t)vector->count, + 0, type, {}, }; @@ -293,6 +296,7 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) *bytelength, uint16_t(matrix->rows), uint16_t(*bytelength / matrix->cols), + type, {}, }; @@ -530,7 +534,7 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) structType = "class"; typeInfo[id] = { - name, VarType::Float, *bytelength, 1, 0, typeInfo[structure->field].members, + name, VarType::Float, *bytelength, 1, 0, type, typeInfo[structure->field].members, }; SPDBLOG( @@ -566,6 +570,7 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) *bytelength, 1, uint16_t(stridedArray->stride), + type, {}, }; @@ -1093,6 +1098,7 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) DEFRANGESYMHLSL *defrange = (DEFRANGESYMHLSL *)sym; LocalMapping mapping; + RegisterRange &range = mapping.var.registers[0]; bool indexable = false; const char *regtype = ""; @@ -1103,7 +1109,7 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) switch((OperandType)defrange->regType) { case TYPE_TEMP: - mapping.var.registerType = RegisterType::Temporary; + range.type = RegisterType::Temporary; regtype = "temp"; regprefix = "r"; break; @@ -1120,7 +1126,7 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) case TYPE_INPUT_COVERAGE_MASK: case TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED: case TYPE_INPUT_GS_INSTANCE_ID: - mapping.var.registerType = RegisterType::Input; + range.type = RegisterType::Input; regtype = "input"; regprefix = "v"; break; @@ -1130,12 +1136,12 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) case TYPE_OUTPUT_DEPTH_GREATER_EQUAL: case TYPE_OUTPUT_STENCIL_REF: case TYPE_OUTPUT_COVERAGE_MASK: - mapping.var.registerType = RegisterType::Output; + range.type = RegisterType::Output; regtype = "output"; regprefix = "o"; break; case TYPE_INDEXABLE_TEMP: - mapping.var.registerType = RegisterType::IndexedTemporary; + range.type = RegisterType::IndexedTemporary; regtype = "indexable"; regprefix = "x"; indexable = true; @@ -1176,42 +1182,48 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) uint32_t regnumcomps = indexable ? 4 : defrange->sizeInParent / 4; bool builtinoutput = false; - ShaderBuiltin builtin = ShaderBuiltin::Undefined; + mapping.var.builtin = ShaderBuiltin::Undefined; switch((OperandType)defrange->regType) { case TYPE_OUTPUT_DEPTH: builtinoutput = true; - builtin = ShaderBuiltin::DepthOutput; + mapping.var.builtin = ShaderBuiltin::DepthOutput; break; case TYPE_OUTPUT_DEPTH_LESS_EQUAL: builtinoutput = true; - builtin = ShaderBuiltin::DepthOutputLessEqual; + mapping.var.builtin = ShaderBuiltin::DepthOutputLessEqual; break; case TYPE_OUTPUT_DEPTH_GREATER_EQUAL: builtinoutput = true; - builtin = ShaderBuiltin::DepthOutputGreaterEqual; + mapping.var.builtin = ShaderBuiltin::DepthOutputGreaterEqual; break; case TYPE_OUTPUT_STENCIL_REF: builtinoutput = true; - builtin = ShaderBuiltin::StencilReference; + mapping.var.builtin = ShaderBuiltin::StencilReference; break; case TYPE_OUTPUT_COVERAGE_MASK: builtinoutput = true; - builtin = ShaderBuiltin::MSAACoverage; + mapping.var.builtin = ShaderBuiltin::MSAACoverage; + break; + case TYPE_INPUT_PRIMITIVEID: mapping.var.builtin = ShaderBuiltin::PrimitiveIndex; break; + case TYPE_INPUT_COVERAGE_MASK: mapping.var.builtin = ShaderBuiltin::MSAACoverage; break; + case TYPE_INPUT_THREAD_ID: + mapping.var.builtin = ShaderBuiltin::DispatchThreadIndex; + break; + case TYPE_INPUT_THREAD_GROUP_ID: mapping.var.builtin = ShaderBuiltin::GroupIndex; break; + case TYPE_INPUT_THREAD_ID_IN_GROUP: + mapping.var.builtin = ShaderBuiltin::GroupThreadIndex; break; - case TYPE_INPUT_PRIMITIVEID: builtin = ShaderBuiltin::PrimitiveIndex; break; - case TYPE_INPUT_COVERAGE_MASK: builtin = ShaderBuiltin::MSAACoverage; break; - case TYPE_INPUT_THREAD_ID: builtin = ShaderBuiltin::DispatchThreadIndex; break; - case TYPE_INPUT_THREAD_GROUP_ID: builtin = ShaderBuiltin::GroupIndex; break; - case TYPE_INPUT_THREAD_ID_IN_GROUP: builtin = ShaderBuiltin::GroupThreadIndex; break; case TYPE_INPUT_THREAD_ID_IN_GROUP_FLATTENED: - builtin = ShaderBuiltin::GroupFlatIndex; + mapping.var.builtin = ShaderBuiltin::GroupFlatIndex; + break; + case TYPE_INPUT_GS_INSTANCE_ID: + mapping.var.builtin = ShaderBuiltin::GSInstanceIndex; break; - case TYPE_INPUT_GS_INSTANCE_ID: builtin = ShaderBuiltin::GSInstanceIndex; break; default: break; } - if(builtin != ShaderBuiltin::Undefined) + if(mapping.var.builtin != ShaderBuiltin::Undefined) { bool found = false; @@ -1219,7 +1231,7 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) { for(size_t i = 0; i < dxbc->m_OutputSig.size(); i++) { - if(dxbc->m_OutputSig[i].systemValue == builtin) + if(dxbc->m_OutputSig[i].systemValue == mapping.var.builtin) { regindex = (uint32_t)i; regfirstcomp = 0; @@ -1232,7 +1244,7 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) { for(size_t i = 0; i < dxbc->m_InputSig.size(); i++) { - if(dxbc->m_InputSig[i].systemValue == builtin) + if(dxbc->m_InputSig[i].systemValue == mapping.var.builtin) { regindex = (uint32_t)i; regfirstcomp = 0; @@ -1242,13 +1254,10 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) } } + // if not found in the signatures, then it's a fixed-function input like threadid - it + // will be matched by builtin if(!found) - { - RDCERR( - "Found variable mapping for %d but no matching register declared in out signature", - defrange->regType); regindex = ~0U; - } } char *regswizzle = regcomps; @@ -1318,25 +1327,35 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) } } + mapping.var.type = vartype->baseType; + mapping.var.rows = 1; + mapping.var.columns = vartype->vecSize; + // if it's an array or matrix, figure out the index if(vartype->matArrayStride) { uint32_t idx = varOffset / vartype->matArrayStride; mapping.var.localName = StringFormat::Fmt("%s[%u]", mapping.var.localName.c_str(), idx); + mapping.var.rows = RDCMAX( + 1U, (vartype->byteSize + (vartype->matArrayStride - 1)) / vartype->matArrayStride); varOffset -= vartype->matArrayStride * idx; } - mapping.var.variableType = vartype->baseType; - - mapping.var.registerIndex = regindex; - for(uint32_t i = 0; i < regnumcomps; i++) + if(vartype->leafType != LF_MATRIX) { - mapping.var.registerSwizzle[i] = uint8_t(regfirstcomp + i); - mapping.var.variableSwizzle[i] = uint8_t((varOffset % 16) / 4 + i); + mapping.var.elements = mapping.var.rows; + mapping.var.rows = 1; } + RDCASSERT(mapping.var.rows <= 4 && mapping.var.columns <= 4); + + range.index = uint16_t(regindex & 0xffff); + mapping.regFirstComp = regfirstcomp; + mapping.varFirstComp = (varOffset % 16) / 4; + mapping.numComps = regnumcomps; + SPDBLOG("Valid from %x to %x", defrange->range.offStart, defrange->range.offStart + defrange->range.cbRange); @@ -1358,9 +1377,7 @@ SPDBChunk::SPDBChunk(DXBCFile *dxbc, void *chunk) mapping.gaps.push_back(r); } - // don't add input variables as they don't change - if(mapping.var.registerType != RegisterType::Input) - m_Locals.push_back(mapping); + m_Locals.push_back(mapping); } else if(type == S_INLINESITE_END) { @@ -1716,103 +1733,55 @@ void SPDBChunk::GetLocals(size_t instruction, uintptr_t offset, bool added = false; - // check for duplicate registers + // we apply each matching local over the top. Where there is an overlap (e.g. two variables with + // the same name) we take the last mapping as authoratitive. This is a good solution for the + // case where one function with a parameter/variable name calls an inner function with the same + // parameter name and there's shadowing. The later mapping will be for the inner function so we + // use it in preference. + + // check if we already have a mapping for this variable for(LocalVariableMapping &a : locals) { const LocalVariableMapping &b = it->var; - // if the mapping was the same register, same variable, etc - if(a.registerIndex == b.registerIndex && a.registerType == b.registerType && - a.variableType == b.variableType && a.localName == b.localName) + if(a.localName == b.localName) { - // check to see if the same variable component is being mapped to multiple registers. This - // can be caused if the same local variable is used in two contexts, e.g. a function foo(a) - // calling a function bar(a) - the variable a will mean different things while inside bar(). - // Or it could be a bug :). - bool alias = false; - for(int i = 0; i < 4; i++) - { - for(int j = 0; j < 4; j++) - { - if(a.variableSwizzle[j] == b.variableSwizzle[i] && - a.registerSwizzle[j] != b.registerSwizzle[j]) - { - alias = true; - break; - } - } + RegisterRange range = b.registers[0]; - if(alias) - break; + for(uint32_t i = 0; i < it->numComps; i++) + { + a.registers[it->varFirstComp + i].type = b.registers[0].type; + a.registers[it->varFirstComp + i].index = b.registers[0].index; + a.registers[it->varFirstComp + i].component = uint16_t(it->regFirstComp + i); } - // if we found aliasing, just add them as separate entries in the local list to be safe. - if(alias) - { - SPDBLOG( - "Found register mapping aliasing of %s, possible variable shadowing in function call", - a.localName.c_str()); - continue; - } - - // insert b into a, in variableSwizzle sorted order. Note the number of nested loops might - // seem scary but they only iterate up to 4 and in many cases will early out. - for(int i = 0; i < 4; i++) - { - if(b.variableSwizzle[i] == -1) - break; - - for(int j = 0; j < 4; j++) - { - if(a.variableSwizzle[j] == b.variableSwizzle[i]) - { - // allow overlaps as long as they come from the same register component - RDCASSERT(a.registerSwizzle[j] == b.registerSwizzle[i]); - break; - } - else if(a.variableSwizzle[j] == -1) - { - // if we reached the end of the swizzles, just append our swizzle here as we know it's - // in sorted order - a.variableSwizzle[j] = b.variableSwizzle[i]; - RDCASSERT(a.registerSwizzle[j] == -1); - a.registerSwizzle[j] = b.registerSwizzle[i]; - break; - } - else if(a.variableSwizzle[j] < b.variableSwizzle[i]) - { - // keep going if we haven't found where we want to insert this component yet - continue; - } - else // a.variableSwizzle[j] > b.variableSwizzle[i] - { - // we shouldn't reach here on the last element, since then we should have found an - // exact match above - there are only 4 possible components - RDCASSERT(j < 3); - - // the hard case - we need to insert our new component in the middle. - // First, shift everything up by one starting from the end and moving j to j+1 - for(int k = 3; k > j; k--) - { - a.variableSwizzle[k] = a.variableSwizzle[k - 1]; - a.registerSwizzle[k] = a.registerSwizzle[k - 1]; - } - - // now insert our variable - a.variableSwizzle[j] = b.variableSwizzle[i]; - a.registerSwizzle[j] = b.registerSwizzle[i]; - break; - } - } - } + a.regCount = RDCMAX(a.regCount, it->varFirstComp + it->numComps); + // we've processed this, no need to add a new entry added = true; break; } } if(!added) + { locals.push_back(it->var); + LocalVariableMapping &a = locals.back(); + + // the register range is stored in [0] but we don't want to actually push that, so make it + // undefined and grab it locally + RegisterRange range; + std::swap(a.registers[0], range); + + for(uint32_t i = 0; i < it->numComps; i++) + { + a.registers[it->varFirstComp + i].type = range.type; + a.registers[it->varFirstComp + i].index = range.index; + a.registers[it->varFirstComp + i].component = uint16_t(it->regFirstComp + i); + } + + a.regCount = RDCMAX(it->var.columns, it->varFirstComp + it->numComps); + } } } diff --git a/renderdoc/driver/shaders/dxbc/dxbc_spdb.h b/renderdoc/driver/shaders/dxbc/dxbc_spdb.h index 19958dcf0..70a22ad70 100644 --- a/renderdoc/driver/shaders/dxbc/dxbc_spdb.h +++ b/renderdoc/driver/shaders/dxbc/dxbc_spdb.h @@ -253,6 +253,9 @@ struct LocalMapping { bool operator<(const LocalMapping &o) const { return range.startRange < o.range.startRange; } LocalRange range; + uint32_t regFirstComp; + uint32_t varFirstComp; + uint32_t numComps; std::vector gaps; LocalVariableMapping var; diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index 122559259..55a63ae1f 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -341,17 +341,28 @@ void DoSerialise(SerialiserType &ser, ShaderVariable &el) SIZE_CHECK(184); } +template +void DoSerialise(SerialiserType &ser, RegisterRange &el) +{ + SERIALISE_MEMBER(type); + SERIALISE_MEMBER(index); + SERIALISE_MEMBER(component); + + SIZE_CHECK(8); +} + template void DoSerialise(SerialiserType &ser, LocalVariableMapping &el) { SERIALISE_MEMBER(localName); - SERIALISE_MEMBER(variableType); - SERIALISE_MEMBER(registerType); - SERIALISE_MEMBER(registerIndex); - SERIALISE_MEMBER(registerSwizzle); - SERIALISE_MEMBER(variableSwizzle); + SERIALISE_MEMBER(type); + SERIALISE_MEMBER(rows); + SERIALISE_MEMBER(columns); + SERIALISE_MEMBER(elements); + SERIALISE_MEMBER(regCount); + SERIALISE_MEMBER(registers); - SIZE_CHECK(40); + SIZE_CHECK(168); } template