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:
baldurk
2022-05-09 14:51:41 +01:00
parent fcdcc9714b
commit 43fa3cd94b
23 changed files with 533 additions and 899 deletions
+140 -44
View File
@@ -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
{
+11 -7
View File
@@ -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,
+1 -2
View File
@@ -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;
+4 -24
View File
@@ -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.
+2 -1
View File
@@ -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());
+294 -21
View File
@@ -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)
{
+34
View File
@@ -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;
+16
View File
@@ -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);
}
+2 -4
View File
@@ -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,
-3
View File
@@ -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 \
-15
View File
@@ -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>