mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-04 09:00:44 +00:00
Support specifying structs in buffer formatter. Closes #805
* Structs and arrays-of-structs are completely flattened for the purposes of displaying them still.
This commit is contained in:
@@ -111,6 +111,12 @@ static QVariant interpret(const ResourceFormat &f, byte comp)
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
struct StructFormatData
|
||||
{
|
||||
QList<FormatElement> elems;
|
||||
uint32_t offset = 0;
|
||||
};
|
||||
|
||||
FormatElement::FormatElement()
|
||||
{
|
||||
buffer = 0;
|
||||
@@ -144,7 +150,11 @@ FormatElement::FormatElement(const QString &Name, int buf, uint offs, bool perIn
|
||||
QList<FormatElement> FormatElement::ParseFormatString(const QString &formatString, uint64_t maxLen,
|
||||
bool tightPacking, QString &errors)
|
||||
{
|
||||
QList<FormatElement> elems;
|
||||
StructFormatData root;
|
||||
StructFormatData *cur = &root;
|
||||
|
||||
QMap<QString, StructFormatData> structelems;
|
||||
QString lastStruct;
|
||||
|
||||
// regex doesn't account for trailing or preceeding whitespace, or comments
|
||||
|
||||
@@ -167,8 +177,8 @@ QList<FormatElement> FormatElement::ParseFormatString(const QString &formatStrin
|
||||
")" // end of the type group
|
||||
"([1-9])?" // might be a vector
|
||||
"(x[1-9])?" // or a matrix
|
||||
"(\\s+[A-Za-z_][A-Za-z0-9_]*)?" // get identifier name
|
||||
"(\\[[0-9]+\\])?" // optional array dimension
|
||||
"(\\s+[A-Za-z@_][A-Za-z0-9@_]*)?" // get identifier name
|
||||
"(\\s*\\[[0-9]+\\])?" // optional array dimension
|
||||
"(\\s*:\\s*[A-Za-z_][A-Za-z0-9_]*)?" // optional semantic
|
||||
"$"));
|
||||
|
||||
@@ -177,22 +187,104 @@ QList<FormatElement> FormatElement::ParseFormatString(const QString &formatStrin
|
||||
|
||||
QString text = formatString;
|
||||
|
||||
text = text.replace(lit("{"), QString()).replace(lit("}"), QString());
|
||||
|
||||
QRegularExpression c_comments(lit("/\\*[^*]*\\*+(?:[^*/][^*]*\\*+)*/"));
|
||||
QRegularExpression cpp_comments(lit("//.*"));
|
||||
text = text.replace(c_comments, QString()).replace(cpp_comments, QString());
|
||||
|
||||
uint32_t offset = 0;
|
||||
QRegularExpression structDeclRegex(lit("^struct\\s+([A-Za-z_][A-Za-z0-9_]*)$"));
|
||||
QRegularExpression structUseRegex(
|
||||
lit("^" // start of the line
|
||||
"([A-Za-z_][A-Za-z0-9_]*)" // struct type name
|
||||
"\\s+([A-Za-z@_][A-Za-z0-9@_]*)" // variable name
|
||||
"(\\s*\\[[0-9]+\\])?" // optional array dimension
|
||||
"$"));
|
||||
|
||||
// get each line and parse it to determine the format the user wanted
|
||||
for(QString &l : text.split(QLatin1Char(';')))
|
||||
for(QString &l : text.split(QRegularExpression(lit("[;\n\r]"))))
|
||||
{
|
||||
QString line = l.trimmed();
|
||||
|
||||
if(line.isEmpty())
|
||||
continue;
|
||||
|
||||
if(cur == &root)
|
||||
{
|
||||
// if we're not in a struct, ignore the braces
|
||||
if(line == lit("{") || line == lit("}"))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we're in a struct, ignore the opening brace and revert back to root elements when we hit
|
||||
// the closing brace. No brace nesting is supported
|
||||
if(line == lit("{"))
|
||||
continue;
|
||||
|
||||
if(line == lit("}"))
|
||||
{
|
||||
cur = &root;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(line.contains(lit("struct")))
|
||||
{
|
||||
QRegularExpressionMatch match = structDeclRegex.match(line);
|
||||
|
||||
if(match.hasMatch())
|
||||
{
|
||||
lastStruct = match.captured(1);
|
||||
|
||||
if(structelems.contains(lastStruct))
|
||||
{
|
||||
errors = tr("Duplicate struct definition: %1\n").arg(lastStruct);
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
|
||||
cur = &structelems[lastStruct];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
QRegularExpressionMatch structMatch = structUseRegex.match(line);
|
||||
|
||||
if(structMatch.hasMatch() && structelems.contains(structMatch.captured(1)))
|
||||
{
|
||||
StructFormatData &structDef = structelems[structMatch.captured(1)];
|
||||
|
||||
QString varName = structMatch.captured(2);
|
||||
|
||||
QString arrayDim = structMatch.captured(3).trimmed();
|
||||
uint32_t arrayCount = 1;
|
||||
if(!arrayDim.isEmpty())
|
||||
{
|
||||
arrayDim = arrayDim.mid(1, arrayDim.count() - 2);
|
||||
bool ok = false;
|
||||
arrayCount = arrayDim.toUInt(&ok);
|
||||
if(!ok)
|
||||
arrayCount = 1;
|
||||
}
|
||||
|
||||
// inline use of this struct in the current parent
|
||||
for(uint32_t arrayIdx = 0; arrayIdx < arrayCount; arrayIdx++)
|
||||
{
|
||||
for(const FormatElement &templ : structDef.elems)
|
||||
{
|
||||
FormatElement el = templ;
|
||||
el.name = arrayCount > 1 ? QFormatStr("%1[%2].%3").arg(varName).arg(arrayIdx).arg(el.name)
|
||||
: QFormatStr("%1.%2").arg(varName).arg(el.name);
|
||||
el.offset += cur->offset;
|
||||
|
||||
cur->elems.push_back(el);
|
||||
}
|
||||
|
||||
cur->offset += structDef.offset;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
QRegularExpressionMatch match = regExpr.match(line);
|
||||
|
||||
if(!match.hasMatch())
|
||||
@@ -217,6 +309,7 @@ QList<FormatElement> FormatElement::ParseFormatString(const QString &formatStrin
|
||||
}
|
||||
|
||||
ResourceFormat fmt;
|
||||
fmt.type = ResourceFormatType::Regular;
|
||||
fmt.compType = CompType::Typeless;
|
||||
|
||||
bool hex = false;
|
||||
@@ -380,7 +473,7 @@ QList<FormatElement> FormatElement::ParseFormatString(const QString &formatStrin
|
||||
|
||||
if(arrayCount == 1)
|
||||
{
|
||||
FormatElement elem(name, 0, offset, false, 1, row_major, matrixCount, fmt, hex, rgb);
|
||||
FormatElement elem(name, 0, cur->offset, false, 1, row_major, matrixCount, fmt, hex, rgb);
|
||||
|
||||
uint32_t advance = elem.byteSize();
|
||||
|
||||
@@ -391,33 +484,33 @@ QList<FormatElement> FormatElement::ParseFormatString(const QString &formatStrin
|
||||
|
||||
// cbuffer packing doesn't allow elements to cross float4 boundaries, nudge up if this was
|
||||
// the case
|
||||
if(offset / 16 != (offset + elem.byteSize() - 1) / 16)
|
||||
if(cur->offset / 16 != (cur->offset + elem.byteSize() - 1) / 16)
|
||||
{
|
||||
elem.offset = offset = (offset + 0xFU) & (~0xFU);
|
||||
elem.offset = cur->offset = (cur->offset + 0xFU) & (~0xFU);
|
||||
}
|
||||
}
|
||||
|
||||
elems.push_back(elem);
|
||||
cur->elems.push_back(elem);
|
||||
|
||||
offset += advance;
|
||||
cur->offset += advance;
|
||||
}
|
||||
else
|
||||
{
|
||||
// when cbuffer packing, arrays are always aligned at float4 boundary
|
||||
if(!tightPacking)
|
||||
{
|
||||
if(offset % 16 != 0)
|
||||
if(cur->offset % 16 != 0)
|
||||
{
|
||||
offset = (offset + 0xFU) & (~0xFU);
|
||||
cur->offset = (cur->offset + 0xFU) & (~0xFU);
|
||||
}
|
||||
}
|
||||
|
||||
for(uint a = 0; a < arrayCount; a++)
|
||||
{
|
||||
FormatElement elem(QFormatStr("%1[%2]").arg(name).arg(a), 0, offset, false, 1, row_major,
|
||||
matrixCount, fmt, hex, rgb);
|
||||
FormatElement elem(QFormatStr("%1[%2]").arg(name).arg(a), 0, cur->offset, false, 1,
|
||||
row_major, matrixCount, fmt, hex, rgb);
|
||||
|
||||
elems.push_back(elem);
|
||||
cur->elems.push_back(elem);
|
||||
|
||||
uint32_t advance = elem.byteSize();
|
||||
|
||||
@@ -427,14 +520,19 @@ QList<FormatElement> FormatElement::ParseFormatString(const QString &formatStrin
|
||||
advance = (advance + 0xFU) & (~0xFU);
|
||||
}
|
||||
|
||||
offset += advance;
|
||||
cur->offset += advance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!success || elems.isEmpty())
|
||||
// if we succeeded parsing but didn't get any root elements, use the last defined struct as the
|
||||
// definition
|
||||
if(success && root.elems.isEmpty() && !lastStruct.isEmpty())
|
||||
root = structelems[lastStruct];
|
||||
|
||||
if(!success || root.elems.isEmpty())
|
||||
{
|
||||
elems.clear();
|
||||
root.elems.clear();
|
||||
|
||||
ResourceFormat fmt;
|
||||
fmt.compType = CompType::UInt;
|
||||
@@ -446,10 +544,10 @@ QList<FormatElement> FormatElement::ParseFormatString(const QString &formatStrin
|
||||
if(maxLen > 0 && maxLen < 4)
|
||||
fmt.compByteWidth = 1;
|
||||
|
||||
elems.push_back(FormatElement(lit("data"), 0, 0, false, 1, false, 1, fmt, true, false));
|
||||
root.elems.push_back(FormatElement(lit("data"), 0, 0, false, 1, false, 1, fmt, true, false));
|
||||
}
|
||||
|
||||
return elems;
|
||||
return root.elems;
|
||||
}
|
||||
|
||||
QString FormatElement::GenerateTextureBufferFormat(const TextureDescription &tex)
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="helpText">
|
||||
<property name="text">
|
||||
<string>Type in a buffer format declaration. Comments and {} braces are skipped, : semantics are ignored.
|
||||
<string>Type in a buffer format declaration, including struct definitions. Comments and {} braces are skipped, : semantics are ignored.
|
||||
Declare each element as an hlsl/glsl variable, e.g: "float4 first; float2 second; uint2 third;" or vec4/vec2.
|
||||
|
||||
Basic types accepted: bool, byte, short, int, half, float, double.
|
||||
@@ -86,7 +86,7 @@ Additionally special formats: unorm[hb] (half, byte) and snorm[hb], uintten/unor
|
||||
|
||||
You can prepend 'rgb' onto an element to colour backgrounds with the RGB values, e.g. 'rgb xbyte4 pixels[256]'.
|
||||
|
||||
Vectors (e.g. float4), matrices ([row_major] half3x4) and arrays (float[16]) are supported.</string>
|
||||
Vectors (e.g. float4), matrices ([row_major] half3x4) and single-dimension arrays (float[16]) are supported.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
|
||||
@@ -614,55 +614,47 @@ void PipelineStateViewer::setMeshViewPixmap(RDLabel *meshView)
|
||||
});
|
||||
}
|
||||
|
||||
QString PipelineStateViewer::formatMembers(int indent, int requiredByteStride,
|
||||
const QString &nameprefix,
|
||||
const rdcarray<ShaderConstant> &vars)
|
||||
QString PipelineStateViewer::declareStruct(QList<QString> &declaredStructs, const QString &name,
|
||||
const rdcarray<ShaderConstant> &members,
|
||||
uint32_t requiredByteStride)
|
||||
{
|
||||
QString indentstr(indent * 4, QLatin1Char(' '));
|
||||
|
||||
QString ret;
|
||||
|
||||
int i = 0;
|
||||
ret = lit("struct %1\n{\n").arg(name);
|
||||
|
||||
for(const ShaderConstant &v : vars)
|
||||
for(int i = 0; i < members.count(); i++)
|
||||
{
|
||||
if(!v.type.members.empty())
|
||||
{
|
||||
if(i > 0)
|
||||
ret += lit("\n");
|
||||
QString arraySize;
|
||||
if(members[i].type.descriptor.elements > 1)
|
||||
arraySize = QFormatStr("[%1]").arg(members[i].type.descriptor.elements);
|
||||
|
||||
if(v.type.descriptor.name == "struct")
|
||||
ret += indentstr + lit("// struct\n");
|
||||
else
|
||||
ret += indentstr + lit("// struct %1\n").arg(v.type.descriptor.name);
|
||||
QString varTypeName = members[i].type.descriptor.name;
|
||||
|
||||
ret += indentstr + lit("{\n") + formatMembers(indent + 1, v.type.descriptor.arrayByteStride,
|
||||
v.name + lit("_"), v.type.members) +
|
||||
indentstr + lit("}\n");
|
||||
if(i < vars.count() - 1)
|
||||
ret += lit("\n");
|
||||
}
|
||||
else
|
||||
if(!members[i].type.members.isEmpty())
|
||||
{
|
||||
QString arr;
|
||||
if(v.type.descriptor.elements > 1)
|
||||
arr = QFormatStr("[%1]").arg(v.type.descriptor.elements);
|
||||
ret += QFormatStr("%1%2 %3%4%5;\n")
|
||||
.arg(indentstr)
|
||||
.arg(v.type.descriptor.name)
|
||||
.arg(nameprefix)
|
||||
.arg(v.name)
|
||||
.arg(arr);
|
||||
// GL structs don't give us typenames (boo!) so give them unique names. This will mean some
|
||||
// structs get duplicated if they're used in multiple places, but not much we can do about
|
||||
// that.
|
||||
if(varTypeName.isEmpty() || varTypeName == lit("struct"))
|
||||
varTypeName = lit("anon%1").arg(declaredStructs.size());
|
||||
|
||||
if(!declaredStructs.contains(varTypeName))
|
||||
{
|
||||
declaredStructs.push_back(varTypeName);
|
||||
ret = declareStruct(declaredStructs, varTypeName, members[i].type.members,
|
||||
members[i].type.descriptor.arrayByteStride) +
|
||||
lit("\n") + ret;
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
ret += QFormatStr(" %1 %2%3;\n").arg(varTypeName).arg(members[i].name).arg(arraySize);
|
||||
}
|
||||
|
||||
if(requiredByteStride > 0)
|
||||
{
|
||||
uint32_t structStart = 0;
|
||||
|
||||
const ShaderConstant *lastChild = &vars.back();
|
||||
const ShaderConstant *lastChild = &members.back();
|
||||
|
||||
structStart += lastChild->byteOffset;
|
||||
while(!lastChild->type.members.isEmpty())
|
||||
@@ -691,9 +683,11 @@ QString PipelineStateViewer::formatMembers(int indent, int requiredByteStride,
|
||||
uint32_t padBytes = requiredByteStride - (lastChild->byteOffset + size);
|
||||
|
||||
if(padBytes > 0)
|
||||
ret += indentstr + padding(padBytes);
|
||||
ret += lit(" ") + padding(padBytes);
|
||||
}
|
||||
|
||||
ret += lit("}\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -705,54 +699,65 @@ QString PipelineStateViewer::GenerateBufferFormatter(const ShaderResource &res,
|
||||
|
||||
if(!res.variableType.members.empty())
|
||||
{
|
||||
const rdcarray<ShaderConstant> *members = &res.variableType.members;
|
||||
|
||||
uint32_t requiredStride = 0;
|
||||
|
||||
if(m_Ctx.APIProps().pipelineType == GraphicsAPI::Vulkan ||
|
||||
m_Ctx.APIProps().pipelineType == GraphicsAPI::OpenGL)
|
||||
{
|
||||
// GL/Vulkan allow fixed-sized members before the array-of-structs. This can't be represented
|
||||
// in a buffer format so we skip it
|
||||
if(members->count() > 1)
|
||||
const rdcarray<ShaderConstant> &members = res.variableType.members;
|
||||
|
||||
format += QFormatStr("struct %1\n{\n").arg(res.name);
|
||||
|
||||
// GL/Vulkan allow fixed-sized members before the array-of-structs. This can't be
|
||||
// represented in a buffer format so we skip it
|
||||
if(members.count() > 1)
|
||||
{
|
||||
format += lit("// %1 members skipped as they are fixed size:\n").arg(res.name);
|
||||
for(int i = 0; i < members->count() - 1; i++)
|
||||
format += tr(" // members skipped as they are fixed size:\n");
|
||||
baseByteOffset += members.back().byteOffset;
|
||||
}
|
||||
|
||||
QString varTypeName;
|
||||
QString comment = lit("// ");
|
||||
for(int i = 0; i < members.count(); i++)
|
||||
{
|
||||
QString arraySize;
|
||||
if(members[i].type.descriptor.elements > 1)
|
||||
arraySize = QFormatStr("[%1]").arg(members[i].type.descriptor.elements);
|
||||
|
||||
varTypeName = members[i].type.descriptor.name;
|
||||
|
||||
if(i + 1 == members.count())
|
||||
{
|
||||
QString arraySize;
|
||||
if(members->at(i).type.descriptor.elements > 1)
|
||||
arraySize = QFormatStr("[%1]").arg(members->at(i).type.descriptor.elements);
|
||||
format += QFormatStr("// %1 %2%3;\n")
|
||||
.arg(members->at(i).type.descriptor.name)
|
||||
.arg(members->at(i).name)
|
||||
.arg(arraySize);
|
||||
comment = arraySize = QString();
|
||||
|
||||
if(members.count() > 1)
|
||||
format +=
|
||||
lit(" // final array struct @ byte offset %1\n").arg(members.back().byteOffset);
|
||||
|
||||
// give GL nameless structs a better name
|
||||
if(varTypeName.isEmpty() || varTypeName == lit("struct"))
|
||||
varTypeName = lit("root_struct");
|
||||
}
|
||||
|
||||
format += lit("// final array struct @ byte offset %1\n{\n").arg(members->back().byteOffset);
|
||||
|
||||
baseByteOffset += members->back().byteOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
format += lit("// struct %1\n{\n").arg(res.name);
|
||||
format +=
|
||||
QFormatStr(" %1%2 %3%4;\n").arg(comment).arg(varTypeName).arg(members[i].name).arg(arraySize);
|
||||
}
|
||||
|
||||
// if the last member is a struct, remove one level of indirection before declaring the
|
||||
// repeated structure
|
||||
if(!members->back().type.members.isEmpty())
|
||||
{
|
||||
requiredStride = members->back().type.descriptor.arrayByteStride;
|
||||
format += lit("}");
|
||||
|
||||
members = &members->back().type.members;
|
||||
// if the last member is a struct, declare it
|
||||
if(!members.back().type.members.isEmpty())
|
||||
{
|
||||
QList<QString> declaredStructs;
|
||||
format = declareStruct(declaredStructs, varTypeName, members.back().type.members,
|
||||
members.back().type.descriptor.arrayByteStride) +
|
||||
lit("\n") + format;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
format = lit("// struct %1\n{\n").arg(res.variableType.descriptor.name);
|
||||
QList<QString> declaredStructs;
|
||||
format = declareStruct(declaredStructs, res.variableType.descriptor.name,
|
||||
res.variableType.members, 0);
|
||||
}
|
||||
|
||||
format += formatMembers(1, requiredStride, QString(), *members);
|
||||
format += lit("}");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -93,8 +93,8 @@ private:
|
||||
|
||||
QMenu *editMenus[6] = {};
|
||||
|
||||
QString formatMembers(int indent, int requiredByteStride, const QString &nameprefix,
|
||||
const rdcarray<ShaderConstant> &vars);
|
||||
QString declareStruct(QList<QString> &declaredStructs, const QString &name,
|
||||
const rdcarray<ShaderConstant> &members, uint32_t requiredByteStride);
|
||||
|
||||
QString GenerateHLSLStub(const ShaderReflection *shaderDetails, const QString &entryFunc);
|
||||
IShaderViewer *EditShader(ResourceId id, ShaderStage shaderType, const rdcstr &entry,
|
||||
|
||||
Reference in New Issue
Block a user