Change mapping representation to be gather-based per variable

* Instead of having an N:N mapping of parts of variables to parts of registers,
  instead we gather everything together under each variable and it has a list of
  registers that comprise it.
* Any gaps are represented as undefined register mappings, for components that
  aren't available in any register.
This commit is contained in:
baldurk
2018-06-22 19:28:33 +01:00
parent 7f117c6356
commit 880f529fda
6 changed files with 288 additions and 253 deletions
+88 -75
View File
@@ -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("<error>");
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("<error>");
value += lit("<error>");
}
}
}
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),
}));
}
}
+5
View File
@@ -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,
+72 -38
View File
@@ -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);
+103 -134
View File
@@ -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<TypeMember> members;
};
std::map<uint32_t, TypeDesc> 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);
}
}
}
@@ -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<LocalRange> gaps;
LocalVariableMapping var;
+17 -6
View File
@@ -341,17 +341,28 @@ void DoSerialise(SerialiserType &ser, ShaderVariable &el)
SIZE_CHECK(184);
}
template <typename SerialiserType>
void DoSerialise(SerialiserType &ser, RegisterRange &el)
{
SERIALISE_MEMBER(type);
SERIALISE_MEMBER(index);
SERIALISE_MEMBER(component);
SIZE_CHECK(8);
}
template <typename SerialiserType>
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 <typename SerialiserType>