in qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp [1187:2253]
void D3D12PipelineStateViewer::setState()
{
if(!m_Ctx.IsCaptureLoaded())
{
clearState();
return;
}
// cache latest state of these checkboxes
m_ShowUnused = ui->showUnused->isChecked();
m_ShowEmpty = ui->showEmpty->isChecked();
const D3D12Pipe::State &state = *m_Ctx.CurD3D12PipelineState();
const ActionDescription *action = m_Ctx.CurAction();
const QPixmap &tick = Pixmaps::tick(this);
const QPixmap &cross = Pixmaps::cross(this);
// highlight the appropriate stages in the flowchart
if(action == NULL)
{
QList<bool> allOn;
for(int i = 0; i < ui->pipeFlow->stageNames().count(); i++)
allOn.append(true);
ui->pipeFlow->setStagesEnabled(allOn);
}
else if(action->flags & ActionFlags::Dispatch)
{
QList<bool> computeOnly;
for(int i = 0; i < ui->pipeFlow->stageNames().count(); i++)
computeOnly.append(false);
computeOnly.back() = true;
ui->pipeFlow->setStagesEnabled(computeOnly);
}
else if(action->flags & ActionFlags::MeshDispatch)
{
setNewMeshPipeFlow();
ui->pipeFlow->setStagesEnabled(
{state.ampShader.resourceId != ResourceId(), true, true, true, true, false});
}
else
{
bool streamOutActive = false;
for(const D3D12Pipe::StreamOutBind &o : state.streamOut.outputs)
{
if(o.resourceId != ResourceId())
{
streamOutActive = true;
break;
}
}
if(state.geometryShader.resourceId == ResourceId() && streamOutActive)
{
ui->pipeFlow->setStageName(4, lit("SO"), tr("Stream Out"));
}
else
{
ui->pipeFlow->setStageName(4, lit("GS"), tr("Geometry Shader"));
}
setOldMeshPipeFlow();
ui->pipeFlow->setStagesEnabled(
{true, true, state.hullShader.resourceId != ResourceId(),
state.domainShader.resourceId != ResourceId(),
state.geometryShader.resourceId != ResourceId() || streamOutActive, true,
state.pixelShader.resourceId != ResourceId(), true, false});
}
////////////////////////////////////////////////
// Vertex Input
int vs = 0;
bool usedVBuffers[128] = {};
uint32_t layoutOffs[128] = {};
if(m_MeshPipe)
{
setShaderState(state.ampShader, ui->asShader, ui->asRootSig);
setShaderState(state.meshShader, ui->msShader, ui->msRootSig);
if(state.meshShader.reflection)
ui->msTopology->setText(ToQStr(state.meshShader.reflection->outputTopology));
else
ui->msTopology->setText(QString());
}
else
{
vs = ui->iaLayouts->verticalScrollBar()->value();
ui->iaLayouts->beginUpdate();
ui->iaLayouts->clear();
{
int i = 0;
for(const D3D12Pipe::Layout &l : state.inputAssembly.layouts)
{
QString byteOffs = Formatter::HumanFormat(l.byteOffset, Formatter::OffsetSize);
// D3D12 specific value
if(l.byteOffset == ~0U)
{
byteOffs = lit("APPEND_ALIGNED (%1)").arg(layoutOffs[l.inputSlot]);
}
else
{
layoutOffs[l.inputSlot] = l.byteOffset;
}
layoutOffs[l.inputSlot] += l.format.compByteWidth * l.format.compCount;
bool filledSlot = true;
bool usedSlot = false;
for(int ia = 0; state.vertexShader.reflection &&
ia < state.vertexShader.reflection->inputSignature.count();
ia++)
{
if(!QString(state.vertexShader.reflection->inputSignature[ia].semanticName)
.compare(l.semanticName, Qt::CaseInsensitive) &&
state.vertexShader.reflection->inputSignature[ia].semanticIndex == l.semanticIndex)
{
usedSlot = true;
break;
}
}
if(showNode(usedSlot, filledSlot))
{
RDTreeWidgetItem *node = new RDTreeWidgetItem(
{i, l.semanticName, l.semanticIndex, l.format.Name(), l.inputSlot, byteOffs,
l.perInstance ? lit("PER_INSTANCE") : lit("PER_VERTEX"), l.instanceDataStepRate,
QString()});
node->setTag(i);
if(usedSlot)
usedVBuffers[l.inputSlot] = true;
if(!usedSlot)
setInactiveRow(node);
ui->iaLayouts->addTopLevelItem(node);
}
i++;
}
}
ui->iaLayouts->clearSelection();
ui->iaLayouts->endUpdate();
ui->iaLayouts->verticalScrollBar()->setValue(vs);
int numCPs = PatchList_Count(state.inputAssembly.topology);
if(numCPs > 0)
{
ui->topology->setText(tr("PatchList (%1 Control Points)").arg(numCPs));
}
else
{
ui->topology->setText(ToQStr(state.inputAssembly.topology));
}
m_Common.setTopologyDiagram(ui->topologyDiagram, state.inputAssembly.topology);
bool ibufferUsed = action && (action->flags & ActionFlags::Indexed);
m_VBNodes.clear();
m_EmptyNodes.clear();
vs = ui->iaBuffers->verticalScrollBar()->value();
ui->iaBuffers->beginUpdate();
ui->iaBuffers->clear();
if(state.inputAssembly.indexBuffer.resourceId != ResourceId())
{
if(ibufferUsed || m_ShowUnused)
{
uint64_t length = state.inputAssembly.indexBuffer.byteSize;
BufferDescription *buf = m_Ctx.GetBuffer(state.inputAssembly.indexBuffer.resourceId);
RDTreeWidgetItem *node = new RDTreeWidgetItem(
{tr("Index"), state.inputAssembly.indexBuffer.resourceId,
(qulonglong)state.inputAssembly.indexBuffer.byteStride,
(qulonglong)state.inputAssembly.indexBuffer.byteOffset, (qulonglong)length, QString()});
QString iformat;
if(state.inputAssembly.indexBuffer.byteStride == 1)
iformat = lit("ubyte");
else if(state.inputAssembly.indexBuffer.byteStride == 2)
iformat = lit("ushort");
else if(state.inputAssembly.indexBuffer.byteStride == 4)
iformat = lit("uint");
iformat +=
lit(" indices[%1]").arg(RENDERDOC_NumVerticesPerPrimitive(state.inputAssembly.topology));
uint32_t drawOffset =
(action ? action->indexOffset * state.inputAssembly.indexBuffer.byteStride : 0);
node->setTag(QVariant::fromValue(
D3D12VBIBTag(state.inputAssembly.indexBuffer.resourceId,
state.inputAssembly.indexBuffer.byteOffset + drawOffset,
drawOffset > state.inputAssembly.indexBuffer.byteSize
? 0
: state.inputAssembly.indexBuffer.byteSize - drawOffset,
iformat)));
for(const D3D12Pipe::ResourceData &res : m_Ctx.CurD3D12PipelineState()->resourceStates)
{
if(res.resourceId == state.inputAssembly.indexBuffer.resourceId)
{
node->setToolTip(tr("Buffer is in the '%1' state").arg(res.states[0].name));
break;
}
}
if(!ibufferUsed)
setInactiveRow(node);
if(state.inputAssembly.indexBuffer.resourceId == ResourceId())
{
setEmptyRow(node);
m_EmptyNodes.push_back(node);
}
ui->iaBuffers->addTopLevelItem(node);
}
}
else
{
if(ibufferUsed || m_ShowEmpty)
{
RDTreeWidgetItem *node = new RDTreeWidgetItem(
{tr("Index"), tr("No Buffer Set"), lit("-"), lit("-"), lit("-"), QString()});
QString iformat;
if(state.inputAssembly.indexBuffer.byteStride == 1)
iformat = lit("ubyte");
else if(state.inputAssembly.indexBuffer.byteStride == 2)
iformat = lit("ushort");
else if(state.inputAssembly.indexBuffer.byteStride == 4)
iformat = lit("uint");
iformat +=
lit(" indices[%1]").arg(RENDERDOC_NumVerticesPerPrimitive(state.inputAssembly.topology));
uint32_t drawOffset =
(action ? action->indexOffset * state.inputAssembly.indexBuffer.byteStride : 0);
node->setTag(QVariant::fromValue(
D3D12VBIBTag(state.inputAssembly.indexBuffer.resourceId,
state.inputAssembly.indexBuffer.byteOffset + drawOffset,
drawOffset > state.inputAssembly.indexBuffer.byteSize
? 0
: state.inputAssembly.indexBuffer.byteSize - drawOffset,
iformat)));
for(const D3D12Pipe::ResourceData &res : m_Ctx.CurD3D12PipelineState()->resourceStates)
{
if(res.resourceId == state.inputAssembly.indexBuffer.resourceId)
{
node->setToolTip(tr("Buffer is in the '%1' state").arg(res.states[0].name));
break;
}
}
setEmptyRow(node);
m_EmptyNodes.push_back(node);
if(!ibufferUsed)
setInactiveRow(node);
ui->iaBuffers->addTopLevelItem(node);
}
}
for(int i = 0; i < 128; i++)
{
if(i >= state.inputAssembly.vertexBuffers.count())
{
// for vbuffers that are referenced but not bound, make sure we add an empty row
if(usedVBuffers[i])
{
RDTreeWidgetItem *node =
new RDTreeWidgetItem({i, tr("No Buffer Set"), lit("-"), lit("-"), lit("-"), QString()});
node->setTag(QVariant::fromValue(D3D12VBIBTag(ResourceId(), 0, 0)));
setEmptyRow(node);
m_EmptyNodes.push_back(node);
m_VBNodes.push_back(node);
ui->iaBuffers->addTopLevelItem(node);
}
else
{
m_VBNodes.push_back(NULL);
}
continue;
}
const D3D12Pipe::VertexBuffer &v = state.inputAssembly.vertexBuffers[i];
bool filledSlot = (v.resourceId != ResourceId());
bool usedSlot = (usedVBuffers[i]);
if(showNode(usedSlot, filledSlot))
{
qulonglong length = v.byteSize;
BufferDescription *buf = m_Ctx.GetBuffer(v.resourceId);
RDTreeWidgetItem *node = NULL;
if(filledSlot)
node = new RDTreeWidgetItem(
{i, v.resourceId, v.byteStride, (qulonglong)v.byteOffset, length, QString()});
else
node =
new RDTreeWidgetItem({i, tr("No Buffer Set"), lit("-"), lit("-"), lit("-"), QString()});
node->setTag(QVariant::fromValue(D3D12VBIBTag(v.resourceId, v.byteOffset, v.byteSize,
m_Common.GetVBufferFormatString(i))));
for(const D3D12Pipe::ResourceData &res : m_Ctx.CurD3D12PipelineState()->resourceStates)
{
if(res.resourceId == v.resourceId)
{
node->setToolTip(tr("Buffer is in the '%1' state").arg(res.states[0].name));
break;
}
}
if(!filledSlot)
{
setEmptyRow(node);
m_EmptyNodes.push_back(node);
}
if(!usedSlot)
setInactiveRow(node);
m_VBNodes.push_back(node);
ui->iaBuffers->addTopLevelItem(node);
}
else
{
m_VBNodes.push_back(NULL);
}
}
ui->iaBuffers->clearSelection();
ui->iaBuffers->endUpdate();
ui->iaBuffers->verticalScrollBar()->setValue(vs);
setShaderState(state.vertexShader, ui->vsShader, ui->vsRootSig);
setShaderState(state.geometryShader, ui->gsShader, ui->gsRootSig);
setShaderState(state.hullShader, ui->hsShader, ui->hsRootSig);
setShaderState(state.domainShader, ui->dsShader, ui->dsRootSig);
}
setShaderState(state.pixelShader, ui->psShader, ui->psRootSig);
setShaderState(state.computeShader, ui->csShader, ui->csRootSig);
// fill in descriptor access
{
RDTreeWidget *resources[] = {
ui->vsResources, ui->hsResources, ui->dsResources, ui->gsResources,
ui->psResources, ui->csResources, ui->asResources, ui->msResources,
};
RDTreeWidget *uavs[] = {
ui->vsUAVs, ui->hsUAVs, ui->dsUAVs, ui->gsUAVs,
ui->psUAVs, ui->csUAVs, ui->asUAVs, ui->msUAVs,
};
RDTreeWidget *samplers[] = {
ui->vsSamplers, ui->hsSamplers, ui->dsSamplers, ui->gsSamplers,
ui->psSamplers, ui->csSamplers, ui->asSamplers, ui->msSamplers,
};
RDTreeWidget *cbuffers[] = {
ui->vsCBuffers, ui->hsCBuffers, ui->dsCBuffers, ui->gsCBuffers,
ui->psCBuffers, ui->csCBuffers, ui->asCBuffers, ui->msCBuffers,
};
ScopedTreeUpdater restorers[] = {
ui->vsResources, ui->hsResources, ui->dsResources, ui->gsResources, ui->psResources,
ui->csResources, ui->asResources, ui->msResources, ui->vsUAVs, ui->hsUAVs,
ui->dsUAVs, ui->gsUAVs, ui->psUAVs, ui->csUAVs, ui->asUAVs,
ui->msUAVs, ui->vsSamplers, ui->hsSamplers, ui->dsSamplers, ui->gsSamplers,
ui->psSamplers, ui->csSamplers, ui->asSamplers, ui->msSamplers, ui->vsCBuffers,
ui->hsCBuffers, ui->dsCBuffers, ui->gsCBuffers, ui->psCBuffers, ui->csCBuffers,
ui->asCBuffers, ui->msCBuffers,
};
const ShaderReflection *shaderRefls[NumShaderStages];
// this is a simple flag to see if any non-zero spaces are used. If not, we can be more concise
// and omit the space when listing the binding for a particular register.
bool spacesUsed[NumShaderStages] = {};
for(ShaderStage stage : values<ShaderStage>())
{
shaderRefls[(uint32_t)stage] = m_Ctx.CurPipelineState().GetShaderReflection(stage);
if(!shaderRefls[(uint32_t)stage])
continue;
for(const ConstantBlock &bind : shaderRefls[(uint32_t)stage]->constantBlocks)
spacesUsed[(uint32_t)stage] |= bind.fixedBindSetOrSpace > 0;
for(const ShaderSampler &bind : shaderRefls[(uint32_t)stage]->samplers)
spacesUsed[(uint32_t)stage] |= bind.fixedBindSetOrSpace > 0;
for(const ShaderResource &bind : shaderRefls[(uint32_t)stage]->readOnlyResources)
spacesUsed[(uint32_t)stage] |= bind.fixedBindSetOrSpace > 0;
for(const ShaderResource &bind : shaderRefls[(uint32_t)stage]->readWriteResources)
spacesUsed[(uint32_t)stage] |= bind.fixedBindSetOrSpace > 0;
}
rdcarray<UsedDescriptor> descriptors = m_Ctx.CurPipelineState().GetAllUsedDescriptors();
std::sort(descriptors.begin(), descriptors.end(),
[](const UsedDescriptor &a, const UsedDescriptor &b) {
// sort by declared shader interface resource order
if(a.access.index != b.access.index)
return a.access.index < b.access.index;
return a.access.arrayElement < b.access.arrayElement;
});
for(const UsedDescriptor &used : descriptors)
{
const ShaderReflection *refl = shaderRefls[(uint32_t)used.access.stage];
if(IsConstantBlockDescriptor(used.access.type))
{
const Descriptor &descriptor = used.descriptor;
QVariant cbuftag;
const ConstantBlock *shaderBind = NULL;
if(used.access.index == DescriptorAccess::NoShaderBinding)
{
cbuftag = QVariant::fromValue(D3D12CBufTag(descriptor));
}
else
{
if(refl && used.access.index < refl->constantBlocks.size())
shaderBind = &refl->constantBlocks[used.access.index];
cbuftag = QVariant::fromValue(D3D12CBufTag(used.access.index, used.access.arrayElement));
}
bool filledSlot = (descriptor.resource != ResourceId());
// D3D12 does not report unused elements at all because we enumerate exclusively from the
// perspective of which descriptors are used
bool usedSlot = true;
if(showNode(usedSlot, filledSlot))
{
ulong length = descriptor.byteSize;
uint64_t offset = descriptor.byteOffset;
int numvars = shaderBind ? shaderBind->variables.count() : 0;
uint32_t bytesize = shaderBind ? shaderBind->byteSize : 0;
QString regname;
if(used.access.index == DescriptorAccess::NoShaderBinding)
{
regname =
m_Locations[{used.access.descriptorStore, used.access.byteOffset}].logicalBindName;
}
else if(shaderBind)
{
if(!spacesUsed[(uint32_t)used.access.stage])
regname = QFormatStr("%1").arg(shaderBind->fixedBindNumber);
else
regname = QFormatStr("space%1, %2")
.arg(shaderBind->fixedBindSetOrSpace)
.arg(shaderBind->fixedBindNumber);
if(!shaderBind->name.empty())
regname += lit(": ") + shaderBind->name;
if(shaderBind->bindArraySize > 1)
regname += QFormatStr("[%1]").arg(used.access.arrayElement);
}
QString sizestr;
if(bytesize == (uint32_t)length)
sizestr = tr("%1 Variables, %2 bytes")
.arg(numvars)
.arg(Formatter::HumanFormat(length, Formatter::OffsetSize));
else
sizestr = tr("%1 Variables, %2 bytes needed, %3 provided")
.arg(numvars)
.arg(Formatter::HumanFormat(bytesize, Formatter::OffsetSize))
.arg(Formatter::HumanFormat(length, Formatter::OffsetSize));
if(length < bytesize)
filledSlot = false;
// ignore offset into virtualised data storage on root constants, display as 0-based
if(descriptor.flags & DescriptorFlags::InlineData)
offset = 0;
RDTreeWidgetItem *node = new RDTreeWidgetItem({
regname,
(descriptor.flags & DescriptorFlags::InlineData) ? ResourceId() : descriptor.resource,
QFormatStr("%1 - %2")
.arg(Formatter::HumanFormat(offset, Formatter::OffsetSize))
.arg(Formatter::HumanFormat(offset + bytesize, Formatter::OffsetSize)),
sizestr,
QString(),
});
node->setTag(cbuftag);
if(!filledSlot)
setEmptyRow(node);
if(!usedSlot)
setInactiveRow(node);
cbuffers[(uint32_t)used.access.stage]->addTopLevelItem(node);
}
}
else if(IsSamplerDescriptor(used.access.type))
{
const SamplerDescriptor &samplerDescriptor = used.sampler;
const ShaderSampler *shaderBind = NULL;
if(refl && used.access.index < refl->samplers.size())
shaderBind = &refl->samplers[used.access.index];
bool filledSlot = samplerDescriptor.filter.minify != FilterMode::NoFilter;
// D3D12 does not report unused elements at all because we enumerate exclusively from the
// perspective of which descriptors are used
bool usedSlot = true;
if(showNode(usedSlot, filledSlot))
{
QString regname;
if(used.access.index == DescriptorAccess::NoShaderBinding)
{
regname =
m_Locations[{used.access.descriptorStore, used.access.byteOffset}].logicalBindName;
}
else if(shaderBind)
{
if(!spacesUsed[(uint32_t)used.access.stage])
regname = QFormatStr("%1").arg(shaderBind->fixedBindNumber);
else
regname = QFormatStr("space%1, %2")
.arg(shaderBind->fixedBindSetOrSpace)
.arg(shaderBind->fixedBindNumber);
if(!shaderBind->name.empty())
regname += lit(": ") + shaderBind->name;
if(shaderBind->bindArraySize > 1)
regname += QFormatStr("[%1]").arg(used.access.arrayElement);
}
QString borderColor;
if(samplerDescriptor.borderColorType == CompType::Float)
borderColor = QFormatStr("%1, %2, %3, %4")
.arg(samplerDescriptor.borderColorValue.floatValue[0])
.arg(samplerDescriptor.borderColorValue.floatValue[1])
.arg(samplerDescriptor.borderColorValue.floatValue[2])
.arg(samplerDescriptor.borderColorValue.floatValue[3]);
else
borderColor = QFormatStr("%1, %2, %3, %4")
.arg(samplerDescriptor.borderColorValue.uintValue[0])
.arg(samplerDescriptor.borderColorValue.uintValue[1])
.arg(samplerDescriptor.borderColorValue.uintValue[2])
.arg(samplerDescriptor.borderColorValue.uintValue[3]);
QString addressing;
QString addPrefix;
QString addVal;
QString addr[] = {ToQStr(samplerDescriptor.addressU, GraphicsAPI::D3D12),
ToQStr(samplerDescriptor.addressV, GraphicsAPI::D3D12),
ToQStr(samplerDescriptor.addressW, GraphicsAPI::D3D12)};
// arrange like either UVW: WRAP or UV: WRAP, W: CLAMP
for(int a = 0; a < 3; a++)
{
const QString str[] = {lit("U"), lit("V"), lit("W")};
QString prefix = str[a];
if(a == 0 || addr[a] == addr[a - 1])
{
addPrefix += prefix;
}
else
{
addressing += QFormatStr("%1: %2, ").arg(addPrefix).arg(addVal);
addPrefix = prefix;
}
addVal = addr[a];
}
addressing += addPrefix + lit(": ") + addVal;
if(samplerDescriptor.UseBorder())
addressing += QFormatStr("<%1>").arg(borderColor);
if(samplerDescriptor.unnormalized)
addressing += lit(" (Non-norm)");
QString filter = ToQStr(samplerDescriptor.filter);
if(samplerDescriptor.maxAnisotropy > 1)
filter += QFormatStr(" %1x").arg(samplerDescriptor.maxAnisotropy);
if(samplerDescriptor.filter.filter == FilterFunction::Comparison)
filter += QFormatStr(" (%1)").arg(ToQStr(samplerDescriptor.compareFunction));
else if(samplerDescriptor.filter.filter != FilterFunction::Normal)
filter += QFormatStr(" (%1)").arg(ToQStr(samplerDescriptor.filter.filter));
RDTreeWidgetItem *node =
new RDTreeWidgetItem({regname, addressing, filter,
QFormatStr("%1 - %2")
.arg(samplerDescriptor.minLOD == -FLT_MAX
? lit("0")
: QString::number(samplerDescriptor.minLOD))
.arg(samplerDescriptor.maxLOD == FLT_MAX
? lit("FLT_MAX")
: QString::number(samplerDescriptor.maxLOD)),
samplerDescriptor.mipBias});
if(!filledSlot)
setEmptyRow(node);
if(!usedSlot)
setInactiveRow(node);
samplers[(uint32_t)used.access.stage]->addTopLevelItem(node);
}
}
else
{
const bool srv = IsReadOnlyDescriptor(used.access.type);
RDTreeWidget *tree =
srv ? resources[(uint32_t)used.access.stage] : uavs[(uint32_t)used.access.stage];
const Descriptor &view = used.descriptor;
D3D12ViewTag tag;
tag.type = srv ? D3D12ViewTag::SRV : D3D12ViewTag::UAV;
tag.access = used.access;
tag.descriptor = view;
const rdcarray<ShaderResource> &resArray =
srv ? refl->readOnlyResources : refl->readWriteResources;
const ShaderResource *shaderBind = NULL;
if(refl && used.access.index < resArray.size())
shaderBind = &resArray[used.access.index];
bool filledSlot = view.resource != ResourceId();
// D3D12 does not report unused elements at all because we enumerate exclusively from the
// perspective of which descriptors are used
bool usedSlot = true;
if(showNode(usedSlot, filledSlot))
{
addResourceRow(tag, shaderBind, spacesUsed[(uint32_t)used.access.stage], tree);
}
}
}
}
QToolButton *shaderButtons[] = {
// view buttons
ui->asShaderViewButton,
ui->msShaderViewButton,
ui->vsShaderViewButton,
ui->hsShaderViewButton,
ui->dsShaderViewButton,
ui->gsShaderViewButton,
ui->psShaderViewButton,
ui->csShaderViewButton,
// edit buttons
ui->asShaderEditButton,
ui->msShaderEditButton,
ui->vsShaderEditButton,
ui->hsShaderEditButton,
ui->dsShaderEditButton,
ui->gsShaderEditButton,
ui->psShaderEditButton,
ui->csShaderEditButton,
// save buttons
ui->asShaderSaveButton,
ui->msShaderSaveButton,
ui->vsShaderSaveButton,
ui->hsShaderSaveButton,
ui->dsShaderSaveButton,
ui->gsShaderSaveButton,
ui->psShaderSaveButton,
ui->csShaderSaveButton,
};
for(QToolButton *b : shaderButtons)
{
const D3D12Pipe::Shader *stage = stageForSender(b);
if(stage == NULL || stage->resourceId == ResourceId())
continue;
b->setEnabled(stage->reflection && state.pipelineResourceId != ResourceId());
m_Common.SetupShaderEditButton(b, state.pipelineResourceId, stage->resourceId, stage->reflection);
}
QToolButton *sigButtons[] = {
ui->vsRootSigButton, ui->hsRootSigButton, ui->dsRootSigButton, ui->gsRootSigButton,
ui->psRootSigButton, ui->csRootSigButton, ui->asRootSigButton, ui->msRootSigButton,
};
for(QToolButton *b : sigButtons)
b->setEnabled(state.rootSignature.resourceId != ResourceId());
bool streamoutSet = false;
vs = ui->gsStreamOut->verticalScrollBar()->value();
ui->gsStreamOut->beginUpdate();
ui->gsStreamOut->clear();
for(int i = 0; i < state.streamOut.outputs.count(); i++)
{
const D3D12Pipe::StreamOutBind &s = state.streamOut.outputs[i];
bool filledSlot = (s.resourceId != ResourceId());
bool usedSlot = (filledSlot);
if(showNode(usedSlot, filledSlot))
{
qulonglong length = s.byteSize;
BufferDescription *buf = m_Ctx.GetBuffer(s.resourceId);
RDTreeWidgetItem *node = new RDTreeWidgetItem({
i,
s.resourceId,
Formatter::HumanFormat(s.byteOffset, Formatter::OffsetSize),
Formatter::HumanFormat(length, Formatter::OffsetSize),
s.writtenCountResourceId,
Formatter::HumanFormat(s.writtenCountByteOffset, Formatter::OffsetSize),
QString(),
});
node->setTag(QVariant::fromValue(s.resourceId));
if(!filledSlot)
setEmptyRow(node);
if(!usedSlot)
setInactiveRow(node);
streamoutSet = true;
ui->gsStreamOut->addTopLevelItem(node);
}
}
ui->gsStreamOut->verticalScrollBar()->setValue(vs);
ui->gsStreamOut->clearSelection();
ui->gsStreamOut->endUpdate();
ui->gsStreamOut->setVisible(streamoutSet);
ui->soGroup->setVisible(streamoutSet);
////////////////////////////////////////////////
// Rasterizer
vs = ui->viewports->verticalScrollBar()->value();
ui->viewports->beginUpdate();
ui->viewports->clear();
for(int i = 0; i < state.rasterizer.viewports.count(); i++)
{
const Viewport &v = state.rasterizer.viewports[i];
RDTreeWidgetItem *node =
new RDTreeWidgetItem({i, v.x, v.y, v.width, v.height, v.minDepth, v.maxDepth});
if(v.width == 0 || v.height == 0 || v.minDepth == v.maxDepth)
setEmptyRow(node);
ui->viewports->addTopLevelItem(node);
}
ui->viewports->verticalScrollBar()->setValue(vs);
ui->viewports->clearSelection();
ui->viewports->endUpdate();
vs = ui->scissors->verticalScrollBar()->value();
ui->scissors->beginUpdate();
ui->scissors->clear();
for(int i = 0; i < state.rasterizer.scissors.count(); i++)
{
const Scissor &s = state.rasterizer.scissors[i];
RDTreeWidgetItem *node = new RDTreeWidgetItem({i, s.x, s.y, s.width, s.height});
if(s.width == 0 || s.height == 0)
setEmptyRow(node);
ui->scissors->addTopLevelItem(node);
}
ui->scissors->clearSelection();
ui->scissors->verticalScrollBar()->setValue(vs);
ui->scissors->endUpdate();
ui->fillMode->setText(ToQStr(state.rasterizer.state.fillMode));
ui->cullMode->setText(ToQStr(state.rasterizer.state.cullMode));
ui->frontCCW->setPixmap(state.rasterizer.state.frontCCW ? tick : cross);
switch(state.rasterizer.state.lineRasterMode)
{
case LineRaster::Default: ui->lineAA->setText(lit("Default")); break;
case LineRaster::Bresenham: ui->lineAA->setText(lit("Aliased")); break;
case LineRaster::RectangularSmooth: ui->lineAA->setText(lit("Alpha antialiased")); break;
case LineRaster::Rectangular: ui->lineAA->setText(lit("Quadrilateral (narrow)")); break;
case LineRaster::RectangularD3D: ui->lineAA->setText(lit("Quadrilateral")); break;
}
ui->sampleMask->setText(Formatter::Format(state.rasterizer.sampleMask, true));
ui->depthClip->setPixmap(state.rasterizer.state.depthClip ? tick : cross);
ui->depthBias->setText(Formatter::Format(state.rasterizer.state.depthBias));
ui->depthBiasClamp->setText(Formatter::Format(state.rasterizer.state.depthBiasClamp));
ui->slopeScaledBias->setText(Formatter::Format(state.rasterizer.state.slopeScaledDepthBias));
ui->forcedSampleCount->setText(QString::number(state.rasterizer.state.forcedSampleCount));
ui->conservativeRaster->setPixmap(
state.rasterizer.state.conservativeRasterization != ConservativeRaster::Disabled ? tick
: cross);
ui->baseShadingRate->setText(QFormatStr("%1x%2")
.arg(state.rasterizer.state.baseShadingRate.first)
.arg(state.rasterizer.state.baseShadingRate.second));
ui->shadingRateCombiners->setText(
QFormatStr("%1, %2")
.arg(ToQStr(state.rasterizer.state.shadingRateCombiners.first, GraphicsAPI::D3D12))
.arg(ToQStr(state.rasterizer.state.shadingRateCombiners.second, GraphicsAPI::D3D12)));
ui->shadingRateImage->setText(ToQStr(state.rasterizer.state.shadingRateImage));
////////////////////////////////////////////////
// Output Merger
bool targets[32] = {};
vs = ui->targetOutputs->verticalScrollBar()->value();
ui->targetOutputs->beginUpdate();
ui->targetOutputs->clear();
{
rdcarray<Descriptor> rts = m_Ctx.CurPipelineState().GetOutputTargets();
for(uint32_t i = 0; i < rts.size(); i++)
{
DescriptorAccess access;
access.index = i;
addResourceRow(D3D12ViewTag(D3D12ViewTag::OMTarget, access, rts[i]), NULL, false,
ui->targetOutputs);
if(rts[i].resource != ResourceId())
targets[i] = true;
}
Descriptor depth = m_Ctx.CurPipelineState().GetDepthTarget();
addResourceRow(D3D12ViewTag(D3D12ViewTag::OMDepth, DescriptorAccess(), depth), NULL, false,
ui->targetOutputs);
}
ui->targetOutputs->clearSelection();
ui->targetOutputs->endUpdate();
ui->targetOutputs->verticalScrollBar()->setValue(vs);
vs = ui->blends->verticalScrollBar()->value();
ui->blends->beginUpdate();
ui->blends->clear();
{
int i = 0;
for(const ColorBlend &blend : state.outputMerger.blendState.blends)
{
bool filledSlot = (blend.enabled || targets[i]);
bool usedSlot = (targets[i]);
if(showNode(usedSlot, filledSlot))
{
RDTreeWidgetItem *node = NULL;
node = new RDTreeWidgetItem(
{i, blend.enabled ? tr("True") : tr("False"),
ToQStr(blend.colorBlend.source), ToQStr(blend.colorBlend.destination),
ToQStr(blend.colorBlend.operation),
ToQStr(blend.alphaBlend.source), ToQStr(blend.alphaBlend.destination),
ToQStr(blend.alphaBlend.operation),
blend.logicOperationEnabled ? ToQStr(blend.logicOperation) : tr("Disabled"),
QFormatStr("%1%2%3%4")
.arg((blend.writeMask & 0x1) == 0 ? lit("_") : lit("R"))
.arg((blend.writeMask & 0x2) == 0 ? lit("_") : lit("G"))
.arg((blend.writeMask & 0x4) == 0 ? lit("_") : lit("B"))
.arg((blend.writeMask & 0x8) == 0 ? lit("_") : lit("A"))});
if(!filledSlot)
setEmptyRow(node);
if(!usedSlot)
setInactiveRow(node);
ui->blends->addTopLevelItem(node);
}
i++;
}
}
ui->blends->clearSelection();
ui->blends->endUpdate();
ui->blends->verticalScrollBar()->setValue(vs);
ui->alphaToCoverage->setPixmap(state.outputMerger.blendState.alphaToCoverage ? tick : cross);
ui->independentBlend->setPixmap(state.outputMerger.blendState.independentBlend ? tick : cross);
ui->blendFactor->setText(QFormatStr("%1, %2, %3, %4")
.arg(state.outputMerger.blendState.blendFactor[0], 0, 'f', 2)
.arg(state.outputMerger.blendState.blendFactor[1], 0, 'f', 2)
.arg(state.outputMerger.blendState.blendFactor[2], 0, 'f', 2)
.arg(state.outputMerger.blendState.blendFactor[3], 0, 'f', 2));
if(state.outputMerger.depthStencilState.depthEnable)
{
ui->depthEnabled->setPixmap(tick);
ui->depthFunc->setText(ToQStr(state.outputMerger.depthStencilState.depthFunction));
ui->depthWrite->setPixmap(state.outputMerger.depthStencilState.depthWrites ? tick : cross);
ui->depthWrite->setText(QString());
}
else
{
ui->depthEnabled->setPixmap(cross);
ui->depthFunc->setText(tr("Disabled"));
ui->depthWrite->setPixmap(QPixmap());
ui->depthWrite->setText(tr("Disabled"));
}
if(state.outputMerger.depthStencilState.depthBoundsEnable)
{
ui->depthBounds->setPixmap(QPixmap());
ui->depthBounds->setText(Formatter::Format(state.outputMerger.depthStencilState.minDepthBounds) +
lit("-") +
Formatter::Format(state.outputMerger.depthStencilState.maxDepthBounds));
}
else
{
ui->depthBounds->setText(QString());
ui->depthBounds->setPixmap(cross);
}
ui->stencils->beginUpdate();
ui->stencils->clear();
if(state.outputMerger.depthStencilState.stencilEnable)
{
ui->stencils->addTopLevelItem(new RDTreeWidgetItem({
tr("Front"),
ToQStr(state.outputMerger.depthStencilState.frontFace.function),
ToQStr(state.outputMerger.depthStencilState.frontFace.failOperation),
ToQStr(state.outputMerger.depthStencilState.frontFace.depthFailOperation),
ToQStr(state.outputMerger.depthStencilState.frontFace.passOperation),
QVariant(),
QVariant(),
QVariant(),
}));
m_Common.SetStencilTreeItemValue(ui->stencils->topLevelItem(0), 5,
state.outputMerger.depthStencilState.frontFace.writeMask);
m_Common.SetStencilTreeItemValue(ui->stencils->topLevelItem(0), 6,
state.outputMerger.depthStencilState.frontFace.compareMask);
m_Common.SetStencilTreeItemValue(ui->stencils->topLevelItem(0), 7,
state.outputMerger.depthStencilState.frontFace.reference);
ui->stencils->addTopLevelItem(new RDTreeWidgetItem({
tr("Back"),
ToQStr(state.outputMerger.depthStencilState.backFace.function),
ToQStr(state.outputMerger.depthStencilState.backFace.failOperation),
ToQStr(state.outputMerger.depthStencilState.backFace.depthFailOperation),
ToQStr(state.outputMerger.depthStencilState.backFace.passOperation),
QVariant(),
QVariant(),
QVariant(),
}));
m_Common.SetStencilTreeItemValue(ui->stencils->topLevelItem(1), 5,
state.outputMerger.depthStencilState.backFace.writeMask);
m_Common.SetStencilTreeItemValue(ui->stencils->topLevelItem(1), 6,
state.outputMerger.depthStencilState.backFace.compareMask);
m_Common.SetStencilTreeItemValue(ui->stencils->topLevelItem(1), 7,
state.outputMerger.depthStencilState.backFace.reference);
}
else
{
ui->stencils->addTopLevelItem(new RDTreeWidgetItem(
{tr("Front"), lit("-"), lit("-"), lit("-"), lit("-"), lit("-"), lit("-"), lit("-")}));
ui->stencils->addTopLevelItem(new RDTreeWidgetItem(
{tr("Back"), lit("-"), lit("-"), lit("-"), lit("-"), lit("-"), lit("-"), lit("-")}));
}
ui->stencils->clearSelection();
ui->stencils->endUpdate();
// set up thread debugging inputs
bool enableDebug = m_Ctx.APIProps().shaderDebugging && state.computeShader.reflection &&
state.computeShader.reflection->debugInfo.debuggable && action &&
(action->flags & ActionFlags::Dispatch);
if(enableDebug)
{
// Validate dispatch/threadgroup dimensions
enableDebug &= action->dispatchDimension[0] > 0;
enableDebug &= action->dispatchDimension[1] > 0;
enableDebug &= action->dispatchDimension[2] > 0;
const rdcfixedarray<uint32_t, 3> &threadDims =
(action->dispatchThreadsDimension[0] == 0)
? state.computeShader.reflection->dispatchThreadsDimension
: action->dispatchThreadsDimension;
enableDebug &= threadDims[0] > 0;
enableDebug &= threadDims[1] > 0;
enableDebug &= threadDims[2] > 0;
}
if(enableDebug)
{
ui->computeDebugSelector->setEnabled(true);
// set maximums for CS debugging
m_ComputeDebugSelector->SetThreadBounds(
action->dispatchDimension, (action->dispatchThreadsDimension[0] == 0)
? state.computeShader.reflection->dispatchThreadsDimension
: action->dispatchThreadsDimension);
ui->computeDebugSelector->setToolTip(
tr("Debug this compute shader by specifying group/thread ID or dispatch ID"));
}
else
{
ui->computeDebugSelector->setEnabled(false);
if(!m_Ctx.APIProps().shaderDebugging)
ui->computeDebugSelector->setToolTip(tr("This API does not support shader debugging"));
else if(!action || !(action->flags & ActionFlags::Dispatch))
ui->computeDebugSelector->setToolTip(tr("No dispatch selected"));
else if(!state.computeShader.reflection)
ui->computeDebugSelector->setToolTip(tr("No compute shader bound"));
else if(!state.computeShader.reflection->debugInfo.debuggable)
ui->computeDebugSelector->setToolTip(
tr("This shader doesn't support debugging: %1")
.arg(state.computeShader.reflection->debugInfo.debugStatus));
else
ui->computeDebugSelector->setToolTip(tr("Invalid dispatch/threadgroup dimensions."));
}
}