mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-09-22 21:55:52 +00:00
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:
@@ -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),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user