in qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp [1190:2116]
void D3D11PipelineStateViewer::setState()
{
if(!m_Ctx.IsCaptureLoaded())
{
clearState();
return;
}
const D3D11Pipe::State &state = *m_Ctx.CurD3D11PipelineState();
const ActionDescription *action = m_Ctx.CurAction();
const QPixmap &tick = Pixmaps::tick(this);
const QPixmap &cross = Pixmaps::cross(this);
////////////////////////////////////////////////
// Vertex Input
if(state.inputAssembly.bytecode)
{
QString layout = ToQStr(state.inputAssembly.resourceId);
if(state.inputAssembly.bytecode && !state.inputAssembly.bytecode->debugInfo.files.empty())
{
const ShaderDebugInfo &dbg = state.inputAssembly.bytecode->debugInfo;
int entryFile = qMax(0, dbg.entryLocation.fileIndex);
layout += QFormatStr(": %1() - %2")
.arg(state.inputAssembly.bytecode->entryPoint)
.arg(QFileInfo(dbg.files[entryFile].filename).fileName());
}
ui->iaBytecode->setText(layout);
}
else
{
ui->iaBytecode->setText(ToQStr(state.inputAssembly.resourceId));
}
ui->iaBytecodeMismatch->setVisible(false);
// check for IA-VS mismatches here.
// This should be moved to a "Render Doctor" window reporting problems
if(state.inputAssembly.bytecode && state.vertexShader.reflection)
{
QString mismatchDetails;
// VS wants more elements
if(state.inputAssembly.bytecode->inputSignature.count() <
state.vertexShader.reflection->inputSignature.count())
{
int excess = state.vertexShader.reflection->inputSignature.count() -
state.inputAssembly.bytecode->inputSignature.count();
bool allSystem = true;
// The VS signature can consume more elements as long as they are all system value types
// (ie. SV_VertexID or SV_InstanceID)
for(int e = 0; e < excess; e++)
{
if(state.vertexShader.reflection
->inputSignature[state.vertexShader.reflection->inputSignature.count() - 1 - e]
.systemValue == ShaderBuiltin::Undefined)
{
allSystem = false;
break;
}
}
if(!allSystem)
mismatchDetails += tr("IA bytecode provides fewer elements than VS wants.\n");
}
{
const rdcarray<SigParameter> &IA = state.inputAssembly.bytecode->inputSignature;
const rdcarray<SigParameter> &VS = state.vertexShader.reflection->inputSignature;
int count = qMin(IA.count(), VS.count());
for(int i = 0; i < count; i++)
{
QString IAname = IA[i].semanticIdxName;
QString VSname = VS[i].semanticIdxName;
// misorder or misnamed semantics
if(IAname.compare(VSname, Qt::CaseInsensitive))
mismatchDetails += tr("IA bytecode semantic %1: %2 != VS bytecode semantic %1: %3\n")
.arg(i)
.arg(IAname)
.arg(VSname);
// VS wants more components
if(IA[i].compCount < VS[i].compCount)
mismatchDetails += tr("IA bytecode semantic %1 (%2) is %4-wide).arg(VS bytecode semantic "
"%1 (%2) %3 is %5-wide\n")
.arg(i)
.arg(IAname)
.arg(VSname)
.arg(IA[i].compCount)
.arg(VS[i].compCount);
// VS wants different types
if(IA[i].varType != VS[i].varType)
mismatchDetails +=
tr("IA bytecode semantic %1 (%2) is %4).arg(VS bytecode semantic %1 (%3) is %5\n")
.arg(i)
.arg(IAname)
.arg(VSname)
.arg(ToQStr(IA[i].varType))
.arg(ToQStr(VS[i].varType));
}
}
if(!mismatchDetails.isEmpty())
{
ui->iaBytecodeMismatch->setText(
tr("Warning: Mismatch detected between bytecode used to create IA and currently bound VS "
"inputs"));
ui->iaBytecodeMismatch->setToolTip(mismatchDetails.trimmed());
ui->iaBytecodeMismatch->setVisible(true);
}
}
int vs = 0;
bool usedVBuffers[128] = {};
uint32_t layoutOffs[128] = {};
vs = ui->iaLayouts->verticalScrollBar()->value();
ui->iaLayouts->beginUpdate();
ui->iaLayouts->clear();
{
int i = 0;
for(const D3D11Pipe::Layout &l : state.inputAssembly.layouts)
{
QString byteOffs = Formatter::HumanFormat(l.byteOffset, Formatter::OffsetSize);
// D3D11 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.inputAssembly.bytecode && ia < state.inputAssembly.bytecode->inputSignature.count();
ia++)
{
if(!QString(state.inputAssembly.bytecode->inputSignature[ia].semanticName)
.compare(l.semanticName, Qt::CaseInsensitive) &&
state.inputAssembly.bytecode->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 || ui->showUnused->isChecked())
{
uint64_t length = 0;
BufferDescription *buf = m_Ctx.GetBuffer(state.inputAssembly.indexBuffer.resourceId);
if(buf)
length = buf->length;
RDTreeWidgetItem *node = new RDTreeWidgetItem({
tr("Index"),
state.inputAssembly.indexBuffer.resourceId,
Formatter::HumanFormat(state.inputAssembly.indexBuffer.byteStride, Formatter::OffsetSize),
Formatter::HumanFormat(state.inputAssembly.indexBuffer.byteOffset, Formatter::OffsetSize),
Formatter::HumanFormat(length, Formatter::OffsetSize),
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));
node->setTag(QVariant::fromValue(D3D11VBIBTag(
state.inputAssembly.indexBuffer.resourceId,
state.inputAssembly.indexBuffer.byteOffset +
(action ? action->indexOffset * state.inputAssembly.indexBuffer.byteStride : 0),
iformat)));
if(!ibufferUsed)
setInactiveRow(node);
if(state.inputAssembly.indexBuffer.resourceId == ResourceId())
{
setEmptyRow(node);
m_EmptyNodes.push_back(node);
}
ui->iaBuffers->addTopLevelItem(node);
}
}
else
{
if(ibufferUsed || ui->showEmpty->isChecked())
{
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));
node->setTag(QVariant::fromValue(D3D11VBIBTag(
state.inputAssembly.indexBuffer.resourceId,
state.inputAssembly.indexBuffer.byteOffset +
(action ? action->indexOffset * state.inputAssembly.indexBuffer.byteStride : 0),
iformat)));
setEmptyRow(node);
m_EmptyNodes.push_back(node);
if(!ibufferUsed)
setInactiveRow(node);
ui->iaBuffers->addTopLevelItem(node);
}
}
for(int i = 0; i < state.inputAssembly.vertexBuffers.count(); i++)
{
const D3D11Pipe::VertexBuffer &v = state.inputAssembly.vertexBuffers[i];
bool filledSlot = (v.resourceId != ResourceId());
bool usedSlot = (usedVBuffers[i]);
if(showNode(usedSlot, filledSlot))
{
qulonglong length = 0;
BufferDescription *buf = m_Ctx.GetBuffer(v.resourceId);
if(buf)
length = buf->length;
RDTreeWidgetItem *node = NULL;
if(filledSlot)
node = new RDTreeWidgetItem({
i,
v.resourceId,
Formatter::HumanFormat(v.byteStride, Formatter::OffsetSize),
Formatter::HumanFormat(v.byteOffset, Formatter::OffsetSize),
Formatter::HumanFormat(length, Formatter::OffsetSize),
QString(),
});
else
node =
new RDTreeWidgetItem({i, tr("No Buffer Set"), lit("-"), lit("-"), lit("-"), QString()});
node->setTag(QVariant::fromValue(
D3D11VBIBTag(v.resourceId, v.byteOffset, m_Common.GetVBufferFormatString(i))));
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);
QToolButton *shaderButtons[] = {
ui->vsShaderViewButton, ui->hsShaderViewButton, ui->dsShaderViewButton,
ui->gsShaderViewButton, ui->psShaderViewButton, ui->csShaderViewButton,
ui->vsShaderEditButton, ui->hsShaderEditButton, ui->dsShaderEditButton,
ui->gsShaderEditButton, ui->psShaderEditButton, ui->csShaderEditButton,
ui->vsShaderSaveButton, ui->hsShaderSaveButton, ui->dsShaderSaveButton,
ui->gsShaderSaveButton, ui->psShaderSaveButton, ui->csShaderSaveButton,
};
for(QToolButton *b : shaderButtons)
{
const D3D11Pipe::Shader *stage = stageForSender(b);
if(stage == NULL || stage->resourceId == ResourceId())
continue;
b->setEnabled(stage->reflection != NULL);
m_Common.SetupShaderEditButton(b, ResourceId(), stage->resourceId, stage->reflection);
}
ui->iaBytecodeViewButton->setEnabled(true);
////////////////////////////////////////////////
// Main iteration over descriptor storage
bool targets[32] = {};
{
ScopedTreeUpdater restorers[] = {
// VS
ui->vsResources,
ui->vsSamplers,
ui->vsCBuffers,
ui->vsClasses,
// GS
ui->gsResources,
ui->gsSamplers,
ui->gsCBuffers,
ui->gsClasses,
// HS
ui->hsResources,
ui->hsSamplers,
ui->hsCBuffers,
ui->hsClasses,
// DS
ui->dsResources,
ui->dsSamplers,
ui->dsCBuffers,
ui->dsClasses,
// PS
ui->psResources,
ui->psSamplers,
ui->psCBuffers,
ui->psClasses,
// CS
ui->csResources,
ui->csSamplers,
ui->csCBuffers,
ui->csClasses,
ui->csUAVs,
// OM - we handle this here since it overlaps with the shader-based UAVs
ui->targetOutputs,
};
rdcarray<Descriptor> outputs = m_Ctx.CurPipelineState().GetOutputTargets();
for(uint32_t i = 0; i < outputs.size(); i++)
{
addResourceRow(D3D11ViewTag(D3D11ViewTag::OMTarget, i, outputs[i]), NULL, true,
ui->targetOutputs);
if(outputs[i].resource != ResourceId())
targets[i] = true;
}
setShaderState(state.vertexShader, ui->vsShader, ui->vsResources, ui->vsSamplers,
ui->vsCBuffers, ui->vsClasses);
setShaderState(state.geometryShader, ui->gsShader, ui->gsResources, ui->gsSamplers,
ui->gsCBuffers, ui->gsClasses);
setShaderState(state.hullShader, ui->hsShader, ui->hsResources, ui->hsSamplers, ui->hsCBuffers,
ui->hsClasses);
setShaderState(state.domainShader, ui->dsShader, ui->dsResources, ui->dsSamplers,
ui->dsCBuffers, ui->dsClasses);
setShaderState(state.pixelShader, ui->psShader, ui->psResources, ui->psSamplers, ui->psCBuffers,
ui->psClasses);
setShaderState(state.computeShader, ui->csShader, ui->csResources, ui->csSamplers,
ui->csCBuffers, ui->csClasses);
const ShaderReflection *shaderRefls[NumShaderStages];
RDTreeWidget *resources[] = {
ui->vsResources, ui->hsResources, ui->dsResources,
ui->gsResources, ui->psResources, ui->csResources,
};
RDTreeWidget *samplers[] = {
ui->vsSamplers, ui->hsSamplers, ui->dsSamplers,
ui->gsSamplers, ui->psSamplers, ui->csSamplers,
};
RDTreeWidget *cbuffers[] = {
ui->vsCBuffers, ui->hsCBuffers, ui->dsCBuffers,
ui->gsCBuffers, ui->psCBuffers, ui->csCBuffers,
};
for(ShaderStage stage : values<ShaderStage>())
shaderRefls[(uint32_t)stage] = m_Ctx.CurPipelineState().GetShaderReflection(stage);
for(uint32_t i = 0; i < m_Locations.size(); i++)
{
// expect only one stage per location
ShaderStage stage = FirstStageForMask(m_Locations[i].stageMask);
uint32_t reg = m_Locations[i].fixedBindNumber;
bool usedSlot = false;
if(m_Locations[i].category == DescriptorCategory::ConstantBlock)
{
const ConstantBlock *shaderBind = NULL;
if(shaderRefls[(uint32_t)stage])
{
for(int b = 0; b < shaderRefls[(uint32_t)stage]->constantBlocks.count(); b++)
{
const ConstantBlock &res = shaderRefls[(uint32_t)stage]->constantBlocks[b];
if(res.fixedBindNumber == reg)
{
shaderBind = &res;
usedSlot = HasAccess(stage, m_Locations[i].category, b);
break;
}
}
}
Descriptor b = m_Descriptors[i];
addCBufferRow(b, reg, shaderBind, usedSlot, cbuffers[(uint32_t)stage]);
}
else if(m_Locations[i].category == DescriptorCategory::Sampler)
{
const ShaderSampler *shaderBind = NULL;
if(shaderRefls[(uint32_t)stage])
{
for(int b = 0; b < shaderRefls[(uint32_t)stage]->samplers.count(); b++)
{
const ShaderSampler &res = shaderRefls[(uint32_t)stage]->samplers[b];
if(res.fixedBindNumber == reg)
{
shaderBind = &res;
usedSlot = HasAccess(stage, m_Locations[i].category, b);
break;
}
}
}
addSamplerRow(m_SamplerDescriptors[i], reg, shaderBind, usedSlot, samplers[(uint32_t)stage]);
}
else if(m_Locations[i].category == DescriptorCategory::ReadOnlyResource)
{
const ShaderResource *shaderBind = NULL;
if(shaderRefls[(uint32_t)stage])
{
for(int b = 0; b < shaderRefls[(uint32_t)stage]->readOnlyResources.count(); b++)
{
const ShaderResource &res = shaderRefls[(uint32_t)stage]->readOnlyResources[b];
if(res.fixedBindNumber == reg)
{
shaderBind = &res;
usedSlot = HasAccess(stage, m_Locations[i].category, b);
break;
}
}
}
addResourceRow(D3D11ViewTag(D3D11ViewTag::SRV, reg, m_Descriptors[i]), shaderBind, usedSlot,
resources[(uint32_t)stage]);
}
else if(m_Locations[i].category == DescriptorCategory::ReadWriteResource)
{
const ShaderResource *shaderBind = NULL;
if(stage == ShaderStage::Compute)
{
if(shaderRefls[(uint32_t)stage])
{
for(int b = 0; b < shaderRefls[(uint32_t)stage]->readWriteResources.count(); b++)
{
const ShaderResource &res = shaderRefls[(uint32_t)stage]->readWriteResources[b];
if(res.fixedBindNumber == reg)
{
shaderBind = &res;
usedSlot = HasAccess(stage, m_Locations[i].category, b);
break;
}
}
}
addResourceRow(D3D11ViewTag(D3D11ViewTag::SRV, reg, m_Descriptors[i]), shaderBind,
usedSlot, ui->csUAVs);
}
else
{
// skip any descriptors from before the first valid OM UAV
if(reg < state.outputMerger.uavStartSlot)
continue;
// only iterate UAV descriptors from the pixel shader stage - they will be duplicated
// per-stage and below we iterate over every stage
if(stage != ShaderStage::Pixel)
continue;
// any non-CS shader can use these. When that's not supported (Before feature level 11.1)
// this search will just boil down to only PS.
// When multiple stages use the UAV, we allow the last stage to 'win' and define its type,
// although it would be very surprising if the types were actually different anyway.
for(const ShaderReflection *refl : shaderRefls)
{
if(refl && refl->stage != ShaderStage::Compute)
{
for(int b = 0; b < refl->readWriteResources.count(); b++)
{
const ShaderResource &res = refl->readWriteResources[b];
if(res.fixedBindNumber == reg)
{
shaderBind = &res;
usedSlot = HasAccess(stage, m_Locations[i].category, b);
break;
}
}
}
}
addResourceRow(D3D11ViewTag(D3D11ViewTag::UAV, reg, m_Descriptors[i]), shaderBind,
usedSlot, ui->targetOutputs);
}
}
}
addResourceRow(D3D11ViewTag(D3D11ViewTag::OMDepth, 0, m_Ctx.CurPipelineState().GetDepthTarget()),
NULL, true, ui->targetOutputs);
ui->vsClasses->parentWidget()->setVisible(ui->vsClasses->topLevelItemCount() > 0);
ui->hsClasses->parentWidget()->setVisible(ui->hsClasses->topLevelItemCount() > 0);
ui->dsClasses->parentWidget()->setVisible(ui->dsClasses->topLevelItemCount() > 0);
ui->gsClasses->parentWidget()->setVisible(ui->gsClasses->topLevelItemCount() > 0);
ui->psClasses->parentWidget()->setVisible(ui->psClasses->topLevelItemCount() > 0);
ui->csClasses->parentWidget()->setVisible(ui->csClasses->topLevelItemCount() > 0);
}
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 D3D11Pipe::StreamOutBind &s = state.streamOut.outputs[i];
bool filledSlot = (s.resourceId != ResourceId());
bool usedSlot = (filledSlot);
if(showNode(usedSlot, filledSlot))
{
qulonglong length = 0;
BufferDescription *buf = m_Ctx.GetBuffer(s.resourceId);
if(buf)
length = buf->length;
RDTreeWidgetItem *node = new RDTreeWidgetItem({
i,
s.resourceId,
Formatter::HumanFormat(length, Formatter::OffsetSize),
Formatter::HumanFormat(s.byteOffset, 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];
if(v.enabled || ui->showEmpty->isChecked())
{
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);
if(!v.enabled)
setInactiveRow(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];
if((s.enabled && state.rasterizer.state.scissorEnable) || ui->showEmpty->isChecked())
{
RDTreeWidgetItem *node = new RDTreeWidgetItem({i, s.x, s.y, s.width, s.height});
if(s.width == 0 || s.height == 0)
setEmptyRow(node);
if(!s.enabled)
setInactiveRow(node);
ui->scissors->addTopLevelItem(node);
}
}
ui->scissors->clearSelection();
ui->scissors->verticalScrollBar()->setValue(vs);
ui->scissors->endUpdate();
ui->rastState->setText(ToQStr(state.rasterizer.state.resourceId));
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);
ui->scissorEnabled->setPixmap(state.rasterizer.state.scissorEnable ? tick : cross);
ui->lineAA->setPixmap(state.rasterizer.state.antialiasedLines ? tick : cross);
ui->multisample->setPixmap(state.rasterizer.state.multisampleEnable ? tick : cross);
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);
////////////////////////////////////////////////
// Predication
if(state.predication.resourceId == ResourceId())
{
ui->predicateGroup->setVisible(false);
}
else
{
ui->predicateGroup->setVisible(true);
ui->predicate->setText(ToQStr(state.predication.resourceId));
ui->predicateValue->setText(state.predication.value ? lit("TRUE") : lit("FALSE"));
ui->predicatePassing->setPixmap(state.predication.isPassing ? tick : cross);
}
////////////////////////////////////////////////
// Output Merger
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->blendState->setText(ToQStr(state.outputMerger.blendState.resourceId));
ui->alphaToCoverage->setPixmap(state.outputMerger.blendState.alphaToCoverage ? tick : cross);
ui->independentBlend->setPixmap(state.outputMerger.blendState.independentBlend ? tick : cross);
ui->sampleMask->setText(Formatter::Format(state.outputMerger.blendState.sampleMask, true));
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));
ui->depthState->setText(ToQStr(state.outputMerger.depthStencilState.resourceId));
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"));
}
ui->stencilEnabled->setPixmap(state.outputMerger.depthStencilState.stencilEnable ? tick : cross);
m_Common.SetStencilLabelValue(
ui->stencilReadMask, (uint8_t)state.outputMerger.depthStencilState.frontFace.compareMask);
m_Common.SetStencilLabelValue(ui->stencilWriteMask,
(uint8_t)state.outputMerger.depthStencilState.frontFace.writeMask);
m_Common.SetStencilLabelValue(ui->stencilRef,
(uint8_t)state.outputMerger.depthStencilState.frontFace.reference);
ui->stencils->beginUpdate();
ui->stencils->clear();
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)}));
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)}));
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."));
}
// highlight the appropriate stages in the flowchart
if(action == NULL)
{
ui->pipeFlow->setStagesEnabled({true, true, true, true, true, true, true, true, true});
}
else if(action->flags & ActionFlags::Dispatch)
{
ui->pipeFlow->setStagesEnabled({false, false, false, false, false, false, false, false, true});
}
else
{
bool streamOutActive = false;
for(const D3D11Pipe::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"));
}
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});
}
}