From e08107ebe89d4012bf033f1b315b9d3c88c4c8e9 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 21 Mar 2024 17:18:23 +0000 Subject: [PATCH] Update D3D11 pipeline state viewer to use new descriptor access --- .../D3D11PipelineStateViewer.cpp | 1134 +++++++++-------- .../PipelineState/D3D11PipelineStateViewer.h | 21 +- renderdoc/api/replay/replay_enums.h | 23 + 3 files changed, 656 insertions(+), 522 deletions(-) diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp index fef253cdb..e44ee800c 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp @@ -64,14 +64,34 @@ struct D3D11ViewTag }; D3D11ViewTag() : type(SRV), index(0) {} - D3D11ViewTag(ResType t, int i, const D3D11Pipe::View &r) : type(t), index(i), res(r) {} + D3D11ViewTag(ResType t, uint32_t i, const Descriptor &d) : type(t), index(i), desc(d) {} ResType type; - int index; - D3D11Pipe::View res; + uint32_t index; + Descriptor desc; }; Q_DECLARE_METATYPE(D3D11ViewTag); +struct ScopedTreeUpdater +{ + ScopedTreeUpdater(RDTreeWidget *widget) : m_Widget(widget) + { + vs = m_Widget->verticalScrollBar()->value(); + m_Widget->beginUpdate(); + m_Widget->clear(); + } + + ~ScopedTreeUpdater() + { + m_Widget->clearSelection(); + m_Widget->endUpdate(); + m_Widget->verticalScrollBar()->setValue(vs); + } + + RDTreeWidget *m_Widget; + int vs; +}; + D3D11PipelineStateViewer::D3D11PipelineStateViewer(ICaptureContext &ctx, PipelineStateViewer &common, QWidget *parent) : QFrame(parent), ui(new Ui::D3D11PipelineStateViewer), m_Ctx(ctx), m_Common(common) @@ -269,7 +289,7 @@ D3D11PipelineStateViewer::D3D11PipelineStateViewer(ICaptureContext &ctx, RDHeaderView *header = new RDHeaderView(Qt::Horizontal, this); cbuffer->setHeader(header); - cbuffer->setColumns({tr("Slot"), tr("Buffer"), tr("Vec4 Range"), tr("Size"), tr("Go")}); + cbuffer->setColumns({tr("Slot"), tr("Buffer"), tr("Byte Range"), tr("Size"), tr("Go")}); header->setColumnStretchHints({1, 2, 3, 3, -1}); cbuffer->setHoverIconColumn(4, action, action_hover); @@ -491,7 +511,32 @@ void D3D11PipelineStateViewer::OnCaptureClosed() void D3D11PipelineStateViewer::OnEventChanged(uint32_t eventId) { - setState(); + m_Ctx.Replay().AsyncInvoke([this](IReplayController *r) { + const D3D11Pipe::State *state = r->GetD3D11PipelineState(); + ResourceId descriptorStore = state->descriptorStore; + DescriptorRange range; + range.offset = 0; + range.descriptorSize = state->descriptorByteSize; + range.count = state->descriptorCount; + + rdcarray ranges = {range}; + + rdcarray locations = + r->GetDescriptorLocations(descriptorStore, ranges); + rdcarray descriptors = r->GetDescriptors(descriptorStore, ranges); + rdcarray samplerDescriptors = + r->GetSamplerDescriptors(descriptorStore, ranges); + + // we only write to m_Locations etc on the GUI thread so we know there's no race here. + GUIInvoke::call(this, + [this, locations = std::move(locations), descriptors = std::move(descriptors), + samplerDescriptors = std::move(samplerDescriptors)]() { + m_Locations = locations; + m_Descriptors = descriptors; + m_SamplerDescriptors = samplerDescriptors; + setState(); + }); + }); } void D3D11PipelineStateViewer::SelectPipelineStage(PipelineStage stage) @@ -518,7 +563,7 @@ ResourceId D3D11PipelineStateViewer::GetResource(RDTreeWidgetItem *item) else if(tag.canConvert()) { D3D11ViewTag viewTag = tag.value(); - return viewTag.res.resourceResourceId; + return viewTag.desc.resource; } else if(tag.canConvert()) { @@ -532,31 +577,7 @@ ResourceId D3D11PipelineStateViewer::GetResource(RDTreeWidgetItem *item) if(stage == NULL) return ResourceId(); - int cb = tag.value(); - - int cbufIdx = -1; - - for(int i = 0; i < stage->bindpointMapping.constantBlocks.count(); i++) - { - if(stage->bindpointMapping.constantBlocks[i].bind == cb) - { - cbufIdx = i; - break; - } - } - - if(cbufIdx == -1) - { - // unused cbuffer, open regular buffer viewer - if(cb >= stage->constantBuffers.count()) - return ResourceId(); - - const D3D11Pipe::ConstantBuffer &bind = stage->constantBuffers[cb]; - - return bind.resourceId; - } - - return m_Ctx.CurPipelineState().GetConstantBuffer(stage->stage, cbufIdx, 0).resourceId; + return FindDescriptor(stage->stage, DescriptorCategory::ConstantBlock, tag.value()).resource; } return ResourceId(); @@ -583,8 +604,7 @@ void D3D11PipelineStateViewer::setEmptyRow(RDTreeWidgetItem *node) node->setForegroundColor(QColor(0, 0, 0)); } -bool D3D11PipelineStateViewer::HasImportantViewParams(const D3D11Pipe::View &view, - TextureDescription *tex) +bool D3D11PipelineStateViewer::HasImportantViewParams(const Descriptor &view, TextureDescription *tex) { // we don't count 'upgrade typeless to typed' as important, we just display the typed format // in the row since there's no real hidden important information there. The formats can't be @@ -597,16 +617,15 @@ bool D3D11PipelineStateViewer::HasImportantViewParams(const D3D11Pipe::View &vie // in the case of the swapchain case, types can be different and it won't have shown // up as taking the view's format because the swapchain already has one. Make sure to mark it // as important - if(view.viewFormat.compType != CompType::Typeless && view.viewFormat != tex->format) + if(view.format.compType != CompType::Typeless && view.format != tex->format) return true; return false; } -bool D3D11PipelineStateViewer::HasImportantViewParams(const D3D11Pipe::View &view, - BufferDescription *buf) +bool D3D11PipelineStateViewer::HasImportantViewParams(const Descriptor &view, BufferDescription *buf) { - if(view.firstElement > 0 || view.numElements * view.elementByteSize < buf->length) + if(view.byteOffset > 0 || view.byteSize < buf->length) return true; return false; @@ -620,15 +639,15 @@ void D3D11PipelineStateViewer::setViewDetails(RDTreeWidgetItem *node, const D3D1 QString text; - const D3D11Pipe::View &res = view.res; + const Descriptor &desc = view.desc; bool viewdetails = false; - if(res.viewFormat != tex->format) + if(desc.format != tex->format) { text += tr("The texture is format %1, the view treats it as %2.\n") .arg(tex->format.Name()) - .arg(res.viewFormat.Name()); + .arg(desc.format.Name()); viewdetails = true; } @@ -641,31 +660,31 @@ void D3D11PipelineStateViewer::setViewDetails(RDTreeWidgetItem *node, const D3D1 text += tr("Stencil component is read-only\n"); } - if(tex->mips > 1 && (tex->mips != res.numMips || res.firstMip > 0)) + if(tex->mips > 1 && (tex->mips != desc.numMips || desc.firstMip > 0)) { - if(res.numMips == 1) + if(desc.numMips == 1) text += - tr("The texture has %1 mips, the view covers mip %2.\n").arg(tex->mips).arg(res.firstMip); + tr("The texture has %1 mips, the view covers mip %2.\n").arg(tex->mips).arg(desc.firstMip); else text += tr("The texture has %1 mips, the view covers mips %2-%3.\n") .arg(tex->mips) - .arg(res.firstMip) - .arg(res.firstMip + res.numMips - 1); + .arg(desc.firstMip) + .arg(desc.firstMip + desc.numMips - 1); viewdetails = true; } - if(tex->arraysize > 1 && (tex->arraysize != res.numSlices || res.firstSlice > 0)) + if(tex->arraysize > 1 && (tex->arraysize != desc.numSlices || desc.firstSlice > 0)) { - if(res.numSlices == 1) + if(desc.numSlices == 1) text += tr("The texture has %1 array slices, the view covers slice %2.\n") .arg(tex->arraysize) - .arg(res.firstSlice); + .arg(desc.firstSlice); else text += tr("The texture has %1 array slices, the view covers slices %2-%3.\n") .arg(tex->arraysize) - .arg(res.firstSlice) - .arg(res.firstSlice + res.numSlices - 1); + .arg(desc.firstSlice) + .arg(desc.firstSlice + desc.numSlices - 1); viewdetails = true; } @@ -686,18 +705,17 @@ void D3D11PipelineStateViewer::setViewDetails(RDTreeWidgetItem *node, const D3D1 QString text; - const D3D11Pipe::View &res = view.res; + const Descriptor &desc = view.desc; - if((res.firstElement * res.elementByteSize) > 0 || - (res.numElements * res.elementByteSize) < buf->length) + if(desc.byteOffset > 0 || desc.byteSize < buf->length) { text += tr("The view covers bytes %1-%2 (%3 elements).\nThe buffer is %4 bytes in length (%5 " "elements).\n") - .arg(res.firstElement * res.elementByteSize) - .arg((res.firstElement + res.numElements) * res.elementByteSize) - .arg(res.numElements) + .arg(desc.byteOffset) + .arg(desc.byteOffset + desc.byteSize) + .arg(desc.byteSize / desc.elementByteSize) .arg(buf->length) - .arg(buf->length / res.elementByteSize); + .arg(buf->length / desc.elementByteSize); } else { @@ -709,10 +727,10 @@ void D3D11PipelineStateViewer::setViewDetails(RDTreeWidgetItem *node, const D3D1 } void D3D11PipelineStateViewer::addResourceRow(const D3D11ViewTag &view, - const ShaderResource *shaderInput, - const Bindpoint *map, RDTreeWidget *resources) + const ShaderResource *shaderInput, bool usedSlot, + RDTreeWidget *resources) { - const D3D11Pipe::View &r = view.res; + const Descriptor &desc = view.desc; bool viewDetails = false; @@ -720,8 +738,7 @@ void D3D11PipelineStateViewer::addResourceRow(const D3D11ViewTag &view, viewDetails = m_Ctx.CurD3D11PipelineState()->outputMerger.depthReadOnly || m_Ctx.CurD3D11PipelineState()->outputMerger.stencilReadOnly; - bool filledSlot = (r.resourceResourceId != ResourceId()); - bool usedSlot = (map && map->used); + bool filledSlot = (desc.resource != ResourceId()); // if a target is set to RTVs or DSV, it is implicitly used if(filledSlot) @@ -746,7 +763,7 @@ void D3D11PipelineStateViewer::addResourceRow(const D3D11ViewTag &view, w = h = d = a = 0; } - TextureDescription *tex = m_Ctx.GetTexture(r.resourceResourceId); + TextureDescription *tex = m_Ctx.GetTexture(desc.resource); if(tex) { @@ -762,14 +779,14 @@ void D3D11PipelineStateViewer::addResourceRow(const D3D11ViewTag &view, typeName += QFormatStr(" %1x").arg(tex->msSamp); } - if(tex->format != r.viewFormat) - format = tr("Viewed as %1").arg(r.viewFormat.Name()); + if(tex->format != desc.format) + format = tr("Viewed as %1").arg(desc.format.Name()); - if(HasImportantViewParams(r, tex)) + if(HasImportantViewParams(desc, tex)) viewDetails = true; } - BufferDescription *buf = m_Ctx.GetBuffer(r.resourceResourceId); + BufferDescription *buf = m_Ctx.GetBuffer(desc.resource); if(buf) { @@ -780,28 +797,28 @@ void D3D11PipelineStateViewer::addResourceRow(const D3D11ViewTag &view, format = QString(); typeName = QFormatStr("%1Buffer").arg(view.type == D3D11ViewTag::UAV ? lit("RW") : QString()); - if(r.bufferFlags & D3DBufferViewFlags::Raw) + if(desc.flags & DescriptorFlags::RawBuffer) { typeName = QFormatStr("%1ByteAddressBuffer") .arg(view.type == D3D11ViewTag::UAV ? lit("RW") : QString()); } - else if(r.elementByteSize > 0 && r.viewFormat.type == ResourceFormatType::Undefined) + else if(desc.elementByteSize > 0 && desc.format.type == ResourceFormatType::Undefined) { // for structured buffers, display how many 'elements' there are in the buffer typeName = QFormatStr("%1StructuredBuffer[%2]") .arg(view.type == D3D11ViewTag::UAV ? lit("RW") : QString()) - .arg(buf->length / r.elementByteSize); + .arg(buf->length / desc.elementByteSize); } - if(r.counterResourceId != ResourceId()) + if(desc.secondary != ResourceId()) { - typeName += tr(" (%1: %2)").arg(ToQStr(r.counterResourceId)).arg(r.bufferStructCount); + typeName += tr(" (%1: %2)").arg(ToQStr(desc.secondary)).arg(desc.bufferStructCount); } // get the buffer type, whether it's just a basic type or a complex struct if(shaderInput && !shaderInput->isTexture) { - if(r.viewFormat.compType == CompType::Typeless) + if(desc.format.compType == CompType::Typeless) { if(shaderInput->variableType.baseType == VarType::Struct) format = lit("struct ") + shaderInput->variableType.name; @@ -810,18 +827,18 @@ void D3D11PipelineStateViewer::addResourceRow(const D3D11ViewTag &view, } else { - format = r.viewFormat.Name(); + format = desc.format.Name(); } } - if(HasImportantViewParams(r, buf)) + if(HasImportantViewParams(desc, buf)) viewDetails = true; } - QVariant name = r.resourceResourceId; + QVariant name = desc.resource; if(viewDetails) - name = tr("%1 viewed by %2").arg(ToQStr(r.resourceResourceId)).arg(ToQStr(r.viewResourceId)); + name = tr("%1 viewed by %2").arg(ToQStr(desc.resource)).arg(ToQStr(desc.view)); RDTreeWidgetItem *node = new RDTreeWidgetItem({slotname, name, typeName, w, h, d, a, format, QString()}); @@ -846,6 +863,140 @@ void D3D11PipelineStateViewer::addResourceRow(const D3D11ViewTag &view, } } +void D3D11PipelineStateViewer::addSamplerRow(const SamplerDescriptor &descriptor, uint32_t reg, + const ShaderSampler *shaderBind, bool usedSlot, + RDTreeWidget *samplers) +{ + bool filledSlot = descriptor.object != ResourceId(); + + if(showNode(usedSlot, filledSlot)) + { + QString slotname = QString::number(reg); + + if(shaderBind && !shaderBind->name.empty()) + slotname += lit(": ") + shaderBind->name; + + QString borderColor = QFormatStr("%1, %2, %3, %4") + .arg(descriptor.borderColorValue.floatValue[0]) + .arg(descriptor.borderColorValue.floatValue[1]) + .arg(descriptor.borderColorValue.floatValue[2]) + .arg(descriptor.borderColorValue.floatValue[3]); + + QString addressing; + + QString addPrefix; + QString addVal; + + QString addr[] = {ToQStr(descriptor.addressU, GraphicsAPI::D3D11), + ToQStr(descriptor.addressV, GraphicsAPI::D3D11), + ToQStr(descriptor.addressW, GraphicsAPI::D3D11)}; + + // 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(descriptor.UseBorder()) + addressing += QFormatStr("<%1>").arg(borderColor); + + QString filter = ToQStr(descriptor.filter); + + if(descriptor.maxAnisotropy > 1) + filter += QFormatStr(" %1x").arg(descriptor.maxAnisotropy); + + if(descriptor.filter.filter == FilterFunction::Comparison) + filter += QFormatStr(" (%1)").arg(ToQStr(descriptor.compareFunction)); + else if(descriptor.filter.filter != FilterFunction::Normal) + filter += QFormatStr(" (%1)").arg(ToQStr(descriptor.filter.filter)); + + RDTreeWidgetItem *node = new RDTreeWidgetItem( + {slotname, descriptor.object, addressing, filter, + QFormatStr("%1 - %2") + .arg(descriptor.minLOD == -FLT_MAX ? lit("0") : QString::number(descriptor.minLOD)) + .arg(descriptor.maxLOD == FLT_MAX ? lit("FLT_MAX") : QString::number(descriptor.maxLOD)), + descriptor.mipBias}); + + if(!filledSlot) + setEmptyRow(node); + + if(!usedSlot) + setInactiveRow(node); + + samplers->addTopLevelItem(node); + } +} + +void D3D11PipelineStateViewer::addCBufferRow(const Descriptor &descriptor, uint32_t reg, + const ConstantBlock *shaderBind, bool usedSlot, + RDTreeWidget *cbuffers) +{ + bool filledSlot = descriptor.resource != ResourceId(); + if(showNode(usedSlot, filledSlot)) + { + ulong length = 0; + int numvars = shaderBind ? shaderBind->variables.count() : 0; + uint32_t bytesize = shaderBind ? shaderBind->byteSize : 0; + + BufferDescription *buf = m_Ctx.GetBuffer(descriptor.resource); + + if(buf) + length = buf->length; + + QString slotname = QString::number(reg); + + if(shaderBind && !shaderBind->name.empty()) + slotname += lit(": ") + shaderBind->name; + + 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; + + QString vecrange = QFormatStr("%1 - %2") + .arg(Formatter::HumanFormat(descriptor.byteOffset, Formatter::OffsetSize)) + .arg(Formatter::HumanFormat(descriptor.byteOffset + descriptor.byteSize, + Formatter::OffsetSize)); + + RDTreeWidgetItem *node = + new RDTreeWidgetItem({slotname, descriptor.resource, vecrange, sizestr, QString()}); + + node->setTag(QVariant::fromValue(reg)); + + if(!filledSlot) + setEmptyRow(node); + + if(!usedSlot) + setInactiveRow(node); + + cbuffers->addTopLevelItem(node); + } +} + bool D3D11PipelineStateViewer::showNode(bool usedSlot, bool filledSlot) { const bool showUnused = ui->showUnused->isChecked(); @@ -866,6 +1017,39 @@ bool D3D11PipelineStateViewer::showNode(bool usedSlot, bool filledSlot) return false; } +const Descriptor &D3D11PipelineStateViewer::FindDescriptor(ShaderStage stage, + DescriptorCategory category, uint32_t reg) +{ + const ShaderStageMask mask = MaskForStage(stage); + // locations for D3D11 descriptors should have an accurate category for us to look up + for(size_t i = 0; i < m_Locations.size(); i++) + { + if((m_Locations[i].stageMask & mask) && m_Locations[i].category == category && + m_Locations[i].fixedBindNumber == reg) + { + return m_Descriptors[i]; + } + } + + static Descriptor empty; + return empty; +} + +bool D3D11PipelineStateViewer::HasAccess(ShaderStage stage, DescriptorCategory category, + uint32_t index) +{ + for(const DescriptorAccess &access : m_Ctx.CurPipelineState().GetDescriptorAccess()) + { + if(access.stage == stage && CategoryForDescriptorType(access.type) == category && + access.index == index) + { + return true; + } + } + + return false; +} + const D3D11Pipe::Shader *D3D11PipelineStateViewer::stageForSender(QWidget *widget) { if(!m_Ctx.IsCaptureLoaded()) @@ -997,7 +1181,6 @@ void D3D11PipelineStateViewer::setShaderState(const D3D11Pipe::Shader &stage, RD RDTreeWidget *cbuffers, RDTreeWidget *classes) { ShaderReflection *shaderDetails = stage.reflection; - const ShaderBindpointMapping &mapping = stage.bindpointMapping; QString shText = ToQStr(stage.resourceId); @@ -1013,229 +1196,6 @@ void D3D11PipelineStateViewer::setShaderState(const D3D11Pipe::Shader &stage, RD shader->setText(shText); - int vs = 0; - - vs = resources->verticalScrollBar()->value(); - resources->beginUpdate(); - resources->clear(); - for(int i = 0; i < stage.srvs.count(); i++) - { - const ShaderResource *shaderInput = NULL; - const Bindpoint *map = NULL; - - if(shaderDetails) - { - for(int b = 0; b < shaderDetails->readOnlyResources.count(); b++) - { - const ShaderResource &res = shaderDetails->readOnlyResources[b]; - const Bindpoint &bind = mapping.readOnlyResources[b]; - - if(bind.bind == i) - { - shaderInput = &res; - map = &bind; - break; - } - } - } - - addResourceRow(D3D11ViewTag(D3D11ViewTag::SRV, i, stage.srvs[i]), shaderInput, map, resources); - } - resources->clearSelection(); - resources->endUpdate(); - resources->verticalScrollBar()->setValue(vs); - - vs = samplers->verticalScrollBar()->value(); - samplers->beginUpdate(); - samplers->clear(); - for(int i = 0; i < stage.samplers.count(); i++) - { - const D3D11Pipe::Sampler &s = stage.samplers[i]; - - const ShaderSampler *shaderInput = NULL; - const Bindpoint *map = NULL; - - if(shaderDetails) - { - for(int b = 0; b < shaderDetails->samplers.count(); b++) - { - const ShaderSampler &res = shaderDetails->samplers[b]; - const Bindpoint &bind = mapping.samplers[b]; - - if(bind.bind == i) - { - shaderInput = &res; - map = &bind; - break; - } - } - } - - bool filledSlot = s.resourceId != ResourceId(); - bool usedSlot = (map && map->used); - - if(showNode(usedSlot, filledSlot)) - { - QString slotname = QString::number(i); - - if(shaderInput && !shaderInput->name.empty()) - slotname += lit(": ") + shaderInput->name; - - QString borderColor = QFormatStr("%1, %2, %3, %4") - .arg(s.borderColor[0]) - .arg(s.borderColor[1]) - .arg(s.borderColor[2]) - .arg(s.borderColor[3]); - - QString addressing; - - QString addPrefix; - QString addVal; - - QString addr[] = {ToQStr(s.addressU, GraphicsAPI::D3D11), - ToQStr(s.addressV, GraphicsAPI::D3D11), - ToQStr(s.addressW, GraphicsAPI::D3D11)}; - - // 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(s.UseBorder()) - addressing += QFormatStr("<%1>").arg(borderColor); - - QString filter = ToQStr(s.filter); - - if(s.maxAnisotropy > 1) - filter += QFormatStr(" %1x").arg(s.maxAnisotropy); - - if(s.filter.filter == FilterFunction::Comparison) - filter += QFormatStr(" (%1)").arg(ToQStr(s.compareFunction)); - else if(s.filter.filter != FilterFunction::Normal) - filter += QFormatStr(" (%1)").arg(ToQStr(s.filter.filter)); - - RDTreeWidgetItem *node = new RDTreeWidgetItem( - {slotname, s.resourceId, addressing, filter, - QFormatStr("%1 - %2") - .arg(s.minLOD == -FLT_MAX ? lit("0") : QString::number(s.minLOD)) - .arg(s.maxLOD == FLT_MAX ? lit("FLT_MAX") : QString::number(s.maxLOD)), - s.mipLODBias}); - - if(!filledSlot) - setEmptyRow(node); - - if(!usedSlot) - setInactiveRow(node); - - samplers->addTopLevelItem(node); - } - } - - samplers->clearSelection(); - samplers->endUpdate(); - samplers->verticalScrollBar()->setValue(vs); - - vs = cbuffers->verticalScrollBar()->value(); - cbuffers->beginUpdate(); - cbuffers->clear(); - for(int i = 0; i < stage.constantBuffers.count(); i++) - { - const D3D11Pipe::ConstantBuffer &b = stage.constantBuffers[i]; - - const ConstantBlock *shaderCBuf = NULL; - const Bindpoint *map = NULL; - - if(shaderDetails) - { - for(int cb = 0; cb < shaderDetails->constantBlocks.count(); cb++) - { - const ConstantBlock &cbuf = shaderDetails->constantBlocks[cb]; - const Bindpoint &bind = mapping.constantBlocks[cb]; - - if(bind.bind == i) - { - shaderCBuf = &cbuf; - map = &bind; - break; - } - } - } - - bool filledSlot = b.resourceId != ResourceId(); - bool usedSlot = (map && map->used); - - if(showNode(usedSlot, filledSlot)) - { - ulong length = 0; - int numvars = shaderCBuf ? shaderCBuf->variables.count() : 0; - uint32_t bytesize = shaderCBuf ? shaderCBuf->byteSize : 0; - - BufferDescription *buf = m_Ctx.GetBuffer(b.resourceId); - - if(buf) - length = buf->length; - - QString slotname = QString::number(i); - - if(shaderCBuf && !shaderCBuf->name.empty()) - slotname += lit(": ") + shaderCBuf->name; - - 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; - - QString vecrange = - QFormatStr("%1 - %2") - .arg(Formatter::HumanFormat(b.vecOffset, Formatter::OffsetSize)) - .arg(Formatter::HumanFormat(b.vecOffset + b.vecCount, Formatter::OffsetSize)); - - RDTreeWidgetItem *node = - new RDTreeWidgetItem({slotname, b.resourceId, vecrange, sizestr, QString()}); - - node->setTag(QVariant::fromValue(i)); - - if(!filledSlot) - setEmptyRow(node); - - if(!usedSlot) - setInactiveRow(node); - - cbuffers->addTopLevelItem(node); - } - } - cbuffers->clearSelection(); - cbuffers->endUpdate(); - cbuffers->verticalScrollBar()->setValue(vs); - - vs = classes->verticalScrollBar()->value(); - classes->beginUpdate(); - classes->clear(); for(int i = 0; i < stage.classInstances.count(); i++) { QString interfaceName = lit("Interface %1").arg(i); @@ -1245,11 +1205,6 @@ void D3D11PipelineStateViewer::setShaderState(const D3D11Pipe::Shader &stage, RD classes->addTopLevelItem(new RDTreeWidgetItem({i, interfaceName, stage.classInstances[i]})); } - classes->clearSelection(); - classes->endUpdate(); - classes->verticalScrollBar()->setValue(vs); - - classes->parentWidget()->setVisible(!stage.classInstances.empty()); } void D3D11PipelineStateViewer::setState() @@ -1602,19 +1557,6 @@ void D3D11PipelineStateViewer::setState() ui->iaBuffers->endUpdate(); ui->iaBuffers->verticalScrollBar()->setValue(vs); - 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); - QToolButton *shaderButtons[] = { ui->vsShaderViewButton, ui->hsShaderViewButton, ui->dsShaderViewButton, ui->gsShaderViewButton, ui->psShaderViewButton, ui->csShaderViewButton, @@ -1638,38 +1580,236 @@ void D3D11PipelineStateViewer::setState() ui->iaBytecodeViewButton->setEnabled(true); - vs = ui->csUAVs->verticalScrollBar()->value(); - ui->csUAVs->beginUpdate(); - ui->csUAVs->clear(); - for(int i = 0; i < state.computeShader.uavs.count(); i++) + //////////////////////////////////////////////// + // Main iteration over descriptor storage + + bool targets[32] = {}; + { - const ShaderResource *shaderInput = NULL; - const Bindpoint *map = NULL; + 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, + }; - const D3D11Pipe::Shader &cs = state.computeShader; - - if(cs.reflection) + rdcarray outputs = m_Ctx.CurPipelineState().GetOutputTargetDescriptors(); + for(uint32_t i = 0; i < outputs.size(); i++) { - for(int b = 0; b < cs.reflection->readWriteResources.count(); b++) - { - const ShaderResource &res = cs.reflection->readWriteResources[b]; - const Bindpoint &bind = cs.bindpointMapping.readWriteResources[b]; + addResourceRow(D3D11ViewTag(D3D11ViewTag::OMTarget, i, outputs[i]), NULL, NULL, + ui->targetOutputs); - if(bind.bind == i) + 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()) + 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]) { - shaderInput = &res; - map = &bind; - break; + 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::UAV, i, state.computeShader.uavs[i]), shaderInput, - map, ui->csUAVs); + addResourceRow( + D3D11ViewTag(D3D11ViewTag::OMDepth, 0, m_Ctx.CurPipelineState().GetDepthTargetDescriptor()), + NULL, NULL, 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); } - ui->csUAVs->clearSelection(); - ui->csUAVs->endUpdate(); - ui->csUAVs->verticalScrollBar()->setValue(vs); bool streamoutSet = false; vs = ui->gsStreamOut->verticalScrollBar()->value(); @@ -1807,62 +1947,6 @@ void D3D11PipelineStateViewer::setState() //////////////////////////////////////////////// // Output Merger - bool targets[32] = {}; - - vs = ui->targetOutputs->verticalScrollBar()->value(); - ui->targetOutputs->beginUpdate(); - ui->targetOutputs->clear(); - { - for(int i = 0; i < state.outputMerger.renderTargets.count(); i++) - { - addResourceRow(D3D11ViewTag(D3D11ViewTag::OMTarget, i, state.outputMerger.renderTargets[i]), - NULL, NULL, ui->targetOutputs); - - if(state.outputMerger.renderTargets[i].resourceResourceId != ResourceId()) - targets[i] = true; - } - - for(int i = 0; i < state.outputMerger.uavs.count(); i++) - { - const ShaderResource *shaderInput = NULL; - const Bindpoint *map = NULL; - - // 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. - const D3D11Pipe::Shader *nonCS[] = {&state.vertexShader, &state.domainShader, &state.hullShader, - &state.geometryShader, &state.pixelShader}; - for(const D3D11Pipe::Shader *stage : nonCS) - { - if(stage->reflection) - { - for(int b = 0; b < stage->reflection->readWriteResources.count(); b++) - { - const ShaderResource &res = stage->reflection->readWriteResources[b]; - const Bindpoint &bind = stage->bindpointMapping.readWriteResources[b]; - - if(bind.bind == i + (int)state.outputMerger.uavStartSlot) - { - shaderInput = &res; - map = &bind; - break; - } - } - } - } - addResourceRow(D3D11ViewTag(D3D11ViewTag::UAV, i + (int)state.outputMerger.uavStartSlot, - state.outputMerger.uavs[i]), - shaderInput, map, ui->targetOutputs); - } - - addResourceRow(D3D11ViewTag(D3D11ViewTag::OMDepth, 0, state.outputMerger.depthTarget), NULL, - NULL, ui->targetOutputs); - } - ui->targetOutputs->clearSelection(); - ui->targetOutputs->endUpdate(); - ui->targetOutputs->verticalScrollBar()->setValue(vs); - vs = ui->blends->verticalScrollBar()->value(); ui->blends->beginUpdate(); ui->blends->clear(); @@ -2074,9 +2158,9 @@ void D3D11PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, in else if(tag.canConvert()) { D3D11ViewTag view = tag.value(); - tex = m_Ctx.GetTexture(view.res.resourceResourceId); - buf = m_Ctx.GetBuffer(view.res.resourceResourceId); - typeCast = view.res.viewFormat.compType; + tex = m_Ctx.GetTexture(view.desc.resource); + buf = m_Ctx.GetBuffer(view.desc.resource); + typeCast = view.desc.format.compType; } if(tex) @@ -2102,7 +2186,7 @@ void D3D11PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, in { D3D11ViewTag view; - view.res.resourceResourceId = buf->resourceId; + view.desc.resource = buf->resourceId; if(tag.canConvert()) view = tag.value(); @@ -2110,10 +2194,10 @@ void D3D11PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, in uint64_t offs = 0; uint64_t size = buf->length; - if(view.res.resourceResourceId != ResourceId()) + if(view.desc.resource != ResourceId()) { - offs = uint64_t(view.res.firstElement) * view.res.elementByteSize; - size = uint64_t(view.res.numElements) * view.res.elementByteSize; + offs = view.desc.byteOffset; + size = view.desc.byteSize; } else { @@ -2137,7 +2221,7 @@ void D3D11PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, in const ShaderResource *shaderRes = NULL; - int bind = view.index; + uint32_t reg = view.index; // for OM UAVs these can be bound to any non-CS stage, so make sure // we have the right shader details for it. @@ -2158,7 +2242,7 @@ void D3D11PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, in { for(const ShaderResource &res : searchstage->reflection->readWriteResources) { - if(!res.isTexture && res.bindPoint == bind) + if(!res.isTexture && res.fixedBindNumber == reg) { stage = searchstage; break; @@ -2174,14 +2258,9 @@ void D3D11PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, in ? stage->reflection->readOnlyResources : stage->reflection->readWriteResources; - const rdcarray &bindArray = view.type == D3D11ViewTag::SRV - ? stage->bindpointMapping.readOnlyResources - : stage->bindpointMapping.readWriteResources; - for(const ShaderResource &res : resArray) { - if(!res.isTexture && res.bindPoint < bindArray.count() && - bindArray[res.bindPoint].bind == bind) + if(!res.isTexture && res.fixedBindNumber == reg) { shaderRes = &res; break; @@ -2192,13 +2271,13 @@ void D3D11PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, in if(shaderRes) { format = BufferFormatter::GetBufferFormatString(Packing::D3DUAV, stage->resourceId, - *shaderRes, view.res.viewFormat); + *shaderRes, view.desc.format); - if(view.res.bufferFlags & D3DBufferViewFlags::Raw) + if(view.desc.flags & DescriptorFlags::RawBuffer) format = lit("xint"); } - IBufferViewer *viewer = m_Ctx.ViewBuffer(offs, size, view.res.resourceResourceId, format); + IBufferViewer *viewer = m_Ctx.ViewBuffer(offs, size, view.desc.resource, format); m_Ctx.AddDockWindow(viewer->Widget(), DockReference::AddTo, this); } @@ -2213,38 +2292,27 @@ void D3D11PipelineStateViewer::cbuffer_itemActivated(RDTreeWidgetItem *item, int QVariant tag = item->tag(); - if(!tag.canConvert()) + if(!tag.canConvert()) return; - int cb = tag.value(); + uint32_t reg = tag.value(); - int cbufIdx = -1; + uint32_t index = ~0U; + for(uint32_t i = 0; i < stage->reflection->constantBlocks.size(); i++) + if(stage->reflection->constantBlocks[i].fixedBindNumber == reg) + index = i; - for(int i = 0; i < stage->bindpointMapping.constantBlocks.count(); i++) + if(index == ~0U) { - if(stage->bindpointMapping.constantBlocks[i].bind == cb) - { - cbufIdx = i; - break; - } - } + const Descriptor &desc = FindDescriptor(stage->stage, DescriptorCategory::ConstantBlock, reg); - if(cbufIdx == -1) - { - // unused cbuffer, open regular buffer viewer - if(cb >= stage->constantBuffers.count()) - return; - - const D3D11Pipe::ConstantBuffer &bind = stage->constantBuffers[cb]; - - IBufferViewer *viewer = m_Ctx.ViewBuffer(bind.vecOffset * sizeof(float) * 4, - bind.vecCount * sizeof(float) * 4, bind.resourceId); + IBufferViewer *viewer = m_Ctx.ViewBuffer(desc.byteOffset, desc.byteSize, desc.resource); m_Ctx.AddDockWindow(viewer->Widget(), DockReference::AddTo, this); return; } - IBufferViewer *prev = m_Ctx.ViewConstantBuffer(stage->stage, cbufIdx, 0); + IBufferViewer *prev = m_Ctx.ViewConstantBuffer(stage->stage, index, 0); m_Ctx.AddDockWindow(prev->Widget(), DockReference::TransientPopupArea, this, 0.3f); } @@ -2435,7 +2503,7 @@ void D3D11PipelineStateViewer::shaderSave_clicked() m_Common.SaveShaderFile(shaderDetails); } -QVariantList D3D11PipelineStateViewer::exportViewHTML(const D3D11Pipe::View &view, int i, +QVariantList D3D11PipelineStateViewer::exportViewHTML(const Descriptor &view, uint32_t reg, ShaderReflection *refl, const QString &extraParams) { @@ -2447,7 +2515,7 @@ QVariantList D3D11PipelineStateViewer::exportViewHTML(const D3D11Pipe::View &vie { for(const ShaderResource &bind : refl->readOnlyResources) { - if(bind.bindPoint == i) + if(bind.fixedBindNumber == reg) { shaderInput = &bind; break; @@ -2455,7 +2523,7 @@ QVariantList D3D11PipelineStateViewer::exportViewHTML(const D3D11Pipe::View &vie } for(const ShaderResource &bind : refl->readWriteResources) { - if(bind.bindPoint == i) + if(bind.fixedBindNumber == reg) { shaderInput = &bind; rw = true; @@ -2464,19 +2532,18 @@ QVariantList D3D11PipelineStateViewer::exportViewHTML(const D3D11Pipe::View &vie } } - QString name = view.resourceResourceId == ResourceId() - ? tr("Empty") - : QString(m_Ctx.GetResourceName(view.resourceResourceId)); + QString name = + view.resource == ResourceId() ? tr("Empty") : QString(m_Ctx.GetResourceName(view.resource)); QString typeName = tr("Unknown"); QString format = tr("Unknown"); uint64_t w = 1; uint32_t h = 1, d = 1; uint32_t a = 0; - QString viewFormat = view.viewFormat.Name(); + QString viewFormat = view.format.Name(); - TextureDescription *tex = m_Ctx.GetTexture(view.resourceResourceId); - BufferDescription *buf = m_Ctx.GetBuffer(view.resourceResourceId); + TextureDescription *tex = m_Ctx.GetTexture(view.resource); + BufferDescription *buf = m_Ctx.GetBuffer(view.resource); QString viewParams; @@ -2508,10 +2575,10 @@ QVariantList D3D11PipelineStateViewer::exportViewHTML(const D3D11Pipe::View &vie h = 0; d = 0; a = 0; - format = view.viewFormat.Name(); + format = view.format.Name(); typeName = lit("Buffer"); - if(view.bufferFlags & D3DBufferViewFlags::Raw) + if(view.flags & DescriptorFlags::RawBuffer) { typeName = rw ? lit("RWByteAddressBuffer") : lit("ByteAddressBuffer"); } @@ -2523,14 +2590,14 @@ QVariantList D3D11PipelineStateViewer::exportViewHTML(const D3D11Pipe::View &vie .arg(buf->length / view.elementByteSize); } - if(view.bufferFlags & D3DBufferViewFlags::Append || view.bufferFlags & D3DBufferViewFlags::Counter) + if(view.flags & DescriptorFlags::AppendBuffer || view.flags & DescriptorFlags::CounterBuffer) { typeName += tr(" (Count: %1)").arg(view.bufferStructCount); } if(shaderInput && !shaderInput->isTexture) { - if(view.viewFormat.compType == CompType::Typeless) + if(view.format.compType == CompType::Typeless) { if(shaderInput->variableType.baseType == VarType::Struct) viewFormat = format = lit("struct ") + shaderInput->variableType.name; @@ -2539,14 +2606,14 @@ QVariantList D3D11PipelineStateViewer::exportViewHTML(const D3D11Pipe::View &vie } else { - format = view.viewFormat.Name(); + format = view.format.Name(); } } - viewParams = tr("First Element: %1, Num Elements %2, Flags %3") - .arg(view.firstElement) - .arg(view.numElements) - .arg(ToQStr(view.bufferFlags)); + viewParams = tr("Byte Offset: %1, Byte Size %2, Flags %3") + .arg(view.byteOffset) + .arg(view.byteSize) + .arg(ToQStr(view.flags)); } if(viewParams.isEmpty()) @@ -2554,8 +2621,8 @@ QVariantList D3D11PipelineStateViewer::exportViewHTML(const D3D11Pipe::View &vie else viewParams += lit(", ") + extraParams; - return {i, name, ToQStr(view.type), typeName, (qulonglong)w, h, - d, a, viewFormat, format, viewParams}; + return {reg, name, ToQStr(view.textureType), typeName, (qulonglong)w, h, d, a, viewFormat, + format, viewParams}; } void D3D11PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D11Pipe::InputAssembly &ia) @@ -2694,12 +2761,20 @@ void D3D11PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D11Pipe QList rows; - for(int i = 0; i < sh.srvs.count(); i++) + // do a plain search here, this is not super efficient but it's simpler + for(uint32_t i = 0; i < m_Locations.size(); i++) { - if(sh.srvs[i].viewResourceId == ResourceId()) + if(!(m_Locations[i].stageMask & MaskForStage(sh.stage))) continue; - rows.push_back(exportViewHTML(sh.srvs[i], i, shaderDetails, QString())); + if(m_Locations[i].category != DescriptorCategory::ReadOnlyResource) + continue; + + if(m_Descriptors[i].view == ResourceId()) + continue; + + rows.push_back(exportViewHTML(m_Descriptors[i], m_Locations[i].fixedBindNumber, shaderDetails, + QString())); } m_Common.exportHTMLTable(xml, @@ -2727,12 +2802,20 @@ void D3D11PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D11Pipe QList rows; - for(int i = 0; i < sh.uavs.count(); i++) + // do a plain search here, this is not super efficient but it's simpler + for(uint32_t i = 0; i < m_Locations.size(); i++) { - if(sh.uavs[i].viewResourceId == ResourceId()) + if(!(m_Locations[i].stageMask & MaskForStage(sh.stage))) continue; - rows.push_back(exportViewHTML(sh.uavs[i], i, shaderDetails, QString())); + if(m_Locations[i].category != DescriptorCategory::ReadWriteResource) + continue; + + if(m_Descriptors[i].view == ResourceId()) + continue; + + rows.push_back(exportViewHTML(m_Descriptors[i], m_Locations[i].fixedBindNumber, shaderDetails, + QString())); } m_Common.exportHTMLTable(xml, @@ -2759,18 +2842,21 @@ void D3D11PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D11Pipe QList rows; - for(int i = 0; i < sh.samplers.count(); i++) - { - const D3D11Pipe::Sampler &s = sh.samplers[i]; + const rdcarray &samplers = + m_Ctx.CurPipelineState().GetSamplerDescriptors(sh.stage); - if(s.resourceId == ResourceId()) + for(int i = 0; i < samplers.count(); i++) + { + const SamplerDescriptor &s = samplers[i].sampler; + + if(s.object == ResourceId()) continue; QString borderColor = QFormatStr("%1, %2, %3, %4") - .arg(s.borderColor[0]) - .arg(s.borderColor[1]) - .arg(s.borderColor[2]) - .arg(s.borderColor[3]); + .arg(s.borderColorValue.floatValue[0]) + .arg(s.borderColorValue.floatValue[1]) + .arg(s.borderColorValue.floatValue[2]) + .arg(s.borderColorValue.floatValue[3]); QString addressing; @@ -2804,7 +2890,7 @@ void D3D11PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D11Pipe rows.push_back({i, addressing, borderColor, ToQStr(s.compareFunction), ToQStr(s.filter), s.maxAnisotropy, s.minLOD == -FLT_MAX ? lit("0") : QString::number(s.minLOD), - s.maxLOD == FLT_MAX ? lit("FLT_MAX") : QString::number(s.maxLOD), s.mipLODBias}); + s.maxLOD == FLT_MAX ? lit("FLT_MAX") : QString::number(s.maxLOD), s.mipBias}); } m_Common.exportHTMLTable(xml, @@ -2829,38 +2915,42 @@ void D3D11PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D11Pipe QList rows; - for(int i = 0; i < sh.constantBuffers.count(); i++) + const rdcarray &cblocks = + m_Ctx.CurPipelineState().GetConstantBlockDescriptors(sh.stage); + + for(int i = 0; i < cblocks.count(); i++) { ConstantBlock *shaderCBuf = NULL; - if(sh.constantBuffers[i].resourceId == ResourceId()) + if(cblocks[i].descriptor.resource == ResourceId()) continue; - if(shaderDetails && i < shaderDetails->constantBlocks.count() && - !shaderDetails->constantBlocks[i].name.isEmpty()) - shaderCBuf = &shaderDetails->constantBlocks[i]; + if(shaderDetails && cblocks[i].access.index < shaderDetails->constantBlocks.count() && + !shaderDetails->constantBlocks[cblocks[i].access.index].name.isEmpty()) + shaderCBuf = &shaderDetails->constantBlocks[cblocks[i].access.index]; - QString name = m_Ctx.GetResourceName(sh.constantBuffers[i].resourceId); + QString name = m_Ctx.GetResourceName(cblocks[i].descriptor.resource); uint64_t length = 1; int numvars = shaderCBuf ? shaderCBuf->variables.count() : 0; uint32_t byteSize = shaderCBuf ? shaderCBuf->byteSize : 0; - if(sh.constantBuffers[i].resourceId == ResourceId()) + if(cblocks[i].descriptor.resource == ResourceId()) { name = tr("Empty"); length = 0; } - BufferDescription *buf = m_Ctx.GetBuffer(sh.constantBuffers[i].resourceId); + BufferDescription *buf = m_Ctx.GetBuffer(cblocks[i].descriptor.resource); if(buf) length = buf->length; - rows.push_back({i, name, sh.constantBuffers[i].vecOffset, sh.constantBuffers[i].vecCount, - numvars, byteSize, (qulonglong)length}); + rows.push_back({i, name, (qulonglong)cblocks[i].descriptor.byteOffset, + (qulonglong)cblocks[i].descriptor.byteSize, numvars, byteSize, + (qulonglong)length}); } m_Common.exportHTMLTable(xml, - {tr("Slot"), tr("Buffer"), tr("Vector Offset"), tr("Vector Count"), + {tr("Slot"), tr("Buffer"), tr("Byte Offset"), tr("Byte Range"), tr("Number of Variables"), tr("Bytes Needed"), tr("Bytes Provided")}, rows); } @@ -3118,12 +3208,13 @@ void D3D11PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D11Pipe QList rows; - for(int i = 0; i < om.renderTargets.count(); i++) + rdcarray rts = m_Ctx.CurPipelineState().GetOutputTargetDescriptors(); + for(int i = 0; i < rts.count(); i++) { - if(om.renderTargets[i].viewResourceId == ResourceId()) + if(rts[i].view == ResourceId()) continue; - rows.push_back(exportViewHTML(om.renderTargets[i], i, NULL, QString())); + rows.push_back(exportViewHTML(rts[i], i, NULL, QString())); } m_Common.exportHTMLTable(xml, @@ -3143,44 +3234,54 @@ void D3D11PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D11Pipe rows); } - if(!om.uavs.isEmpty() && om.uavs[0].viewResourceId != ResourceId()) { - xml.writeStartElement(lit("h3")); - xml.writeCharacters(tr("Unordered Access Views")); - xml.writeEndElement(); - QList rows; - uint32_t i = 0; - - for(; i < om.uavStartSlot; i++) - rows.push_back({i, tr("Empty"), QString(), QString(), QString(), QString(), 0, 0, 0, 0, - QString(), QString(), QString()}); - - for(; i < (uint32_t)om.renderTargets.count(); i++) + // do a plain search here, this is not super efficient but it's simpler + for(uint32_t i = 0; i < m_Locations.size(); i++) { - if(om.uavs[i - om.uavStartSlot].viewResourceId == ResourceId()) + // only look for PS UAVs + if(m_Locations[i].category != DescriptorCategory::ReadWriteResource || + !(m_Locations[i].stageMask & ShaderStageMask::Pixel)) continue; - rows.push_back(exportViewHTML(om.uavs[i - om.uavStartSlot], i, + if(m_Descriptors[i].view == ResourceId()) + continue; + + // skip any descriptors from before the first valid OM UAV + if(m_Locations[i].fixedBindNumber < om.uavStartSlot) + continue; + + rows.push_back(exportViewHTML(m_Descriptors[i], m_Locations[i].fixedBindNumber, m_Ctx.CurD3D11PipelineState()->pixelShader.reflection, QString())); } - m_Common.exportHTMLTable(xml, - { - tr("Slot"), - tr("Name"), - tr("View Type"), - tr("Resource Type"), - tr("Width"), - tr("Height"), - tr("Depth"), - tr("Array Size"), - tr("View Format"), - tr("Resource Format"), - tr("View Parameters"), - }, - rows); + if(!rows.isEmpty()) + { + xml.writeStartElement(lit("h3")); + xml.writeCharacters(tr("Unordered Access Views")); + xml.writeEndElement(); + + for(uint32_t i = 0; i < om.uavStartSlot; i++) + rows.insert(0, {i, tr("Empty"), QString(), QString(), QString(), QString(), 0, 0, 0, 0, + QString(), QString(), QString()}); + + m_Common.exportHTMLTable(xml, + { + tr("Slot"), + tr("Name"), + tr("View Type"), + tr("Resource Type"), + tr("Width"), + tr("Height"), + tr("Depth"), + tr("Array Size"), + tr("View Format"), + tr("Resource Format"), + tr("View Parameters"), + }, + rows); + } } { @@ -3199,7 +3300,8 @@ void D3D11PipelineStateViewer::exportHTML(QXmlStreamWriter &xml, const D3D11Pipe else if(om.stencilReadOnly) extra = tr("Stencil Read-Only"); - rows.push_back(exportViewHTML(om.depthTarget, 0, NULL, extra)); + rows.push_back( + exportViewHTML(m_Ctx.CurPipelineState().GetDepthTargetDescriptor(), 0, NULL, extra)); m_Common.exportHTMLTable(xml, { @@ -3291,8 +3393,6 @@ void D3D11PipelineStateViewer::on_computeDebugSelector_clicked() const ShaderReflection *shaderDetails = m_Ctx.CurPipelineState().GetShaderReflection(ShaderStage::Compute); - const ShaderBindpointMapping &bindMapping = - m_Ctx.CurPipelineState().GetBindpointMapping(ShaderStage::Compute); if(!shaderDetails) return; diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.h index 927e6c833..c8fed589f 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.h @@ -92,15 +92,19 @@ private: void setShaderState(const D3D11Pipe::Shader &stage, RDLabel *shader, RDTreeWidget *tex, RDTreeWidget *samp, RDTreeWidget *cbuffer, RDTreeWidget *classes); - void addResourceRow(const D3D11ViewTag &view, const ShaderResource *shaderInput, - const Bindpoint *map, RDTreeWidget *resources); + void addResourceRow(const D3D11ViewTag &view, const ShaderResource *shaderBind, bool usedSlot, + RDTreeWidget *resources); + void addSamplerRow(const SamplerDescriptor &s, uint32_t reg, const ShaderSampler *shaderBind, + bool usedSlot, RDTreeWidget *samplers); + void addCBufferRow(const Descriptor &b, uint32_t reg, const ConstantBlock *shaderBind, + bool usedSlot, RDTreeWidget *cbuffers); void clearShaderState(RDLabel *shader, RDTreeWidget *tex, RDTreeWidget *samp, RDTreeWidget *cbuffer, RDTreeWidget *classes); void setState(); void clearState(); - QVariantList exportViewHTML(const D3D11Pipe::View &view, int i, ShaderReflection *refl, + QVariantList exportViewHTML(const Descriptor &view, uint32_t reg, ShaderReflection *refl, const QString &extraParams); void exportHTML(QXmlStreamWriter &xml, const D3D11Pipe::InputAssembly &ia); void exportHTML(QXmlStreamWriter &xml, const D3D11Pipe::Shader &sh); @@ -114,14 +118,21 @@ private: const D3D11Pipe::Shader *stageForSender(QWidget *widget); - bool HasImportantViewParams(const D3D11Pipe::View &view, TextureDescription *tex); - bool HasImportantViewParams(const D3D11Pipe::View &view, BufferDescription *buf); + const Descriptor &FindDescriptor(ShaderStage stage, DescriptorCategory category, uint32_t reg); + bool HasAccess(ShaderStage stage, DescriptorCategory category, uint32_t index); + + bool HasImportantViewParams(const Descriptor &view, TextureDescription *tex); + bool HasImportantViewParams(const Descriptor &view, BufferDescription *buf); void setViewDetails(RDTreeWidgetItem *node, const D3D11ViewTag &view, TextureDescription *tex); void setViewDetails(RDTreeWidgetItem *node, const D3D11ViewTag &view, BufferDescription *buf); bool showNode(bool usedSlot, bool filledSlot); + rdcarray m_Locations; + rdcarray m_Descriptors; + rdcarray m_SamplerDescriptors; + // keep track of the VB nodes (we want to be able to highlight them easily on hover) QList m_VBNodes; // list of empty VB nodes that shouldn't be highlighted on hover diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 8559b5b23..4c62f38ac 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -4803,6 +4803,29 @@ constexpr inline ShaderStageMask MaskForStage(ShaderStage stage) return ShaderStageMask(1 << uint32_t(stage)); } +DOCUMENT(R"(For a shader stage mask that only covers one shader stage, return the shader stage. + +.. note:: + If the shader stage mask covers multiple stages, only the first matching stage will be returned. + If the mask is empty, :data:`ShaderStage.Count` will be returned. + +:param ShaderStageMask stageMask: The shader stage mask. +:return: The first shader stage covered by the mask. +:rtype: ShaderStage +)"); +constexpr inline ShaderStage FirstStageForMask(ShaderStageMask stageMask) +{ + return (stageMask & ShaderStageMask::Vertex) ? ShaderStage::Vertex + : (stageMask & ShaderStageMask::Hull) ? ShaderStage::Hull + : (stageMask & ShaderStageMask::Domain) ? ShaderStage::Domain + : (stageMask & ShaderStageMask::Geometry) ? ShaderStage::Geometry + : (stageMask & ShaderStageMask::Pixel) ? ShaderStage::Pixel + : (stageMask & ShaderStageMask::Compute) ? ShaderStage::Compute + : (stageMask & ShaderStageMask::Task) ? ShaderStage::Task + : (stageMask & ShaderStageMask::Mesh) ? ShaderStage::Mesh + : ShaderStage::Count; +} + DOCUMENT(R"(A set of flags for events that may occur while debugging a shader .. data:: NoEvent