mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-17 13:07:14 +00:00
Display high-level language locals corresponding to registers
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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++)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 &);
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user