Display high-level language locals corresponding to registers

This commit is contained in:
baldurk
2018-05-17 13:36:34 +01:00
parent e2dcd902cd
commit ff9f5675e9
13 changed files with 834 additions and 20 deletions
+1
View File
@@ -264,6 +264,7 @@ TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderResource)
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderSampler)
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderSourceFile)
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderVariable)
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, LocalVariableMapping)
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, SigParameter)
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, TextureDescription)
TEMPLATE_ARRAY_INSTANTIATE(rdcarray, ShaderEntryPoint)
+135
View File
@@ -66,6 +66,7 @@ ShaderViewer::ShaderViewer(ICaptureContext &ctx, QWidget *parent)
ui->constants->setFont(Formatter::PreferredFont());
ui->variables->setFont(Formatter::PreferredFont());
ui->locals->setFont(Formatter::PreferredFont());
ui->watch->setFont(Formatter::PreferredFont());
ui->inputSig->setFont(Formatter::PreferredFont());
ui->outputSig->setFont(Formatter::PreferredFont());
@@ -210,6 +211,7 @@ void ShaderViewer::editShader(bool customShader, const QString &entryPoint, cons
ui->variables->hide();
ui->constants->hide();
ui->callstack->hide();
ui->locals->hide();
ui->snippets->setVisible(customShader);
@@ -433,6 +435,12 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR
ui->variables->header()->setSectionResizeMode(1, QHeaderView::Interactive);
ui->variables->header()->setSectionResizeMode(2, QHeaderView::Stretch);
ui->locals->setColumns({tr("Name"), tr("Register"), 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);
ui->locals->header()->setSectionResizeMode(3, QHeaderView::Stretch);
ui->constants->setColumns({tr("Name"), tr("Type"), tr("Value")});
ui->constants->header()->setSectionResizeMode(0, QHeaderView::Interactive);
ui->constants->header()->setSectionResizeMode(1, QHeaderView::Interactive);
@@ -469,6 +477,20 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR
ui->docking->setToolWindowProperties(
ui->callstack, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
if(m_Trace->hasLocals)
{
ui->locals->setWindowTitle(tr("Local Variables"));
ui->docking->addToolWindow(
ui->locals, ToolWindowManager::AreaReference(ToolWindowManager::AddTo,
ui->docking->areaOf(ui->variables)));
ui->docking->setToolWindowProperties(
ui->locals, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
}
else
{
ui->locals->hide();
}
m_DisassemblyView->setMarginWidthN(1, 20.0 * devicePixelRatioF());
// display current line in margin 2, distinct from breakpoint in margin 1
@@ -526,6 +548,8 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR
ui->watch->hide();
ui->variables->hide();
ui->constants->hide();
ui->locals->hide();
ui->callstack->hide();
// hide debugging toolbar buttons
ui->debugSep->hide();
@@ -1360,6 +1384,117 @@ void ShaderViewer::updateDebugging()
}
}
if(m_Trace->hasLocals)
{
ui->locals->clear();
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>");
if(var)
{
value.clear();
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);
}
else
{
for(int i = 1; i < 4; i++)
{
if(i == 3 || l.variableSwizzle[i] == -1)
{
typeName += QString::number(i);
break;
}
}
regName += lit(".");
localName += lit(".");
QString swizzle = lit("xyzw");
for(uint32_t i = 0; i < 4; i++)
{
if(l.variableSwizzle[i] != -1)
{
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]);
}
}
}
}
RDTreeWidgetItem *node = new RDTreeWidgetItem({localName, regName, typeName, value});
if(l.registerType == RegisterType::IndexedTemporary)
{
for(int t = 0; 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),
}));
}
}
ui->locals->addTopLevelItem(node);
}
}
if(ui->variables->topLevelItemCount() == 0)
{
for(int i = 0; i < state.registers.count(); i++)
+18 -11
View File
@@ -137,15 +137,6 @@
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="indentation">
<number>0</number>
</property>
<property name="rootIsDecorated">
<bool>false</bool>
</property>
<property name="itemsExpandable">
<bool>false</bool>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
@@ -492,8 +483,8 @@
<widget class="RDTableWidget" name="watch">
<property name="geometry">
<rect>
<x>390</x>
<y>90</y>
<x>790</x>
<y>60</y>
<width>151</width>
<height>131</height>
</rect>
@@ -554,6 +545,22 @@
<enum>QAbstractItemView::NoSelection</enum>
</property>
</widget>
<widget class="RDTreeWidget" name="locals">
<property name="geometry">
<rect>
<x>290</x>
<y>90</y>
<width>256</width>
<height>192</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="allColumnsShowFocus">
<bool>true</bool>
</property>
</widget>
</widget>
<customwidgets>
<customwidget>
+12
View File
@@ -683,6 +683,18 @@ std::string DoStringise(const VarType &el)
END_ENUM_STRINGISE();
}
template <>
std::string DoStringise(const RegisterType &el)
{
BEGIN_ENUM_STRINGISE(RegisterType)
{
STRINGISE_ENUM_CLASS(Temporary);
STRINGISE_ENUM_CLASS(IndexedTemporary);
STRINGISE_ENUM_CLASS(Output);
}
END_ENUM_STRINGISE();
}
template <>
std::string DoStringise(const GPUCounter &el)
{
+28
View File
@@ -95,6 +95,34 @@ DECLARE_REFLECTION_ENUM(SectionType);
// replay_shader.h
DOCUMENT(R"(Represents the type of register a local variable maps to.
.. data:: Input
An input register.
.. data:: Temporary
A normal temporary register.
.. data:: IndexedTemporary
An indexed temporary register.
.. data:: Output
An output register.
)");
enum class RegisterType : uint32_t
{
Input,
Temporary,
IndexedTemporary,
Output,
};
DECLARE_REFLECTION_ENUM(RegisterType);
DOCUMENT(R"(Represents the base type of a shader variable in debugging or constant blocks.
.. data:: Float
+78 -5
View File
@@ -226,6 +226,69 @@ 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.
Locals can also be split and mapped to multiple registers, so a given high level variable may appear
several times with different subsections.
)");
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;
}
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];
}
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;
DOCUMENT("The :class:`RegisterType` of the register being mapped to.");
RegisterType registerType = RegisterType::Temporary;
DOCUMENT("The index of the register within its type.");
uint32_t registerIndex = 0;
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`.
)");
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};
};
DECLARE_REFLECTION_STRUCT(LocalVariableMapping);
DOCUMENT(R"(This stores the current state of shader debugging at one particular step in the shader,
with all mutable variable contents.
)");
@@ -235,7 +298,8 @@ struct ShaderDebugState
bool operator==(const ShaderDebugState &o) const
{
return registers == o.registers && outputs == o.outputs && indexableTemps == o.indexableTemps &&
nextInstruction == o.nextInstruction && flags == o.flags && callstack == o.callstack;
locals == o.locals && nextInstruction == o.nextInstruction && flags == o.flags &&
callstack == o.callstack;
}
bool operator<(const ShaderDebugState &o) const
{
@@ -245,6 +309,8 @@ struct ShaderDebugState
return outputs < o.outputs;
if(!(indexableTemps == o.indexableTemps))
return indexableTemps < o.indexableTemps;
if(!(locals == o.locals))
return locals < o.locals;
if(!(nextInstruction == o.nextInstruction))
return nextInstruction < o.nextInstruction;
if(!(flags == o.flags))
@@ -253,15 +319,19 @@ struct ShaderDebugState
return callstack < o.callstack;
return false;
}
DOCUMENT("The temporary variables for this shader as a list of :class:`ShaderValue`.");
DOCUMENT("The temporary variables for this shader as a list of :class:`ShaderVariable`.");
rdcarray<ShaderVariable> registers;
DOCUMENT("The output variables for this shader as a list of :class:`ShaderValue`.");
DOCUMENT("The output variables for this shader as a list of :class:`ShaderVariable`.");
rdcarray<ShaderVariable> outputs;
DOCUMENT(
"Indexable temporary variables for this shader as a list of :class:`ShaderValue` lists.");
DOCUMENT("Indexable temporary variables for this shader as a list of :class:`ShaderVariable`.");
rdcarray<ShaderVariable> indexableTemps;
DOCUMENT(R"(An optional list of :class:`ShaderVariableRef` indicating which high-level locals map
to which registers, and their type
)");
rdcarray<LocalVariableMapping> locals;
DOCUMENT("An optional callstack listing function calls at the present instruction");
rdcarray<rdcstr> callstack;
@@ -295,6 +365,9 @@ Each entry in this list corresponds to a constant block with the same index in t
instruction was executed
)");
rdcarray<ShaderDebugState> states;
DOCUMENT("A flag indicating whether this trace has locals information");
bool hasLocals = false;
};
DECLARE_REFLECTION_STRUCT(ShaderDebugTrace);
@@ -941,6 +941,7 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin
vector<ShaderDebugState> states;
dxbc->m_DebugInfo->GetStack(0, dxbc->GetInstruction(0).offset, initialState.callstack);
dxbc->m_DebugInfo->GetLocals(0, dxbc->GetInstruction(0).offset, initialState.locals);
states.push_back((State)initialState);
@@ -956,6 +957,7 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin
{
const ASMOperation &op = dxbc->GetInstruction((size_t)initialState.nextInstruction);
dxbc->m_DebugInfo->GetStack(initialState.nextInstruction, op.offset, initialState.callstack);
dxbc->m_DebugInfo->GetLocals(initialState.nextInstruction, op.offset, initialState.locals);
}
states.push_back((State)initialState);
@@ -969,6 +971,8 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin
ret.states = states;
ret.hasLocals = dxbc->m_DebugInfo->HasLocals();
return ret;
}
@@ -1818,6 +1822,7 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
vector<ShaderDebugState> states;
dxbc->m_DebugInfo->GetStack(0, dxbc->GetInstruction(0).offset, quad[destIdx].callstack);
dxbc->m_DebugInfo->GetLocals(0, dxbc->GetInstruction(0).offset, quad[destIdx].locals);
states.push_back((State)quad[destIdx]);
@@ -1858,6 +1863,7 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
{
const ASMOperation &op = dxbc->GetInstruction((size_t)s.nextInstruction);
dxbc->m_DebugInfo->GetStack(s.nextInstruction, op.offset, s.callstack);
dxbc->m_DebugInfo->GetLocals(s.nextInstruction, op.offset, s.locals);
}
states.push_back(s);
@@ -1953,6 +1959,8 @@ ShaderDebugTrace D3D11Replay::DebugPixel(uint32_t eventId, uint32_t x, uint32_t
traces[destIdx].states = states;
traces[destIdx].hasLocals = dxbc->m_DebugInfo->HasLocals();
return traces[destIdx];
}
@@ -2011,6 +2019,7 @@ ShaderDebugTrace D3D11Replay::DebugThread(uint32_t eventId, const uint32_t group
vector<ShaderDebugState> states;
dxbc->m_DebugInfo->GetStack(0, dxbc->GetInstruction(0).offset, initialState.callstack);
dxbc->m_DebugInfo->GetLocals(0, dxbc->GetInstruction(0).offset, initialState.locals);
states.push_back((State)initialState);
@@ -2024,6 +2033,7 @@ ShaderDebugTrace D3D11Replay::DebugThread(uint32_t eventId, const uint32_t group
{
const ASMOperation &op = dxbc->GetInstruction((size_t)initialState.nextInstruction);
dxbc->m_DebugInfo->GetStack(initialState.nextInstruction, op.offset, initialState.callstack);
dxbc->m_DebugInfo->GetLocals(initialState.nextInstruction, op.offset, initialState.locals);
}
states.push_back((State)initialState);
@@ -2037,5 +2047,7 @@ ShaderDebugTrace D3D11Replay::DebugThread(uint32_t eventId, const uint32_t group
ret.states = states;
ret.hasLocals = dxbc->m_DebugInfo->HasLocals();
return ret;
}
@@ -333,6 +333,10 @@ public:
virtual void GetLineInfo(size_t instruction, uintptr_t offset, int32_t &fileIdx, int32_t &lineNum,
std::string &funcName) const = 0;
virtual void GetStack(size_t instruction, uintptr_t offset, rdcarray<rdcstr> &stack) const = 0;
virtual bool HasLocals() const = 0;
virtual void GetLocals(size_t instruction, uintptr_t offset,
rdcarray<LocalVariableMapping> &locals) const = 0;
};
uint32_t DecodeFlags(const ShaderCompileFlags &compileFlags);
@@ -120,6 +120,16 @@ void SDBGChunk::GetStack(size_t instruction, uintptr_t offset, rdcarray<rdcstr>
stack = {"Stack not available"};
}
bool SDBGChunk::HasLocals() const
{
return false;
}
void SDBGChunk::GetLocals(size_t instruction, uintptr_t offset,
rdcarray<LocalVariableMapping> &locals) const
{
}
string SDBGChunk::GetSymbolName(int symbolID)
{
RDCASSERT(symbolID >= 0 && symbolID < (int)m_SymbolTable.size());
@@ -264,6 +264,9 @@ public:
std::string &func) const;
void GetStack(size_t instruction, uintptr_t offset, rdcarray<rdcstr> &stack) const;
bool HasLocals() const;
void GetLocals(size_t instruction, uintptr_t offset, rdcarray<LocalVariableMapping> &locals) const;
private:
SDBGChunk();
SDBGChunk(const SDBGChunk &);
+490 -2
View File
@@ -175,6 +175,357 @@ SPDBChunk::SPDBChunk(void *chunk)
}
}
std::map<uint32_t, VarType> typeInfo;
// prepopulate with basic types
typeInfo[T_INT4] = VarType::Int;
typeInfo[T_INT2] = VarType::Int;
typeInfo[T_INT1] = VarType::Int;
typeInfo[T_LONG] = VarType::Int;
typeInfo[T_SHORT] = VarType::Int;
typeInfo[T_CHAR] = VarType::Int;
typeInfo[T_UINT4] = VarType::UInt;
typeInfo[T_UINT2] = VarType::UInt;
typeInfo[T_UINT1] = VarType::UInt;
typeInfo[T_ULONG] = VarType::UInt;
typeInfo[T_USHORT] = VarType::UInt;
typeInfo[T_UCHAR] = VarType::UInt;
typeInfo[T_REAL16] = VarType::Float;
typeInfo[T_REAL32] = VarType::Float;
typeInfo[T_REAL64] = VarType::Double;
if(streams.size() >= 3)
{
SPDBLOG("Got types stream");
PDBStream &s = streams[2];
PageMapping fileContents(pages, header->PageSize, &s.pageIndices[0],
(uint32_t)s.pageIndices.size());
byte *bytes = (byte *)fileContents.Data();
byte *end = bytes + s.byteLength;
TPIHeader *tpi = (TPIHeader *)bytes;
// skip header
bytes += tpi->headerSize;
RDCASSERT(bytes + tpi->dataSize == end);
// this isn't needed, but this is the hash stream
#if 0
PageMapping hashContents;
if(tpi->hash.streamNumber < streams.size())
{
PDBStream &hashstrm = streams[tpi->hash.streamNumber];
hashContents = PageMapping(pages, header->PageSize, &hashstrm.pageIndices[0],
(uint32_t)hashstrm.pageIndices.size());
}
#endif
uint32_t id = tpi->typeMin;
while(bytes < end)
{
uint16_t *leafheader = (uint16_t *)bytes;
uint16_t length = leafheader[0];
LEAF_ENUM_e type = (LEAF_ENUM_e)leafheader[1];
byte *leaf = (byte *)&leafheader[1];
bytes += 2 + length;
switch(type)
{
case LF_VECTOR:
{
lfVector *vector = (lfVector *)leaf;
// documentation isn't clear, but seems like byte size is always a uint16_t
uint16_t *bytelength = (uint16_t *)vector->data;
char *name = (char *)(bytelength + 1);
SPDBLOG("Type %x is '%s': a vector of %x with %u elements over %u bytes", id, name,
vector->elemtype, vector->count, *bytelength);
typeInfo[id] = typeInfo[vector->elemtype];
break;
}
case LF_MATRIX:
{
lfMatrix *matrix = (lfMatrix *)leaf;
// documentation isn't clear, but seems like byte size is always a uint16_t
uint16_t *bytelength = (uint16_t *)matrix->data;
char *name = (char *)(bytelength + 1);
SPDBLOG(
"Type %x is '%s': a matrix of %x with %u rows, %u columns over %u bytes with %u "
"byte %s major stride",
id, name, matrix->elemtype, matrix->rows, matrix->cols, *bytelength,
matrix->majorStride, matrix->matattr.row_major ? "row" : "column");
typeInfo[id] = typeInfo[matrix->elemtype];
break;
}
case LF_HLSL:
{
lfHLSL *hlsl = (lfHLSL *)leaf;
// documentation mentions "numeric properties followed by byte size" but we don't need
// that
const char *hlslTypeName = "";
switch((CV_builtin_e)hlsl->kind)
{
case CV_BI_HLSL_INTERFACE_POINTER: hlslTypeName = "INTERFACE_POINTER"; break;
case CV_BI_HLSL_TEXTURE1D: hlslTypeName = "TEXTURE1D"; break;
case CV_BI_HLSL_TEXTURE1D_ARRAY: hlslTypeName = "TEXTURE1D_ARRAY"; break;
case CV_BI_HLSL_TEXTURE2D: hlslTypeName = "TEXTURE2D"; break;
case CV_BI_HLSL_TEXTURE2D_ARRAY: hlslTypeName = "TEXTURE2D_ARRAY"; break;
case CV_BI_HLSL_TEXTURE3D: hlslTypeName = "TEXTURE3D"; break;
case CV_BI_HLSL_TEXTURECUBE: hlslTypeName = "TEXTURECUBE"; break;
case CV_BI_HLSL_TEXTURECUBE_ARRAY: hlslTypeName = "TEXTURECUBE_ARRAY"; break;
case CV_BI_HLSL_TEXTURE2DMS: hlslTypeName = "TEXTURE2DMS"; break;
case CV_BI_HLSL_TEXTURE2DMS_ARRAY: hlslTypeName = "TEXTURE2DMS_ARRAY"; break;
case CV_BI_HLSL_SAMPLER: hlslTypeName = "SAMPLER"; break;
case CV_BI_HLSL_SAMPLERCOMPARISON: hlslTypeName = "SAMPLERCOMPARISON"; break;
case CV_BI_HLSL_BUFFER: hlslTypeName = "BUFFER"; break;
case CV_BI_HLSL_POINTSTREAM: hlslTypeName = "POINTSTREAM"; break;
case CV_BI_HLSL_LINESTREAM: hlslTypeName = "LINESTREAM"; break;
case CV_BI_HLSL_TRIANGLESTREAM: hlslTypeName = "TRIANGLESTREAM"; break;
case CV_BI_HLSL_INPUTPATCH: hlslTypeName = "INPUTPATCH"; break;
case CV_BI_HLSL_OUTPUTPATCH: hlslTypeName = "OUTPUTPATCH"; break;
case CV_BI_HLSL_RWTEXTURE1D: hlslTypeName = "RWTEXTURE1D"; break;
case CV_BI_HLSL_RWTEXTURE1D_ARRAY: hlslTypeName = "RWTEXTURE1D_ARRAY"; break;
case CV_BI_HLSL_RWTEXTURE2D: hlslTypeName = "RWTEXTURE2D"; break;
case CV_BI_HLSL_RWTEXTURE2D_ARRAY: hlslTypeName = "RWTEXTURE2D_ARRAY"; break;
case CV_BI_HLSL_RWTEXTURE3D: hlslTypeName = "RWTEXTURE3D"; break;
case CV_BI_HLSL_RWBUFFER: hlslTypeName = "RWBUFFER"; break;
case CV_BI_HLSL_BYTEADDRESS_BUFFER: hlslTypeName = "BYTEADDRESS_BUFFER"; break;
case CV_BI_HLSL_RWBYTEADDRESS_BUFFER: hlslTypeName = "RWBYTEADDRESS_BUFFER"; break;
case CV_BI_HLSL_STRUCTURED_BUFFER: hlslTypeName = "STRUCTURED_BUFFER"; break;
case CV_BI_HLSL_RWSTRUCTURED_BUFFER: hlslTypeName = "RWSTRUCTURED_BUFFER"; break;
case CV_BI_HLSL_APPEND_STRUCTURED_BUFFER:
hlslTypeName = "APPEND_STRUCTURED_BUFFER";
break;
case CV_BI_HLSL_CONSUME_STRUCTURED_BUFFER:
hlslTypeName = "CONSUME_STRUCTURED_BUFFER";
break;
case CV_BI_HLSL_MIN8FLOAT: hlslTypeName = "MIN8FLOAT"; break;
case CV_BI_HLSL_MIN10FLOAT: hlslTypeName = "MIN10FLOAT"; break;
case CV_BI_HLSL_MIN16FLOAT: hlslTypeName = "MIN16FLOAT"; break;
case CV_BI_HLSL_MIN12INT: hlslTypeName = "MIN12INT"; break;
case CV_BI_HLSL_MIN16INT: hlslTypeName = "MIN16INT"; break;
case CV_BI_HLSL_MIN16UINT: hlslTypeName = "MIN16UINT"; break;
default: hlslTypeName = "Unknown type";
}
SPDBLOG("Type %x is an hlsl %s[%u] (subtype %x)", id, hlslTypeName, hlsl->numprops,
hlsl->subtype);
break;
}
case LF_MODIFIER_EX:
{
lfModifierEx *modifier = (lfModifierEx *)leaf;
SPDBLOG("Type %x is %x modified with:", id, modifier->type);
typeInfo[id] = typeInfo[modifier->type];
uint16_t *mods = (uint16_t *)modifier->mods;
for(unsigned short i = 0; i < modifier->count; i++)
{
CV_modifier_e mod = (CV_modifier_e)mods[i];
const char *modName = "";
switch(mod)
{
case CV_MOD_CONST: modName = "CONST"; break;
case CV_MOD_VOLATILE: modName = "VOLATILE"; break;
case CV_MOD_UNALIGNED: modName = "UNALIGNED"; break;
case CV_MOD_HLSL_UNIFORM: modName = "HLSL_UNIFORM"; break;
case CV_MOD_HLSL_LINE: modName = "HLSL_LINE"; break;
case CV_MOD_HLSL_TRIANGLE: modName = "HLSL_TRIANGLE"; break;
case CV_MOD_HLSL_LINEADJ: modName = "HLSL_LINEADJ"; break;
case CV_MOD_HLSL_TRIANGLEADJ: modName = "HLSL_TRIANGLEADJ"; break;
case CV_MOD_HLSL_LINEAR: modName = "HLSL_LINEAR"; break;
case CV_MOD_HLSL_CENTROID: modName = "HLSL_CENTROID"; break;
case CV_MOD_HLSL_CONSTINTERP: modName = "HLSL_CONSTINTERP"; break;
case CV_MOD_HLSL_NOPERSPECTIVE: modName = "HLSL_NOPERSPECTIVE"; break;
case CV_MOD_HLSL_SAMPLE: modName = "HLSL_SAMPLE"; break;
case CV_MOD_HLSL_CENTER: modName = "HLSL_CENTER"; break;
case CV_MOD_HLSL_SNORM: modName = "HLSL_SNORM"; break;
case CV_MOD_HLSL_UNORM: modName = "HLSL_UNORM"; break;
case CV_MOD_HLSL_PRECISE: modName = "HLSL_PRECISE"; break;
case CV_MOD_HLSL_UAV_GLOBALLY_COHERENT: modName = "HLSL_UAV_GLOBALLY_COHERENT"; break;
default: modName = "Unknown modification";
}
SPDBLOG(" + %s", modName);
}
break;
}
case LF_FIELDLIST:
{
lfFieldList *fieldList = (lfFieldList *)leaf;
SPDBLOG("Type %x is a field list containing:", id);
uint32_t idx = 0;
byte *iter = (byte *)fieldList->data;
while(iter < bytes)
{
if(*iter >= LF_PAD0)
{
iter += (*iter) - LF_PAD0;
continue;
}
LEAF_ENUM_e memberType = LEAF_ENUM_e(*(uint16_t *)iter);
switch(memberType)
{
case LF_MEMBER:
{
lfMember *member = (lfMember *)iter;
uint16_t *byteoffset = (uint16_t *)member->offset;
char *name = (char *)(byteoffset + 1);
char *access = "???";
if(member->attr.access == 1)
access = "private";
else if(member->attr.access == 2)
access = "protected";
else if(member->attr.access == 3)
access = "public";
SPDBLOG(" [%u]: %x %s (%s) (at offset %u bytes)", idx, member->index, name, access,
*byteoffset);
idx++;
iter = (byte *)(name + strlen(name) + 1);
break;
}
case LF_ONEMETHOD:
{
lfOneMethod *method = (lfOneMethod *)iter;
iter = (byte *)method->vbaseoff;
// MTintro = 0x04, MTpureintro = 0x06
if(method->attr.mprop == 0x04 || method->attr.mprop == 0x06)
{
iter += 4;
}
char *name = (char *)iter;
iter += strlen(name) + 1;
char *access = "???";
if(method->attr.access == 1)
access = "private";
else if(method->attr.access == 2)
access = "protected";
else if(method->attr.access == 3)
access = "public";
SPDBLOG(" [%u]: Method %s (%s) (at offset %u bytes)", idx, name, access);
idx++;
break;
}
case LF_BINTERFACE:
{
lfBClass *binterface = (lfBClass *)iter;
char *access = "???";
if(binterface->attr.access == 1)
access = "private";
else if(binterface->attr.access == 2)
access = "protected";
else if(binterface->attr.access == 3)
access = "public";
SPDBLOG(" [%u]: %x Interface (%s)", idx, binterface->index, access);
iter = (byte *)binterface->offset + 2;
idx++;
break;
}
default:
{
RDCERR("Unexpected member type %x", memberType);
// skip the remaining data as we don't know how to safely advance - no length fields
// to use
iter = bytes;
break;
}
}
}
break;
}
case LF_ARGLIST:
{
lfArgList *argList = (lfArgList *)leaf;
SPDBLOG("Type %x is a field list containing:", id);
for(unsigned long i = 0; i < argList->count; i++)
SPDBLOG(" %x", argList->arg[i]);
break;
}
case LF_INTERFACE:
case LF_CLASS:
case LF_STRUCTURE:
{
lfStructure *structure = (lfStructure *)leaf;
// documentation isn't clear, but seems like byte size is always a uint16_t
uint16_t *bytelength = (uint16_t *)structure->data;
char *name = (char *)(bytelength + 1);
const char *structType = "struct";
if(type == LF_INTERFACE)
structType = "interface";
else if(type == LF_CLASS)
structType = "class";
SPDBLOG(
"Type %x is '%s': a %s with %u fields %x derived from %x and vshape %x over %u "
"bytes",
id, name, structType, structure->count, structure->field, structure->derived,
structure->vshape, *bytelength);
break;
}
case LF_PROCEDURE:
{
lfProc *procedure = (lfProc *)leaf;
SPDBLOG("Type %x is a procedure returning %x with %u args: %x", id, procedure->rvtype,
procedure->parmcount, procedure->arglist);
break;
}
case LF_MFUNCTION:
{
lfMFunc *mfunction = (lfMFunc *)leaf;
SPDBLOG("Type %x is a member function of class %x returning %x with %u args: %x", id,
mfunction->classtype, mfunction->rvtype, mfunction->parmcount, mfunction->arglist);
break;
}
default:
{
SPDBLOG("Encountered unknown type leaf %x", type);
break;
}
}
id++;
}
RDCASSERT(id == tpi->typeMax);
}
if(streams.size() >= 5)
{
SPDBLOG("Got function calls stream");
@@ -668,25 +1019,33 @@ SPDBChunk::SPDBChunk(void *chunk)
{
DEFRANGESYMHLSL *defrange = (DEFRANGESYMHLSL *)sym;
LocalMapping mapping;
bool indexable = false;
const char *regtype = "";
const char *regprefix = "?";
switch((CV_HLSLREG_e)defrange->regType)
{
case CV_HLSLREG_TEMP:
mapping.var.registerType = RegisterType::Temporary;
regtype = "temp";
regprefix = "r";
break;
case CV_HLSLREG_INPUT:
mapping.var.registerType = RegisterType::Input;
regtype = "input";
regprefix = "v";
break;
case CV_HLSLREG_OUTPUT:
mapping.var.registerType = RegisterType::Output;
regtype = "output";
regprefix = "o";
break;
case CV_HLSLREG_INDEXABLE_TEMP:
mapping.var.registerType = RegisterType::IndexedTemporary;
regtype = "indexable";
regprefix = "x";
indexable = true;
break;
default: break;
}
@@ -709,9 +1068,9 @@ SPDBChunk::SPDBChunk(void *chunk)
char regcomps[] = "xyzw";
uint32_t regindex = regoffset / 16;
uint32_t regindex = indexable ? regoffset : regoffset / 16;
uint32_t regfirstcomp = (regoffset % 16) / 4;
uint32_t regnumcomps = defrange->sizeInParent / 4;
uint32_t regnumcomps = indexable ? 4 : defrange->sizeInParent / 4;
char *regswizzle = regcomps;
regswizzle += regfirstcomp;
@@ -719,9 +1078,23 @@ SPDBChunk::SPDBChunk(void *chunk)
SPDBLOG("Stored in %s%u.%s", regprefix, regindex, regswizzle);
mapping.var.localName = localName;
mapping.var.variableType = typeInfo[localType];
mapping.var.registerIndex = regindex;
for(uint32_t i = 0; i < regnumcomps; i++)
{
mapping.var.registerSwizzle[i] = uint8_t(regfirstcomp + i);
mapping.var.variableSwizzle[i] = uint8_t((defrange->offsetParent % 16) / 4 + i);
}
SPDBLOG("Valid from %x to %x", defrange->range.offStart,
defrange->range.offStart + defrange->range.cbRange);
mapping.range.startRange = defrange->range.offStart;
mapping.range.endRange = defrange->range.offStart + defrange->range.cbRange;
const CV_LVAR_ADDR_GAP *gaps = CV_DEFRANGESYMHLSL_GAPS_CONST_PTR(defrange);
size_t gapcount = CV_DEFRANGESYMHLSL_GAPS_COUNT(defrange);
if(gapcount > 0)
@@ -730,7 +1103,16 @@ SPDBChunk::SPDBChunk(void *chunk)
{
SPDBLOG(" Gap %zu: %x -> %x", i, defrange->range.offStart + gaps[i].gapStartOffset,
defrange->range.offStart + gaps[i].gapStartOffset + gaps[i].cbRange);
LocalRange r = {defrange->range.offStart + gaps[i].gapStartOffset,
defrange->range.offStart + gaps[i].gapStartOffset + gaps[i].cbRange};
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);
}
else if(type == S_INLINESITE_END)
{
@@ -1041,6 +1423,8 @@ SPDBChunk::SPDBChunk(void *chunk)
it->second.fileIndex = remapping[filenames[it->second.fileIndex]];
}
std::sort(m_Locals.begin(), m_Locals.end());
m_HasDebugInfo = true;
}
@@ -1069,4 +1453,108 @@ void SPDBChunk::GetStack(size_t instruction, uintptr_t offset, rdcarray<rdcstr>
}
}
bool SPDBChunk::HasLocals() const
{
return true;
}
void SPDBChunk::GetLocals(size_t instruction, uintptr_t offset,
rdcarray<LocalVariableMapping> &locals) const
{
locals.clear();
for(auto it = m_Locals.begin(); it != m_Locals.end(); ++it)
{
if(it->range.startRange > offset)
break;
if(it->range.endRange <= offset)
continue;
bool ingap = false;
for(auto gapit = it->gaps.begin(); gapit != it->gaps.end(); gapit++)
{
if(gapit->startRange >= offset && gapit->endRange < offset)
{
ingap = true;
break;
}
}
if(ingap)
continue;
bool added = false;
// check for duplicate registers
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)
{
// 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;
}
}
}
added = true;
break;
}
}
if(!added)
locals.push_back(it->var);
}
}
}; // namespace DXBC
+25
View File
@@ -249,6 +249,26 @@ struct PDBStream
vector<uint32_t> pageIndices;
};
struct LocalRange
{
uint32_t startRange;
uint32_t endRange;
bool operator==(const LocalRange &o) const
{
return startRange == o.startRange && endRange == o.endRange;
}
};
struct LocalMapping
{
bool operator<(const LocalMapping &o) const { return range.startRange < o.range.startRange; }
LocalRange range;
std::vector<LocalRange> gaps;
LocalVariableMapping var;
};
class SPDBChunk : public DXBCDebugChunk
{
public:
@@ -262,6 +282,9 @@ public:
std::string &func) const;
void GetStack(size_t instruction, uintptr_t offset, rdcarray<rdcstr> &stack) const;
bool HasLocals() const;
void GetLocals(size_t instruction, uintptr_t offset, rdcarray<LocalVariableMapping> &locals) const;
private:
SPDBChunk(const SPDBChunk &);
SPDBChunk &operator=(const SPDBChunk &o);
@@ -275,6 +298,8 @@ private:
uint32_t m_ShaderFlags;
std::vector<LocalMapping> m_Locals;
std::map<uint32_t, Function> m_Functions;
std::map<uint32_t, LineColumnInfo> m_Lines;
};
+18 -2
View File
@@ -341,17 +341,31 @@ void DoSerialise(SerialiserType &ser, ShaderVariable &el)
SIZE_CHECK(184);
}
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);
SIZE_CHECK(40);
}
template <typename SerialiserType>
void DoSerialise(SerialiserType &ser, ShaderDebugState &el)
{
SERIALISE_MEMBER(registers);
SERIALISE_MEMBER(outputs);
SERIALISE_MEMBER(indexableTemps);
SERIALISE_MEMBER(locals);
SERIALISE_MEMBER(nextInstruction);
SERIALISE_MEMBER(flags);
SERIALISE_MEMBER(callstack);
SIZE_CHECK(56);
SIZE_CHECK(88);
}
template <typename SerialiserType>
@@ -360,8 +374,9 @@ void DoSerialise(SerialiserType &ser, ShaderDebugTrace &el)
SERIALISE_MEMBER(inputs);
SERIALISE_MEMBER(constantBlocks);
SERIALISE_MEMBER(states);
SERIALISE_MEMBER(hasLocals);
SIZE_CHECK(48);
SIZE_CHECK(56);
}
template <typename SerialiserType>
@@ -2099,6 +2114,7 @@ INSTANTIATE_SERIALISE_TYPE(ShaderCompileFlags)
INSTANTIATE_SERIALISE_TYPE(ShaderDebugInfo)
INSTANTIATE_SERIALISE_TYPE(ShaderReflection)
INSTANTIATE_SERIALISE_TYPE(ShaderVariable)
INSTANTIATE_SERIALISE_TYPE(LocalVariableMapping);
INSTANTIATE_SERIALISE_TYPE(ShaderDebugState)
INSTANTIATE_SERIALISE_TYPE(ShaderDebugTrace)
INSTANTIATE_SERIALISE_TYPE(ResourceDescription)