mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-06 01:50:38 +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);
|
||||
|
||||
Reference in New Issue
Block a user