mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-08 08:40:55 +00:00
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:
+289
-276
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
)");
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user