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);