From 43fa3cd94b77ed600bce655aa78d613b2cd4da89 Mon Sep 17 00:00:00 2001 From: baldurk Date: Mon, 9 May 2022 14:51:41 +0100 Subject: [PATCH] Use BufferViewer for viewing constant buffers * Unifying these views means that constant buffers have all the same reformatting and it avoids having multiple paths for what is now effectively the same control (a buffer can either have fixed data, repeating data, or both) --- qrenderdoc/Code/BufferFormatter.cpp | 184 ++++++-- qrenderdoc/Code/CaptureContext.cpp | 18 +- qrenderdoc/Code/CaptureContext.h | 3 +- qrenderdoc/Code/Interface/QRDInterface.h | 28 +- qrenderdoc/Code/QRDUtils.h | 3 +- qrenderdoc/Widgets/BufferFormatSpecifier.cpp | 5 + qrenderdoc/Widgets/BufferFormatSpecifier.h | 1 + qrenderdoc/Widgets/Extended/RDHeaderView.cpp | 3 + qrenderdoc/Windows/BufferViewer.cpp | 315 +++++++++++++- qrenderdoc/Windows/BufferViewer.h | 34 ++ qrenderdoc/Windows/BufferViewer.ui | 16 + .../Windows/ConstantBufferPreviewer.cpp | 401 ------------------ qrenderdoc/Windows/ConstantBufferPreviewer.h | 94 ---- qrenderdoc/Windows/ConstantBufferPreviewer.ui | 249 ----------- .../D3D11PipelineStateViewer.cpp | 2 +- .../D3D12PipelineStateViewer.cpp | 2 +- .../PipelineState/GLPipelineStateViewer.cpp | 2 +- .../VulkanPipelineStateViewer.cpp | 2 +- qrenderdoc/Windows/PythonShell.cpp | 6 +- qrenderdoc/qrenderdoc.pro | 3 - qrenderdoc/qrenderdoc_local.vcxproj | 15 - qrenderdoc/qrenderdoc_local.vcxproj.filters | 15 - renderdoc/api/replay/pipestate.inl | 31 +- 23 files changed, 533 insertions(+), 899 deletions(-) delete mode 100644 qrenderdoc/Windows/ConstantBufferPreviewer.cpp delete mode 100644 qrenderdoc/Windows/ConstantBufferPreviewer.h delete mode 100644 qrenderdoc/Windows/ConstantBufferPreviewer.ui diff --git a/qrenderdoc/Code/BufferFormatter.cpp b/qrenderdoc/Code/BufferFormatter.cpp index e602ae0b6..78eaed928 100644 --- a/qrenderdoc/Code/BufferFormatter.cpp +++ b/qrenderdoc/Code/BufferFormatter.cpp @@ -34,6 +34,8 @@ struct StructFormatData uint32_t offset = 0; uint32_t alignment = 0; uint32_t paddedStride = 0; + bool singleDef = false; + bool singleMember = false; }; GraphicsAPI BufferFormatter::m_API; @@ -390,8 +392,7 @@ bool BufferFormatter::CheckInvalidUnbounded(const ShaderConstant &structDef, QSt { errors = tr("%1 in %2 can't be unbounded when not the last member.\n") .arg(structDef.type.members[i].name) - .arg(structDef.type.descriptor.name.empty() ? "implicit_root" - : structDef.type.descriptor.name); + .arg(structDef.type.descriptor.name); return false; } @@ -400,8 +401,7 @@ bool BufferFormatter::CheckInvalidUnbounded(const ShaderConstant &structDef, QSt { errors = tr("%1 in %2 can't have unbounded array in a child.\n") .arg(structDef.type.members[i].name) - .arg(structDef.type.descriptor.name.empty() ? "implicit_root" - : structDef.type.descriptor.name); + .arg(structDef.type.descriptor.name); return false; } @@ -412,9 +412,8 @@ bool BufferFormatter::CheckInvalidUnbounded(const ShaderConstant &structDef, QSt return true; } -rdcpair BufferFormatter::ParseFormatString(const QString &formatString, - uint64_t maxLen, - QString &errors) +rdcpair BufferFormatter::ParseFormatString( + const QString &formatString, uint64_t maxLen, bool cbuffer, QString &errors) { StructFormatData root; StructFormatData *cur = &root; @@ -707,6 +706,10 @@ rdcpair BufferFormatter::ParseFormatString(const { cur->paddedStride = annot.param.toUInt(); } + else if(annot.name == lit("single") || annot.name == lit("fixed")) + { + cur->singleDef = true; + } else { errors = tr("Unrecognised annotation on struct definition: %1\n").arg(annot.name); @@ -716,6 +719,9 @@ rdcpair BufferFormatter::ParseFormatString(const } annotations.clear(); + + if(!success) + break; } else { @@ -737,6 +743,9 @@ rdcpair BufferFormatter::ParseFormatString(const annotations.clear(); + if(!success) + break; + QString baseType = match.captured(4); if(baseType.isEmpty()) @@ -806,6 +815,9 @@ rdcpair BufferFormatter::ParseFormatString(const annotations.clear(); + if(!success) + break; + cur->structDef.type.members.push_back(el); continue; @@ -835,9 +847,21 @@ rdcpair BufferFormatter::ParseFormatString(const annotations.clear(); + if(!success) + break; + continue; } + if(cur->singleMember) + { + errors = + tr("[[single]] can only be used if there is only one variable in the root.\n" + "Consider wrapping the variables in a struct and marking it as [[single]].\n"); + success = false; + break; + } + QRegularExpressionMatch structMatch = structUseRegex.match(line); bool isPadding = false; @@ -848,6 +872,13 @@ rdcpair BufferFormatter::ParseFormatString(const bool isPointer = !structMatch.captured(2).trimmed().isEmpty(); + if(structContext.singleDef) + { + errors = tr("[[single]] annotated structs cannot be used, only defined.\n"); + success = false; + break; + } + QString varName = structMatch.captured(3).trimmed(); if(varName.isEmpty()) @@ -864,6 +895,27 @@ rdcpair BufferFormatter::ParseFormatString(const { isPadding = true; } + else if(annot.name == lit("single") || annot.name == lit("fixed")) + { + if(cur != &root) + { + errors = tr("[[single]] can only be used on variables in the root\n"); + success = false; + break; + } + else if(!cur->structDef.type.members.empty()) + { + errors = + tr("[[single]] can only be used if there is only one variable in the root.\n" + "Consider wrapping the variables in a struct and marking it as [[single]].\n"); + success = false; + break; + } + else + { + cur->singleMember = true; + } + } else { errors = tr("Unrecognised annotation on variable: %1\n").arg(annot.name); @@ -872,6 +924,9 @@ rdcpair BufferFormatter::ParseFormatString(const } } + if(!success) + break; + annotations.clear(); QString arrayDim = structMatch.captured(4).trimmed(); @@ -887,6 +942,13 @@ rdcpair BufferFormatter::ParseFormatString(const arrayCount = 1; } + if(cur->singleMember && arrayCount == ~0U) + { + errors = tr("[[single]] can't be used on unbounded arrays.\n"); + success = false; + break; + } + QString bitfield = structMatch.captured(6).trimmed(); if(isPointer) @@ -1340,6 +1402,27 @@ rdcpair BufferFormatter::ParseFormatString(const { isPadding = true; } + else if(annot.name == lit("single") || annot.name == lit("fixed")) + { + if(cur != &root) + { + errors = tr("[[single]] can only be used on variables in the root\n"); + success = false; + break; + } + else if(!cur->structDef.type.members.empty()) + { + errors = + tr("[[single]] can only be used if there is only one variable in the root.\n" + "Consider wrapping the variables in a struct and marking it as [[single]].\n"); + success = false; + break; + } + else + { + cur->singleMember = true; + } + } else { errors = tr("Unrecognised annotation on variable: %1\n").arg(annot.name); @@ -1564,36 +1647,6 @@ rdcpair BufferFormatter::ParseFormatString(const if(success && root.structDef.type.members.isEmpty() && !lastStruct.isEmpty()) root = structelems[lastStruct]; - // on D3D it's not possible to have anything but AoS, no preamble, so always consider the root - // member to be the struct definition of an array, as long as we're declaring a UAV - if(IsD3D(m_API) && pack != Packing::D3DCB) - { - // if there's already only one root member just make it infinite - if(root.structDef.type.members.size() == 1) - { - root.structDef.type.members[0].type.descriptor.elements = ~0U; - } - else if(root.structDef.type.members.empty()) - { - // do nothing, we'll hit the invalid failure case below - } - else - { - // otherwise wrap a struct around the members, to be the infinite AoS - rdcarray inners; - inners.swap(root.structDef.type.members); - - ShaderConstant el; - el.byteOffset = 0; - el.type.descriptor.type = VarType::Struct; - el.type.descriptor.elements = ~0U; - el.type.descriptor.arrayByteStride = root.structDef.type.descriptor.arrayByteStride; - - root.structDef.type.members.push_back(el); - inners.swap(root.structDef.type.members[0].type.members); - } - } - root.structDef.type.descriptor.arrayByteStride = AlignUp(root.offset, GetAlignment(pack, root.structDef)); @@ -1610,11 +1663,7 @@ rdcpair BufferFormatter::ParseFormatString(const // check that unbounded arrays are only the last member of each struct. Doing this separately // makes the below check easier since we only have to consider last members if(!CheckInvalidUnbounded(root.structDef, errors)) - { - if(IsD3D(m_API) && pack != Packing::D3DCB) - errors += tr("D3D structured buffers always define an unbounded array.\n"); success = false; - } } // we only allow one 'infinite' array. You can't have an infinite member inside an already @@ -1660,8 +1709,6 @@ rdcpair BufferFormatter::ParseFormatString(const errors = tr("Can't have unbounded %1[] as child of an unbounded %2[].\n") .arg(iter->name) .arg(infiniteArrayName); - if(IsD3D(m_API) && pack != Packing::D3DCB) - errors += tr("D3D structured buffers always define an unbounded array.\n"); break; } @@ -1680,6 +1727,54 @@ rdcpair BufferFormatter::ParseFormatString(const } } + // on D3D if we have an unbounded array it *must* be the root element, as D3D does not support + // some fixed elements before it, structured buffers are strictly just an AoS. + // we do allow specifying cbuffers which are all fixed and not unbounded, so we just check to see + // that if there is an unbounded array that it's the root + if( + // on D3D + IsD3D(m_API) && + // if the parsing worked + success && !root.structDef.type.members.empty() && + // if we have an unbounded array somewhere (we know there's only one, from above) + ContainsUnbounded(root.structDef.type.members) && + // it must be in the root and it must be alone with no siblings + !(root.structDef.type.members.size() == 1 && + root.structDef.type.members[0].type.descriptor.elements == ~0U)) + { + errors += tr("On D3D an unbounded array must be only be used alone as the root element.\n"); + success = false; + } + + // when not viewing a cbuffer, if the root hasn't been explicitly marked as a single struct and we + // don't have an unbounded array then consider it an AoS definition in all other cases as that is + // very likely what the user expects + if(success && !root.structDef.type.members.empty() && + !ContainsUnbounded(root.structDef.type.members) && !root.singleMember && !root.singleDef && + !cbuffer) + { + // if there's already only one root member just make it infinite + if(root.structDef.type.members.size() == 1) + { + root.structDef.type.members[0].type.descriptor.elements = ~0U; + } + else + { + // otherwise wrap a struct around the members, to be the infinite AoS + rdcarray inners; + inners.swap(root.structDef.type.members); + + ShaderConstant el; + el.byteOffset = 0; + el.type.descriptor.type = VarType::Struct; + el.type.descriptor.elements = ~0U; + el.type.descriptor.arrayByteStride = root.structDef.type.descriptor.arrayByteStride; + + root.structDef.type.members.push_back(el); + inners.swap(root.structDef.type.members[0].type.members); + } + } + if(!success || root.structDef.type.members.isEmpty()) { root.structDef.type.members.clear(); @@ -1881,7 +1976,8 @@ QString BufferFormatter::GetBufferFormatString(Packing::Rules pack, const Shader QList declaredStructs; format = DeclareStruct(pack, declaredStructs, structName, res.variableType.members, 0, QString()); - format = QFormatStr("%1\n\n%2").arg(DeclarePacking(pack)).arg(format); + format = + QFormatStr("%1\n\n%2\n\n%3 buffer[];").arg(DeclarePacking(pack)).arg(format).arg(structName); } else { diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index 85d48c635..f61ad55d4 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -41,7 +41,6 @@ #include "Windows/APIInspector.h" #include "Windows/BufferViewer.h" #include "Windows/CommentView.h" -#include "Windows/ConstantBufferPreviewer.h" #include "Windows/DebugMessageView.h" #include "Windows/Dialogs/CaptureDialog.h" #include "Windows/Dialogs/CrashDialog.h" @@ -2597,14 +2596,17 @@ IBufferViewer *CaptureContext::ViewTextureAsBuffer(ResourceId id, const Subresou return viewer; } -IConstantBufferPreviewer *CaptureContext::ViewConstantBuffer(ShaderStage stage, uint32_t slot, - uint32_t idx) +IBufferViewer *CaptureContext::ViewConstantBuffer(ShaderStage stage, uint32_t slot, uint32_t idx) { - ConstantBufferPreviewer *existing = ConstantBufferPreviewer::has(stage, slot, idx); + BufferViewer *existing = BufferViewer::HasCBufferView(stage, slot, idx); if(existing != NULL) return existing; - return new ConstantBufferPreviewer(*this, stage, slot, idx, m_MainWindow); + BufferViewer *viewer = new BufferViewer(*this, false, m_MainWindow); + + viewer->ViewCBuffer(stage, slot, idx); + + return viewer; } IPixelHistoryView *CaptureContext::ViewPixelHistory(ResourceId texID, uint32_t x, uint32_t y, @@ -2756,11 +2758,13 @@ void CaptureContext::AddDockWindow(QWidget *newWindow, DockReference ref, QWidge if(ref == DockReference::TransientPopupArea) { - if(qobject_cast(newWindow)) + BufferViewer *buf = qobject_cast(newWindow); + + if(buf && buf->IsCBufferView()) { ToolWindowManager *manager = ToolWindowManager::managerOf(refWindow); - ConstantBufferPreviewer *cb = manager->findChild(); + BufferViewer *cb = BufferViewer::GetFirstCBufferView(buf); if(cb) { manager->addToolWindow(newWindow, ToolWindowManager::AreaReference(ToolWindowManager::AddTo, diff --git a/qrenderdoc/Code/CaptureContext.h b/qrenderdoc/Code/CaptureContext.h index 44f03f5c5..551ef7f7e 100644 --- a/qrenderdoc/Code/CaptureContext.h +++ b/qrenderdoc/Code/CaptureContext.h @@ -267,8 +267,7 @@ public: IBufferViewer *ViewTextureAsBuffer(ResourceId id, const Subresource &sub, const rdcstr &format = "") override; - IConstantBufferPreviewer *ViewConstantBuffer(ShaderStage stage, uint32_t slot, - uint32_t idx) override; + IBufferViewer *ViewConstantBuffer(ShaderStage stage, uint32_t slot, uint32_t idx) override; IPixelHistoryView *ViewPixelHistory(ResourceId texID, uint32_t x, uint32_t y, const TextureDisplay &display) override; diff --git a/qrenderdoc/Code/Interface/QRDInterface.h b/qrenderdoc/Code/Interface/QRDInterface.h index 29ec903a8..855030bec 100644 --- a/qrenderdoc/Code/Interface/QRDInterface.h +++ b/qrenderdoc/Code/Interface/QRDInterface.h @@ -1251,25 +1251,6 @@ protected: DECLARE_REFLECTION_STRUCT(IShaderMessageViewer); -DOCUMENT("A constant buffer preview window."); -struct IConstantBufferPreviewer -{ - DOCUMENT(R"(Retrieves the PySide2 QWidget for this :class:`ConstantBufferPreviewer` if PySide2 is available, or otherwise -returns a unique opaque pointer that can be passed back to any RenderDoc functions expecting a -QWidget. - -:return: Return the widget handle, either a PySide2 handle or an opaque handle. -:rtype: QWidget -)"); - virtual QWidget *Widget() = 0; - -protected: - IConstantBufferPreviewer() = default; - ~IConstantBufferPreviewer() = default; -}; - -DECLARE_REFLECTION_STRUCT(IConstantBufferPreviewer); - DOCUMENT("A pixel history window."); struct IPixelHistoryView { @@ -2593,18 +2574,17 @@ bytes. virtual IBufferViewer *ViewTextureAsBuffer(ResourceId id, const Subresource &sub, const rdcstr &format = "") = 0; - DOCUMENT(R"(Show a new :class:`ConstantBufferPreviewer` window, showing a read-only view of a the + DOCUMENT(R"(Show a new :class:`BufferViewer` window, showing a read-only view of a the variables in a constant buffer with their values. :param renderdoc.ShaderStage stage: The stage that the constant buffer is bound to. :param int slot: The index in the shader's constant buffer list to look up. :param int idx: For APIs that support arrayed resource binds, the index in the constant buffer array. -:return: The new :class:`ConstantBufferPreviewer` window opened, but not shown. -:rtype: ConstantBufferPreviewer +:return: The new :class:`BufferViewer` window opened, but not shown. +:rtype: BufferViewer )"); - virtual IConstantBufferPreviewer *ViewConstantBuffer(ShaderStage stage, uint32_t slot, - uint32_t idx) = 0; + virtual IBufferViewer *ViewConstantBuffer(ShaderStage stage, uint32_t slot, uint32_t idx) = 0; DOCUMENT(R"(Show a new :class:`PixelHistoryView` window, showing the results from a pixel history operation. diff --git a/qrenderdoc/Code/QRDUtils.h b/qrenderdoc/Code/QRDUtils.h index 46dd0f8ee..3e99b04aa 100644 --- a/qrenderdoc/Code/QRDUtils.h +++ b/qrenderdoc/Code/QRDUtils.h @@ -201,7 +201,8 @@ public: static void Init(GraphicsAPI api) { m_API = api; } static rdcpair ParseFormatString(const QString &formatString, - uint64_t maxLen, QString &errors); + uint64_t maxLen, bool cbuffer, + QString &errors); static uint32_t GetVarSize(const ShaderConstant &var); static Packing::Rules EstimatePackingRules(const rdcarray &members); diff --git a/qrenderdoc/Widgets/BufferFormatSpecifier.cpp b/qrenderdoc/Widgets/BufferFormatSpecifier.cpp index 04aecf83f..42c773ebc 100644 --- a/qrenderdoc/Widgets/BufferFormatSpecifier.cpp +++ b/qrenderdoc/Widgets/BufferFormatSpecifier.cpp @@ -52,6 +52,11 @@ void BufferFormatSpecifier::setContext(ICaptureContext *ctx) showHelp(m_Ctx->Config().BufferFormatter_ShowHelp); } +void BufferFormatSpecifier::setTitle(QString title) +{ + ui->formatGroup->setTitle(title); +} + void BufferFormatSpecifier::toggleHelp() { ui->helpText->setVisible(!ui->helpText->isVisible()); diff --git a/qrenderdoc/Widgets/BufferFormatSpecifier.h b/qrenderdoc/Widgets/BufferFormatSpecifier.h index 98cdfd139..1f529de1a 100644 --- a/qrenderdoc/Widgets/BufferFormatSpecifier.h +++ b/qrenderdoc/Widgets/BufferFormatSpecifier.h @@ -42,6 +42,7 @@ public: ~BufferFormatSpecifier(); void setContext(ICaptureContext *ctx); + void setTitle(QString title); signals: void processFormat(const QString &format); diff --git a/qrenderdoc/Widgets/Extended/RDHeaderView.cpp b/qrenderdoc/Widgets/Extended/RDHeaderView.cpp index 2f2b3bdac..e427b896d 100644 --- a/qrenderdoc/Widgets/Extended/RDHeaderView.cpp +++ b/qrenderdoc/Widgets/Extended/RDHeaderView.cpp @@ -122,6 +122,9 @@ void RDHeaderView::cacheSections() QAbstractItemModel *m = this->model(); + if(!m) + return; + int oldCount = m_sections.count(); m_sections.resize(m->columnCount()); diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index c3dfb7c2c..5029c15f3 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -38,6 +38,7 @@ #include "Code/QRDUtils.h" #include "Code/Resources.h" #include "Widgets/CollapseGroupBox.h" +#include "Widgets/Extended/RDLabel.h" #include "Widgets/Extended/RDSplitter.h" #include "Windows/Dialogs/AxisMappingDialog.h" #include "ui_BufferViewer.h" @@ -470,6 +471,7 @@ struct BufferConfiguration uint32_t pagingOffset = 0; ShaderConstant fixedVars; + rdcarray evalVars; uint32_t repeatStride = 1; uint32_t repeatOffset = 0; @@ -507,6 +509,7 @@ struct BufferConfiguration pagingOffset = o.pagingOffset; fixedVars = o.fixedVars; + evalVars = o.evalVars; repeatStride = o.repeatStride; repeatOffset = o.repeatOffset; @@ -1431,6 +1434,8 @@ struct PopulateBufferData int vsoutVert; int gsoutVert; + CBufferData cb; + QString highlightNames[6]; BufferConfiguration vsinConfig, vsoutConfig, gsoutConfig; @@ -2081,6 +2086,8 @@ static void UnrollConstant(const ShaderConstant &constant, rdcarray BufferViewer::m_CBufferViews; + BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) : QFrame(parent), ui(new Ui::BufferViewer), m_Ctx(ctx) { @@ -2215,6 +2222,8 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) // wireframe only available on solid shaded options ui->wireframeRender->setEnabled(false); + ui->setFormat->setVisible(false); + ui->fovGuess->setValue(90.0); on_controlType_currentIndexChanged(0); @@ -2341,6 +2350,16 @@ void BufferViewer::SetupRawView() controlLayout->setSpacing(2); controlLayout->setContentsMargins(6, 2, 6, 2); + m_RepeatedOffset = new RDLabel(this); + + QFrame *line = new QFrame(this); + line->setFrameShape(QFrame::VLine); + line->setFrameShadow(QFrame::Sunken); + + controlLayout->addWidget(line); + + controlLayout->addWidget(m_RepeatedOffset); + controlLayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); QVBoxLayout *fixedLayout = new QVBoxLayout(m_FixedGroup); @@ -2349,7 +2368,7 @@ void BufferViewer::SetupRawView() QVBoxLayout *repeatedLayout = new QVBoxLayout(m_RepeatedGroup); repeatedLayout->setSpacing(3); - repeatedLayout->setContentsMargins(0, 0, 0, 0); + repeatedLayout->setContentsMargins(2, 0, 0, 0); repeatedLayout->addWidget(m_RepeatedControlBar); @@ -2368,6 +2387,8 @@ void BufferViewer::SetupRawView() m_InnerSplitter->setHandleWidth(12); m_InnerSplitter->setChildrenCollapsible(false); + m_InnerSplitter->setVisible(false); + // inner splitter is only used when we have these groups, so we can add these unconditionally m_InnerSplitter->addWidget(m_FixedGroup); m_InnerSplitter->addWidget(m_RepeatedGroup); @@ -2663,6 +2684,8 @@ BufferViewer::~BufferViewer() m_Ctx.RemoveCaptureViewer(this); delete ui; + + m_CBufferViews.removeOne(this); } void BufferViewer::OnCaptureLoaded() @@ -2741,8 +2764,6 @@ void BufferViewer::OnEventChanged(uint32_t eventId) bufdata->highlightNames[4] = m_ModelGSOut->posName(); bufdata->highlightNames[5] = m_ModelGSOut->secondaryName(); - updateWindowTitle(); - const ActionDescription *action = m_Ctx.CurAction(); configureDrawRange(); @@ -2793,10 +2814,50 @@ void BufferViewer::OnEventChanged(uint32_t eventId) } else { + // update with the current cbuffer for the current slot + if(IsCBufferView()) + { + BoundCBuffer cb = m_Ctx.CurPipelineState().GetConstantBuffer( + m_CBufferSlot.stage, m_CBufferSlot.slot, m_CBufferSlot.arrayIdx); + m_BufferID = cb.resourceId; + m_ByteOffset = cb.byteOffset; + m_ByteSize = cb.byteSize; + + const ShaderReflection *reflection = + m_Ctx.CurPipelineState().GetShaderReflection(m_CBufferSlot.stage); + bufdata->cb.valid = + (reflection != NULL && m_CBufferSlot.slot < reflection->constantBlocks.size()); + if(bufdata->cb.valid) + bufdata->cb.bufferBacked = reflection->constantBlocks[m_CBufferSlot.slot].bufferBacked; + + ui->setFormat->setEnabled(bufdata->cb.bufferBacked); + if(ui->setFormat->isEnabled()) + ui->setFormat->setToolTip(tr("Specify a custom format for this constant buffer")); + else + ui->setFormat->setToolTip(tr("Cannot specify custom format without backing memory")); + + bufdata->cb.pipe = m_CBufferSlot.stage == ShaderStage::Compute + ? m_Ctx.CurPipelineState().GetComputePipelineObject() + : m_Ctx.CurPipelineState().GetGraphicsPipelineObject(); + bufdata->cb.shader = m_Ctx.CurPipelineState().GetShader(m_CBufferSlot.stage); + bufdata->cb.entryPoint = m_Ctx.CurPipelineState().GetShaderEntryPoint(m_CBufferSlot.stage); + bufdata->cb.inlinedata = cb.inlineData; + + if(m_Format.isEmpty()) + { + // stage, slot, and array index are all invariant when viewing a constant buffer + // ee only need to use the actual bound shader as a key. + RDTreeViewExpansionState &prevShaderExpansionState = + ui->fixedVars->getInternalExpansion(qHash(ToQStr(m_CurCBuffer.shader))); + + ui->fixedVars->saveExpansion(prevShaderExpansionState, 0); + } + } + QString errors; ShaderConstant repeating; rdctie(bufdata->vsinConfig.fixedVars, repeating) = - BufferFormatter::ParseFormatString(m_Format, m_ByteSize, errors); + BufferFormatter::ParseFormatString(m_Format, m_ByteSize, IsCBufferView(), errors); if(repeating.type.descriptor.type != VarType::Unknown) { @@ -2809,6 +2870,8 @@ void BufferViewer::OnEventChanged(uint32_t eventId) ClearModels(); } + updateWindowTitle(); + bufdata->vsinConfig.curInstance = bufdata->vsoutConfig.curInstance = bufdata->gsoutConfig.curInstance = m_Config.curInstance; bufdata->vsinConfig.curView = bufdata->vsoutConfig.curView = bufdata->gsoutConfig.curView = @@ -2820,6 +2883,8 @@ void BufferViewer::OnEventChanged(uint32_t eventId) bufdata->vsinConfig.baseVertex = action ? action->baseVertex : 0; + ui->formatSpecifier->setEnabled(!IsCBufferView() || bufdata->cb.bufferBacked); + ui->instance->setEnabled(action && (action->flags & ActionFlags::Instanced)); if(!ui->instance->isEnabled()) ui->instance->setValue(0); @@ -2908,7 +2973,11 @@ void BufferViewer::OnEventChanged(uint32_t eventId) if(m_IsBuffer) { - if(repeatedRangeStart > fixedLength) + if(m_BufferID == ResourceId()) + { + buf->storage = bufdata->cb.inlinedata; + } + else if(repeatedRangeStart > fixedLength) { // if the repeated range subsection we're fetching is paged further in, we still need to // fetch the fixed data from the 'start' @@ -2943,6 +3012,17 @@ void BufferViewer::OnEventChanged(uint32_t eventId) } } + // for cbuffers, if the format is empty or if we're not buffer-backed, we evaluate variables + // here and don't use the format override with a fetched buffer + if((m_Format.isEmpty() || !bufdata->cb.bufferBacked) && IsCBufferView()) + { + // only fetch the cbuffer constants if this binding is currently valid + if(bufdata->cb.valid) + bufdata->vsinConfig.evalVars = r->GetCBufferVariableContents( + bufdata->cb.pipe, bufdata->cb.shader, m_CBufferSlot.stage, bufdata->cb.entryPoint, + m_CBufferSlot.slot, m_BufferID, m_ByteOffset, m_ByteSize); + } + GUIInvoke::call(this, [this, bufdata]() { if(bufdata->sequence != m_Sequence) return; @@ -2954,6 +3034,8 @@ void BufferViewer::OnEventChanged(uint32_t eventId) m_PostVS = bufdata->postVS; m_PostGS = bufdata->postGS; + m_CurCBuffer = bufdata->cb; + // if we didn't have a position column selected before, or the name has changed, re-guess if(m_ModelVSIn->posColumn() == -1 || bufdata->highlightNames[0] != bufdata->vsinConfig.columnName(m_ModelVSIn->posColumn())) @@ -3021,10 +3103,24 @@ void BufferViewer::OnEventChanged(uint32_t eventId) if(!m_MeshView) { + m_RepeatedOffset->setText( + tr("Starting at: %1 bytes").arg(m_ByteOffset + bufdata->vsinConfig.repeatOffset)); + { - ShaderVariable var = InterpretShaderVar(bufdata->vsinConfig.fixedVars, - bufdata->vsinConfig.buffers[0]->storage.begin(), - bufdata->vsinConfig.buffers[0]->storage.end()); + rdcarray vars; + + if((m_BufferID == ResourceId() && m_CurCBuffer.inlinedata.empty()) || m_Format.isEmpty()) + { + vars = bufdata->vsinConfig.evalVars; + } + else + { + ShaderVariable var = InterpretShaderVar(bufdata->vsinConfig.fixedVars, + bufdata->vsinConfig.buffers[0]->storage.begin(), + bufdata->vsinConfig.buffers[0]->storage.end()); + + vars.swap(var.members); + } bool wasEmpty = ui->fixedVars->topLevelItemCount() == 0; @@ -3035,8 +3131,8 @@ void BufferViewer::OnEventChanged(uint32_t eventId) ui->fixedVars->clear(); - if(!var.members.isEmpty()) - UI_AddFixedVariables(ui->fixedVars->invisibleRootItem(), var.members); + if(!vars.isEmpty()) + UI_AddFixedVariables(ui->fixedVars->invisibleRootItem(), vars); ui->fixedVars->endUpdate(); @@ -3049,7 +3145,14 @@ void BufferViewer::OnEventChanged(uint32_t eventId) ui->fixedVars->collapseAll(); } - ui->fixedVars->applyExpansion(state, 0); + // if we have saved expansion state for the new shader, apply it, otherwise apply the + // previous one to get any overlap (e.g. two different shaders with very similar or + // identical constants) + if(ui->fixedVars->hasInternalExpansion(qHash(ToQStr(m_CurCBuffer.shader)))) + ui->fixedVars->applyExpansion( + ui->fixedVars->getInternalExpansion(qHash(ToQStr(m_CurCBuffer.shader))), 0); + else + ui->fixedVars->applyExpansion(state, 0); } on_rowOffset_valueChanged(ui->rowOffset->value()); @@ -3857,6 +3960,59 @@ void BufferViewer::ViewBuffer(uint64_t byteOffset, uint64_t byteSize, ResourceId processFormat(format); } +BufferViewer *BufferViewer::HasCBufferView(ShaderStage stage, uint32_t slot, uint32_t idx) +{ + CBufferSlot cbuffer = {stage, slot, idx}; + + for(BufferViewer *c : m_CBufferViews) + { + if(c->m_CBufferSlot == cbuffer) + return c; + } + + return NULL; +} + +BufferViewer *BufferViewer::GetFirstCBufferView(BufferViewer *exclude) +{ + for(BufferViewer *b : m_CBufferViews) + { + if(b != exclude) + return b; + } + + return NULL; +} + +void BufferViewer::ViewCBuffer(const ShaderStage stage, uint32_t slot, uint32_t idx) +{ + if(!m_Ctx.IsCaptureLoaded()) + return; + + m_IsBuffer = true; + m_ByteOffset = 0; + m_ByteSize = UINT64_MAX; + m_BufferID = ResourceId(); + m_CBufferSlot = {stage, slot, idx}; + m_TexSub = {0, 0, 0}; + + updateWindowTitle(); + + m_ObjectByteSize = 0; + m_PagingByteOffset = 0; + + // enable the button to toggle on formatting, so we can pre-fill with a sensible format when it's + // enabled + ui->setFormat->setVisible(true); + + ui->formatSpecifier->setFormat(QString()); + ui->formatSpecifier->setVisible(false); + + processFormat(QString()); + + m_CBufferViews.push_back(this); +} + void BufferViewer::ViewTexture(ResourceId id, const Subresource &sub, const rdcstr &format) { if(!m_Ctx.IsCaptureLoaded()) @@ -3920,11 +4076,66 @@ bool BufferViewer::eventFilter(QObject *watched, QEvent *event) void BufferViewer::updateWindowTitle() { if(!m_MeshView) - setWindowTitle(m_Ctx.GetResourceName(m_BufferID) + lit(" - Contents")); + { + if(IsCBufferView()) + { + QString bufName; + + const ShaderReflection *reflection = + m_Ctx.CurPipelineState().GetShaderReflection(m_CBufferSlot.stage); + + int32_t bindPoint = -1; + if(reflection != NULL) + { + if(m_CBufferSlot.slot < reflection->constantBlocks.size() && + !reflection->constantBlocks[m_CBufferSlot.slot].name.isEmpty()) + { + bufName = QFormatStr("<%1>").arg(reflection->constantBlocks[m_CBufferSlot.slot].name); + bindPoint = reflection->constantBlocks[m_CBufferSlot.slot].bindPoint; + } + } + + if(bufName.isEmpty()) + { + if(m_BufferID != ResourceId()) + bufName = m_Ctx.GetResourceName(m_BufferID); + else + bufName = tr("Unbound"); + } + + const ShaderBindpointMapping &mapping = + m_Ctx.CurPipelineState().GetBindpointMapping(m_CBufferSlot.stage); + + uint32_t arraySize = ~0U; + if(bindPoint >= 0 && bindPoint < mapping.constantBlocks.count()) + arraySize = mapping.constantBlocks[bindPoint].arraySize; + + GraphicsAPI pipeType = m_Ctx.APIProps().pipelineType; + + QString title = QFormatStr("%1 %2 %3") + .arg(ToQStr(m_CBufferSlot.stage, pipeType)) + .arg(IsD3D(pipeType) ? lit("CB") : lit("UBO")) + .arg(m_CBufferSlot.slot); + + if(m_Ctx.CurPipelineState().SupportsResourceArrays() && arraySize > 1) + title += QFormatStr("[%1]").arg(m_CBufferSlot.arrayIdx); + + title += QFormatStr(" - %1").arg(bufName); + + setWindowTitle(title); + } + else + { + setWindowTitle(m_Ctx.GetResourceName(m_BufferID) + lit(" - Contents")); + } + } } void BufferViewer::on_resourceDetails_clicked() { + if(m_BufferID == ResourceId()) + return; + if(!m_Ctx.HasResourceInspector()) m_Ctx.ShowResourceInspector(); @@ -4362,6 +4573,40 @@ void BufferViewer::on_axisMappingButton_clicked() showAxisMappingDialog(); } +void BufferViewer::on_setFormat_toggled(bool checked) +{ + if(!checked) + { + ui->formatSpecifier->setVisible(false); + + processFormat(QString()); + return; + } + + ui->formatSpecifier->setVisible(true); + + const ShaderReflection *reflection = + m_Ctx.CurPipelineState().GetShaderReflection(m_CBufferSlot.stage); + + if(m_CBufferSlot.slot >= reflection->constantBlocks.size()) + { + ui->formatSpecifier->setVisible(false); + + processFormat(QString()); + return; + } + + if(IsD3D(m_Ctx.APIProps().pipelineType)) + ui->formatSpecifier->setFormat(BufferFormatter::DeclareStruct( + Packing::D3DCB, reflection->constantBlocks[m_CBufferSlot.slot].name, + reflection->constantBlocks[m_CBufferSlot.slot].variables, 0)); + else + ui->formatSpecifier->setFormat(BufferFormatter::DeclareStruct( + BufferFormatter::EstimatePackingRules(reflection->constantBlocks[m_CBufferSlot.slot].variables), + reflection->constantBlocks[m_CBufferSlot.slot].name, + reflection->constantBlocks[m_CBufferSlot.slot].variables, 0)); +} + void BufferViewer::processFormat(const QString &format) { QString errors; @@ -4375,7 +4620,18 @@ void BufferViewer::processFormat(const QString &format) BufferConfiguration bufconfig; ShaderConstant fixed, repeating; - rdctie(fixed, repeating) = BufferFormatter::ParseFormatString(format, m_ByteSize, errors); + + if(IsCBufferView() && format.isEmpty()) + { + // insert a dummy member so we get identified as plain fixed vars - we will automatically + // evaluate ignoring the format + fixed.type.members.push_back(ShaderConstant()); + } + else + { + rdctie(fixed, repeating) = + BufferFormatter::ParseFormatString(format, m_ByteSize, IsCBufferView(), errors); + } const bool repeatedVars = repeating.type.descriptor.type != VarType::Unknown; const bool fixedVars = !fixed.type.members.empty(); @@ -4467,16 +4723,33 @@ void BufferViewer::processFormat(const QString &format) m_Format = format; - qulonglong stride = qMax(1U, repeating.type.descriptor.arrayByteStride); + if(IsCBufferView()) + { + ui->byteRangeLine->setVisible(false); + ui->byteRangeStartLabel->setVisible(false); + byteRangeStart->setVisible(false); + ui->byteRangeLengthLabel->setVisible(false); + byteRangeLength->setVisible(false); + GraphicsAPI pipeType = m_Ctx.APIProps().pipelineType; - byteRangeStart->setSingleStep(stride); - byteRangeLength->setSingleStep(stride); + if(IsD3D(pipeType)) + ui->formatSpecifier->setTitle(tr("Constant Buffer Custom Format")); + else + ui->formatSpecifier->setTitle(tr("Uniform Buffer Custom Format")); + } + else + { + qulonglong stride = qMax(1U, repeating.type.descriptor.arrayByteStride); - byteRangeStart->setMaximum((qulonglong)m_ObjectByteSize); - byteRangeLength->setMaximum((qulonglong)m_ObjectByteSize); + byteRangeStart->setSingleStep(stride); + byteRangeLength->setSingleStep(stride); - byteRangeStart->setValue(m_ByteOffset); - byteRangeLength->setValue(m_ByteSize); + byteRangeStart->setMaximum((qulonglong)m_ObjectByteSize); + byteRangeLength->setMaximum((qulonglong)m_ObjectByteSize); + + byteRangeStart->setValue(m_ByteOffset); + byteRangeLength->setValue(m_ByteSize); + } ui->formatSpecifier->setErrors(errors); @@ -4527,7 +4800,7 @@ void BufferViewer::updateExportActionNames() } m_ExportCSV->setEnabled(true); - m_ExportBytes->setEnabled(true); + m_ExportBytes->setEnabled(m_BufferID != ResourceId()); if(m_MeshView) { diff --git a/qrenderdoc/Windows/BufferViewer.h b/qrenderdoc/Windows/BufferViewer.h index 6f7eda583..e9ffa6778 100644 --- a/qrenderdoc/Windows/BufferViewer.h +++ b/qrenderdoc/Windows/BufferViewer.h @@ -39,6 +39,7 @@ class QItemSelection; class QMenu; class QPushButton; class QVBoxLayout; +class RDLabel; class RDTableView; class RDSplitter; class BufferItemModel; @@ -72,6 +73,16 @@ struct BBoxData } bounds[3]; }; +struct CBufferData +{ + bool valid = false; + bool bufferBacked = false; + ResourceId pipe; + ResourceId shader; + rdcstr entryPoint; + bytebuf inlinedata; +}; + class BufferViewer : public QFrame, public IBufferViewer, public ICaptureViewer { Q_OBJECT @@ -82,7 +93,11 @@ public: explicit BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent = 0); ~BufferViewer(); + static BufferViewer *HasCBufferView(ShaderStage stage, uint32_t slot, uint32_t idx); + static BufferViewer *GetFirstCBufferView(BufferViewer *exclude); + bool IsCBufferView() const { return m_CBufferSlot.stage != ShaderStage::Count; } void ViewBuffer(uint64_t byteOffset, uint64_t byteSize, ResourceId id, const rdcstr &format = ""); + void ViewCBuffer(const ShaderStage stage, uint32_t slot, uint32_t idx); void ViewTexture(ResourceId id, const Subresource &sub, const rdcstr &format = ""); // IBufferViewer @@ -123,6 +138,7 @@ private slots: void on_byteRangeLength_valueChanged(double value); void on_axisMappingCombo_currentIndexChanged(int index); void on_axisMappingButton_clicked(); + void on_setFormat_toggled(bool checked); // manual slots void render_mouseMove(QMouseEvent *e); @@ -197,6 +213,22 @@ private: uint64_t m_ByteSize = UINT64_MAX; ResourceId m_BufferID; + struct CBufferSlot + { + ShaderStage stage; + uint32_t slot; + uint32_t arrayIdx; + + bool operator==(const CBufferSlot &c) const + { + return stage == c.stage && slot == c.slot && arrayIdx == c.arrayIdx; + } + } m_CBufferSlot = {ShaderStage::Count, 0, 0}; + + CBufferData m_CurCBuffer; + + static QList m_CBufferViews; + CameraWrapper *m_CurrentCamera = NULL; ArcballWrapper *m_Arcball = NULL; FlycamWrapper *m_Flycam = NULL; @@ -235,6 +267,8 @@ private: QFrame *m_RepeatedControlBar = NULL; + RDLabel *m_RepeatedOffset = NULL; + QMenu *m_HeaderMenu = NULL; QAction *m_ResetColumnSel = NULL; diff --git a/qrenderdoc/Windows/BufferViewer.ui b/qrenderdoc/Windows/BufferViewer.ui index 2775af5c4..8dd47056e 100644 --- a/qrenderdoc/Windows/BufferViewer.ui +++ b/qrenderdoc/Windows/BufferViewer.ui @@ -875,6 +875,22 @@ Enter 0.0 to use automatic/guessed value derived from data. + + + + {} + + + true + + + Qt::ToolButtonTextOnly + + + true + + + diff --git a/qrenderdoc/Windows/ConstantBufferPreviewer.cpp b/qrenderdoc/Windows/ConstantBufferPreviewer.cpp deleted file mode 100644 index 53ed9e48e..000000000 --- a/qrenderdoc/Windows/ConstantBufferPreviewer.cpp +++ /dev/null @@ -1,401 +0,0 @@ -/****************************************************************************** - * The MIT License (MIT) - * - * Copyright (c) 2019-2022 Baldur Karlsson - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - ******************************************************************************/ - -#include "ConstantBufferPreviewer.h" -#include -#include -#include "Code/QRDUtils.h" -#include "toolwindowmanager/ToolWindowManager.h" -#include "ui_ConstantBufferPreviewer.h" - -QList ConstantBufferPreviewer::m_Previews; - -ConstantBufferPreviewer::ConstantBufferPreviewer(ICaptureContext &ctx, const ShaderStage stage, - uint32_t slot, uint32_t idx, QWidget *parent) - : QFrame(parent), ui(new Ui::ConstantBufferPreviewer), m_Ctx(ctx) -{ - ui->setupUi(this); - - m_stage = stage; - m_slot = slot; - m_arrayIdx = idx; - - QObject::connect(ui->formatSpecifier, &BufferFormatSpecifier::processFormat, this, - &ConstantBufferPreviewer::processFormat); - - ui->formatSpecifier->setContext(&m_Ctx); - - ui->splitter->setCollapsible(1, true); - ui->splitter->setSizes({1, 0}); - ui->splitter->handle(1)->setEnabled(false); - - ui->variables->setColumns({tr("Name"), tr("Value"), tr("Type")}); - { - ui->variables->header()->setSectionResizeMode(0, QHeaderView::Interactive); - ui->variables->header()->setSectionResizeMode(1, QHeaderView::Interactive); - ui->variables->header()->setSectionResizeMode(2, QHeaderView::Interactive); - } - - ui->variables->setFont(Formatter::FixedFont()); - - m_Previews.push_back(this); - m_Ctx.AddCaptureViewer(this); -} - -ConstantBufferPreviewer::~ConstantBufferPreviewer() -{ - m_Ctx.RemoveCaptureViewer(this); - m_Previews.removeOne(this); - delete ui; -} - -ConstantBufferPreviewer *ConstantBufferPreviewer::has(ShaderStage stage, uint32_t slot, uint32_t idx) -{ - for(ConstantBufferPreviewer *c : m_Previews) - { - if(c->m_stage == stage && c->m_slot == slot && c->m_arrayIdx == idx) - return c; - } - - return NULL; -} - -void ConstantBufferPreviewer::OnCaptureLoaded() -{ - Reset(); -} - -void ConstantBufferPreviewer::OnCaptureClosed() -{ - Reset(); - - ToolWindowManager::closeToolWindow(this); -} - -void ConstantBufferPreviewer::Reset() -{ - ui->variables->clear(); - ui->variables->clearInternalExpansions(); - - ui->saveCSV->setEnabled(false); -} - -void ConstantBufferPreviewer::OnEventChanged(uint32_t eventId) -{ - BoundCBuffer cb = m_Ctx.CurPipelineState().GetConstantBuffer(m_stage, m_slot, m_arrayIdx); - m_cbuffer = cb.resourceId; - uint64_t offset = cb.byteOffset; - uint64_t size = cb.byteSize; - bytebuf inlineData = cb.inlineData; - - ResourceId prevShader = m_shader; - - m_pipe = m_stage == ShaderStage::Compute ? m_Ctx.CurPipelineState().GetComputePipelineObject() - : m_Ctx.CurPipelineState().GetGraphicsPipelineObject(); - m_shader = m_Ctx.CurPipelineState().GetShader(m_stage); - rdcstr entryPoint = m_Ctx.CurPipelineState().GetShaderEntryPoint(m_stage); - const ShaderReflection *reflection = m_Ctx.CurPipelineState().GetShaderReflection(m_stage); - - bool wasEmpty = ui->variables->topLevelItemCount() == 0; - - updateLabels(); - - if(reflection == NULL || m_slot >= reflection->constantBlocks.size()) - { - // save expansion before clearing - if(m_formatOverride.type.members.empty()) - { - RDTreeViewExpansionState &prevShaderExpansionState = - ui->variables->getInternalExpansion(qHash(ToQStr(prevShader))); - ui->variables->saveExpansion(prevShaderExpansionState, 0); - } - - setVariables({}); - return; - } - - ui->setFormat->setEnabled(reflection->constantBlocks[m_slot].bufferBacked); - - if(!m_formatOverride.type.members.empty() && reflection->constantBlocks[m_slot].bufferBacked) - { - if(!inlineData.empty() && m_cbuffer == ResourceId()) - { - setVariablesPreserveExpansion(applyFormatOverride(inlineData), wasEmpty); - } - else - { - m_Ctx.Replay().AsyncInvoke([this, offset, size, wasEmpty](IReplayController *r) { - bytebuf data; - - if(size > 0 && m_cbuffer != ResourceId()) - data = r->GetBufferData(m_cbuffer, offset, size); - - rdcarray vars = applyFormatOverride(data); - - GUIInvoke::call(this, - [this, vars, wasEmpty] { setVariablesPreserveExpansion(vars, wasEmpty); }); - }); - } - } - else - { - m_Ctx.Replay().AsyncInvoke( - [this, prevShader, entryPoint, offset, size, wasEmpty](IReplayController *r) { - rdcarray vars = r->GetCBufferVariableContents( - m_pipe, m_shader, m_stage, entryPoint, m_slot, m_cbuffer, offset, size); - GUIInvoke::call(this, [this, prevShader, vars, wasEmpty] { - - RDTreeViewExpansionState &prevShaderExpansionState = - ui->variables->getInternalExpansion(qHash(ToQStr(prevShader))); - - // stage, slot, and array index are all invariant across a given ConstantBufferPreviewer - // instance. We only need to use the actual bound shader as a key. - ui->variables->saveExpansion(prevShaderExpansionState, 0); - - setVariables(vars); - if(wasEmpty) - { - // Expand before resizing so that collapsed data will already be visible when expanded - ui->variables->expandAll(); - for(int i = 0; i < 3; i++) - ui->variables->resizeColumnToContents(i); - ui->variables->collapseAll(); - } - - // if we have saved expansion state for the new shader, apply it, otherwise apply the - // previous one to get any overlap (e.g. two different shaders with very similar or - // identical constants) - if(ui->variables->hasInternalExpansion(qHash(ToQStr(m_shader)))) - ui->variables->applyExpansion( - ui->variables->getInternalExpansion(qHash(ToQStr(m_shader))), 0); - else - ui->variables->applyExpansion(prevShaderExpansionState, 0); - }); - }); - } -} - -void ConstantBufferPreviewer::on_setFormat_toggled(bool checked) -{ - if(!checked) - { - ui->splitter->setCollapsible(1, true); - ui->splitter->setSizes({1, 0}); - ui->splitter->handle(1)->setEnabled(false); - - processFormat(QString()); - return; - } - - ui->splitter->setCollapsible(1, false); - ui->splitter->setSizes({1, 1}); - ui->splitter->handle(1)->setEnabled(true); - - const ShaderReflection *reflection = m_Ctx.CurPipelineState().GetShaderReflection(m_stage); - - if(IsD3D(m_Ctx.APIProps().pipelineType)) - ui->formatSpecifier->setFormat( - BufferFormatter::DeclareStruct(Packing::D3DCB, reflection->constantBlocks[m_slot].name, - reflection->constantBlocks[m_slot].variables, 0)); - else - ui->formatSpecifier->setFormat(BufferFormatter::DeclareStruct( - BufferFormatter::EstimatePackingRules(reflection->constantBlocks[m_slot].variables), - reflection->constantBlocks[m_slot].name, reflection->constantBlocks[m_slot].variables, 0)); -} - -void ConstantBufferPreviewer::on_resourceDetails_clicked() -{ - if(!m_Ctx.HasResourceInspector()) - m_Ctx.ShowResourceInspector(); - - m_Ctx.GetResourceInspector()->Inspect(m_cbuffer); - - ToolWindowManager::raiseToolWindow(m_Ctx.GetResourceInspector()->Widget()); -} - -void ConstantBufferPreviewer::on_saveCSV_clicked() -{ - QString filename = RDDialog::getSaveFileName(this, tr("Export buffer data as CSV"), QString(), - tr("CSV Files (*.csv)")); - - if(!filename.isEmpty()) - { - QDir dirinfo = QFileInfo(filename).dir(); - if(dirinfo.exists()) - { - QFile f(filename, this); - if(f.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) - { - QTextStream ts(&f); - - ts << tr("Name,Value,Type\n"); - - for(int i = 0; i < ui->variables->topLevelItemCount(); i++) - exportCSV(ts, QString(), ui->variables->topLevelItem(i)); - - return; - } - - RDDialog::critical( - this, tr("Error exporting buffer data"), - tr("Couldn't open path %1 for write.\n%2").arg(filename).arg(f.errorString())); - } - else - { - RDDialog::critical(this, tr("Invalid directory"), - tr("Cannot find target directory to save to")); - } - } -} - -void ConstantBufferPreviewer::exportCSV(QTextStream &ts, const QString &prefix, RDTreeWidgetItem *item) -{ - if(item->childCount() == 0) - { - ts << QFormatStr("%1,\"%2\",%3\n").arg(item->text(0)).arg(item->text(1)).arg(item->text(2)); - } - else - { - ts << QFormatStr("%1,,%2\n").arg(item->text(0)).arg(item->text(2)); - for(int i = 0; i < item->childCount(); i++) - exportCSV(ts, item->text(0) + lit("."), item->child(i)); - } -} - -void ConstantBufferPreviewer::processFormat(const QString &format) -{ - if(format.isEmpty()) - { - m_formatOverride = ShaderConstant(); - ui->formatSpecifier->setErrors(QString()); - } - else - { - QString errors; - - ShaderConstant fixed, repeating; - rdctie(fixed, repeating) = BufferFormatter::ParseFormatString(format, ~0ULL, errors); - m_formatOverride = fixed; - // we don't handle true unbounded repeating data here, add it as another single element - if(repeating.type.descriptor.type != VarType::Unknown) - m_formatOverride.type.members.push_back(repeating); - ui->formatSpecifier->setErrors(errors); - } - - OnEventChanged(m_Ctx.CurEvent()); -} - -void ConstantBufferPreviewer::addVariables(RDTreeWidgetItem *root, - const rdcarray &vars) -{ - for(const ShaderVariable &v : vars) - { - RDTreeWidgetItem *n = new RDTreeWidgetItem({v.name, VarString(v), TypeString(v)}); - - root->addChild(n); - - if(v.rows > 1) - { - for(uint32_t i = 0; i < v.rows; i++) - n->addChild(new RDTreeWidgetItem( - {QFormatStr("%1.row%2").arg(v.name).arg(i), RowString(v, i), RowTypeString(v)})); - } - - if(!v.members.isEmpty()) - addVariables(n, v.members); - } -} - -void ConstantBufferPreviewer::setVariables(const rdcarray &vars) -{ - ui->variables->beginUpdate(); - - ui->variables->clear(); - - ui->saveCSV->setEnabled(false); - - if(!vars.isEmpty()) - { - addVariables(ui->variables->invisibleRootItem(), vars); - ui->saveCSV->setEnabled(true); - } - - ui->variables->endUpdate(); -} - -void ConstantBufferPreviewer::setVariablesPreserveExpansion(const rdcarray &vars, - bool wasEmpty) -{ - RDTreeViewExpansionState state; - ui->variables->saveExpansion(state, 0); - - setVariables(vars); - - if(wasEmpty) - { - // Expand before resizing so that collapsed data will already be visible when expanded - ui->variables->expandAll(); - for(int i = 0; i < 3; i++) - ui->variables->resizeColumnToContents(i); - ui->variables->collapseAll(); - } - - ui->variables->applyExpansion(state, 0); -} - -void ConstantBufferPreviewer::updateLabels() -{ - QString bufName = m_Ctx.GetResourceName(m_cbuffer); - - const ShaderReflection *reflection = m_Ctx.CurPipelineState().GetShaderReflection(m_stage); - - if(reflection != NULL) - { - if(m_Ctx.IsAutogeneratedName(m_cbuffer) && m_slot < reflection->constantBlocks.size() && - !reflection->constantBlocks[m_slot].name.isEmpty()) - bufName = QFormatStr("<%1>").arg(reflection->constantBlocks[m_slot].name); - } - - ui->nameLabel->setText(bufName); - - GraphicsAPI pipeType = m_Ctx.APIProps().pipelineType; - - QString title = QFormatStr("%1 %2 %3") - .arg(ToQStr(m_stage, pipeType)) - .arg(IsD3D(pipeType) ? lit("CB") : lit("UBO")) - .arg(m_slot); - - if(m_Ctx.CurPipelineState().SupportsResourceArrays()) - title += QFormatStr(" [%1]").arg(m_arrayIdx); - - ui->slotLabel->setText(title); - setWindowTitle(title); -} - -rdcarray ConstantBufferPreviewer::applyFormatOverride(const bytebuf &bytes) -{ - ShaderVariable var = InterpretShaderVar(m_formatOverride, bytes.begin(), bytes.end()); - - return var.members; -} diff --git a/qrenderdoc/Windows/ConstantBufferPreviewer.h b/qrenderdoc/Windows/ConstantBufferPreviewer.h deleted file mode 100644 index 42533ab30..000000000 --- a/qrenderdoc/Windows/ConstantBufferPreviewer.h +++ /dev/null @@ -1,94 +0,0 @@ -/****************************************************************************** - * The MIT License (MIT) - * - * Copyright (c) 2019-2022 Baldur Karlsson - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - ******************************************************************************/ - -#pragma once - -#include -#include "Code/Interface/QRDInterface.h" - -namespace Ui -{ -class ConstantBufferPreviewer; -} - -class QTextStream; -class RDTreeWidgetItem; -struct BufferFormatter; - -class ConstantBufferPreviewer : public QFrame, public IConstantBufferPreviewer, public ICaptureViewer -{ - Q_OBJECT - -public: - explicit ConstantBufferPreviewer(ICaptureContext &ctx, const ShaderStage stage, uint32_t slot, - uint32_t idx, QWidget *parent = 0); - ~ConstantBufferPreviewer(); - - static ConstantBufferPreviewer *has(ShaderStage stage, uint32_t slot, uint32_t idx); - - // IConstantBufferPreviewer - QWidget *Widget() override { return this; } - // ICaptureViewer - void OnCaptureLoaded() override; - void OnCaptureClosed() override; - - void OnSelectedEventChanged(uint32_t eventId) override {} - void OnEventChanged(uint32_t eventId) override; - -private slots: - // automatic slots - void on_setFormat_toggled(bool checked); - void on_saveCSV_clicked(); - void on_resourceDetails_clicked(); - - // manual slots - void processFormat(const QString &format); - -private: - void Reset(); - - Ui::ConstantBufferPreviewer *ui; - ICaptureContext &m_Ctx; - - ResourceId m_cbuffer; - ResourceId m_pipe; - ResourceId m_shader; - ShaderStage m_stage = ShaderStage::Vertex; - uint32_t m_slot = 0; - uint32_t m_arrayIdx = 0; - - void exportCSV(QTextStream &ts, const QString &prefix, RDTreeWidgetItem *item); - - rdcarray applyFormatOverride(const bytebuf &data); - - void addVariables(RDTreeWidgetItem *root, const rdcarray &vars); - void setVariables(const rdcarray &vars); - void setVariablesPreserveExpansion(const rdcarray &vars, bool wasEmpty); - - void updateLabels(); - - static QList m_Previews; - - ShaderConstant m_formatOverride; -}; diff --git a/qrenderdoc/Windows/ConstantBufferPreviewer.ui b/qrenderdoc/Windows/ConstantBufferPreviewer.ui deleted file mode 100644 index 0607cd1b0..000000000 --- a/qrenderdoc/Windows/ConstantBufferPreviewer.ui +++ /dev/null @@ -1,249 +0,0 @@ - - - ConstantBufferPreviewer - - - - 0 - 0 - 400 - 300 - - - - Constant Buffer - - - - 0 - - - 3 - - - 3 - - - 3 - - - 3 - - - - - Qt::Vertical - - - false - - - - QFrame::NoFrame - - - QFrame::Plain - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - 0 - 28 - - - - QFrame::Panel - - - QFrame::Raised - - - - 2 - - - 6 - - - 2 - - - 6 - - - 2 - - - - - Subresource - - - - - - - Qt::Vertical - - - - - - - Mip - - - - - - - Qt::Vertical - - - - - - - {} - - - true - - - Qt::ToolButtonTextOnly - - - true - - - - - - - Qt::Vertical - - - - - - - Export to CSV - - - - - - - :/save.png:/save.png - - - true - - - - - - - Open the currently visible buffer's resource details in the resource inspector - - - - :/link.png:/link.png - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - QFrame::Panel - - - QFrame::Plain - - - QAbstractItemView::NoEditTriggers - - - false - - - QAbstractItemView::ContiguousSelection - - - QAbstractItemView::ScrollPerPixel - - - true - - - false - - - - - - - - - - - - - RDTreeWidget - QTreeView -
Widgets/Extended/RDTreeWidget.h
-
- - BufferFormatSpecifier - QWidget -
Widgets/BufferFormatSpecifier.h
- 1 -
-
- - - - -
diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp index 19564ce51..663837fd7 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp @@ -2210,7 +2210,7 @@ void D3D11PipelineStateViewer::cbuffer_itemActivated(RDTreeWidgetItem *item, int return; } - IConstantBufferPreviewer *prev = m_Ctx.ViewConstantBuffer(stage->stage, cbufIdx, 0); + IBufferViewer *prev = m_Ctx.ViewConstantBuffer(stage->stage, cbufIdx, 0); m_Ctx.AddDockWindow(prev->Widget(), DockReference::TransientPopupArea, this, 0.3f); } diff --git a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp index 4b1498fa3..56c80ae5d 100644 --- a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp @@ -2314,7 +2314,7 @@ void D3D12PipelineStateViewer::cbuffer_itemActivated(RDTreeWidgetItem *item, int return; } - IConstantBufferPreviewer *prev = m_Ctx.ViewConstantBuffer(stage->stage, cb.idx, cb.arrayIdx); + IBufferViewer *prev = m_Ctx.ViewConstantBuffer(stage->stage, cb.idx, cb.arrayIdx); m_Ctx.AddDockWindow(prev->Widget(), DockReference::TransientPopupArea, this, 0.3f); } diff --git a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp index f4c006c4b..eb8e9068b 100644 --- a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp @@ -2342,7 +2342,7 @@ void GLPipelineStateViewer::ubo_itemActivated(RDTreeWidgetItem *item, int column int cb = tag.value(); - IConstantBufferPreviewer *prev = m_Ctx.ViewConstantBuffer(stage->stage, cb, 0); + IBufferViewer *prev = m_Ctx.ViewConstantBuffer(stage->stage, cb, 0); m_Ctx.AddDockWindow(prev->Widget(), DockReference::TransientPopupArea, this, 0.3f); } diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index 123722dfc..49d6aa236 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -3109,7 +3109,7 @@ void VulkanPipelineStateViewer::ubo_itemActivated(RDTreeWidgetItem *item, int co return; } - IConstantBufferPreviewer *prev = m_Ctx.ViewConstantBuffer(stage->stage, cb.slotIdx, cb.arrayIdx); + IBufferViewer *prev = m_Ctx.ViewConstantBuffer(stage->stage, cb.slotIdx, cb.arrayIdx); m_Ctx.AddDockWindow(prev->Widget(), DockReference::TransientPopupArea, this, 0.3f); } diff --git a/qrenderdoc/Windows/PythonShell.cpp b/qrenderdoc/Windows/PythonShell.cpp index 46edc0181..5d7818a47 100644 --- a/qrenderdoc/Windows/PythonShell.cpp +++ b/qrenderdoc/Windows/PythonShell.cpp @@ -789,11 +789,9 @@ struct CaptureContextInvoker : ObjectForwarder return InvokeRetFunction(&ICaptureContext::ViewTextureAsBuffer, id, sub, format); } - virtual IConstantBufferPreviewer *ViewConstantBuffer(ShaderStage stage, uint32_t slot, - uint32_t idx) override + virtual IBufferViewer *ViewConstantBuffer(ShaderStage stage, uint32_t slot, uint32_t idx) override { - return InvokeRetFunction(&ICaptureContext::ViewConstantBuffer, - stage, slot, idx); + return InvokeRetFunction(&ICaptureContext::ViewConstantBuffer, stage, slot, idx); } virtual IPixelHistoryView *ViewPixelHistory(ResourceId texID, uint32_t x, uint32_t y, diff --git a/qrenderdoc/qrenderdoc.pro b/qrenderdoc/qrenderdoc.pro index 40d269ec9..dc2de5342 100644 --- a/qrenderdoc/qrenderdoc.pro +++ b/qrenderdoc/qrenderdoc.pro @@ -218,7 +218,6 @@ SOURCES += Code/qrenderdoc.cpp \ Windows/PipelineState/GLPipelineStateViewer.cpp \ Widgets/Extended/RDTreeView.cpp \ Widgets/Extended/RDTreeWidget.cpp \ - Windows/ConstantBufferPreviewer.cpp \ Widgets/BufferFormatSpecifier.cpp \ Windows/BufferViewer.cpp \ Widgets/Extended/RDTableView.cpp \ @@ -304,7 +303,6 @@ HEADERS += Code/CaptureContext.h \ Windows/PipelineState/GLPipelineStateViewer.h \ Widgets/Extended/RDTreeView.h \ Widgets/Extended/RDTreeWidget.h \ - Windows/ConstantBufferPreviewer.h \ Widgets/BufferFormatSpecifier.h \ Windows/BufferViewer.h \ Widgets/Extended/RDTableView.h \ @@ -353,7 +351,6 @@ FORMS += Windows/Dialogs/AboutDialog.ui \ Windows/PipelineState/D3D11PipelineStateViewer.ui \ Windows/PipelineState/D3D12PipelineStateViewer.ui \ Windows/PipelineState/GLPipelineStateViewer.ui \ - Windows/ConstantBufferPreviewer.ui \ Widgets/BufferFormatSpecifier.ui \ Widgets/ComputeDebugSelector.ui \ Windows/BufferViewer.ui \ diff --git a/qrenderdoc/qrenderdoc_local.vcxproj b/qrenderdoc/qrenderdoc_local.vcxproj index e50669409..41bcba3ed 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj +++ b/qrenderdoc/qrenderdoc_local.vcxproj @@ -623,7 +623,6 @@ - @@ -746,7 +745,6 @@ - @@ -964,7 +962,6 @@ - @@ -1207,12 +1204,6 @@ MOC %(Filename).h $(IntDir)generated\moc_%(Filename).cpp - - %(Fullpath);$(QtBinDir)\moc.exe;%(AdditionalInputs) - "$(QtBinDir)\moc.exe" -DUNICODE -DWIN32 -DWIN64 -D_WIN32 -D_WIN64 -DRENDERDOC_PLATFORM_WIN32 -DSCINTILLA_QT=1 -DSCI_LEXER=1 -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1900 -I"$(ProjectDir)." -I"$(SolutionDir)\renderdoc\api\replay" -I"$(QtIncludeDir)" -I"$(QtIncludeDir)\QtWidgets" -I"$(QtIncludeDir)\QtGui" -I"$(QtIncludeDir)\QtCore" "%(Fullpath)" -o "$(IntDir)generated\moc_%(Filename).cpp" - MOC %(Filename).h - $(IntDir)generated\moc_%(Filename).cpp - %(Fullpath);$(QtBinDir)\moc.exe;%(AdditionalInputs) "$(QtBinDir)\moc.exe" -DUNICODE -DWIN32 -DWIN64 -D_WIN32 -D_WIN64 -DRENDERDOC_PLATFORM_WIN32 -DSCINTILLA_QT=1 -DSCI_LEXER=1 -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -D_MSC_VER=1900 -I"$(ProjectDir)." -I"$(SolutionDir)\renderdoc\api\replay" -I"$(QtIncludeDir)" -I"$(QtIncludeDir)\QtWidgets" -I"$(QtIncludeDir)\QtGui" -I"$(QtIncludeDir)\QtCore" "%(Fullpath)" -o "$(IntDir)generated\moc_%(Filename).cpp" @@ -1508,12 +1499,6 @@ UIC %(Filename).ui $(IntDir)generated\ui_%(Filename).h - - %(Fullpath);$(QtBinDir)\uic.exe;%(AdditionalInputs) - "$(QtBinDir)\uic.exe" "%(Fullpath)" -o "$(IntDir)generated\ui_%(Filename).h" - UIC %(Filename).ui - $(IntDir)generated\ui_%(Filename).h - %(Fullpath);$(QtBinDir)\uic.exe;%(AdditionalInputs) "$(QtBinDir)\uic.exe" "%(Fullpath)" -o "$(IntDir)generated\ui_%(Filename).h" diff --git a/qrenderdoc/qrenderdoc_local.vcxproj.filters b/qrenderdoc/qrenderdoc_local.vcxproj.filters index e1112c090..0f8d601be 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj.filters +++ b/qrenderdoc/qrenderdoc_local.vcxproj.filters @@ -186,9 +186,6 @@ Widgets\Extended - - Windows - Widgets @@ -417,9 +414,6 @@ Generated Files - - Generated Files - Generated Files @@ -983,9 +977,6 @@ Generated Files - - Generated Files - Generated Files @@ -1184,9 +1175,6 @@ Windows - - Windows - Windows @@ -1274,9 +1262,6 @@ Windows - - Windows - Windows diff --git a/renderdoc/api/replay/pipestate.inl b/renderdoc/api/replay/pipestate.inl index 65bf09d19..a0b2b0c17 100644 --- a/renderdoc/api/replay/pipestate.inl +++ b/renderdoc/api/replay/pipestate.inl @@ -1013,7 +1013,16 @@ BoundCBuffer PipeState::GetConstantBuffer(ShaderStage stage, uint32_t BufIdx, ui const Bindpoint &bind = s.bindpointMapping.constantBlocks[s.reflection->constantBlocks[BufIdx].bindPoint]; - if(s.reflection->constantBlocks[BufIdx].bufferBacked == false) + const VKPipe::BindingElement *descriptorBind = NULL; + if(bind.bindset < pipe.descriptorSets.count() && + bind.bind < pipe.descriptorSets[bind.bindset].bindings.count() && + ArrayIdx < pipe.descriptorSets[bind.bindset].bindings[bind.bind].binds.size()) + { + descriptorBind = &pipe.descriptorSets[bind.bindset].bindings[bind.bind].binds[ArrayIdx]; + } + + if(s.reflection->constantBlocks[BufIdx].bufferBacked == false || + (descriptorBind && descriptorBind->inlineBlock)) { if(s.reflection->constantBlocks[BufIdx].compileConstants) { @@ -1035,11 +1044,8 @@ BoundCBuffer PipeState::GetConstantBuffer(ShaderStage stage, uint32_t BufIdx, ui src = &pipe.descriptorSets[bind.bindset].inlineData; - const VKPipe::BindingElement &descriptorBind = - pipe.descriptorSets[bind.bindset].bindings[bind.bind].binds[ArrayIdx]; - - ret.byteOffset = descriptorBind.byteOffset; - ret.byteSize = descriptorBind.byteSize; + ret.byteOffset = descriptorBind->byteOffset; + ret.byteSize = descriptorBind->byteSize; } else { @@ -1089,17 +1095,12 @@ BoundCBuffer PipeState::GetConstantBuffer(ShaderStage stage, uint32_t BufIdx, ui return ret; } - if(bind.bindset >= pipe.descriptorSets.count() || - bind.bind >= pipe.descriptorSets[bind.bindset].bindings.count() || - ArrayIdx >= pipe.descriptorSets[bind.bindset].bindings[bind.bind].binds.size()) + if(descriptorBind == NULL) return BoundCBuffer(); - const VKPipe::BindingElement &descriptorBind = - pipe.descriptorSets[bind.bindset].bindings[bind.bind].binds[ArrayIdx]; - - ret.resourceId = descriptorBind.resourceResourceId; - ret.byteOffset = descriptorBind.byteOffset; - ret.byteSize = descriptorBind.byteSize; + ret.resourceId = descriptorBind->resourceResourceId; + ret.byteOffset = descriptorBind->byteOffset; + ret.byteSize = descriptorBind->byteSize; } } }