mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-05 17:40:39 +00:00
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)
This commit is contained in:
@@ -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<ShaderConstant, ShaderConstant> BufferFormatter::ParseFormatString(const QString &formatString,
|
||||
uint64_t maxLen,
|
||||
QString &errors)
|
||||
rdcpair<ShaderConstant, ShaderConstant> BufferFormatter::ParseFormatString(
|
||||
const QString &formatString, uint64_t maxLen, bool cbuffer, QString &errors)
|
||||
{
|
||||
StructFormatData root;
|
||||
StructFormatData *cur = &root;
|
||||
@@ -707,6 +706,10 @@ rdcpair<ShaderConstant, ShaderConstant> 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<ShaderConstant, ShaderConstant> BufferFormatter::ParseFormatString(const
|
||||
}
|
||||
|
||||
annotations.clear();
|
||||
|
||||
if(!success)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -737,6 +743,9 @@ rdcpair<ShaderConstant, ShaderConstant> BufferFormatter::ParseFormatString(const
|
||||
|
||||
annotations.clear();
|
||||
|
||||
if(!success)
|
||||
break;
|
||||
|
||||
QString baseType = match.captured(4);
|
||||
|
||||
if(baseType.isEmpty())
|
||||
@@ -806,6 +815,9 @@ rdcpair<ShaderConstant, ShaderConstant> BufferFormatter::ParseFormatString(const
|
||||
|
||||
annotations.clear();
|
||||
|
||||
if(!success)
|
||||
break;
|
||||
|
||||
cur->structDef.type.members.push_back(el);
|
||||
|
||||
continue;
|
||||
@@ -835,9 +847,21 @@ rdcpair<ShaderConstant, ShaderConstant> 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<ShaderConstant, ShaderConstant> 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<ShaderConstant, ShaderConstant> 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<ShaderConstant, ShaderConstant> BufferFormatter::ParseFormatString(const
|
||||
}
|
||||
}
|
||||
|
||||
if(!success)
|
||||
break;
|
||||
|
||||
annotations.clear();
|
||||
|
||||
QString arrayDim = structMatch.captured(4).trimmed();
|
||||
@@ -887,6 +942,13 @@ rdcpair<ShaderConstant, ShaderConstant> 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<ShaderConstant, ShaderConstant> 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<ShaderConstant, ShaderConstant> 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<ShaderConstant> 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<ShaderConstant, ShaderConstant> 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<ShaderConstant, ShaderConstant> 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<ShaderConstant, ShaderConstant> 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<ShaderConstant> 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<QString> 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
|
||||
{
|
||||
|
||||
@@ -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<ConstantBufferPreviewer *>(newWindow))
|
||||
BufferViewer *buf = qobject_cast<BufferViewer *>(newWindow);
|
||||
|
||||
if(buf && buf->IsCBufferView())
|
||||
{
|
||||
ToolWindowManager *manager = ToolWindowManager::managerOf(refWindow);
|
||||
|
||||
ConstantBufferPreviewer *cb = manager->findChild<ConstantBufferPreviewer *>();
|
||||
BufferViewer *cb = BufferViewer::GetFirstCBufferView(buf);
|
||||
if(cb)
|
||||
{
|
||||
manager->addToolWindow(newWindow, ToolWindowManager::AreaReference(ToolWindowManager::AddTo,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -201,7 +201,8 @@ public:
|
||||
|
||||
static void Init(GraphicsAPI api) { m_API = api; }
|
||||
static rdcpair<ShaderConstant, ShaderConstant> 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<ShaderConstant> &members);
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
~BufferFormatSpecifier();
|
||||
|
||||
void setContext(ICaptureContext *ctx);
|
||||
void setTitle(QString title);
|
||||
|
||||
signals:
|
||||
void processFormat(const QString &format);
|
||||
|
||||
@@ -122,6 +122,9 @@ void RDHeaderView::cacheSections()
|
||||
|
||||
QAbstractItemModel *m = this->model();
|
||||
|
||||
if(!m)
|
||||
return;
|
||||
|
||||
int oldCount = m_sections.count();
|
||||
m_sections.resize(m->columnCount());
|
||||
|
||||
|
||||
@@ -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<ShaderVariable> 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<ShaderConsta
|
||||
UnrollConstant("", 0, constant, columns, props);
|
||||
}
|
||||
|
||||
QList<BufferViewer *> 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<ShaderVariable> 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)
|
||||
{
|
||||
|
||||
@@ -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<BufferViewer *> 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;
|
||||
|
||||
@@ -875,6 +875,22 @@ Enter 0.0 to use automatic/guessed value derived from data.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="setFormat">
|
||||
<property name="text">
|
||||
<string>{}</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextOnly</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="resourceDetails">
|
||||
<property name="toolTip">
|
||||
|
||||
@@ -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 <QFontDatabase>
|
||||
#include <QTextStream>
|
||||
#include "Code/QRDUtils.h"
|
||||
#include "toolwindowmanager/ToolWindowManager.h"
|
||||
#include "ui_ConstantBufferPreviewer.h"
|
||||
|
||||
QList<ConstantBufferPreviewer *> 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<ShaderVariable> 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<ShaderVariable> 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<ShaderVariable> &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<ShaderVariable> &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<ShaderVariable> &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<ShaderVariable> ConstantBufferPreviewer::applyFormatOverride(const bytebuf &bytes)
|
||||
{
|
||||
ShaderVariable var = InterpretShaderVar(m_formatOverride, bytes.begin(), bytes.end());
|
||||
|
||||
return var.members;
|
||||
}
|
||||
@@ -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 <QFrame>
|
||||
#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<ShaderVariable> applyFormatOverride(const bytebuf &data);
|
||||
|
||||
void addVariables(RDTreeWidgetItem *root, const rdcarray<ShaderVariable> &vars);
|
||||
void setVariables(const rdcarray<ShaderVariable> &vars);
|
||||
void setVariablesPreserveExpansion(const rdcarray<ShaderVariable> &vars, bool wasEmpty);
|
||||
|
||||
void updateLabels();
|
||||
|
||||
static QList<ConstantBufferPreviewer *> m_Previews;
|
||||
|
||||
ShaderConstant m_formatOverride;
|
||||
};
|
||||
@@ -1,249 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ConstantBufferPreviewer</class>
|
||||
<widget class="QFrame" name="ConstantBufferPreviewer">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>300</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Constant Buffer</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QSplitter" name="splitter">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="childrenCollapsible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QFrame" name="frame">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="toolbar">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>28</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Panel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="slotLabel">
|
||||
<property name="text">
|
||||
<string>Subresource</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="nameLabel">
|
||||
<property name="text">
|
||||
<string>Mip</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="setFormat">
|
||||
<property name="text">
|
||||
<string>{}</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonTextOnly</enum>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="saveCSV">
|
||||
<property name="toolTip">
|
||||
<string>Export to CSV</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../Resources/resources.qrc">
|
||||
<normaloff>:/save.png</normaloff>:/save.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="resourceDetails">
|
||||
<property name="toolTip">
|
||||
<string>Open the currently visible buffer's resource details in the resource inspector</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../Resources/resources.qrc">
|
||||
<normaloff>:/link.png</normaloff>:/link.png</iconset>
|
||||
</property>
|
||||
<property name="autoRaise">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="RDTreeWidget" name="variables">
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::Panel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::ContiguousSelection</enum>
|
||||
</property>
|
||||
<property name="verticalScrollMode">
|
||||
<enum>QAbstractItemView::ScrollPerPixel</enum>
|
||||
</property>
|
||||
<property name="allColumnsShowFocus">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="headerStretchLastSection">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="BufferFormatSpecifier" name="formatSpecifier" native="true"/>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>RDTreeWidget</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>Widgets/Extended/RDTreeWidget.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>BufferFormatSpecifier</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>Widgets/BufferFormatSpecifier.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="../Resources/resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -2342,7 +2342,7 @@ void GLPipelineStateViewer::ubo_itemActivated(RDTreeWidgetItem *item, int column
|
||||
|
||||
int cb = tag.value<int>();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -789,11 +789,9 @@ struct CaptureContextInvoker : ObjectForwarder<ICaptureContext>
|
||||
return InvokeRetFunction<IBufferViewer *>(&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<IConstantBufferPreviewer *>(&ICaptureContext::ViewConstantBuffer,
|
||||
stage, slot, idx);
|
||||
return InvokeRetFunction<IBufferViewer *>(&ICaptureContext::ViewConstantBuffer, stage, slot, idx);
|
||||
}
|
||||
|
||||
virtual IPixelHistoryView *ViewPixelHistory(ResourceId texID, uint32_t x, uint32_t y,
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -623,7 +623,6 @@
|
||||
<ClCompile Include="$(IntDir)generated\moc_FindReplace.cpp" />
|
||||
<ClCompile Include="$(IntDir)generated\moc_BufferViewer.cpp" />
|
||||
<ClCompile Include="$(IntDir)generated\moc_CaptureDialog.cpp" />
|
||||
<ClCompile Include="$(IntDir)generated\moc_ConstantBufferPreviewer.cpp" />
|
||||
<ClCompile Include="$(IntDir)generated\moc_CustomPaintWidget.cpp" />
|
||||
<ClCompile Include="$(IntDir)generated\moc_D3D11PipelineStateViewer.cpp" />
|
||||
<ClCompile Include="$(IntDir)generated\moc_D3D12PipelineStateViewer.cpp" />
|
||||
@@ -746,7 +745,6 @@
|
||||
<ClCompile Include="Windows\APIInspector.cpp" />
|
||||
<ClCompile Include="Windows\ResourceInspector.cpp" />
|
||||
<ClCompile Include="Windows\BufferViewer.cpp" />
|
||||
<ClCompile Include="Windows\ConstantBufferPreviewer.cpp" />
|
||||
<ClCompile Include="Windows\DebugMessageView.cpp" />
|
||||
<ClCompile Include="Windows\LogView.cpp" />
|
||||
<ClCompile Include="Windows\CommentView.cpp" />
|
||||
@@ -964,7 +962,6 @@
|
||||
<ClInclude Include="$(IntDir)generated\ui_FindReplace.h" />
|
||||
<ClInclude Include="$(IntDir)generated\ui_BufferViewer.h" />
|
||||
<ClInclude Include="$(IntDir)generated\ui_CaptureDialog.h" />
|
||||
<ClInclude Include="$(IntDir)generated\ui_ConstantBufferPreviewer.h" />
|
||||
<ClInclude Include="$(IntDir)generated\ui_D3D11PipelineStateViewer.h" />
|
||||
<ClInclude Include="$(IntDir)generated\ui_D3D12PipelineStateViewer.h" />
|
||||
<ClInclude Include="$(IntDir)generated\ui_DebugMessageView.h" />
|
||||
@@ -1207,12 +1204,6 @@
|
||||
<Message>MOC %(Filename).h</Message>
|
||||
<Outputs>$(IntDir)generated\moc_%(Filename).cpp</Outputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="Windows\ConstantBufferPreviewer.h">
|
||||
<AdditionalInputs>%(Fullpath);$(QtBinDir)\moc.exe;%(AdditionalInputs)</AdditionalInputs>
|
||||
<Command>"$(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"</Command>
|
||||
<Message>MOC %(Filename).h</Message>
|
||||
<Outputs>$(IntDir)generated\moc_%(Filename).cpp</Outputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="Windows\DebugMessageView.h">
|
||||
<AdditionalInputs>%(Fullpath);$(QtBinDir)\moc.exe;%(AdditionalInputs)</AdditionalInputs>
|
||||
<Command>"$(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"</Command>
|
||||
@@ -1508,12 +1499,6 @@
|
||||
<Message>UIC %(Filename).ui</Message>
|
||||
<Outputs>$(IntDir)generated\ui_%(Filename).h</Outputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="Windows\ConstantBufferPreviewer.ui">
|
||||
<AdditionalInputs>%(Fullpath);$(QtBinDir)\uic.exe;%(AdditionalInputs)</AdditionalInputs>
|
||||
<Command>"$(QtBinDir)\uic.exe" "%(Fullpath)" -o "$(IntDir)generated\ui_%(Filename).h"</Command>
|
||||
<Message>UIC %(Filename).ui</Message>
|
||||
<Outputs>$(IntDir)generated\ui_%(Filename).h</Outputs>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="Windows\DebugMessageView.ui">
|
||||
<AdditionalInputs>%(Fullpath);$(QtBinDir)\uic.exe;%(AdditionalInputs)</AdditionalInputs>
|
||||
<Command>"$(QtBinDir)\uic.exe" "%(Fullpath)" -o "$(IntDir)generated\ui_%(Filename).h"</Command>
|
||||
|
||||
@@ -186,9 +186,6 @@
|
||||
<ClCompile Include="Widgets\Extended\RDTreeWidget.cpp">
|
||||
<Filter>Widgets\Extended</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Windows\ConstantBufferPreviewer.cpp">
|
||||
<Filter>Windows</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Widgets\BufferFormatSpecifier.cpp">
|
||||
<Filter>Widgets</Filter>
|
||||
</ClCompile>
|
||||
@@ -417,9 +414,6 @@
|
||||
<ClCompile Include="$(IntDir)generated\moc_ComputeDebugSelector.cpp">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(IntDir)generated\moc_ConstantBufferPreviewer.cpp">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(IntDir)generated\moc_CustomPaintWidget.cpp">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClCompile>
|
||||
@@ -983,9 +977,6 @@
|
||||
<ClInclude Include="$(IntDir)generated\ui_ComputeDebugSelector.h">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(IntDir)generated\ui_ConstantBufferPreviewer.h">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="$(IntDir)generated\ui_D3D11PipelineStateViewer.h">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClInclude>
|
||||
@@ -1184,9 +1175,6 @@
|
||||
<CustomBuild Include="Windows\BufferViewer.ui">
|
||||
<Filter>Windows</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="Windows\ConstantBufferPreviewer.ui">
|
||||
<Filter>Windows</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="Windows\DebugMessageView.ui">
|
||||
<Filter>Windows</Filter>
|
||||
</CustomBuild>
|
||||
@@ -1274,9 +1262,6 @@
|
||||
<CustomBuild Include="Windows\BufferViewer.h">
|
||||
<Filter>Windows</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="Windows\ConstantBufferPreviewer.h">
|
||||
<Filter>Windows</Filter>
|
||||
</CustomBuild>
|
||||
<CustomBuild Include="Windows\DebugMessageView.h">
|
||||
<Filter>Windows</Filter>
|
||||
</CustomBuild>
|
||||
|
||||
Reference in New Issue
Block a user