Add fake source-mapping information for constants in debug traces

* cbuffer and input registers no longer have any names elided into them, they
  are 'raw' registers. The source mapping provides the information of where they
  come from as best as possible.
* Since we're now using source-mapping for constants, fix some source mapping
  issues with arrays, nested structs/arrays, and matrix major-ness.
This commit is contained in:
baldurk
2020-01-29 14:10:21 +00:00
parent 66cebe0a4c
commit 0e0ff8714c
14 changed files with 697 additions and 477 deletions
+289 -276
View File
@@ -55,9 +55,17 @@ struct VariableTag
return cat == o.cat && index == o.index && member == o.member;
}
};
struct SourceVariableTag
{
SourceVariableTag(QString name = QString(), uint32_t offs = 0) : fullname(name), offset(offs) {}
QString fullname;
uint32_t offset;
};
};
Q_DECLARE_METATYPE(VariableTag);
Q_DECLARE_METATYPE(SourceVariableTag);
ShaderViewer::ShaderViewer(ICaptureContext &ctx, QWidget *parent)
: QFrame(parent), ui(new Ui::ShaderViewer), m_Ctx(ctx)
@@ -65,8 +73,8 @@ ShaderViewer::ShaderViewer(ICaptureContext &ctx, QWidget *parent)
ui->setupUi(this);
ui->constants->setFont(Formatter::PreferredFont());
ui->registers->setFont(Formatter::PreferredFont());
ui->locals->setFont(Formatter::PreferredFont());
ui->debugVars->setFont(Formatter::PreferredFont());
ui->sourceVars->setFont(Formatter::PreferredFont());
ui->watch->setFont(Formatter::PreferredFont());
ui->inputSig->setFont(Formatter::PreferredFont());
ui->outputSig->setFont(Formatter::PreferredFont());
@@ -224,10 +232,10 @@ void ShaderViewer::editShader(bool customShader, ShaderStage stage, const QStrin
// hide debugging windows
ui->watch->hide();
ui->registers->hide();
ui->debugVars->hide();
ui->constants->hide();
ui->callstack->hide();
ui->locals->hide();
ui->sourceVars->hide();
ui->snippets->setVisible(customShader);
@@ -493,23 +501,25 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR
ui->debugToggle->setText(tr("HLSL Unavailable"));
}
ui->registers->setColumns({tr("Name"), tr("Type"), tr("Value")});
ui->registers->header()->setSectionResizeMode(0, QHeaderView::Interactive);
ui->registers->header()->setSectionResizeMode(1, QHeaderView::Interactive);
ui->registers->header()->setSectionResizeMode(2, QHeaderView::Stretch);
ui->debugVars->setColumns({tr("Name"), tr("Value")});
ui->debugVars->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
ui->debugVars->header()->setSectionResizeMode(1, QHeaderView::Interactive);
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);
ui->locals->header()->setSectionResizeMode(3, QHeaderView::Stretch);
ui->sourceVars->setColumns({tr("Name"), tr("Register(s)"), tr("Type"), tr("Value")});
ui->sourceVars->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
ui->sourceVars->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
ui->sourceVars->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
ui->sourceVars->header()->setSectionResizeMode(3, QHeaderView::Interactive);
ui->constants->setColumns({tr("Name"), tr("Type"), tr("Value")});
ui->constants->header()->setSectionResizeMode(0, QHeaderView::Interactive);
ui->constants->header()->setSectionResizeMode(1, QHeaderView::Interactive);
ui->constants->header()->setSectionResizeMode(2, QHeaderView::Stretch);
ui->constants->setColumns({tr("Name"), tr("Register(s)"), tr("Type"), tr("Value")});
ui->constants->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
ui->constants->header()->setSectionResizeMode(1, QHeaderView::ResizeToContents);
ui->constants->header()->setSectionResizeMode(2, QHeaderView::ResizeToContents);
ui->constants->header()->setSectionResizeMode(3, QHeaderView::Interactive);
ui->registers->setTooltipElidedItems(false);
ui->constants->header()->resizeSection(0, 80);
ui->debugVars->setTooltipElidedItems(false);
ui->constants->setTooltipElidedItems(false);
ui->watch->setWindowTitle(tr("Watch"));
@@ -519,39 +529,41 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR
ui->docking->setToolWindowProperties(
ui->watch, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
ui->registers->setWindowTitle(tr("Registers"));
ui->debugVars->setWindowTitle(tr("Variable Values"));
ui->docking->addToolWindow(
ui->registers,
ui->debugVars,
ToolWindowManager::AreaReference(ToolWindowManager::AddTo, ui->docking->areaOf(ui->watch)));
ui->docking->setToolWindowProperties(
ui->registers, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
ui->debugVars, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
ui->constants->setWindowTitle(tr("Constants && Resources"));
ui->docking->addToolWindow(
ui->constants, ToolWindowManager::AreaReference(ToolWindowManager::LeftOf,
ui->docking->areaOf(ui->registers), 0.5f));
ui->docking->areaOf(ui->debugVars), 0.5f));
ui->docking->setToolWindowProperties(
ui->constants, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
ui->callstack->setWindowTitle(tr("Callstack"));
ui->docking->addToolWindow(
ui->callstack, ToolWindowManager::AreaReference(ToolWindowManager::RightOf,
ui->docking->areaOf(ui->registers), 0.2f));
ui->docking->setToolWindowProperties(
ui->callstack, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
if(m_Trace->hasSourceMapping)
{
ui->locals->setWindowTitle(tr("Local Variables"));
ui->callstack->setWindowTitle(tr("Callstack"));
ui->docking->addToolWindow(
ui->locals, ToolWindowManager::AreaReference(ToolWindowManager::AddTo,
ui->docking->areaOf(ui->registers)));
ui->callstack, ToolWindowManager::AreaReference(
ToolWindowManager::RightOf, ui->docking->areaOf(ui->debugVars), 0.2f));
ui->docking->setToolWindowProperties(
ui->locals, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
ui->callstack, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
ui->sourceVars->setWindowTitle(tr("High-level Variables"));
ui->docking->addToolWindow(
ui->sourceVars, ToolWindowManager::AreaReference(ToolWindowManager::AddTo,
ui->docking->areaOf(ui->debugVars)));
ui->docking->setToolWindowProperties(
ui->sourceVars,
ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
}
else
{
ui->locals->hide();
ui->callstack->hide();
ui->sourceVars->hide();
}
m_Line2Insts.resize(m_ShaderDetails->debugInfo.files.count());
@@ -628,7 +640,7 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR
// event filter to pick up tooltip events
ui->constants->installEventFilter(this);
ui->registers->installEventFilter(this);
ui->debugVars->installEventFilter(this);
ui->watch->installEventFilter(this);
SetCurrentStep(0);
@@ -638,11 +650,11 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR
ui->watch->setContextMenuPolicy(Qt::CustomContextMenu);
QObject::connect(ui->watch, &RDTableWidget::customContextMenuRequested, this,
&ShaderViewer::variables_contextMenu);
ui->registers->setContextMenuPolicy(Qt::CustomContextMenu);
QObject::connect(ui->registers, &RDTreeWidget::customContextMenuRequested, this,
ui->debugVars->setContextMenuPolicy(Qt::CustomContextMenu);
QObject::connect(ui->debugVars, &RDTreeWidget::customContextMenuRequested, this,
&ShaderViewer::variables_contextMenu);
ui->locals->setContextMenuPolicy(Qt::CustomContextMenu);
QObject::connect(ui->locals, &RDTreeWidget::customContextMenuRequested, this,
ui->sourceVars->setContextMenuPolicy(Qt::CustomContextMenu);
QObject::connect(ui->sourceVars, &RDTreeWidget::customContextMenuRequested, this,
&ShaderViewer::variables_contextMenu);
ui->watch->insertRow(0);
@@ -663,9 +675,9 @@ void ShaderViewer::debugShader(const ShaderBindpointMapping *bind, const ShaderR
{
// hide watch, constants, variables
ui->watch->hide();
ui->registers->hide();
ui->debugVars->hide();
ui->constants->hide();
ui->locals->hide();
ui->sourceVars->hide();
ui->callstack->hide();
// hide debugging toolbar buttons
@@ -1118,8 +1130,8 @@ void ShaderViewer::variables_contextMenu(const QPoint &pos)
addWatch.setEnabled(tree->selectedItem() != NULL);
QObject::connect(&addWatch, &QAction::triggered, [this, tree] {
if(tree == ui->locals)
AddWatch(tree->selectedItem()->tag().toString());
if(tree == ui->sourceVars)
AddWatch(tree->selectedItem()->tag().value<SourceVariableTag>().fullname);
else
AddWatch(tree->selectedItem()->text(0));
});
@@ -1168,9 +1180,9 @@ void ShaderViewer::disassembly_buttonReleased(QMouseEvent *event)
start = 0;
end = m_DisassemblyView->length();
for(int i = 0; i < ui->registers->topLevelItemCount(); i++)
for(int i = 0; i < ui->debugVars->topLevelItemCount(); i++)
{
RDTreeWidgetItem *item = ui->registers->topLevelItem(i);
RDTreeWidgetItem *item = ui->debugVars->topLevelItem(i);
if(item->tag().value<VariableTag>() == tag)
item->setBackgroundColor(QColor::fromHslF(
0.333f, 1.0f, qBound(0.25, palette().color(QPalette::Base).lightnessF(), 0.85)));
@@ -1522,60 +1534,6 @@ QString ShaderViewer::stringRep(const ShaderVariable &var, bool useType)
return RowString(var, 0, VarType::Float);
}
RDTreeWidgetItem *ShaderViewer::makeResourceRegister(const Bindpoint &bind, uint32_t idx,
const BoundResource &bound,
const ShaderResource &res)
{
QString name = QFormatStr(" (%1)").arg(res.name);
const TextureDescription *tex = m_Ctx.GetTexture(bound.resourceId);
const BufferDescription *buf = m_Ctx.GetBuffer(bound.resourceId);
QChar regChar(QLatin1Char('u'));
if(res.isReadOnly)
regChar = QLatin1Char('t');
QString regname;
if(m_Ctx.APIProps().pipelineType == GraphicsAPI::D3D12)
{
if(bind.arraySize == 1)
regname = QFormatStr("%1%2:%3").arg(regChar).arg(bind.bindset).arg(bind.bind);
else
regname = QFormatStr("%1%2:%3[%4]").arg(regChar).arg(bind.bindset).arg(bind.bind).arg(idx);
}
else
{
regname = QFormatStr("%1%2").arg(regChar).arg(bind.bind);
}
if(tex)
{
QString type = QFormatStr("%1x%2x%3[%4] @ %5 - %6")
.arg(tex->width)
.arg(tex->height)
.arg(tex->depth > 1 ? tex->depth : tex->arraysize)
.arg(tex->mips)
.arg(tex->format.Name())
.arg(m_Ctx.GetResourceName(bound.resourceId));
return new RDTreeWidgetItem({regname + name, lit("Texture"), type});
}
else if(buf)
{
QString type =
QFormatStr("%1 - %2").arg(buf->length).arg(m_Ctx.GetResourceName(bound.resourceId));
return new RDTreeWidgetItem({regname + name, lit("Buffer"), type});
}
else
{
return new RDTreeWidgetItem(
{regname + name, lit("Resource"), m_Ctx.GetResourceName(bound.resourceId)});
}
}
QString ShaderViewer::targetName(const ShaderProcessingTool &disasm)
{
return lit("%1 (%2)").arg(ToQStr(disasm.output)).arg(disasm.name);
@@ -1609,7 +1567,7 @@ void ShaderViewer::addFileList()
list, ToolWindowManager::HideCloseButton | ToolWindowManager::DisallowFloatWindow);
}
void ShaderViewer::combineStructures(RDTreeWidgetItem *root)
void ShaderViewer::combineStructures(RDTreeWidgetItem *root, int skipPrefixLength)
{
RDTreeWidgetItem temp;
@@ -1630,8 +1588,8 @@ void ShaderViewer::combineStructures(RDTreeWidgetItem *root)
QString name = child->text(0);
int dotIndex = name.indexOf(QLatin1Char('.'));
int arrIndex = name.indexOf(QLatin1Char('['));
int dotIndex = name.indexOf(QLatin1Char('.'), skipPrefixLength);
int arrIndex = name.indexOf(QLatin1Char('['), skipPrefixLength);
// if this node doesn't have any segments, just move it across.
if(dotIndex < 0 && arrIndex < 0)
@@ -1642,12 +1600,9 @@ void ShaderViewer::combineStructures(RDTreeWidgetItem *root)
// store the index of the first separator
int sepIndex = dotIndex;
bool isArray = false;
bool isLeafArray = (sepIndex == -1);
if(sepIndex == -1 || (arrIndex > 0 && arrIndex < sepIndex))
{
sepIndex = arrIndex;
isArray = true;
}
// we have a valid node to match against, record the prefix (including separator character)
QString prefix = name.mid(0, sepIndex + 1);
@@ -1681,9 +1636,13 @@ void ShaderViewer::combineStructures(RDTreeWidgetItem *root)
continue;
}
// sort the children by name
// sort the children by offset, then name (in case all offsets are empty)
std::sort(matches.begin(), matches.end(),
[](const RDTreeWidgetItem *a, const RDTreeWidgetItem *b) {
SourceVariableTag at = a->tag().value<SourceVariableTag>();
SourceVariableTag bt = b->tag().value<SourceVariableTag>();
if(at.offset != bt.offset)
return at.offset < bt.offset;
return a->text(0) < b->text(0);
});
@@ -1696,7 +1655,7 @@ void ShaderViewer::combineStructures(RDTreeWidgetItem *root)
// add all the children (stripping the prefix from their name)
for(RDTreeWidgetItem *item : matches)
{
if(!isArray)
if(sepIndex == dotIndex)
item->setText(0, item->text(0).mid(sepIndex + 1));
parent->addChild(item);
@@ -1707,8 +1666,13 @@ void ShaderViewer::combineStructures(RDTreeWidgetItem *root)
}
// recurse and combine members of this object if a struct
if(!isArray)
combineStructures(parent);
if(!isLeafArray)
{
if(sepIndex != dotIndex)
combineStructures(parent, sepIndex + 1);
else
combineStructures(parent);
}
// now add to the list
temp.insertChild(0, parent);
@@ -1724,7 +1688,7 @@ void ShaderViewer::combineStructures(RDTreeWidgetItem *root)
RDTreeWidgetItem *ShaderViewer::findLocal(RDTreeWidgetItem *root, QString name)
{
if(root->tag().toString() == name)
if(root->tag().value<SourceVariableTag>().fullname == name)
return root;
for(int i = 0; i < root->childCount(); i++)
@@ -1858,31 +1822,67 @@ void ShaderViewer::updateDebugging()
if(ui->constants->topLevelItemCount() == 0)
{
// track all debug variables that have been mapped by source vars
QSet<QString> varsMapped;
RDTreeWidgetItem fakeroot;
for(const SourceVariableMapping &sourceVar : m_Trace->sourceVars)
{
for(const DebugVariableReference &r : sourceVar.variables)
varsMapped.insert(r.name);
fakeroot.addChild(makeSourceVariableNode(sourceVar));
}
// recursively combine nodes with the same prefix together
combineStructures(&fakeroot);
while(fakeroot.childCount() > 0)
ui->constants->addTopLevelItem(fakeroot.takeChild(0));
// add any raw registers that weren't mapped with something better. We assume for inputs that
// everything has a source mapping, even if it's faked from reflection info, but just to be sure
// we add any remainders here. Constants might be un-touched by reflection info
for(int i = 0; i < m_Trace->constantBlocks.count(); i++)
{
rdcstr name = m_Trace->constantBlocks[i].name;
RDTreeWidgetItem *node = new RDTreeWidgetItem({name, name, lit("Constant"), QString()});
for(int j = 0; j < m_Trace->constantBlocks[i].members.count(); j++)
{
if(m_Trace->constantBlocks[i].members[j].rows > 0 ||
m_Trace->constantBlocks[i].members[j].columns > 0)
{
RDTreeWidgetItem *node =
new RDTreeWidgetItem({m_Trace->constantBlocks[i].members[j].name, lit("cbuffer"),
stringRep(m_Trace->constantBlocks[i].members[j], false)});
node->setTag(QVariant::fromValue(VariableTag(VariableCategory::Constants, j, i)));
ui->constants->addTopLevelItem(node);
name = m_Trace->constantBlocks[i].members[j].name;
if(!varsMapped.contains(name))
node->addChild(
new RDTreeWidgetItem({name, name, lit("Constant"),
stringRep(m_Trace->constantBlocks[i].members[j], false)}));
}
}
if(node->childCount() == 0)
{
delete node;
continue;
}
node->setTag(QVariant::fromValue(VariableTag(VariableCategory::Constants, i)));
ui->constants->addTopLevelItem(node);
}
for(int i = 0; i < m_Trace->inputs.count(); i++)
{
const ShaderVariable &input = m_Trace->inputs[i];
if(varsMapped.contains(input.name))
continue;
if(input.rows > 0 || input.columns > 0)
{
RDTreeWidgetItem *node = new RDTreeWidgetItem(
{input.name, ToQStr(input.type) + lit(" input"), stringRep(input, true)});
RDTreeWidgetItem *node =
new RDTreeWidgetItem({input.name, input.name, lit("Input"), stringRep(input, false)});
node->setTag(QVariant::fromValue(VariableTag(VariableCategory::Inputs, i)));
ui->constants->addTopLevelItem(node);
@@ -1908,22 +1908,26 @@ void ShaderViewer::updateDebugging()
if(idx < 0 || rw[idx].resources.isEmpty())
continue;
QString bindname = makeBindName(bind, false);
if(bind.arraySize == 1)
{
RDTreeWidgetItem *node = makeResourceRegister(bind, 0, rw[idx].resources[0],
m_ShaderDetails->readWriteResources[i]);
RDTreeWidgetItem *node =
new RDTreeWidgetItem({bindname, m_ShaderDetails->readWriteResources[i].name,
lit("RW Resource"), ToQStr(rw[idx].resources[0].resourceId)});
if(node)
ui->constants->addTopLevelItem(node);
}
else
{
RDTreeWidgetItem *node =
new RDTreeWidgetItem({m_ShaderDetails->readWriteResources[i].name,
new RDTreeWidgetItem({bindname, m_ShaderDetails->readWriteResources[i].name,
QFormatStr("[%1]").arg(bind.arraySize), QString()});
for(uint32_t a = 0; a < bind.arraySize; a++)
node->addChild(makeResourceRegister(bind, a, rw[idx].resources[a],
m_ShaderDetails->readWriteResources[i]));
node->addChild(new RDTreeWidgetItem(
{QFormatStr("%1[%2]").arg(bindname).arg(a), m_ShaderDetails->readWriteResources[i].name,
lit("Resource"), ToQStr(rw[idx].resources[a].resourceId)}));
tree = true;
@@ -1945,183 +1949,76 @@ void ShaderViewer::updateDebugging()
if(idx < 0 || ro[idx].resources.isEmpty())
continue;
QString bindname = makeBindName(bind, true);
if(bind.arraySize == 1)
{
RDTreeWidgetItem *node = makeResourceRegister(bind, 0, ro[idx].resources[0],
m_ShaderDetails->readOnlyResources[i]);
RDTreeWidgetItem *node =
new RDTreeWidgetItem({bindname, m_ShaderDetails->readOnlyResources[i].name,
lit("Resource"), ToQStr(ro[idx].resources[0].resourceId)});
if(node)
ui->constants->addTopLevelItem(node);
}
else
{
RDTreeWidgetItem *node =
new RDTreeWidgetItem({m_ShaderDetails->readOnlyResources[i].name,
new RDTreeWidgetItem({bindname, m_ShaderDetails->readOnlyResources[i].name,
m_ShaderDetails->readOnlyResources[i].name,
QFormatStr("[%1]").arg(bind.arraySize), QString()});
for(uint32_t a = 0; a < bind.arraySize; a++)
node->addChild(makeResourceRegister(bind, a, ro[idx].resources[a],
m_ShaderDetails->readOnlyResources[i]));
node->addChild(new RDTreeWidgetItem(
{QFormatStr("%1[%2]").arg(bindname).arg(a), m_ShaderDetails->readOnlyResources[i].name,
lit("Resource"), ToQStr(ro[idx].resources[a].resourceId)}));
tree = true;
ui->constants->addTopLevelItem(node);
}
}
if(tree)
{
ui->constants->setIndentation(20);
ui->constants->setRootIsDecorated(true);
}
}
if(m_Trace->hasSourceMapping)
{
RDTreeViewExpansionState expansion;
ui->locals->saveExpansion(expansion, 0);
ui->sourceVars->saveExpansion(expansion, 0);
ui->locals->clear();
const QString xyzw = lit("xyzw");
ui->sourceVars->clear();
RDTreeWidgetItem fakeroot;
for(size_t lidx = 0; lidx < state.sourceVars.size(); lidx++)
const rdcarray<SourceVariableMapping> &sourceVars = state.sourceVars;
for(size_t lidx = 0; lidx < sourceVars.size(); lidx++)
{
// iterate in reverse order, so newest locals tend to end up on top
const SourceVariableMapping &l = state.sourceVars[state.sourceVars.size() - 1 - lidx];
QString localName = l.name;
QString regNames, typeName;
QString value;
const SourceVariableMapping &l = sourceVars[sourceVars.size() - 1 - lidx];
bool modified = false;
if(l.type == VarType::UInt)
typeName = lit("uint");
else if(l.type == VarType::SInt)
typeName = lit("int");
else if(l.type == VarType::Float)
typeName = lit("float");
else if(l.type == VarType::Double)
typeName = lit("double");
const ShaderVariable *variable = NULL;
if(!l.variables.empty() && l.variables[0].type == DebugVariableType::Variable)
{
for(const ShaderVariable &v : state.variables)
for(const DebugVariableReference &r : l.variables)
{
if(v.name == l.variables[0].name)
for(const DebugVariableReference &mr : state.modified)
{
variable = &v;
if(mr.name == r.name)
{
modified = true;
break;
}
}
if(modified)
break;
}
}
}
if(variable && !variable->members.empty())
{
typeName += lit("[]");
regNames = l.variables[0].name;
for(const DebugVariableReference &mr : state.modified)
{
if(mr.name == l.variables[0].name)
{
modified = true;
break;
}
}
}
else
{
if(l.rows > 1)
typeName += QFormatStr("%1x%2").arg(l.rows).arg(l.columns);
else
typeName += QString::number(l.columns);
for(size_t i = 0; i < l.variables.size(); i++)
{
const DebugVariableReference &r = l.variables[i];
if(l.variables[0].type == DebugVariableType::Variable)
{
for(const DebugVariableReference &mr : state.modified)
{
if(mr.name == r.name)
{
modified = true;
break;
}
}
}
if(!value.isEmpty())
value += lit(", ");
if(!regNames.isEmpty())
regNames += lit(", ");
if(r.name.empty())
{
regNames += lit("-");
value += lit("?");
continue;
}
const ShaderVariable *reg = GetRegisterVariable(r);
if(reg)
{
// if the previous register was the same, just append our component
if(i > 0 && r.name == l.variables[i - 1].name)
{
// 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(reg->name).arg(xyzw[r.component]);
}
if(l.type == VarType::UInt)
value += Formatter::Format(reg->value.uv[r.component]);
else if(l.type == VarType::SInt)
value += Formatter::Format(reg->value.iv[r.component]);
else if(l.type == VarType::Float)
value += Formatter::Format(reg->value.fv[r.component]);
else if(l.type == VarType::Double)
value += Formatter::Format(reg->value.dv[r.component]);
}
else
{
regNames += lit("<error>");
value += lit("<error>");
}
}
}
RDTreeWidgetItem *node = new RDTreeWidgetItem({localName, regNames, typeName, value});
node->setTag(localName);
RDTreeWidgetItem *node = makeSourceVariableNode(l);
if(modified)
node->setForegroundColor(QColor(Qt::red));
if(variable)
{
for(int t = 0; t < variable->members.count(); t++)
{
node->addChild(new RDTreeWidgetItem({
QFormatStr("%1[%2]").arg(localName).arg(t), QFormatStr("%1[%2]").arg(regNames).arg(t),
typeName, RowString(variable->members[t], 0, l.type),
}));
}
}
fakeroot.addChild(node);
}
@@ -2129,29 +2026,27 @@ void ShaderViewer::updateDebugging()
combineStructures(&fakeroot);
while(fakeroot.childCount() > 0)
ui->locals->addTopLevelItem(fakeroot.takeChild(0));
ui->sourceVars->addTopLevelItem(fakeroot.takeChild(0));
ui->locals->applyExpansion(expansion, 0);
ui->sourceVars->applyExpansion(expansion, 0);
}
if(ui->registers->topLevelItemCount() == 0)
if(ui->debugVars->topLevelItemCount() == 0)
{
for(int i = 0; i < state.variables.count(); i++)
{
RDTreeWidgetItem *node =
new RDTreeWidgetItem({state.variables[i].name, lit("variable"), QString()});
RDTreeWidgetItem *node = new RDTreeWidgetItem({state.variables[i].name, QString()});
for(int t = 0; t < state.variables[i].members.count(); t++)
node->addChild(
new RDTreeWidgetItem({state.variables[i].members[t].name, lit("variable"), QString()}));
ui->registers->addTopLevelItem(node);
node->addChild(new RDTreeWidgetItem({state.variables[i].members[t].name, QString()}));
ui->debugVars->addTopLevelItem(node);
}
}
ui->registers->beginUpdate();
ui->debugVars->beginUpdate();
for(int i = 0; i < state.variables.count(); i++)
{
RDTreeWidgetItem *node = ui->registers->topLevelItem(i);
RDTreeWidgetItem *node = ui->debugVars->topLevelItem(i);
bool modified = false;
@@ -2166,7 +2061,7 @@ void ShaderViewer::updateDebugging()
if(state.variables[i].members.empty())
{
node->setText(2, stringRep(state.variables[i], false));
node->setText(1, stringRep(state.variables[i], false));
node->setTag(QVariant::fromValue(VariableTag(VariableCategory::Variables, i)));
if(modified)
@@ -2185,13 +2080,13 @@ void ShaderViewer::updateDebugging()
else
child->setForeground(QBrush());
child->setText(2, stringRep(state.variables[i].members[t], false));
child->setText(1, stringRep(state.variables[i].members[t], false));
child->setTag(QVariant::fromValue(VariableTag(VariableCategory::Variables, i, t)));
}
}
}
ui->registers->endUpdate();
ui->debugVars->endUpdate();
ui->watch->setUpdatesEnabled(false);
@@ -2361,7 +2256,7 @@ void ShaderViewer::updateDebugging()
{
QString variablename = match.captured(1);
RDTreeWidgetItem *local = findLocal(ui->locals->invisibleRootItem(), match.captured(1));
RDTreeWidgetItem *local = findLocal(ui->sourceVars->invisibleRootItem(), match.captured(1));
if(local)
{
@@ -2405,14 +2300,132 @@ void ShaderViewer::updateDebugging()
ui->watch->setUpdatesEnabled(true);
ui->constants->resizeColumnToContents(0);
ui->registers->resizeColumnToContents(0);
ui->constants->resizeColumnToContents(1);
ui->registers->resizeColumnToContents(1);
ui->debugVars->resizeColumnToContents(0);
ui->debugVars->resizeColumnToContents(1);
updateVariableTooltip();
}
QString ShaderViewer::makeBindName(Bindpoint &bind, bool readOnly)
{
QChar regChar(QLatin1Char('u'));
if(readOnly)
regChar = QLatin1Char('t');
if(m_Ctx.APIProps().pipelineType == GraphicsAPI::D3D12)
return QFormatStr("%1%2:%3").arg(regChar).arg(bind.bindset).arg(bind.bind);
else
return QFormatStr("%1%2").arg(regChar).arg(bind.bind);
}
RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMapping &l)
{
const ShaderDebugState &state = m_Trace->states[m_CurrentStep];
const QString xyzw = lit("xyzw");
QString localName = l.name;
QString regNames, typeName;
QString value;
if(l.type == VarType::UInt)
typeName = lit("uint");
else if(l.type == VarType::SInt)
typeName = lit("int");
else if(l.type == VarType::Float)
typeName = lit("float");
else if(l.type == VarType::Double)
typeName = lit("double");
QList<RDTreeWidgetItem *> children;
{
QString childType = typeName;
if(l.rows > 1)
{
childType += QString::number(l.columns);
typeName += QFormatStr("%1x%2").arg(l.rows).arg(l.columns);
}
else
{
typeName += QString::number(l.columns);
}
for(size_t i = 0; i < l.variables.size(); i++)
{
const DebugVariableReference &r = l.variables[i];
if(!value.isEmpty())
value += lit(", ");
if(!regNames.isEmpty())
regNames += lit(", ");
if(r.name.empty())
{
regNames += lit("-");
value += lit("?");
}
else
{
const ShaderVariable *reg = GetRegisterVariable(r);
if(reg)
{
// if the previous register was the same, just append our component
if(i > 0 && r.name == l.variables[i - 1].name)
{
// 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(reg->name).arg(xyzw[r.component]);
}
if(l.type == VarType::UInt)
value += Formatter::Format(reg->value.uv[r.component]);
else if(l.type == VarType::SInt)
value += Formatter::Format(reg->value.iv[r.component]);
else if(l.type == VarType::Float)
value += Formatter::Format(reg->value.fv[r.component]);
else if(l.type == VarType::Double)
value += Formatter::Format(reg->value.dv[r.component]);
}
else
{
regNames += lit("<error>");
value += lit("<error>");
}
}
if(l.rows > 1 && l.variables.size() > l.columns)
{
if(((i + 1) % l.columns) == 0)
{
uint32_t row = (uint32_t)i / l.columns;
children.push_back(new RDTreeWidgetItem(
{QFormatStr("%1.row%2").arg(localName).arg(row), regNames, typeName, value}));
regNames = QString();
value = QString();
}
}
}
}
RDTreeWidgetItem *node = new RDTreeWidgetItem({localName, regNames, typeName, value});
for(RDTreeWidgetItem *c : children)
node->addChild(c);
node->setTag(QVariant::fromValue(SourceVariableTag(localName, l.offset)));
return node;
}
const ShaderVariable *ShaderViewer::GetRegisterVariable(const DebugVariableReference &r)
{
if(r.type == DebugVariableType::Input)
+5 -3
View File
@@ -266,6 +266,10 @@ private:
void updateDebugging();
QString makeBindName(Bindpoint &bind, bool readOnly);
RDTreeWidgetItem *makeSourceVariableNode(const SourceVariableMapping &l);
const ShaderVariable *GetRegisterVariable(const DebugVariableReference &r);
const ShaderVariable *GetRegisterVariable(VariableCategory category, int index, int member);
@@ -277,8 +281,6 @@ private:
ShaderEvents condition = ShaderEvents::NoEvent);
QString stringRep(const ShaderVariable &var, bool useType);
RDTreeWidgetItem *makeResourceRegister(const Bindpoint &bind, uint32_t idx,
const BoundResource &ro, const ShaderResource &resources);
void combineStructures(RDTreeWidgetItem *root);
void combineStructures(RDTreeWidgetItem *root, int skipPrefixLength = 0);
RDTreeWidgetItem *findLocal(RDTreeWidgetItem *root, QString name);
};
+2 -11
View File
@@ -112,20 +112,11 @@
<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>
</widget>
<widget class="RDTreeWidget" name="registers">
<widget class="RDTreeWidget" name="debugVars">
<property name="geometry">
<rect>
<x>310</x>
@@ -576,7 +567,7 @@
<enum>QAbstractItemView::NoSelection</enum>
</property>
</widget>
<widget class="RDTreeWidget" name="locals">
<widget class="RDTreeWidget" name="sourceVars">
<property name="geometry">
<rect>
<x>290</x>
+11 -5
View File
@@ -383,7 +383,7 @@ struct SourceVariableMapping
bool operator==(const SourceVariableMapping &o) const
{
return name == o.name && type == o.type && rows == o.rows && columns == o.columns &&
elements == o.elements && variables == o.variables;
variables == o.variables;
}
bool operator<(const SourceVariableMapping &o) const
{
@@ -395,8 +395,6 @@ struct SourceVariableMapping
return rows < o.rows;
if(!(columns == o.columns))
return columns < o.columns;
if(!(elements == o.elements))
return elements < o.elements;
if(!(variables == o.variables))
return variables < o.variables;
return false;
@@ -414,8 +412,8 @@ struct SourceVariableMapping
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 offset in the parent source variable, for struct members. Useful for sorting.");
uint32_t offset;
DOCUMENT(R"(The debug variables that the components of this high level variable map to. Multiple
ranges could refer to the same variable if a contiguous range is mapped to - the mapping is
@@ -577,6 +575,14 @@ it may have been vectorised and flattened.
)");
rdcarray<ShaderVariable> constantBlocks;
DOCUMENT(R"(An optional list of :class:`SourceVariableMapping` indicating which high-level source
variables map to which debug variables and includes extra type information.
This list contains source variable mapping that is valid for the lifetime of a debug trace. It may
be empty if there is no source variable mapping that extends to the life of the debug trace.
)");
rdcarray<SourceVariableMapping> sourceVars;
DOCUMENT(R"(A list of :class:`ShaderDebugState` states representing the state after each
instruction was executed
)");
+3 -3
View File
@@ -2100,7 +2100,7 @@ ShaderDebugTrace D3D11Replay::DebugVertex(uint32_t eventId, uint32_t vertid, uin
ret.states = states;
ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasLocals();
ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping();
dxbc->FillTraceLineInfo(ret);
@@ -2961,7 +2961,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim
traces[destIdx].states = states;
traces[destIdx].hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasLocals();
traces[destIdx].hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping();
dxbc->FillTraceLineInfo(traces[destIdx]);
@@ -3046,7 +3046,7 @@ ShaderDebugTrace D3D11Replay::DebugThread(uint32_t eventId, const uint32_t group
ret.states = states;
ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasLocals();
ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping();
dxbc->FillTraceLineInfo(ret);
+2 -2
View File
@@ -1797,7 +1797,7 @@ void ExtractInputsPS(PSInput IN, float4 debug_pixelPos : SV_Position, uint prim
traces[destIdx].states = states;
traces[destIdx].hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasLocals();
traces[destIdx].hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping();
dxbc->FillTraceLineInfo(traces[destIdx]);
@@ -1894,7 +1894,7 @@ ShaderDebugTrace D3D12Replay::DebugThread(uint32_t eventId, const uint32_t group
ret.states = states;
ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasLocals();
ret.hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping();
dxbc->FillTraceLineInfo(ret);
@@ -127,7 +127,7 @@ public:
virtual void GetCallstack(size_t instruction, uintptr_t offset,
rdcarray<rdcstr> &callstack) const = 0;
virtual bool HasLocals() const = 0;
virtual bool HasSourceMapping() const = 0;
virtual void GetLocals(DXBCBytecode::Program *program, size_t instruction, uintptr_t offset,
rdcarray<SourceVariableMapping> &locals) const = 0;
};
+270 -125
View File
@@ -1639,6 +1639,145 @@ static uint32_t PopCount(uint32_t x)
return (((x + (x >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
void FlattenSingleVariable(const rdcstr &cbname, uint32_t byteOffset, const rdcstr &basename,
const ShaderVariable &v, rdcarray<ShaderVariable> &outvars,
rdcarray<SourceVariableMapping> &sourcevars)
{
size_t outIdx = byteOffset / 16;
size_t outComp = (byteOffset % 16) / 4;
if(v.rowMajor)
outvars.resize(RDCMAX(outIdx + v.rows, outvars.size()));
else
outvars.resize(RDCMAX(outIdx + v.columns, outvars.size()));
if(outvars[outIdx].columns > 0)
{
// if we already have a variable in this slot, just copy the data for this variable and add the
// source mapping.
// We should not overlap into the next register as that's not allowed.
memcpy(&outvars[outIdx].value.uv[outComp], &v.value.uv[0], sizeof(uint32_t) * v.columns);
SourceVariableMapping mapping;
mapping.name = basename;
mapping.type = v.type;
mapping.rows = v.rows;
mapping.columns = v.columns;
mapping.offset = byteOffset;
mapping.variables.resize(v.columns);
for(int i = 0; i < v.columns; i++)
{
mapping.variables[i].type = DebugVariableType::Constant;
mapping.variables[i].name = StringFormat::Fmt("%s[%u]", cbname.c_str(), outIdx);
mapping.variables[i].component = uint16_t(outComp + i);
}
sourcevars.push_back(mapping);
}
else
{
const uint32_t numRegisters = v.rowMajor ? v.rows : v.columns;
for(uint32_t reg = 0; reg < numRegisters; reg++)
{
outvars[outIdx + reg].rows = 1;
outvars[outIdx + reg].type = v.type;
outvars[outIdx + reg].isStruct = false;
outvars[outIdx + reg].columns = v.columns;
outvars[outIdx + reg].rowMajor = v.rowMajor;
}
if(v.rowMajor)
{
for(size_t ri = 0; ri < v.rows; ri++)
memcpy(&outvars[outIdx + ri].value.uv[0], &v.value.uv[ri * v.columns],
sizeof(uint32_t) * v.columns);
}
else
{
// if we have a matrix stored in column major order, we need to transpose it back so we can
// unroll it into vectors.
for(size_t ci = 0; ci < v.columns; ci++)
for(size_t ri = 0; ri < v.rows; ri++)
outvars[outIdx + ci].value.uv[ri] = v.value.uv[ri * v.columns + ci];
}
SourceVariableMapping mapping;
mapping.name = basename;
mapping.type = v.type;
mapping.rows = v.rows;
mapping.columns = v.columns;
mapping.offset = byteOffset;
mapping.variables.resize(v.rows * v.columns);
for(size_t i = 0; i < mapping.variables.size(); i++)
{
mapping.variables[i].type = DebugVariableType::Constant;
mapping.variables[i].name =
StringFormat::Fmt("%s[%u]", cbname.c_str(), uint32_t(outIdx + (outComp + i) / 4));
mapping.variables[i].component = uint16_t((outComp + i) % 4);
}
sourcevars.push_back(mapping);
}
}
void FlattenVariables(const rdcstr &cbname, const rdcarray<ShaderConstant> &constants,
const rdcarray<ShaderVariable> &invars, rdcarray<ShaderVariable> &outvars,
const rdcstr &prefix, uint32_t baseOffset,
rdcarray<SourceVariableMapping> &sourceVars)
{
RDCASSERTEQUAL(constants.size(), invars.size());
for(size_t i = 0; i < constants.size(); i++)
{
const ShaderConstant &c = constants[i];
const ShaderVariable &v = invars[i];
uint32_t byteOffset = baseOffset + c.byteOffset;
rdcstr basename = prefix + rdcstr(v.name);
if(!v.members.empty())
{
if(v.isStruct)
{
FlattenVariables(cbname, c.type.members, v.members, outvars, basename + ".", byteOffset,
sourceVars);
}
else
{
if(c.type.members.empty())
{
// if there are no members in this type, it means it's a basic array - unroll directly
for(int m = 0; m < v.members.count(); m++)
{
FlattenSingleVariable(cbname, byteOffset + m * c.type.descriptor.arrayByteStride,
StringFormat::Fmt("%s[%zu]", basename.c_str(), m), v.members[m],
outvars, sourceVars);
}
}
else
{
// otherwise we recurse into each member and flatten
for(int m = 0; m < v.members.count(); m++)
{
FlattenVariables(cbname, c.type.members, v.members[m].members, outvars,
StringFormat::Fmt("%s[%zu].", basename.c_str(), m),
byteOffset + m * c.type.descriptor.arrayByteStride, sourceVars);
}
}
}
continue;
}
FlattenSingleVariable(cbname, byteOffset, basename, v, outvars, sourceVars);
}
}
State State::GetNext(GlobalState &global, DebugAPIWrapper *apiWrapper, State quad[4]) const
{
State s = *this;
@@ -4030,6 +4169,8 @@ void CreateShaderDebugStateAndTrace(ShaderDebug::State &initialState, ShaderDebu
{
initialState = ShaderDebug::State(quadIdx, &trace, dxbc->GetReflection(), dxbc->GetDXBCByteCode());
bool hasSourceMapping = dxbc->GetDebugInfo() && dxbc->GetDebugInfo()->HasSourceMapping();
dxbc->GetDXBCByteCode()->SetupRegisterFile(initialState.variables);
int32_t maxReg = -1;
@@ -4074,15 +4215,54 @@ void CreateShaderDebugStateAndTrace(ShaderDebug::State &initialState, ShaderDebu
dst = v;
else
dst.columns = RDCMAX(dst.columns, v.columns);
{
SourceVariableMapping mapping;
mapping.name = sig.semanticIdxName;
if(mapping.name.empty() && sig.systemValue != ShaderBuiltin::Undefined)
mapping.name = ToStr(sig.systemValue);
mapping.type = v.type;
mapping.rows = 1;
mapping.columns = sig.compCount;
mapping.variables.reserve(sig.compCount);
for(uint16_t c = 0; c < 4; c++)
{
if(sig.regChannelMask & (1 << c))
{
DebugVariableReference ref;
ref.name = v.name;
ref.type = DebugVariableType::Input;
ref.component = c;
mapping.variables.push_back(ref);
}
}
trace.sourceVars.push_back(mapping);
}
}
// Put the coverage mask at the end
if(inputCoverage)
{
trace.inputs[maxReg + 1] = ShaderVariable(
trace.inputs.back() = ShaderVariable(
dxbc->GetDXBCByteCode()->GetRegisterName(DXBCBytecode::TYPE_INPUT_COVERAGE_MASK, 0), 0U,
0U, 0U, 0U);
trace.inputs[maxReg + 1].columns = 1;
trace.inputs.back().columns = 1;
{
SourceVariableMapping mapping;
mapping.name = "SV_Coverage";
mapping.type = VarType::UInt;
mapping.rows = 1;
mapping.columns = 1;
DebugVariableReference ref;
ref.type = DebugVariableType::Input;
ref.name = trace.inputs.back().name;
mapping.variables.push_back(ref);
trace.sourceVars.push_back(mapping);
}
}
}
@@ -4140,6 +4320,77 @@ void CreateShaderDebugStateAndTrace(ShaderDebug::State &initialState, ShaderDebu
dst = v;
else
dst.columns = RDCMAX(dst.columns, v.columns);
// if we don't have any debug info we can at least map to the semantic to give a better name
// than the raw register from the reflection info, at least for normal outputs
if(!hasSourceMapping)
{
if(type == DXBCBytecode::TYPE_OUTPUT)
{
SourceVariableMapping mapping;
mapping.name = sig.semanticIdxName;
if(mapping.name.empty() && sig.systemValue != ShaderBuiltin::Undefined)
mapping.name = ToStr(sig.systemValue);
mapping.type = v.type;
mapping.rows = 1;
mapping.columns = sig.compCount;
mapping.variables.reserve(sig.compCount);
for(uint16_t c = 0; c < 4; c++)
{
if(sig.regChannelMask & (1 << c))
{
DebugVariableReference ref;
ref.type = DebugVariableType::Variable;
ref.name = v.name;
ref.component = c;
mapping.variables.push_back(ref);
}
}
trace.sourceVars.push_back(mapping);
}
else
{
SourceVariableMapping mapping;
if(sig.systemValue == ShaderBuiltin::DepthOutput)
{
mapping.name = "SV_Depth";
mapping.type = VarType::Float;
}
else if(sig.systemValue == ShaderBuiltin::DepthOutputLessEqual)
{
mapping.name = "SV_DepthLessEqual";
mapping.type = VarType::Float;
}
else if(sig.systemValue == ShaderBuiltin::DepthOutputGreaterEqual)
{
mapping.name = "SV_DepthGreaterEqual";
mapping.type = VarType::Float;
}
else if(sig.systemValue == ShaderBuiltin::MSAACoverage)
{
mapping.name = "SV_Coverage";
mapping.type = VarType::UInt;
}
else if(sig.systemValue == ShaderBuiltin::StencilReference)
{
mapping.name = "SV_StencilRef";
mapping.type = VarType::UInt;
}
// all these variables are 1 scalar component
mapping.rows = 1;
mapping.columns = 1;
DebugVariableReference ref;
ref.type = DebugVariableType::Variable;
ref.name = v.name;
mapping.variables.push_back(ref);
trace.sourceVars.push_back(mapping);
}
}
}
// Set the number of constant buffers in the trace, but assignment happens later
@@ -4160,17 +4411,29 @@ void AddCBufferToDebugTrace(const DXBCBytecode::Program &program, ShaderDebugTra
{
RDCASSERTMSG("Reassigning previously filled cbuffer", trace.constantBlocks[i].members.empty());
uint32_t cbufferIndex = program.IsShaderModel51() ? (uint32_t)i : slot.shaderRegister;
trace.constantBlocks[i].name =
program.GetRegisterName(DXBCBytecode::TYPE_CONSTANT_BUFFER, cbufferIndex);
SourceVariableMapping cbSourceMapping;
cbSourceMapping.name = refl.constantBlocks[i].name;
cbSourceMapping.variables.push_back(
DebugVariableReference(DebugVariableType::Constant, global.constantBlocks[i].name));
sourceVars.push_back(cbSourceMapping);
rdcarray<ShaderVariable> vars;
StandardFillCBufferVariables(refl.resourceId, refl.constantBlocks[i].variables, vars, cbufData);
FlattenVariables(refl.constantBlocks[i].variables, vars, trace.constantBlocks[i].members);
FlattenVariables(trace.constantBlocks[i].name, refl.constantBlocks[i].variables, vars,
trace.constantBlocks[i].members, refl.constantBlocks[i].name + ".", 0,
trace.sourceVars);
const char *format = program.IsShaderModel51() ? "CB%u[%u] (%s)" : "cb%u[%u] (%s)";
uint32_t regSlot = program.IsShaderModel51() ? (uint32_t)i : slot.shaderRegister;
for(size_t c = 0; c < trace.constantBlocks[i].members.size(); c++)
{
trace.constantBlocks[i].members[c].name = StringFormat::Fmt(
format, regSlot, (uint32_t)c, trace.constantBlocks[i].members[c].name.c_str());
trace.constantBlocks[i].members[c].name =
StringFormat::Fmt("%s[%u]", trace.constantBlocks[i].name.c_str(), (uint32_t)c);
}
return;
}
}
@@ -4364,124 +4627,6 @@ void ApplyAllDerivatives(ShaderDebug::GlobalState &global, ShaderDebugTrace trac
}
}
void FlattenSingleVariable(uint32_t byteOffset, const rdcstr &basename, const ShaderVariable &v,
rdcarray<ShaderVariable> &outvars)
{
size_t outIdx = byteOffset / 16;
size_t outComp = (byteOffset % 16) / 4;
if(v.rowMajor)
outvars.resize(RDCMAX(outIdx + v.rows, outvars.size()));
else
outvars.resize(RDCMAX(outIdx + v.columns, outvars.size()));
if(!outvars[outIdx].name.empty())
{
// if we already have a variable in this slot, just append this variable to it. We should not
// overlap into the next register as that's not allowed.
outvars[outIdx].name = rdcstr(outvars[outIdx].name) + ", " + basename;
outvars[outIdx].rows = 1;
outvars[outIdx].isStruct = false;
outvars[outIdx].columns += v.columns;
RDCASSERT(outvars[outIdx].columns <= 4, outvars[outIdx].columns);
memcpy(&outvars[outIdx].value.uv[outComp], &v.value.uv[0], sizeof(uint32_t) * v.columns);
}
else
{
const uint32_t numRegisters = v.rowMajor ? v.rows : v.columns;
const char *regName = v.rowMajor ? "row" : "col";
for(uint32_t reg = 0; reg < numRegisters; reg++)
{
if(numRegisters > 1)
outvars[outIdx + reg].name = StringFormat::Fmt("%s.%s%u", basename.c_str(), regName, reg);
else
outvars[outIdx + reg].name = basename;
outvars[outIdx + reg].rows = 1;
outvars[outIdx + reg].type = v.type;
outvars[outIdx + reg].isStruct = false;
outvars[outIdx + reg].columns = v.columns;
outvars[outIdx + reg].rowMajor = v.rowMajor;
}
if(v.rowMajor)
{
for(size_t ri = 0; ri < v.rows; ri++)
memcpy(&outvars[outIdx + ri].value.uv[0], &v.value.uv[ri * v.columns],
sizeof(uint32_t) * v.columns);
}
else
{
// if we have a matrix stored in column major order, we need to transpose it back so we can
// unroll it into vectors.
for(size_t ci = 0; ci < v.columns; ci++)
for(size_t ri = 0; ri < v.rows; ri++)
outvars[outIdx + ci].value.uv[ri] = v.value.uv[ri * v.columns + ci];
}
}
}
void FlattenVariables(const rdcarray<ShaderConstant> &constants,
const rdcarray<ShaderVariable> &invars, rdcarray<ShaderVariable> &outvars,
const rdcstr &prefix, uint32_t baseOffset)
{
RDCASSERTEQUAL(constants.size(), invars.size());
for(size_t i = 0; i < constants.size(); i++)
{
const ShaderConstant &c = constants[i];
const ShaderVariable &v = invars[i];
uint32_t byteOffset = baseOffset + c.byteOffset;
rdcstr basename = prefix + rdcstr(v.name);
if(!v.members.empty())
{
if(v.isStruct)
{
FlattenVariables(c.type.members, v.members, outvars, basename + ".", byteOffset);
}
else
{
if(c.type.members.empty())
{
// if there are no members in this type, it means it's a basic array - unroll directly
for(int m = 0; m < v.members.count(); m++)
{
FlattenSingleVariable(byteOffset + m * c.type.descriptor.arrayByteStride,
StringFormat::Fmt("%s[%zu]", basename.c_str(), m), v.members[m],
outvars);
}
}
else
{
// otherwise we recurse into each member and flatten
for(int m = 0; m < v.members.count(); m++)
{
FlattenVariables(c.type.members, v.members[m].members, outvars,
StringFormat::Fmt("%s[%zu].", basename.c_str(), m),
byteOffset + m * c.type.descriptor.arrayByteStride);
}
}
}
continue;
}
FlattenSingleVariable(byteOffset, basename, v, outvars);
}
}
void FlattenVariables(const rdcarray<ShaderConstant> &constants,
const rdcarray<ShaderVariable> &invars, rdcarray<ShaderVariable> &outvars)
{
FlattenVariables(constants, invars, outvars, "", 0);
}
void FillViewFmt(DXGI_FORMAT format, GlobalState::ViewFmt &viewFmt)
{
if(format != DXGI_FORMAT_UNKNOWN)
@@ -209,11 +209,6 @@ void ApplyAllDerivatives(ShaderDebug::GlobalState &global, ShaderDebugTrace trac
void FlattenSingleVariable(uint32_t byteOffset, const rdcstr &basename, const ShaderVariable &v,
rdcarray<ShaderVariable> &outvars);
void FlattenVariables(const rdcarray<ShaderConstant> &constants,
const rdcarray<ShaderVariable> &invars, rdcarray<ShaderVariable> &outvars,
const rdcstr &prefix, uint32_t baseOffset);
void FlattenVariables(const rdcarray<ShaderConstant> &constants,
const rdcarray<ShaderVariable> &invars, rdcarray<ShaderVariable> &outvars);
void FillViewFmt(DXGI_FORMAT format, GlobalState::ViewFmt &viewFmt);
+1 -1
View File
@@ -122,7 +122,7 @@ void SDBGChunk::GetCallstack(size_t instruction, uintptr_t offset, rdcarray<rdcs
}
}
bool SDBGChunk::HasLocals() const
bool SDBGChunk::HasSourceMapping() const
{
return false;
}
+1 -1
View File
@@ -251,7 +251,7 @@ public:
void GetLineInfo(size_t instruction, uintptr_t offset, LineColumnInfo &lineInfo) const;
void GetCallstack(size_t instruction, uintptr_t offset, rdcarray<rdcstr> &callstack) const;
bool HasLocals() const;
bool HasSourceMapping() const;
void GetLocals(DXBCBytecode::Program *program, size_t instruction, uintptr_t offset,
rdcarray<SourceVariableMapping> &locals) const;
+107 -41
View File
@@ -189,7 +189,8 @@ SPDBChunk::SPDBChunk(void *chunk)
VarType baseType;
uint32_t byteSize;
uint16_t vecSize;
uint16_t matArrayStride;
uint16_t matArrayStride : 15;
uint16_t colMajorMatrix : 1;
LEAF_ENUM_e leafType;
rdcarray<TypeMember> members;
};
@@ -199,24 +200,24 @@ SPDBChunk::SPDBChunk(void *chunk)
// prepopulate with basic types
// for now we stick to full-precision 32-bit VarTypes. It's not clear if HLSL even emits the other
// types
typeInfo[T_INT4] = {"int32_t", VarType::SInt, 4, 1, 0, LF_NUMERIC, {}};
typeInfo[T_INT2] = {"int16_t", VarType::SInt, 2, 1, 0, LF_NUMERIC, {}};
typeInfo[T_INT1] = {"int8_t", VarType::SInt, 1, 1, 0, LF_NUMERIC, {}};
typeInfo[T_LONG] = {"int32_t", VarType::SInt, 4, 1, 0, LF_NUMERIC, {}};
typeInfo[T_SHORT] = {"int16_t", VarType::SInt, 2, 1, 0, LF_NUMERIC, {}};
typeInfo[T_CHAR] = {"char", VarType::SInt, 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, {}};
typeInfo[T_INT4] = {"int32_t", VarType::SInt, 4, 1, 0, 0, LF_NUMERIC, {}};
typeInfo[T_INT2] = {"int16_t", VarType::SInt, 2, 1, 0, 0, LF_NUMERIC, {}};
typeInfo[T_INT1] = {"int8_t", VarType::SInt, 1, 1, 0, 0, LF_NUMERIC, {}};
typeInfo[T_LONG] = {"int32_t", VarType::SInt, 4, 1, 0, 0, LF_NUMERIC, {}};
typeInfo[T_SHORT] = {"int16_t", VarType::SInt, 2, 1, 0, 0, LF_NUMERIC, {}};
typeInfo[T_CHAR] = {"char", VarType::SInt, 1, 1, 0, 0, LF_NUMERIC, {}};
typeInfo[T_BOOL32FF] = {"bool", VarType::UInt, 4, 1, 0, 0, LF_NUMERIC, {}};
typeInfo[T_UINT4] = {"uint32_t", VarType::UInt, 4, 1, 0, 0, LF_NUMERIC, {}};
typeInfo[T_UINT2] = {"uint16_t", VarType::UInt, 2, 1, 0, 0, LF_NUMERIC, {}};
typeInfo[T_UINT1] = {"uint8_t", VarType::UInt, 1, 1, 0, 0, LF_NUMERIC, {}};
typeInfo[T_ULONG] = {"uint32_t", VarType::UInt, 4, 1, 0, 0, LF_NUMERIC, {}};
typeInfo[T_USHORT] = {"uint16_t", VarType::UInt, 2, 1, 0, 0, LF_NUMERIC, {}};
typeInfo[T_UCHAR] = {"unsigned char", VarType::UInt, 1, 1, 0, 0, LF_NUMERIC, {}};
typeInfo[T_REAL16] = {"half", VarType::Float, 2, 1, 0, 0, LF_NUMERIC, {}};
typeInfo[T_REAL32] = {"float", VarType::Float, 4, 1, 0, 0, LF_NUMERIC, {}};
// modern HLSL fake half
typeInfo[T_REAL32PP] = {"half", VarType::Float, 4, 1, 0, LF_NUMERIC, {}};
typeInfo[T_REAL64] = {"double", VarType::Double, 8, 1, 0, LF_NUMERIC, {}};
typeInfo[T_REAL32PP] = {"half", VarType::Float, 4, 1, 0, 0, LF_NUMERIC, {}};
typeInfo[T_REAL64] = {"double", VarType::Double, 8, 1, 0, 0, LF_NUMERIC, {}};
if(streams.size() >= 3)
{
@@ -275,8 +276,8 @@ SPDBChunk::SPDBChunk(void *chunk)
typeInfo[id] = {
name, typeInfo[vector->elemtype].baseType,
*bytelength, (uint16_t)vector->count,
0, type,
{},
0, 0,
type, {},
};
break;
@@ -293,14 +294,22 @@ SPDBChunk::SPDBChunk(void *chunk)
id, name, matrix->elemtype, matrix->rows, matrix->cols, *bytelength,
matrix->majorStride, matrix->matattr.row_major ? "row" : "column");
uint16_t vecSize = 0, matStride = 0;
if(matrix->matattr.row_major)
{
vecSize = uint16_t(matrix->cols);
matStride = uint16_t(*bytelength / matrix->rows);
}
else
{
vecSize = uint16_t(matrix->rows);
matStride = uint16_t(*bytelength / matrix->cols);
}
typeInfo[id] = {
name,
typeInfo[matrix->elemtype].baseType,
*bytelength,
uint16_t(matrix->rows),
uint16_t(*bytelength / matrix->cols),
type,
{},
name, typeInfo[matrix->elemtype].baseType, *bytelength, vecSize,
matStride, matrix->matattr.row_major == 0, type, {},
};
break;
@@ -547,7 +556,7 @@ SPDBChunk::SPDBChunk(void *chunk)
structType = "class";
typeInfo[id] = {
name, VarType::Float, *bytelength, 1, 0, type, typeInfo[structure->field].members,
name, VarType::Float, *bytelength, 1, 0, 0, type, typeInfo[structure->field].members,
};
SPDBLOG(
@@ -583,6 +592,7 @@ SPDBChunk::SPDBChunk(void *chunk)
*bytelength,
1,
uint16_t(stridedArray->stride),
0,
type,
{},
};
@@ -1159,6 +1169,8 @@ SPDBChunk::SPDBChunk(void *chunk)
uint32_t varOffset = defrange->offsetParent;
uint32_t varLen = defrange->sizeInParent;
mapping.varOffset = varOffset;
const TypeDesc *vartype = &typeInfo[localType];
RDCASSERT(varOffset + varLen <= vartype->byteSize);
@@ -1218,23 +1230,59 @@ SPDBChunk::SPDBChunk(void *chunk)
mapping.var.type = vartype->baseType;
mapping.var.rows = 1;
mapping.var.columns = uint8_t(vartype->vecSize);
mapping.var.elements = 1;
// if it's an array or matrix, figure out the index
if(vartype->matArrayStride)
{
// number of rows is the number of vectors in the matrix's total byte size (each vector is
// a row)
mapping.var.rows = uint8_t(vartype->byteSize / vartype->matArrayStride);
// unless this is a column major matrix, in which case each vector is a column so swap the
// rows/columns (the number of ROWS is the vector size, when each vector is a column)
if(vartype->colMajorMatrix)
std::swap(mapping.var.rows, mapping.var.columns);
// calculate which vector we're on, and which component
uint32_t idx = varOffset / vartype->matArrayStride;
mapping.var.name += StringFormat::Fmt("[%u]", idx);
mapping.var.rows = (uint8_t)RDCMAX(
1U, (vartype->byteSize + (vartype->matArrayStride - 1)) / vartype->matArrayStride);
varOffset -= vartype->matArrayStride * idx;
}
uint32_t comp = (varOffset % 16) / 4;
if(vartype->leafType != LF_MATRIX)
{
mapping.var.elements = mapping.var.rows;
mapping.var.rows = 1;
// should now be down to a vector, so the remaining offset is the component
RDCASSERT(varOffset < 16);
if(vartype->leafType == LF_MATRIX)
{
// if this is a matrix, start with the index as row, and component as column
uint32_t row = idx;
uint32_t col = comp;
// flip them if this is column major
if(vartype->colMajorMatrix)
std::swap(row, col);
// add the row to the name since we want our mapping row-major for better display
mapping.var.name += StringFormat::Fmt(".row%u", row);
// and set the component after flipping
comp = col;
}
else
{
// the number of rows is actually the number of elements (and number of rows is 1)
mapping.var.elements = mapping.var.rows;
mapping.var.rows = 1;
// if this is an array, the index is just the array index. However if we're mapping the
// whole array, don't add the index as the mapping will do that for us
if(varLen < mapping.var.elements * vartype->matArrayStride)
mapping.var.name += StringFormat::Fmt("[%u]", idx);
}
// set the offset explicitly to the component within the final vector we chose (whatever
// it is)
varOffset = comp * 4;
}
RDCASSERT(mapping.var.rows <= 4 && mapping.var.columns <= 4);
@@ -1623,7 +1671,7 @@ void SPDBChunk::GetCallstack(size_t instruction, uintptr_t offset, rdcarray<rdcs
callstack = it->second.callstack;
}
bool SPDBChunk::HasLocals() const
bool SPDBChunk::HasSourceMapping() const
{
return true;
}
@@ -1676,6 +1724,9 @@ void SPDBChunk::GetLocals(DXBCBytecode::Program *program, size_t instruction, ui
else
range.type = DebugVariableType::Variable;
// we don't handle more than float4 at a time (unless in an array)
RDCASSERT(it->numComps <= 4);
// 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
@@ -1696,6 +1747,8 @@ void SPDBChunk::GetLocals(DXBCBytecode::Program *program, size_t instruction, ui
a.variables[it->varFirstComp + i].component += (uint8_t)i;
}
RDCASSERT(it->var.elements == 1);
// we've processed this, no need to add a new entry
added = true;
break;
@@ -1710,7 +1763,7 @@ void SPDBChunk::GetLocals(DXBCBytecode::Program *program, size_t instruction, ui
a.type = it->var.type;
a.rows = it->var.rows;
a.columns = it->var.columns;
a.elements = it->var.elements;
a.offset = it->varOffset;
a.variables.resize(it->varFirstComp + it->numComps);
@@ -1720,7 +1773,20 @@ void SPDBChunk::GetLocals(DXBCBytecode::Program *program, size_t instruction, ui
a.variables[it->varFirstComp + i].component += (uint8_t)i;
}
locals.push_back(a);
for(uint32_t e = 0; e < it->var.elements; e++)
{
if(it->var.elements > 1)
{
a.name = StringFormat::Fmt("%s[%u]", it->var.name.c_str(), e);
for(uint32_t i = 0; i < it->numComps; i++)
{
a.variables[it->varFirstComp + i].name =
StringFormat::Fmt("%s[%u]", range.name.c_str(), e);
}
}
locals.push_back(a);
}
}
}
}
+2 -1
View File
@@ -249,6 +249,7 @@ struct LocalMapping
LocalRange range;
uint8_t regFirstComp;
uint32_t varFirstComp;
uint32_t varOffset;
uint32_t numComps;
rdcarray<LocalRange> gaps;
@@ -274,7 +275,7 @@ public:
void GetLineInfo(size_t instruction, uintptr_t offset, LineColumnInfo &lineInfo) const;
void GetCallstack(size_t instruction, uintptr_t offset, rdcarray<rdcstr> &callstack) const;
bool HasLocals() const;
bool HasSourceMapping() const;
void GetLocals(DXBCBytecode::Program *program, size_t instruction, uintptr_t offset,
rdcarray<SourceVariableMapping> &locals) const;
+3 -2
View File
@@ -356,7 +356,7 @@ void DoSerialise(SerialiserType &ser, SourceVariableMapping &el)
SERIALISE_MEMBER(type);
SERIALISE_MEMBER(rows);
SERIALISE_MEMBER(columns);
SERIALISE_MEMBER(elements);
SERIALISE_MEMBER(offset);
SERIALISE_MEMBER(variables);
SIZE_CHECK(64);
@@ -393,11 +393,12 @@ void DoSerialise(SerialiserType &ser, ShaderDebugTrace &el)
{
SERIALISE_MEMBER(inputs);
SERIALISE_MEMBER(constantBlocks);
SERIALISE_MEMBER(sourceVars);
SERIALISE_MEMBER(states);
SERIALISE_MEMBER(hasSourceMapping);
SERIALISE_MEMBER(lineInfo);
SIZE_CHECK(104);
SIZE_CHECK(128);
}
template <typename SerialiserType>