mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-22 06:26:30 +00:00
Explicitly handle unbounded arrays and display declared fixed vars
* GL and Vulkan allow buffers to have fixed variables before a trailing AoS unbounded array. These fixed variables can't be easily displayed in a table and previously we skipped them. Now we display these in a tree format. * We also support formats which don't have an unbounded array at all and display these just with the tree. This will allow the BufferViewer to subsume the capabilities of the ConstantBufferPreviewer (though it needs to handle opaque non-buffer-backed variables, and slot-following).
This commit is contained in:
@@ -365,8 +365,56 @@ Packing::Rules BufferFormatter::EstimatePackingRules(const rdcarray<ShaderConsta
|
||||
return pack;
|
||||
}
|
||||
|
||||
ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, uint64_t maxLen,
|
||||
QString &errors)
|
||||
bool BufferFormatter::ContainsUnbounded(const rdcarray<ShaderConstant> &members)
|
||||
{
|
||||
for(size_t i = 0; i < members.size(); i++)
|
||||
{
|
||||
if(members[i].type.descriptor.elements == ~0U)
|
||||
return true;
|
||||
|
||||
if(ContainsUnbounded(members[i].type.members))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BufferFormatter::CheckInvalidUnbounded(const ShaderConstant &structDef, QString &errors)
|
||||
{
|
||||
for(size_t i = 0; i < structDef.type.members.size(); i++)
|
||||
{
|
||||
const bool isLast = i == structDef.type.members.size() - 1;
|
||||
|
||||
// if it's not the last member and it's unbounded, that's a problem!
|
||||
if(!isLast && structDef.type.members[i].type.descriptor.elements == ~0U)
|
||||
{
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
// if it's not the last member, no child can have an unbounded array
|
||||
if(!isLast && ContainsUnbounded(structDef.type.members[i].type.members))
|
||||
{
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!CheckInvalidUnbounded(structDef.type.members[i], errors))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
rdcpair<ShaderConstant, ShaderConstant> BufferFormatter::ParseFormatString(const QString &formatString,
|
||||
uint64_t maxLen,
|
||||
QString &errors)
|
||||
{
|
||||
StructFormatData root;
|
||||
StructFormatData *cur = &root;
|
||||
@@ -397,7 +445,7 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u
|
||||
"(?<vec>[1-9])?" // might be a vector
|
||||
"(?<mat>x[1-9])?" // or a matrix
|
||||
"(?<name>\\s+[A-Za-z@_][A-Za-z0-9@_]*)?" // get identifier name
|
||||
"(?<array>\\s*\\[[0-9]+\\])?" // optional array dimension
|
||||
"(?<array>\\s*\\[[0-9]*\\])?" // optional array dimension
|
||||
"(\\s*:\\s*" // optional specifier after :
|
||||
"(" // bitfield or semantic
|
||||
"(?<bitfield>[1-9][0-9]*)|" // bitfield packing
|
||||
@@ -431,12 +479,12 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u
|
||||
QRegularExpression structDeclRegex(
|
||||
lit("^(struct|enum)\\s+([A-Za-z_][A-Za-z0-9_]*)(\\s*:\\s*([a-z]+))?$"));
|
||||
QRegularExpression structUseRegex(
|
||||
lit("^" // start of the line
|
||||
"([A-Za-z_][A-Za-z0-9_]*)" // struct type name
|
||||
"\\s*(\\*)?" // maybe a pointer
|
||||
"\\s+([A-Za-z@_][A-Za-z0-9@_]*)" // variable name
|
||||
"(\\s*\\[[0-9]+\\])?" // optional array dimension
|
||||
"(\\s*:\\s*([1-9][0-9]*))?" // optional bitfield packing
|
||||
lit("^" // start of the line
|
||||
"([A-Za-z_][A-Za-z0-9_]*)" // struct type name
|
||||
"\\s*(\\*)?" // maybe a pointer
|
||||
"\\s+([A-Za-z@_][A-Za-z0-9@_]*)?" // variable name
|
||||
"(\\s*\\[[0-9]*\\])?" // optional array dimension
|
||||
"(\\s*:\\s*([1-9][0-9]*))?" // optional bitfield packing
|
||||
"$"));
|
||||
QRegularExpression enumValueRegex(
|
||||
lit("^" // start of the line
|
||||
@@ -800,7 +848,10 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u
|
||||
|
||||
bool isPointer = !structMatch.captured(2).trimmed().isEmpty();
|
||||
|
||||
QString varName = structMatch.captured(3);
|
||||
QString varName = structMatch.captured(3).trimmed();
|
||||
|
||||
if(varName.isEmpty())
|
||||
varName = lit("data");
|
||||
|
||||
uint32_t specifiedOffset = ~0U;
|
||||
for(const Annotation &annot : annotations)
|
||||
@@ -828,6 +879,8 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u
|
||||
if(!arrayDim.isEmpty())
|
||||
{
|
||||
arrayDim = arrayDim.mid(1, arrayDim.count() - 2);
|
||||
if(arrayDim.isEmpty())
|
||||
arrayDim = lit("%1").arg(~0U);
|
||||
bool ok = false;
|
||||
arrayCount = arrayDim.toUInt(&ok);
|
||||
if(!ok)
|
||||
@@ -993,7 +1046,13 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u
|
||||
QString arrayDim = !match.captured(lit("array")).isEmpty()
|
||||
? match.captured(lit("array")).trimmed()
|
||||
: lit("[1]");
|
||||
arrayDim = arrayDim.mid(1, arrayDim.count() - 2);
|
||||
|
||||
{
|
||||
bool isArray = !arrayDim.isEmpty();
|
||||
arrayDim = arrayDim.mid(1, arrayDim.count() - 2).trimmed();
|
||||
if(isArray && arrayDim.isEmpty())
|
||||
arrayDim = lit("%1").arg(~0U);
|
||||
}
|
||||
|
||||
const bool isUnsigned = match.captured(lit("sign")).trimmed() == lit("unsigned");
|
||||
|
||||
@@ -1505,9 +1564,122 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u
|
||||
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));
|
||||
|
||||
if(!root.structDef.type.members.isEmpty() &&
|
||||
root.structDef.type.members.back().type.descriptor.elements == ~0U)
|
||||
{
|
||||
root.structDef.type.descriptor.arrayByteStride =
|
||||
AlignUp(root.structDef.type.members.back().type.descriptor.arrayByteStride,
|
||||
GetAlignment(pack, root.structDef));
|
||||
}
|
||||
|
||||
if(success)
|
||||
{
|
||||
// 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
|
||||
// infinite struct. E.g. this is invalid:
|
||||
//
|
||||
// struct foo {
|
||||
// uint a;
|
||||
// float b[];
|
||||
// };
|
||||
//
|
||||
// foo data[];
|
||||
//
|
||||
// but it's valid to have either this:
|
||||
//
|
||||
// struct foo {
|
||||
// uint a;
|
||||
// float b[3];
|
||||
// };
|
||||
//
|
||||
// foo data[];
|
||||
//
|
||||
// or this:
|
||||
//
|
||||
// struct foo {
|
||||
// uint a;
|
||||
// float b[];
|
||||
// };
|
||||
//
|
||||
// foo data;
|
||||
if(success)
|
||||
{
|
||||
ShaderConstant *iter = &root.structDef;
|
||||
bool foundInfinite = false;
|
||||
QString infiniteArrayName;
|
||||
|
||||
while(iter)
|
||||
{
|
||||
if(iter->type.descriptor.elements == ~0U)
|
||||
{
|
||||
if(foundInfinite)
|
||||
{
|
||||
success = false;
|
||||
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;
|
||||
}
|
||||
|
||||
infiniteArrayName = iter->type.descriptor.name;
|
||||
if(infiniteArrayName.isEmpty())
|
||||
infiniteArrayName = tr("implicit_root");
|
||||
|
||||
foundInfinite = true;
|
||||
}
|
||||
|
||||
// if there are no more members, stop looking
|
||||
if(iter->type.members.empty())
|
||||
break;
|
||||
|
||||
iter = &iter->type.members.back();
|
||||
}
|
||||
}
|
||||
|
||||
if(!success || root.structDef.type.members.isEmpty())
|
||||
{
|
||||
root.structDef.type.members.clear();
|
||||
@@ -1519,6 +1691,7 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u
|
||||
el.name = "data";
|
||||
el.type.descriptor.type = VarType::UInt;
|
||||
el.type.descriptor.columns = 4;
|
||||
el.type.descriptor.elements = ~0U;
|
||||
|
||||
if(maxLen > 0 && maxLen < 16)
|
||||
el.type.descriptor.columns = 1;
|
||||
@@ -1532,7 +1705,41 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u
|
||||
root.structDef.type.descriptor.arrayByteStride = el.type.descriptor.arrayByteStride;
|
||||
}
|
||||
|
||||
return root.structDef;
|
||||
// split the struct definition we have now into fixed and repeating. We've enforced above that
|
||||
// there's only one struct which is unbounded (no other children at any level are unbounded) and
|
||||
// it's the last member, so we find it, remove it from the hierarchy, and present it separately.
|
||||
ShaderConstant repeat;
|
||||
|
||||
{
|
||||
ShaderConstant *iter = &root.structDef;
|
||||
rdcstr addedprefix;
|
||||
|
||||
while(iter)
|
||||
{
|
||||
// if there are no more members, stop looking, there's no repeated member
|
||||
if(iter->type.members.empty())
|
||||
break;
|
||||
|
||||
// add the prefix, so that the repeated element that's a child like buffer { foo { blah[] } }
|
||||
// shows up as buffer.foo.blah
|
||||
if(!iter->name.empty())
|
||||
addedprefix += iter->name + ".";
|
||||
|
||||
// we want to search the members so we can remove from the current iter
|
||||
if(iter->type.members.back().type.descriptor.elements == ~0U)
|
||||
{
|
||||
repeat = iter->type.members.back();
|
||||
repeat.name = addedprefix + repeat.name;
|
||||
repeat.type.descriptor.elements = 1;
|
||||
iter->type.members.pop_back();
|
||||
break;
|
||||
}
|
||||
|
||||
iter = &iter->type.members.back();
|
||||
}
|
||||
}
|
||||
|
||||
return {root.structDef, repeat};
|
||||
}
|
||||
|
||||
QString BufferFormatter::GetTextureFormatString(const TextureDescription &tex)
|
||||
@@ -1661,73 +1868,19 @@ QString BufferFormatter::GetTextureFormatString(const TextureDescription &tex)
|
||||
}
|
||||
|
||||
QString BufferFormatter::GetBufferFormatString(Packing::Rules pack, const ShaderResource &res,
|
||||
const ResourceFormat &viewFormat,
|
||||
uint64_t &baseByteOffset)
|
||||
const ResourceFormat &viewFormat)
|
||||
{
|
||||
QString format;
|
||||
|
||||
if(!res.variableType.members.empty())
|
||||
{
|
||||
QString structName = res.variableType.descriptor.name;
|
||||
|
||||
if(structName.isEmpty())
|
||||
structName = lit("el");
|
||||
|
||||
QList<QString> declaredStructs;
|
||||
if(m_API == GraphicsAPI::Vulkan || m_API == GraphicsAPI::OpenGL)
|
||||
{
|
||||
const rdcarray<ShaderConstant> &members = res.variableType.members;
|
||||
|
||||
// if there is only one member in the root array, we can just call DeclareStruct directly
|
||||
if(members.count() <= 1)
|
||||
{
|
||||
format = DeclareStruct(pack, declaredStructs, res.name, members, 0, QString());
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise we need to build up the comment indicating which fixed-size members we
|
||||
// skipped
|
||||
QString fixedPrefixString = tr(" // members skipped as they are fixed size:\n");
|
||||
baseByteOffset += members.back().byteOffset;
|
||||
|
||||
// list each member before the last, commented out.
|
||||
for(int i = 0; i < members.count() - 1; i++)
|
||||
{
|
||||
QString arraySize;
|
||||
if(members[i].type.descriptor.elements > 1 && members[i].type.descriptor.elements != ~0U)
|
||||
arraySize = QFormatStr("[%1]").arg(members[i].type.descriptor.elements);
|
||||
|
||||
QString varName = members[i].name;
|
||||
|
||||
if(varName.isEmpty())
|
||||
varName = QFormatStr("_child%1").arg(i);
|
||||
|
||||
fixedPrefixString += QFormatStr(" // %1 %2%3;\n")
|
||||
.arg(members[i].type.descriptor.name)
|
||||
.arg(varName)
|
||||
.arg(arraySize);
|
||||
}
|
||||
|
||||
fixedPrefixString +=
|
||||
lit(" // final array struct @ byte offset %1\n").arg(members.back().byteOffset);
|
||||
|
||||
// construct a fake list of members with only the last arrayed one, to pass to
|
||||
// DeclareStruct
|
||||
rdcarray<ShaderConstant> fakeLastMember;
|
||||
fakeLastMember.push_back(members.back());
|
||||
// rebase offset of this member to 0 so that DeclareStruct doesn't think any padding is
|
||||
// needed
|
||||
fakeLastMember[0].byteOffset = 0;
|
||||
|
||||
if(fakeLastMember[0].type.descriptor.elements != ~0U)
|
||||
fakeLastMember[0].type.descriptor.arrayByteStride *=
|
||||
fakeLastMember[0].type.descriptor.elements;
|
||||
|
||||
format = DeclareStruct(pack, declaredStructs, res.name, fakeLastMember,
|
||||
fakeLastMember[0].type.descriptor.arrayByteStride, fixedPrefixString);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
format = DeclareStruct(pack, declaredStructs, res.variableType.descriptor.name,
|
||||
res.variableType.members, 0, QString());
|
||||
}
|
||||
|
||||
format = DeclareStruct(pack, declaredStructs, structName, res.variableType.members, 0, QString());
|
||||
format = QFormatStr("%1\n\n%2").arg(DeclarePacking(pack)).arg(format);
|
||||
}
|
||||
else
|
||||
@@ -2013,8 +2166,13 @@ QString BufferFormatter::DeclareStruct(Packing::Rules pack, QList<QString> &decl
|
||||
}
|
||||
|
||||
QString arraySize;
|
||||
if(members[i].type.descriptor.elements > 1 && members[i].type.descriptor.elements != ~0U)
|
||||
arraySize = QFormatStr("[%1]").arg(members[i].type.descriptor.elements);
|
||||
if(members[i].type.descriptor.elements > 1)
|
||||
{
|
||||
if(members[i].type.descriptor.elements != ~0U)
|
||||
arraySize = QFormatStr("[%1]").arg(members[i].type.descriptor.elements);
|
||||
else
|
||||
arraySize = lit("[]");
|
||||
}
|
||||
|
||||
QString varTypeName = members[i].type.descriptor.name;
|
||||
|
||||
@@ -2279,6 +2437,9 @@ ShaderVariable InterpretShaderVar(const ShaderConstant &elem, const byte *data,
|
||||
ret.members = members;
|
||||
}
|
||||
}
|
||||
else if(elem.type.descriptor.type == VarType::Struct && elem.type.members.isEmpty())
|
||||
{
|
||||
}
|
||||
else if(elem.type.descriptor.elements > 1 && elem.type.descriptor.elements != ~0U)
|
||||
{
|
||||
rdcarray<ShaderVariable> arrayElements;
|
||||
|
||||
@@ -141,7 +141,7 @@ struct Rules
|
||||
vector_straddle_16b == o.vector_straddle_16b && tight_arrays == o.tight_arrays &&
|
||||
trailing_overlap == o.trailing_overlap;
|
||||
}
|
||||
|
||||
bool operator!=(Packing::Rules o) const { return !(*this == o); }
|
||||
// is a vector's alignment equal to its component alignment? If not, vectors must have an
|
||||
// a larger alignment e.g. for floats a float2 has 8 byte alignment, float3 and float4 have
|
||||
// 16-byte alignment
|
||||
@@ -198,14 +198,16 @@ public:
|
||||
BufferFormatter() = default;
|
||||
|
||||
static void Init(GraphicsAPI api) { m_API = api; }
|
||||
static ShaderConstant ParseFormatString(const QString &formatString, uint64_t maxLen,
|
||||
QString &errors);
|
||||
static rdcpair<ShaderConstant, ShaderConstant> ParseFormatString(const QString &formatString,
|
||||
uint64_t maxLen, QString &errors);
|
||||
static bool CheckInvalidUnbounded(const ShaderConstant &structDef, QString &errors);
|
||||
static bool ContainsUnbounded(const rdcarray<ShaderConstant> &members);
|
||||
|
||||
static Packing::Rules EstimatePackingRules(const rdcarray<ShaderConstant> &members);
|
||||
|
||||
static QString GetTextureFormatString(const TextureDescription &tex);
|
||||
static QString GetBufferFormatString(Packing::Rules pack, const ShaderResource &res,
|
||||
const ResourceFormat &viewFormat, uint64_t &baseByteOffset);
|
||||
const ResourceFormat &viewFormat);
|
||||
|
||||
static QString DeclareStruct(Packing::Rules pack, const QString &name,
|
||||
const rdcarray<ShaderConstant> &members, uint32_t requiredByteStride);
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
#include <QPainter>
|
||||
|
||||
RDSplitterHandle::RDSplitterHandle(Qt::Orientation orientation, QSplitter *parent)
|
||||
: QSplitterHandle(orientation, parent), m_index(-1), m_isCollapsed(false)
|
||||
: QSplitterHandle(orientation, parent), m_parent(parent), m_index(-1), m_isCollapsed(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -72,71 +72,100 @@ void RDSplitterHandle::paintEvent(QPaintEvent *event)
|
||||
int w = width();
|
||||
int h = height();
|
||||
|
||||
if(orientation() == Qt::Vertical)
|
||||
if(!m_title.isEmpty())
|
||||
{
|
||||
painter.drawText(QRect(0, 0, w, 25), Qt::AlignHCenter, m_title);
|
||||
}
|
||||
else
|
||||
{
|
||||
painter.drawText(QRect(0, h / 2 - 12, w, 25), Qt::AlignHCenter, m_title);
|
||||
if(orientation() == Qt::Vertical)
|
||||
{
|
||||
painter.drawText(QRect(0, 0, w, 25), Qt::AlignHCenter, m_title);
|
||||
}
|
||||
else
|
||||
{
|
||||
painter.drawText(QRect(0, h / 2 - 12, w, 25), Qt::AlignHCenter, m_title);
|
||||
}
|
||||
}
|
||||
|
||||
painter.setRenderHint(QPainter::Antialiasing, true);
|
||||
|
||||
// draw the arrow
|
||||
if(orientation() == Qt::Vertical)
|
||||
if(m_parent->childrenCollapsible())
|
||||
{
|
||||
if(m_isCollapsed)
|
||||
{
|
||||
m_arrowPoints[0] = QPoint(w / 2, h - 9);
|
||||
m_arrowPoints[1] = QPoint(w / 2 - 10, h - 1);
|
||||
m_arrowPoints[2] = QPoint(w / 2 + 10, h - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_arrowPoints[0] = QPoint(w / 2, h - 1);
|
||||
m_arrowPoints[1] = QPoint(w / 2 - 10, h - 9);
|
||||
m_arrowPoints[2] = QPoint(w / 2 + 10, h - 9);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(m_isCollapsed)
|
||||
{
|
||||
m_arrowPoints[0] = QPoint(w - 9, h / 2 + 15);
|
||||
m_arrowPoints[1] = QPoint(w - 1, h / 2 + 5);
|
||||
m_arrowPoints[2] = QPoint(w - 1, h / 2 + 20);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_arrowPoints[0] = QPoint(w - 1, h / 2 + 15);
|
||||
m_arrowPoints[1] = QPoint(w - 9, h / 2 + 5);
|
||||
m_arrowPoints[2] = QPoint(w - 9, h / 2 + 25);
|
||||
}
|
||||
}
|
||||
bool arrowUpLeft = (m_index == 0);
|
||||
|
||||
painter.drawPolygon(m_arrowPoints, 3);
|
||||
if(m_isCollapsed)
|
||||
arrowUpLeft = !arrowUpLeft;
|
||||
|
||||
if(orientation() == Qt::Vertical)
|
||||
{
|
||||
if(arrowUpLeft)
|
||||
{
|
||||
m_arrowPoints[0] = QPoint(w / 2, h - 9);
|
||||
m_arrowPoints[1] = QPoint(w / 2 - 10, h - 1);
|
||||
m_arrowPoints[2] = QPoint(w / 2 + 10, h - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_arrowPoints[0] = QPoint(w / 2, h - 1);
|
||||
m_arrowPoints[1] = QPoint(w / 2 - 10, h - 9);
|
||||
m_arrowPoints[2] = QPoint(w / 2 + 10, h - 9);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(arrowUpLeft)
|
||||
{
|
||||
m_arrowPoints[0] = QPoint(w - 9, h / 2 + 15);
|
||||
m_arrowPoints[1] = QPoint(w - 1, h / 2 + 5);
|
||||
m_arrowPoints[2] = QPoint(w - 1, h / 2 + 20);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_arrowPoints[0] = QPoint(w - 1, h / 2 + 15);
|
||||
m_arrowPoints[1] = QPoint(w - 9, h / 2 + 5);
|
||||
m_arrowPoints[2] = QPoint(w - 9, h / 2 + 25);
|
||||
}
|
||||
}
|
||||
|
||||
painter.drawPolygon(m_arrowPoints, 3);
|
||||
}
|
||||
|
||||
// draw the bullets
|
||||
if(orientation() == Qt::Vertical)
|
||||
{
|
||||
painter.drawEllipse(QPoint(w / 4 - 10, h - 10), 3, 3);
|
||||
painter.drawEllipse(QPoint(w / 4, h - 10), 3, 3);
|
||||
painter.drawEllipse(QPoint(w / 4 + 10, h - 10), 3, 3);
|
||||
// in the middle if we're not collapsible in one direction
|
||||
int y = h / 2;
|
||||
|
||||
painter.drawEllipse(QPoint(3 * w / 4 - 10, h - 10), 3, 3);
|
||||
painter.drawEllipse(QPoint(3 * w / 4, h - 10), 3, 3);
|
||||
painter.drawEllipse(QPoint(3 * w / 4 + 10, h - 10), 3, 3);
|
||||
// or away from the arrow
|
||||
if(m_index == 0)
|
||||
y = 10;
|
||||
else if(m_index == 1)
|
||||
y = h - 10;
|
||||
|
||||
painter.drawEllipse(QPoint(w / 4 - 10, y), 3, 3);
|
||||
painter.drawEllipse(QPoint(w / 4, y), 3, 3);
|
||||
painter.drawEllipse(QPoint(w / 4 + 10, y), 3, 3);
|
||||
|
||||
painter.drawEllipse(QPoint(3 * w / 4 - 10, y), 3, 3);
|
||||
painter.drawEllipse(QPoint(3 * w / 4, y), 3, 3);
|
||||
painter.drawEllipse(QPoint(3 * w / 4 + 10, y), 3, 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
painter.drawEllipse(QPoint(w - 10, h / 4 - 10), 3, 3);
|
||||
painter.drawEllipse(QPoint(w - 10, h / 4), 3, 3);
|
||||
painter.drawEllipse(QPoint(w - 10, h / 4 + 10), 3, 3);
|
||||
// in the middle if we're not collapsible in one direction
|
||||
int x = w / 2;
|
||||
|
||||
painter.drawEllipse(QPoint(w - 10, 3 * h / 4 - 10), 3, 3);
|
||||
painter.drawEllipse(QPoint(w - 10, 3 * h / 4), 3, 3);
|
||||
painter.drawEllipse(QPoint(w - 10, 3 * h / 4 + 10), 3, 3);
|
||||
// or away from the arrow
|
||||
if(m_index == 0)
|
||||
x = 10;
|
||||
else if(m_index == 1)
|
||||
x = w - 10;
|
||||
|
||||
painter.drawEllipse(QPoint(x, h / 4 - 10), 3, 3);
|
||||
painter.drawEllipse(QPoint(x, h / 4), 3, 3);
|
||||
painter.drawEllipse(QPoint(x, h / 4 + 10), 3, 3);
|
||||
|
||||
painter.drawEllipse(QPoint(x, 3 * h / 4 - 10), 3, 3);
|
||||
painter.drawEllipse(QPoint(x, 3 * h / 4), 3, 3);
|
||||
painter.drawEllipse(QPoint(x, 3 * h / 4 + 10), 3, 3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +188,7 @@ RDSplitter::RDSplitter(QWidget *parent) : QSplitter(parent)
|
||||
|
||||
void RDSplitter::handleDoubleClicked(int index)
|
||||
{
|
||||
if(index < 0 || index >= count())
|
||||
if(index < 0 || index >= count() || !childrenCollapsible())
|
||||
return;
|
||||
|
||||
RDSplitterHandle *rdHandle = (RDSplitterHandle *)handle(index);
|
||||
@@ -184,6 +213,9 @@ void RDSplitter::handleDoubleClicked(int index)
|
||||
|
||||
void RDSplitter::setHandleCollapsed(int pos, int index)
|
||||
{
|
||||
if(!childrenCollapsible())
|
||||
return;
|
||||
|
||||
QList<int> totalSizes = sizes();
|
||||
RDSplitterHandle *rdHandle = (RDSplitterHandle *)handle(index);
|
||||
if(totalSizes[index] == 0)
|
||||
|
||||
@@ -50,6 +50,7 @@ protected:
|
||||
virtual void paintEvent(QPaintEvent *event);
|
||||
virtual void mouseDoubleClickEvent(QMouseEvent *event);
|
||||
|
||||
QSplitter *m_parent;
|
||||
QString m_title;
|
||||
int m_index;
|
||||
bool m_isCollapsed;
|
||||
|
||||
@@ -32,10 +32,13 @@
|
||||
#include <QMutexLocker>
|
||||
#include <QPushButton>
|
||||
#include <QScrollBar>
|
||||
#include <QSplitter>
|
||||
#include <QTimer>
|
||||
#include <QtMath>
|
||||
#include "Code/QRDUtils.h"
|
||||
#include "Code/Resources.h"
|
||||
#include "Widgets/CollapseGroupBox.h"
|
||||
#include "Widgets/Extended/RDSplitter.h"
|
||||
#include "Windows/Dialogs/AxisMappingDialog.h"
|
||||
#include "ui_BufferViewer.h"
|
||||
|
||||
@@ -466,7 +469,9 @@ struct BufferConfiguration
|
||||
uint32_t numRows = 0, unclampedNumRows = 0;
|
||||
uint32_t pagingOffset = 0;
|
||||
|
||||
uint32_t formatStride = 0;
|
||||
ShaderConstant fixedVars;
|
||||
uint32_t repeatStride = 1;
|
||||
uint32_t repeatOffset = 0;
|
||||
|
||||
QString noDraw;
|
||||
|
||||
@@ -501,6 +506,10 @@ struct BufferConfiguration
|
||||
unclampedNumRows = o.unclampedNumRows;
|
||||
pagingOffset = o.pagingOffset;
|
||||
|
||||
fixedVars = o.fixedVars;
|
||||
repeatStride = o.repeatStride;
|
||||
repeatOffset = o.repeatOffset;
|
||||
|
||||
noDraw = o.noDraw;
|
||||
|
||||
noVertices = o.noVertices;
|
||||
@@ -2052,7 +2061,10 @@ static void UnrollConstant(rdcstr prefix, uint32_t baseOffset, const ShaderConst
|
||||
}
|
||||
|
||||
// struct, expand by members
|
||||
for(uint32_t a = 0; a < qMax(1U, constant.type.descriptor.elements); a++)
|
||||
uint32_t arraySize = qMax(1U, constant.type.descriptor.elements);
|
||||
if(arraySize == ~0U)
|
||||
arraySize = 1U;
|
||||
for(uint32_t a = 0; a < arraySize; a++)
|
||||
{
|
||||
for(const ShaderConstant &child : constant.type.members)
|
||||
{
|
||||
@@ -2272,8 +2284,8 @@ void BufferViewer::SetupRawView()
|
||||
ui->viewIndex->setVisible(false);
|
||||
ui->dockarea->setVisible(false);
|
||||
|
||||
ui->vsinData->setWindowTitle(tr("Buffer Contents"));
|
||||
ui->vsinData->setFrameShape(QFrame::NoFrame);
|
||||
ui->fixedVars->setFrameShape(QFrame::NoFrame);
|
||||
|
||||
ui->vsinData->setPinnedColumns(1);
|
||||
ui->vsinData->setColumnGroupRole(columnGroupRole);
|
||||
@@ -2293,14 +2305,63 @@ void BufferViewer::SetupRawView()
|
||||
processFormat(format);
|
||||
});
|
||||
|
||||
QVBoxLayout *vertical = new QVBoxLayout(this);
|
||||
ui->fixedVars->setColumns({tr("Name"), tr("Value"), tr("Type")});
|
||||
{
|
||||
ui->fixedVars->header()->setSectionResizeMode(0, QHeaderView::Interactive);
|
||||
ui->fixedVars->header()->setSectionResizeMode(1, QHeaderView::Interactive);
|
||||
ui->fixedVars->header()->setSectionResizeMode(2, QHeaderView::Interactive);
|
||||
}
|
||||
|
||||
vertical->setSpacing(3);
|
||||
vertical->setContentsMargins(3, 3, 3, 3);
|
||||
ui->fixedVars->setFont(Formatter::FixedFont());
|
||||
|
||||
vertical->addWidget(ui->meshToolbar);
|
||||
vertical->addWidget(ui->vsinData);
|
||||
vertical->addWidget(ui->formatSpecifier);
|
||||
m_FixedGroup = new CollapseGroupBox(this);
|
||||
m_RepeatedGroup = new CollapseGroupBox(this);
|
||||
|
||||
m_RepeatedControlBar = new QFrame(this);
|
||||
|
||||
m_RepeatedControlBar->setFrameShape(QFrame::Panel);
|
||||
m_RepeatedControlBar->setFrameShadow(QFrame::Raised);
|
||||
|
||||
QHBoxLayout *controlLayout = new QHBoxLayout(m_RepeatedControlBar);
|
||||
controlLayout->setSpacing(2);
|
||||
controlLayout->setContentsMargins(6, 2, 6, 2);
|
||||
|
||||
controlLayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum));
|
||||
|
||||
QVBoxLayout *fixedLayout = new QVBoxLayout(m_FixedGroup);
|
||||
fixedLayout->setSpacing(0);
|
||||
fixedLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
QVBoxLayout *repeatedLayout = new QVBoxLayout(m_RepeatedGroup);
|
||||
repeatedLayout->setSpacing(3);
|
||||
repeatedLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
repeatedLayout->addWidget(m_RepeatedControlBar);
|
||||
|
||||
m_FixedGroup->setTitle(tr("Fixed SoA data"));
|
||||
m_RepeatedGroup->setTitle(tr("Repeated AoS values"));
|
||||
|
||||
m_VLayout = new QVBoxLayout(this);
|
||||
m_VLayout->setSpacing(3);
|
||||
m_VLayout->setContentsMargins(3, 3, 3, 3);
|
||||
|
||||
m_OuterSplitter = new RDSplitter(Qt::Vertical, this);
|
||||
m_OuterSplitter->setHandleWidth(12);
|
||||
m_OuterSplitter->setChildrenCollapsible(false);
|
||||
|
||||
m_InnerSplitter = new RDSplitter(Qt::Vertical, this);
|
||||
m_InnerSplitter->setHandleWidth(12);
|
||||
m_InnerSplitter->setChildrenCollapsible(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);
|
||||
|
||||
m_VLayout->addWidget(ui->meshToolbar);
|
||||
// 0 will be variable, but set it to something here so QSplitter doesn't barf
|
||||
m_OuterSplitter->insertWidget(0, ui->vsinData);
|
||||
m_OuterSplitter->insertWidget(1, ui->formatSpecifier);
|
||||
m_VLayout->addWidget(m_OuterSplitter);
|
||||
}
|
||||
|
||||
void BufferViewer::SetupMeshView()
|
||||
@@ -2314,6 +2375,8 @@ void BufferViewer::SetupMeshView()
|
||||
ui->byteRangeLengthLabel->setVisible(false);
|
||||
byteRangeLength->setVisible(false);
|
||||
|
||||
ui->fixedVars->setVisible(false);
|
||||
|
||||
ui->resourceDetails->setVisible(false);
|
||||
ui->formatSpecifier->setVisible(false);
|
||||
ui->cameraControlsGroup->setVisible(false);
|
||||
@@ -2670,11 +2733,17 @@ void BufferViewer::OnEventChanged(uint32_t eventId)
|
||||
else
|
||||
{
|
||||
QString errors;
|
||||
ShaderConstant constant = BufferFormatter::ParseFormatString(m_Format, m_ByteSize, errors);
|
||||
ShaderConstant repeating;
|
||||
rdctie(bufdata->vsinConfig.fixedVars, repeating) =
|
||||
BufferFormatter::ParseFormatString(m_Format, m_ByteSize, errors);
|
||||
|
||||
UnrollConstant(constant, bufdata->vsinConfig.columns, bufdata->vsinConfig.props);
|
||||
if(repeating.type.descriptor.type != VarType::Unknown)
|
||||
{
|
||||
bufdata->vsinConfig.repeatStride = repeating.type.descriptor.arrayByteStride;
|
||||
bufdata->vsinConfig.repeatOffset = repeating.byteOffset;
|
||||
|
||||
bufdata->vsinConfig.formatStride = constant.type.descriptor.arrayByteStride;
|
||||
UnrollConstant(repeating, bufdata->vsinConfig.columns, bufdata->vsinConfig.props);
|
||||
}
|
||||
|
||||
ClearModels();
|
||||
}
|
||||
@@ -2735,16 +2804,21 @@ void BufferViewer::OnEventChanged(uint32_t eventId)
|
||||
buf = new BufferData;
|
||||
|
||||
// calculate tight stride
|
||||
buf->stride = std::max(1U, bufdata->vsinConfig.formatStride);
|
||||
buf->stride = std::max(1U, bufdata->vsinConfig.repeatStride);
|
||||
|
||||
// the "permanent" range starts at ByteOffset and goes for m_ByteSize
|
||||
uint64_t rangeStart = m_ByteOffset;
|
||||
uint64_t rangeEnd = m_ByteOffset + m_ByteSize;
|
||||
// we want to fetch the data for fixed and repeated sections (either of which might be 0)
|
||||
// but calculate the number of rows etc for the repeated sections based on just the data
|
||||
// available for it
|
||||
const uint64_t fixedLength = bufdata->vsinConfig.repeatOffset;
|
||||
|
||||
// the "permanent" repeated range starts after the fixed data and goes for m_ByteSize
|
||||
uint64_t repeatedRangeStart = m_ByteOffset + fixedLength;
|
||||
uint64_t repeatedRangeEnd = m_ByteOffset + m_ByteSize;
|
||||
|
||||
// if the byte size is unbounded, the end is unbounded - fix the potential overflow from
|
||||
// adding the offset
|
||||
if(m_ByteSize == UINT64_MAX)
|
||||
rangeEnd = UINT64_MAX;
|
||||
repeatedRangeEnd = UINT64_MAX;
|
||||
|
||||
// get the underlying buffer length
|
||||
uint64_t bufferLength = 0;
|
||||
@@ -2757,34 +2831,46 @@ void BufferViewer::OnEventChanged(uint32_t eventId)
|
||||
}
|
||||
|
||||
// clamp the range to the buffer length, which may end up with it being empty
|
||||
rangeEnd = qMin(rangeEnd, bufferLength);
|
||||
rangeStart = qMin(rangeStart, bufferLength);
|
||||
repeatedRangeEnd = qMin(repeatedRangeEnd, bufferLength);
|
||||
repeatedRangeStart = qMin(repeatedRangeStart, bufferLength);
|
||||
|
||||
// store the number of rows unclamped without the paging window
|
||||
bufdata->vsinConfig.unclampedNumRows =
|
||||
uint32_t((rangeEnd - rangeStart + buf->stride - 1) / buf->stride);
|
||||
uint32_t((repeatedRangeEnd - repeatedRangeStart + buf->stride - 1) / buf->stride);
|
||||
|
||||
// advance the range by the paging offset
|
||||
rangeStart = qMin(rangeEnd, rangeStart + m_PagingByteOffset);
|
||||
repeatedRangeStart = qMin(repeatedRangeEnd, repeatedRangeStart + m_PagingByteOffset);
|
||||
|
||||
// calculate the length clamped to the MaxVisibleRows
|
||||
uint64_t clampedLength =
|
||||
qMin(rangeEnd - rangeStart, uint64_t(buf->stride * (MaxVisibleRows + 2)));
|
||||
const uint64_t clampedRepeatedLength =
|
||||
qMin(repeatedRangeEnd - repeatedRangeStart, uint64_t(buf->stride * (MaxVisibleRows + 2)));
|
||||
|
||||
if(m_IsBuffer)
|
||||
{
|
||||
if(clampedLength > 0)
|
||||
buf->storage = r->GetBufferData(m_BufferID, CurrentByteOffset(), clampedLength);
|
||||
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'
|
||||
buf->storage = r->GetBufferData(m_BufferID, m_ByteOffset, fixedLength);
|
||||
// then append the data from where we're paged to
|
||||
buf->storage.append(r->GetBufferData(m_BufferID, repeatedRangeStart, clampedRepeatedLength));
|
||||
}
|
||||
else
|
||||
{
|
||||
// otherwise we can fetch it all at once
|
||||
buf->storage =
|
||||
r->GetBufferData(m_BufferID, m_ByteOffset, fixedLength + clampedRepeatedLength);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buf->storage = r->GetTextureData(m_BufferID, m_TexSub);
|
||||
}
|
||||
|
||||
uint32_t bufCount = uint32_t(buf->size());
|
||||
uint32_t repeatedDataAvailable = uint32_t(buf->size() - fixedLength);
|
||||
|
||||
bufdata->vsinConfig.pagingOffset = uint32_t(m_PagingByteOffset / buf->stride);
|
||||
bufdata->vsinConfig.numRows = uint32_t((bufCount + buf->stride - 1) / buf->stride);
|
||||
bufdata->vsinConfig.numRows = uint32_t((repeatedDataAvailable + buf->stride - 1) / buf->stride);
|
||||
|
||||
// ownership passes to model
|
||||
bufdata->vsinConfig.buffers.push_back(buf);
|
||||
@@ -2874,6 +2960,37 @@ void BufferViewer::OnEventChanged(uint32_t eventId)
|
||||
|
||||
if(!m_MeshView)
|
||||
{
|
||||
{
|
||||
ShaderVariable var = InterpretShaderVar(bufdata->vsinConfig.fixedVars,
|
||||
bufdata->vsinConfig.buffers[0]->storage.begin(),
|
||||
bufdata->vsinConfig.buffers[0]->storage.end());
|
||||
|
||||
bool wasEmpty = ui->fixedVars->topLevelItemCount() == 0;
|
||||
|
||||
RDTreeViewExpansionState state;
|
||||
ui->fixedVars->saveExpansion(state, 0);
|
||||
|
||||
ui->fixedVars->beginUpdate();
|
||||
|
||||
ui->fixedVars->clear();
|
||||
|
||||
if(!var.members.isEmpty())
|
||||
UI_AddFixedVariables(ui->fixedVars->invisibleRootItem(), var.members);
|
||||
|
||||
ui->fixedVars->endUpdate();
|
||||
|
||||
if(wasEmpty)
|
||||
{
|
||||
// Expand before resizing so that collapsed data will already be visible when expanded
|
||||
ui->fixedVars->expandAll();
|
||||
for(int i = 0; i < ui->fixedVars->header()->count(); i++)
|
||||
ui->fixedVars->resizeColumnToContents(i);
|
||||
ui->fixedVars->collapseAll();
|
||||
}
|
||||
|
||||
ui->fixedVars->applyExpansion(state, 0);
|
||||
}
|
||||
|
||||
on_rowOffset_valueChanged(ui->rowOffset->value());
|
||||
|
||||
const bool prev = (bufdata->vsinConfig.pagingOffset > 0);
|
||||
@@ -3005,6 +3122,26 @@ void BufferViewer::setPersistData(const QVariant &persistData)
|
||||
}
|
||||
}
|
||||
|
||||
void BufferViewer::UI_AddFixedVariables(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())
|
||||
UI_AddFixedVariables(n, v.members);
|
||||
}
|
||||
}
|
||||
|
||||
void BufferViewer::calcBoundingData(CalcBoundingBoxData &bbox)
|
||||
{
|
||||
for(size_t stage = 0; stage < ARRAY_COUNT(bbox.input); stage++)
|
||||
@@ -3219,11 +3356,6 @@ void BufferViewer::UI_ResetArcball()
|
||||
INVOKE_MEMFN(RT_UpdateAndDisplay);
|
||||
}
|
||||
|
||||
uint64_t BufferViewer::CurrentByteOffset()
|
||||
{
|
||||
return m_ByteOffset + m_PagingByteOffset;
|
||||
}
|
||||
|
||||
void BufferViewer::UI_CalculateMeshFormats()
|
||||
{
|
||||
if(!m_MeshView)
|
||||
@@ -3659,6 +3791,8 @@ void BufferViewer::ViewBuffer(uint64_t byteOffset, uint64_t byteSize, ResourceId
|
||||
|
||||
m_PagingByteOffset = 0;
|
||||
|
||||
ui->formatSpecifier->setFormat(format);
|
||||
|
||||
processFormat(format);
|
||||
}
|
||||
|
||||
@@ -4178,17 +4312,91 @@ void BufferViewer::processFormat(const QString &format)
|
||||
|
||||
BufferConfiguration bufconfig;
|
||||
|
||||
ShaderConstant cols = BufferFormatter::ParseFormatString(format, m_ByteSize, errors);
|
||||
ShaderConstant fixed, repeating;
|
||||
rdctie(fixed, repeating) = BufferFormatter::ParseFormatString(format, m_ByteSize, errors);
|
||||
|
||||
CalcColumnWidth(MaxNumRows(cols));
|
||||
const bool repeatedVars = repeating.type.descriptor.type != VarType::Unknown;
|
||||
const bool fixedVars = !fixed.type.members.empty();
|
||||
|
||||
if(fixedVars && repeatedVars)
|
||||
{
|
||||
if(m_OuterSplitter->widget(0) != m_InnerSplitter)
|
||||
m_OuterSplitter->replaceWidget(0, m_InnerSplitter);
|
||||
|
||||
m_FixedGroup->layout()->addWidget(ui->fixedVars);
|
||||
m_RepeatedGroup->layout()->addWidget(ui->vsinData);
|
||||
|
||||
// row offset should be shown in the repeated control bar, but no separator line is needed
|
||||
ui->offsetLine->setVisible(false);
|
||||
ui->rowOffsetLabel->setVisible(true);
|
||||
ui->rowOffset->setVisible(true);
|
||||
if(ui->rowOffset->parentWidget() != m_RepeatedControlBar)
|
||||
{
|
||||
QHBoxLayout *hbox = qobject_cast<QHBoxLayout *>(m_RepeatedControlBar->layout());
|
||||
hbox->insertWidget(0, ui->rowOffsetLabel);
|
||||
hbox->insertWidget(1, ui->rowOffset);
|
||||
}
|
||||
ui->fixedVars->setVisible(true);
|
||||
ui->vsinData->setVisible(true);
|
||||
|
||||
m_InnerSplitter->setVisible(true);
|
||||
}
|
||||
else if(fixedVars)
|
||||
{
|
||||
if(m_OuterSplitter->widget(0) != ui->fixedVars)
|
||||
m_OuterSplitter->replaceWidget(0, ui->fixedVars);
|
||||
|
||||
// row offset should not be shown
|
||||
ui->offsetLine->setVisible(false);
|
||||
ui->rowOffsetLabel->setVisible(false);
|
||||
ui->rowOffset->setVisible(false);
|
||||
|
||||
ui->fixedVars->setVisible(true);
|
||||
ui->vsinData->setVisible(false);
|
||||
|
||||
m_InnerSplitter->setVisible(false);
|
||||
}
|
||||
else if(repeatedVars)
|
||||
{
|
||||
if(m_OuterSplitter->widget(0) != ui->vsinData)
|
||||
m_OuterSplitter->replaceWidget(0, ui->vsinData);
|
||||
|
||||
// row offset should be shown with the other controls
|
||||
ui->offsetLine->setVisible(true);
|
||||
ui->rowOffsetLabel->setVisible(true);
|
||||
ui->rowOffset->setVisible(true);
|
||||
// insert after the offsetLine
|
||||
if(ui->rowOffset->parentWidget() != ui->meshToolbar)
|
||||
{
|
||||
QHBoxLayout *hbox = qobject_cast<QHBoxLayout *>(ui->meshToolbar->layout());
|
||||
|
||||
int i = 0;
|
||||
for(; i < hbox->count(); i++)
|
||||
{
|
||||
if(hbox->itemAt(i)->widget() == ui->offsetLine)
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
if(i < hbox->count())
|
||||
{
|
||||
hbox->insertWidget(i, ui->rowOffset);
|
||||
hbox->insertWidget(i, ui->rowOffsetLabel);
|
||||
}
|
||||
}
|
||||
|
||||
ui->fixedVars->setVisible(false);
|
||||
ui->vsinData->setVisible(true);
|
||||
|
||||
m_InnerSplitter->setVisible(false);
|
||||
}
|
||||
|
||||
CalcColumnWidth(MaxNumRows(repeating));
|
||||
|
||||
ClearModels();
|
||||
|
||||
m_Format = format;
|
||||
|
||||
ui->formatSpecifier->setFormat(format);
|
||||
|
||||
qulonglong stride = qMax(1U, cols.type.descriptor.arrayByteStride);
|
||||
qulonglong stride = qMax(1U, repeating.type.descriptor.arrayByteStride);
|
||||
|
||||
byteRangeStart->setSingleStep(stride);
|
||||
byteRangeLength->setSingleStep(stride);
|
||||
|
||||
@@ -38,8 +38,11 @@ class RDSpinBox64;
|
||||
class QItemSelection;
|
||||
class QMenu;
|
||||
class QPushButton;
|
||||
class QVBoxLayout;
|
||||
class RDTableView;
|
||||
class RDSplitter;
|
||||
class BufferItemModel;
|
||||
class CollapseGroupBox;
|
||||
class CameraWrapper;
|
||||
class ArcballWrapper;
|
||||
class FlycamWrapper;
|
||||
@@ -175,12 +178,12 @@ private:
|
||||
void UI_UpdateBoundingBox(const CalcBoundingBoxData &bbox);
|
||||
void UI_UpdateBoundingBoxLabels(int compCount = 0);
|
||||
|
||||
void UI_AddFixedVariables(RDTreeWidgetItem *root, const rdcarray<ShaderVariable> &vars);
|
||||
|
||||
void FillScrolls(PopulateBufferData *bufdata);
|
||||
|
||||
void UI_ResetArcball();
|
||||
|
||||
uint64_t CurrentByteOffset();
|
||||
|
||||
// data from raw buffer view
|
||||
bool m_IsBuffer = true;
|
||||
QString m_Format;
|
||||
@@ -219,6 +222,15 @@ private:
|
||||
|
||||
RichTextViewDelegate *m_delegate = NULL;
|
||||
|
||||
QVBoxLayout *m_VLayout = NULL;
|
||||
RDSplitter *m_OuterSplitter = NULL;
|
||||
RDSplitter *m_InnerSplitter = NULL;
|
||||
|
||||
CollapseGroupBox *m_FixedGroup = NULL;
|
||||
CollapseGroupBox *m_RepeatedGroup = NULL;
|
||||
|
||||
QFrame *m_RepeatedControlBar = NULL;
|
||||
|
||||
QMenu *m_HeaderMenu = NULL;
|
||||
|
||||
QAction *m_ResetColumnSel = NULL;
|
||||
|
||||
@@ -13,6 +13,34 @@
|
||||
<property name="windowTitle">
|
||||
<string>Buffer Viewer</string>
|
||||
</property>
|
||||
<widget class="RDTreeWidget" name="fixedVars">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>510</x>
|
||||
<y>150</y>
|
||||
<width>191</width>
|
||||
<height>71</height>
|
||||
</rect>
|
||||
</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>
|
||||
<widget class="RDTableView" name="vsinData">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
@@ -1001,7 +1029,7 @@ Enter 0.0 to use automatic/guessed value derived from data.</string>
|
||||
<rect>
|
||||
<x>139</x>
|
||||
<y>160</y>
|
||||
<width>521</width>
|
||||
<width>361</width>
|
||||
<height>61</height>
|
||||
</rect>
|
||||
</property>
|
||||
@@ -1023,6 +1051,11 @@ Enter 0.0 to use automatic/guessed value derived from data.</string>
|
||||
<extends>QTableView</extends>
|
||||
<header>Widgets/Extended/RDTableView.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>RDTreeWidget</class>
|
||||
<extends>QTreeView</extends>
|
||||
<header>Widgets/Extended/RDTreeWidget.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>BufferFormatSpecifier</class>
|
||||
<extends>QWidget</extends>
|
||||
|
||||
@@ -294,7 +294,12 @@ void ConstantBufferPreviewer::processFormat(const QString &format)
|
||||
{
|
||||
QString errors;
|
||||
|
||||
m_formatOverride = BufferFormatter::ParseFormatString(format, ~0ULL, 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -2157,8 +2157,8 @@ void D3D11PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, in
|
||||
|
||||
if(shaderRes)
|
||||
{
|
||||
format = BufferFormatter::GetBufferFormatString(Packing::D3DUAV, *shaderRes,
|
||||
view.res.viewFormat, offs);
|
||||
format =
|
||||
BufferFormatter::GetBufferFormatString(Packing::D3DUAV, *shaderRes, view.res.viewFormat);
|
||||
|
||||
if(view.res.bufferFlags & D3DBufferViewFlags::Raw)
|
||||
format = lit("xint");
|
||||
|
||||
@@ -2271,8 +2271,8 @@ void D3D12PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, in
|
||||
|
||||
if(shaderRes)
|
||||
{
|
||||
format = BufferFormatter::GetBufferFormatString(Packing::D3DUAV, *shaderRes,
|
||||
view.res.viewFormat, offs);
|
||||
format =
|
||||
BufferFormatter::GetBufferFormatString(Packing::D3DUAV, *shaderRes, view.res.viewFormat);
|
||||
|
||||
if(view.res.bufferFlags & D3DBufferViewFlags::Raw)
|
||||
format = lit("xint");
|
||||
|
||||
@@ -2317,7 +2317,7 @@ void GLPipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, int c
|
||||
|
||||
QString format = BufferFormatter::GetBufferFormatString(
|
||||
BufferFormatter::EstimatePackingRules(shaderRes->variableType.members), *shaderRes,
|
||||
ResourceFormat(), buf.offset);
|
||||
ResourceFormat());
|
||||
|
||||
if(buf.ID != ResourceId())
|
||||
{
|
||||
|
||||
@@ -3036,8 +3036,7 @@ void VulkanPipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, i
|
||||
: stage->reflection->readOnlyResources[buf.bindPoint];
|
||||
|
||||
format = BufferFormatter::GetBufferFormatString(
|
||||
BufferFormatter::EstimatePackingRules(shaderRes.variableType.members), shaderRes, buf.fmt,
|
||||
buf.offset);
|
||||
BufferFormatter::EstimatePackingRules(shaderRes.variableType.members), shaderRes, buf.fmt);
|
||||
}
|
||||
|
||||
if(buf.ID != ResourceId())
|
||||
|
||||
Reference in New Issue
Block a user