From cee4b25b289a95427eb4e43d4aa75dc253486648 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 10 Mar 2022 12:35:56 +0000 Subject: [PATCH] Allow specifying offsets and struct strides manually with offsets * Use these when generating buffer formatter strings instead of declaring manual padding variables. --- qrenderdoc/Code/BufferFormatter.cpp | 210 ++++++++++++------ qrenderdoc/Code/QRDUtils.h | 2 - .../PipelineState/PipelineStateViewer.cpp | 15 +- 3 files changed, 148 insertions(+), 79 deletions(-) diff --git a/qrenderdoc/Code/BufferFormatter.cpp b/qrenderdoc/Code/BufferFormatter.cpp index a2216fcfe..d49480786 100644 --- a/qrenderdoc/Code/BufferFormatter.cpp +++ b/qrenderdoc/Code/BufferFormatter.cpp @@ -32,6 +32,7 @@ struct StructFormatData ShaderConstant structDef; uint32_t pointerTypeId = 0; uint32_t offset = 0; + uint32_t paddedStride = 0; }; GraphicsAPI BufferFormatter::m_API; @@ -306,6 +307,21 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u cur->structDef.type.descriptor.arrayByteStride = (cur->offset + 0xFU) & (~0xFU); cur->pointerTypeId = PointerTypeRegistry::GetTypeID(cur->structDef.type); + + // only pad up to the stride, not down + if(cur->paddedStride >= cur->structDef.type.descriptor.arrayByteStride) + { + cur->structDef.type.descriptor.arrayByteStride = cur->paddedStride; + } + else if(cur->paddedStride > 0) + { + errors = tr("Declared struct %1 stride %2 is less than structure size %3\n") + .arg(cur->structDef.type.descriptor.name) + .arg(cur->paddedStride) + .arg(cur->structDef.type.descriptor.arrayByteStride); + success = false; + break; + } } cur = &root; @@ -315,13 +331,6 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u if(line.startsWith(lit("struct")) || line.startsWith(lit("enum"))) { - if(!annotations.empty()) - { - errors = tr("Annotations are not supported on struct definitions: %1\n").arg(line); - success = false; - break; - } - QRegularExpressionMatch match = structDeclRegex.match(line); if(match.hasMatch()) @@ -343,11 +352,43 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u { lastStruct = name; cur->structDef.type.descriptor.type = VarType::Struct; + + for(const Annotation &annot : annotations) + { + if(annot.name == lit("size") || annot.name == lit("byte_size")) + { + cur->paddedStride = annot.param.toUInt(); + } + else + { + errors = tr("Unrecognised annotation on struct definition: %1\n").arg(annot.name); + success = false; + break; + } + } + + annotations.clear(); } else { cur->structDef.type.descriptor.type = VarType::Enum; + for(const Annotation &annot : annotations) + { + if(false) + { + // no annotations supported currently on enums + } + else + { + errors = tr("Unrecognised annotation on enum definition: %1\n").arg(annot.name); + success = false; + break; + } + } + + annotations.clear(); + QString baseType = match.captured(4); if(baseType.isEmpty()) @@ -401,13 +442,22 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u el.name = enumMatch.captured(1); el.defaultValue = val; - if(!annotations.empty()) + for(const Annotation &annot : annotations) { - errors = tr("Annotations are not supported on enum values: %1\n").arg(el.name); - success = false; - break; + if(false) + { + // no annotations supported currently on enums + } + else + { + errors = tr("Unrecognised annotation on enum value: %1\n").arg(annot.name); + success = false; + break; + } } + annotations.clear(); + cur->structDef.type.members.push_back(el); continue; @@ -421,13 +471,22 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u bitfieldCurPos = 0; bitfieldCurPos += bitfieldSkipMatch.captured(3).toUInt(); - if(!annotations.empty()) + for(const Annotation &annot : annotations) { - errors = tr("Annotations are not supported on bitfield skips: %1\n").arg(line); - success = false; - break; + if(false) + { + // no annotations supported currently on enums + } + else + { + errors = tr("Unrecognised annotation on bitfield skip: %1\n").arg(annot.name); + success = false; + break; + } } + annotations.clear(); + continue; } @@ -441,13 +500,31 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u QString varName = structMatch.captured(3); - if(!annotations.empty()) + uint32_t specifiedOffset = ~0U; + for(const Annotation &annot : annotations) { - errors = tr("Annotations are not supported on struct %2\n").arg(varName); - success = false; - break; + if(annot.name == lit("offset") || annot.name == lit("byte_offset")) + { + specifiedOffset = annot.param.toUInt(); + + if(specifiedOffset < cur->offset) + { + errors = + tr("Offset %1 on variable %2 overlaps previous data\n").arg(specifiedOffset).arg(varName); + success = false; + break; + } + } + else + { + errors = tr("Unrecognised annotation on variable: %1\n").arg(annot.name); + success = false; + break; + } } + annotations.clear(); + QString arrayDim = structMatch.captured(4).trimmed(); uint32_t arrayCount = 1; if(!arrayDim.isEmpty()) @@ -474,6 +551,9 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u if(!tightPacking) cur->offset = (cur->offset + 0x7) & (~0x7); + if(specifiedOffset != ~0U) + cur->offset = specifiedOffset; + el.name = varName; el.byteOffset = cur->offset; el.type.descriptor.pointerTypeID = structContext.pointerTypeId; @@ -496,6 +576,9 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u break; } + if(specifiedOffset != ~0U) + cur->offset = specifiedOffset; + el = structContext.structDef; el.name = varName; el.byteOffset = cur->offset; @@ -522,6 +605,9 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u if(!tightPacking) cur->offset = (cur->offset + 0xFU) & (~0xFU); + if(specifiedOffset != ~0U) + cur->offset = specifiedOffset; + el = structContext.structDef; el.name = varName; el.byteOffset = cur->offset; @@ -833,9 +919,27 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u { // already processed } + else if(annot.name == lit("offset") || annot.name == lit("byte_offset")) + { + uint32_t specifiedOffset = annot.param.toUInt(); + + if(specifiedOffset < cur->offset) + { + errors = + tr("Offset %1 on variable %2 overlaps previous data\n").arg(specifiedOffset).arg(el.name); + success = false; + break; + } + + cur->offset = specifiedOffset; + } + else if(annot.name == lit("matrix_stride")) + { + el.type.descriptor.matrixByteStride = annot.param.toUInt(); + } else { - errors = tr("Unrecognised annotation: %1\n").arg(annot.name); + errors = tr("Unrecognised annotation on variable: %1\n").arg(annot.name); success = false; break; } @@ -888,7 +992,11 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u // normally the array stride is the size of an element const uint32_t elemScalarByteSize = fmt.ElementSize(); - el.type.descriptor.arrayByteStride = el.type.descriptor.matrixByteStride = elemScalarByteSize; + el.type.descriptor.arrayByteStride = elemScalarByteSize; + + // if a manual byte stride wasn't specified + if(el.type.descriptor.matrixByteStride == 0) + el.type.descriptor.matrixByteStride = elemScalarByteSize; if(el.bitFieldSize > 0) { @@ -1413,32 +1521,6 @@ uint32_t BufferFormatter::GetStructVarSize(const rdcarray &membe return lastMemberStart + GetVarSize(*lastChild); } -QString BufferFormatter::DeclarePaddingBytes(uint32_t bytes) -{ - if(bytes == 0) - return QString(); - - QString ret; - - if(bytes > 4) - { - ret += lit("[[hex]] int pad[%1];").arg(bytes / 4); - - bytes = bytes % 4; - } - - if(bytes == 4) - ret += lit("[[hex]] int pad;"); - else if(bytes == 3) - ret += lit("[[hex]] short pad; [[hex]] byte pad;"); - else if(bytes == 2) - ret += lit("[[hex]] short pad;"); - else if(bytes == 1) - ret += lit("[[hex]] xbyte pad;"); - - return ret + lit("\n"); -} - QString BufferFormatter::DeclareStruct(QList &declaredStructs, const QString &name, const rdcarray &members, uint32_t requiredByteStride, QString innerSkippedPrefixString) @@ -1454,7 +1536,7 @@ QString BufferFormatter::DeclareStruct(QList &declaredStructs, const QS for(int i = 0; i < members.count(); i++) { if(offset < members[i].byteOffset) - ret += lit(" ") + DeclarePaddingBytes(members[i].byteOffset - offset); + ret += lit(" [[offset(%1)]]\n").arg(members[i].byteOffset); else if(offset > members[i].byteOffset) qCritical() << "Unexpected offset overlow at" << QString(members[i].name) << "in" << QString(name); @@ -1517,18 +1599,9 @@ QString BufferFormatter::DeclareStruct(QList &declaredStructs, const QS if(tightStride < members[i].type.descriptor.matrixByteStride) { - uint32_t padSize = members[i].type.descriptor.matrixByteStride - tightStride; - for(uint32_t r = 0; r < members[i].type.descriptor.rows; r++) - { - ret += QFormatStr(" %1%2 %3_row%4; %5") - .arg(ToQStr(members[i].type.descriptor.type)) - .arg(members[i].type.descriptor.columns) - .arg(varName) - .arg(r) - .arg(DeclarePaddingBytes(padSize)); - } - - continue; + varTypeName = lit("[[matrix_stride(%1)]] %2") + .arg(members[i].type.descriptor.matrixByteStride) + .arg(varTypeName); } } else @@ -1538,18 +1611,9 @@ QString BufferFormatter::DeclareStruct(QList &declaredStructs, const QS if(tightStride < members[i].type.descriptor.matrixByteStride) { - uint32_t padSize = members[i].type.descriptor.matrixByteStride - tightStride; - for(uint32_t c = 0; c < members[i].type.descriptor.columns; c++) - { - ret += QFormatStr(" %1%2 %3_col%4; %5") - .arg(ToQStr(members[i].type.descriptor.type)) - .arg(members[i].type.descriptor.rows) - .arg(varName) - .arg(c) - .arg(DeclarePaddingBytes(padSize)); - } - - continue; + varTypeName = lit("[[matrix_stride(%1)]] %2") + .arg(members[i].type.descriptor.matrixByteStride) + .arg(varTypeName); } } } @@ -1562,7 +1626,7 @@ QString BufferFormatter::DeclareStruct(QList &declaredStructs, const QS const uint32_t structEnd = GetStructVarSize(members); if(requiredByteStride > structEnd) - ret += lit(" ") + DeclarePaddingBytes(requiredByteStride - structEnd); + ret = lit("[[size(%1)]] %2").arg(requiredByteStride).arg(ret); else if(requiredByteStride != structEnd) qCritical() << "Unexpected stride overlow at struct" << name; } diff --git a/qrenderdoc/Code/QRDUtils.h b/qrenderdoc/Code/QRDUtils.h index 805db09af..11ed194c7 100644 --- a/qrenderdoc/Code/QRDUtils.h +++ b/qrenderdoc/Code/QRDUtils.h @@ -102,8 +102,6 @@ public: uint32_t requiredByteStride); static uint32_t GetStructVarSize(const rdcarray &members); - - static QString DeclarePaddingBytes(uint32_t bytes); }; QVariantList GetVariants(ResourceFormat format, const ShaderConstant &var, const byte *&data, diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp index 0c3606d70..1a74be67e 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp @@ -1370,6 +1370,8 @@ QString PipelineStateViewer::GetVBufferFormatString(uint32_t slot) uint32_t offset = 0; + format = lit("struct vbuffer {\n"); + for(size_t i = 0; i < attrs.size(); i++) { // we disallowed overlaps above, but we do allow *duplicates*. So if our offset has already @@ -1377,8 +1379,11 @@ QString PipelineStateViewer::GetVBufferFormatString(uint32_t slot) if(attrs[i].byteOffset < offset) continue; - // declare any padding from previous element to this one - format += BufferFormatter::DeclarePaddingBytes(attrs[i].byteOffset - offset); + // declare an explicit offset if there's a gap from previous element to this one + if(attrs[i].byteOffset > offset) + format += lit(" [[offset(%1)]]\n").arg(attrs[i].byteOffset); + + format += lit(" "); const ResourceFormat &fmt = attrs[i].format; @@ -1460,8 +1465,10 @@ QString PipelineStateViewer::GetVBufferFormatString(uint32_t slot) format += QFormatStr(" %1; // %2\n").arg(sanitised_name).arg(real_name); } - if(stride > 0) - format += BufferFormatter::DeclarePaddingBytes(stride - offset); + format += lit("}\n\nvbuffer vertex[];"); + + if(stride > offset) + format = lit("[[size(%1)]]\n").arg(stride) + format; return format; }