From 2399c9d1369810b0147183c5514b3e6baebaf66b Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 22 Feb 2022 13:17:03 +0000 Subject: [PATCH] Add support for bitfield packing in the buffer viewer formatting --- qrenderdoc/Code/BufferFormatter.cpp | 411 +++++++++++++++++++---- qrenderdoc/Code/QRDUtils.h | 5 +- qrenderdoc/Windows/BufferViewer.cpp | 14 +- renderdoc/api/replay/shader_types.h | 23 ++ renderdoc/replay/renderdoc_serialise.inl | 2 + 5 files changed, 383 insertions(+), 72 deletions(-) diff --git a/qrenderdoc/Code/BufferFormatter.cpp b/qrenderdoc/Code/BufferFormatter.cpp index 01ed219a7..7760b341e 100644 --- a/qrenderdoc/Code/BufferFormatter.cpp +++ b/qrenderdoc/Code/BufferFormatter.cpp @@ -48,27 +48,33 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u // regex doesn't account for trailing or preceeding whitespace, or comments QRegularExpression regExpr( - lit("^" // start of the line - "(row_major\\s+|column_major\\s+)?" // row_major matrix - "(rgb\\s+)?" // rgb element colourising - "(" // group the options for the type - "uintten|unormten" // R10G10B10A2 types - "|floateleven" // R11G11B10 special type - "|unormh|unormb" // UNORM 16-bit and 8-bit types - "|snormh|snormb" // SNORM 16-bit and 8-bit types - "|bool" // bool is stored as 4-byte int - "|byte|short|int|long" // signed ints - "|ubyte|ushort|uint|ulong" // unsigned ints - "|xbyte|xshort|xint|xlong" // hex ints - "|half|float|double" // float types - "|vec|uvec|ivec|dvec" // OpenGL vector types - "|mat|umat|imat|dmat" // OpenGL matrix types - ")" // 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 - "(\\s*\\[[0-9]+\\])?" // optional array dimension - "(\\s*:\\s*[A-Za-z_][A-Za-z0-9_]*)?" // optional semantic + lit("^" // start of the line + "(?row_major\\s+|column_major\\s+)?" // matrix majorness + "(?unsigned\\s+|signed\\s+)?" // allow 'signed int' or 'unsigned char' + "(?rgb\\s+)?" // rgb element colourising + "(?" // group the options for the type + "uintten|unormten" // R10G10B10A2 types + "|floateleven" // R11G11B10 special type + "|unormh|unormb" // UNORM 16-bit and 8-bit types + "|snormh|snormb" // SNORM 16-bit and 8-bit types + "|bool" // bool is stored as 4-byte int + "|byte|short|int|long|char" // signed ints + "|ubyte|ushort|uint|ulong" // unsigned ints + "|xbyte|xshort|xint|xlong" // hex ints + "|half|float|double" // float types + "|vec|uvec|ivec|dvec" // OpenGL vector types + "|mat|umat|imat|dmat" // OpenGL matrix types + ")" // 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 + "(?\\s*\\[[0-9]+\\])?" // optional array dimension + "(\\s*:\\s*" // optional specifier after : + "(" // bitfield or semantic + "(?[1-9][0-9]*)|" // bitfield packing + "(?[A-Za-z_][A-Za-z0-9_]*)" // semantic to ignore + ")" // end bitfield or semantic + ")?" "$")); bool success = true; @@ -92,6 +98,21 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u "(\\s*\\[[0-9]+\\])?" // optional array dimension "$")); + QRegularExpression bitfieldSkipRegex( + lit("^" // start of the line + "(unsigned\\s+|signed\\s+)?" // allow 'signed int' or 'unsigned char' + "(" // type group + "|bool" // bool is stored as 4-byte int + "|byte|short|int|long|char" // signed ints + "|ubyte|ushort|uint|ulong" // unsigned ints + "|xbyte|xshort|xint|xlong" // hex ints + ")" // end of the type group + // no variable name + "\\s*:\\s*([1-9][0-9]*)" // bitfield packing + "$")); + + uint32_t bitfieldCurPos = ~0U; + // get each line and parse it to determine the format the user wanted for(QString &l : text.split(QRegularExpression(lit("[;\n\r]")))) { @@ -115,6 +136,16 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u if(line == lit("}")) { + if(bitfieldCurPos != ~0U) + { + // update final offset to account for any bits consumed by a trailing bitfield, including + // any bits in the last byte that weren't allocated + cur->offset += (bitfieldCurPos + 7) / 8; + + // reset bitpacking state. + bitfieldCurPos = ~0U; + } + cur->structDef.type.descriptor.arrayByteStride = cur->offset; // struct strides are aligned up to float4 boundary @@ -145,6 +176,7 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u cur = &structelems[lastStruct]; cur->structDef.type.descriptor.name = lastStruct; + bitfieldCurPos = ~0U; continue; } } @@ -211,6 +243,16 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u continue; } + QRegularExpressionMatch bitfieldSkipMatch = bitfieldSkipRegex.match(line); + + if(bitfieldSkipMatch.hasMatch()) + { + if(bitfieldCurPos == ~0U) + bitfieldCurPos = 0; + bitfieldCurPos += bitfieldSkipMatch.captured(3).toUInt(); + continue; + } + QRegularExpressionMatch match = regExpr.match(line); if(!match.hasMatch()) @@ -220,29 +262,62 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u break; } - el.name = !match.captured(6).isEmpty() ? match.captured(6).trimmed() : lit("data"); + el.name = !match.captured(lit("name")).isEmpty() ? match.captured(lit("name")).trimmed() + : lit("data"); - QString basetype = match.captured(3); - if(match.captured(1).trimmed() == lit("row_major")) + QString basetype = match.captured(lit("type")); + if(match.captured(lit("major")).trimmed() == lit("row_major")) el.type.descriptor.flags |= ShaderVariableFlags::RowMajorMatrix; - if(!match.captured(2).isEmpty()) + if(!match.captured(lit("rgb")).isEmpty()) el.type.descriptor.flags |= ShaderVariableFlags::RGBDisplay; - QString firstDim = !match.captured(4).isEmpty() ? match.captured(4) : lit("1"); - QString secondDim = !match.captured(5).isEmpty() ? match.captured(5).mid(1) : lit("1"); - QString arrayDim = !match.captured(7).isEmpty() ? match.captured(7).trimmed() : lit("[1]"); + QString firstDim = !match.captured(lit("vec")).isEmpty() ? match.captured(lit("vec")) : lit("1"); + QString secondDim = + !match.captured(lit("mat")).isEmpty() ? match.captured(lit("mat")).mid(1) : lit("1"); + QString arrayDim = !match.captured(lit("array")).isEmpty() + ? match.captured(lit("array")).trimmed() + : lit("[1]"); arrayDim = arrayDim.mid(1, arrayDim.count() - 2); - if(!match.captured(5).isEmpty() && basetype != lit("mat")) - firstDim.swap(secondDim); + const bool isUnsigned = match.captured(lit("sign")).trimmed() == lit("unsigned"); - el.type.descriptor.name = match.captured(1) + match.captured(2) + match.captured(3) + - match.captured(4) + match.captured(5); + QString bitfield = match.captured(lit("bitfield")); + + if(!bitfield.isEmpty() && !arrayDim.isEmpty()) + { + errors = tr("Bitfield packing is not allowed on arrays on line: %1\n").arg(line); + success = false; + break; + } + + QString vecMatSizeSuffix; + + // if we have a matrix and it's not GL style, then typeAxB means A rows and B columns + // for GL matAxB that means A columns and B rows. This is in contrast to typeA which means A + // columns for HLSL and A columns for GLSL, hence only the swap for matrices + if(!match.captured(lit("mat")).isEmpty() && basetype != lit("mat")) + { + vecMatSizeSuffix = match.captured(lit("vec")) + match.captured(lit("mat")); + firstDim.swap(secondDim); + } + else + { + if(!match.captured(lit("mat")).isEmpty()) + vecMatSizeSuffix = match.captured(lit("mat")).mid(1) + lit("x"); + vecMatSizeSuffix += match.captured(lit("vec")); + } + + // check for square matrix declarations like 'mat4' and 'mat3' + if(basetype == lit("mat") && match.captured(lit("mat")).isEmpty()) + { + secondDim = firstDim; + vecMatSizeSuffix = firstDim + lit("x") + firstDim; + } ResourceFormatType interpretType = ResourceFormatType::Regular; CompType interpretCompType = CompType::Typeless; // check for square matrix declarations like 'mat4' and 'mat3' - if(basetype == lit("mat") && match.captured(5).isEmpty()) + if(basetype == lit("mat") && match.captured(lit("mat")).isEmpty()) secondDim = firstDim; // calculate format @@ -269,6 +344,10 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u break; } + el.bitFieldSize = qMax(1U, bitfield.toUInt(&ok)); + if(!ok) + el.bitFieldSize = 0; + // vectors are marked as row-major by convention if(el.type.descriptor.rows == 1) el.type.descriptor.flags |= ShaderVariableFlags::RowMajorMatrix; @@ -277,9 +356,12 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u { el.type.descriptor.type = VarType::Bool; } - else if(basetype == lit("byte")) + else if(basetype == lit("byte") || basetype == lit("char")) { el.type.descriptor.type = VarType::SByte; + + if(isUnsigned) + el.type.descriptor.type = VarType::UByte; } else if(basetype == lit("ubyte") || basetype == lit("xbyte")) { @@ -288,6 +370,9 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u else if(basetype == lit("short")) { el.type.descriptor.type = VarType::SShort; + + if(isUnsigned) + el.type.descriptor.type = VarType::UShort; } else if(basetype == lit("ushort") || basetype == lit("xshort")) { @@ -296,6 +381,9 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u else if(basetype == lit("long")) { el.type.descriptor.type = VarType::SLong; + + if(isUnsigned) + el.type.descriptor.type = VarType::ULong; } else if(basetype == lit("ulong") || basetype == lit("xlong")) { @@ -304,6 +392,9 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u else if(basetype == lit("int") || basetype == lit("ivec") || basetype == lit("imat")) { el.type.descriptor.type = VarType::SInt; + + if(isUnsigned) + el.type.descriptor.type = VarType::UInt; } else if(basetype == lit("uint") || basetype == lit("xint") || basetype == lit("uvec") || basetype == lit("umat")) @@ -364,7 +455,39 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u } else { - errors = tr("Unrecognised basic type on line: %1\n").arg(line); + errors = tr("Unrecognised type on line: %1\n").arg(line); + success = false; + break; + } + } + + el.type.descriptor.name = ToStr(el.type.descriptor.type) + vecMatSizeSuffix; + + // validate that bitfields are only allowed for regular scalars + if(el.bitFieldSize > 0) + { + if(el.type.descriptor.rows > 1 || el.type.descriptor.columns > 1) + { + errors = tr("Bitfield packing only allowed on scalar values on line: %1\n").arg(line); + success = false; + break; + } + if(el.type.descriptor.elements > 1) + { + errors = tr("Bitfield packing not allowed on arrays on line: %1\n").arg(line); + success = false; + break; + } + if(interpretType != ResourceFormatType::Regular || interpretCompType != CompType::Typeless) + { + errors = + tr("Bitfield packing not allowed on interpreted/packed formats on line: %1\n").arg(line); + success = false; + break; + } + if(VarTypeCompType(el.type.descriptor.type) == CompType::Float) + { + errors = tr("Bitfield packing not allowed on floating point formats on line: %1\n").arg(line); success = false; break; } @@ -379,7 +502,60 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u ResourceFormat fmt = GetInterpretedResourceFormat(el); // normally the array stride is the size of an element - el.type.descriptor.arrayByteStride = el.type.descriptor.matrixByteStride = fmt.ElementSize(); + const uint32_t elemScalarByteSize = fmt.ElementSize(); + el.type.descriptor.arrayByteStride = el.type.descriptor.matrixByteStride = elemScalarByteSize; + + if(el.bitFieldSize > 0) + { + const uint32_t elemScalarBitSize = elemScalarByteSize * 8; + + // bitfields can't be larger than the base type + if(el.bitFieldSize > elemScalarBitSize) + { + errors = + tr("Bitfield cannot specify a larger size than the base type on line: %1\n").arg(line); + success = false; + break; + } + + uint32_t start = bitfieldCurPos; + if(start == ~0U) + start = 0; + + // if we would end past the current base type size, first roll over and start at the next byte + // this could be: + // unsigned int a : 24; + // unsigned byte b : 4; + // unsigned byte c : 4; + // where we just 'rollover' the 3 bytes packed into the unsigned int and start the byte on + // the next byte, there's no extra padding added + // or it could be: + // unsigned int a : 29; + // unsigned byte b : 4; + // unsigned byte c : 4; + // where b would pass through the end of the fourth byte so there ends up being 3 bits of + // padding between a and b when b is rolled onto the next byte + // similarly this can happen if the types are the same: + // unsigned int a : 29; + // unsigned int b : 4; + // since b would still pass through the end of the first dword. + // similarly this allows 'more' padding when the types are bigger: + // unsigned int a : 17; + // unsigned int b : 17; + // which would produce 15 bytes of padding + // Note that if the types are the same and big enough we won't roll over, as in: + // unsigned int a : 24; + // unsigned int b : 4; + // unsigned int c : 4; + if(start + el.bitFieldSize > elemScalarBitSize) + { + // align the offset up to where this bitfield needs to start + cur->offset += ((bitfieldCurPos + (elemScalarBitSize - 1)) / elemScalarBitSize) * + (elemScalarBitSize / 8); + // reset the current bitfield pos + bitfieldCurPos = 0; + } + } uint32_t padding = 0; @@ -400,8 +576,46 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u el.type.descriptor.arrayByteStride = el.type.descriptor.matrixByteStride * majorDim; } + el.byteOffset = cur->offset; + bool updateCurOffset = true; + + // handle bitfield packing + if(el.bitFieldSize > 0) + { + // if there's no previous bitpacking, nothing much to do + if(bitfieldCurPos == ~0U) + { + // start the next bitfield at our size + bitfieldCurPos = el.bitFieldSize; + } + else + { + // start the next bitfield at the end of the previous + el.bitFieldOffset = bitfieldCurPos; + // update by our size + bitfieldCurPos += el.bitFieldSize; + } + + // don't update the current position + updateCurOffset = false; + } + else + { + // this element is not bitpacked + + // update offset to account for any bits consumed by the previous bitfield, which won't have + // happened yet, including any bits in the last byte that weren't allocated + cur->offset += (bitfieldCurPos + 7) / 8; + + // align to our base element size + cur->offset = (cur->offset + (elemScalarByteSize - 1)) & (~(elemScalarByteSize - 1)); + + // reset bitpacking state. + bitfieldCurPos = ~0U; + } + // cbuffer packing rules - if(!tightPacking) + if(!tightPacking && bitfieldCurPos == ~0U) { if(el.type.descriptor.elements == 1) { @@ -431,11 +645,20 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u } } - el.byteOffset = cur->offset; - cur->structDef.type.members.push_back(el); - cur->offset += el.type.descriptor.arrayByteStride * el.type.descriptor.elements - padding; + if(updateCurOffset) + cur->offset += el.type.descriptor.arrayByteStride * el.type.descriptor.elements - padding; + } + + if(bitfieldCurPos != ~0U) + { + // update final offset to account for any bits consumed by a trailing bitfield, including any + // bits in the last byte that weren't allocated + cur->offset += (bitfieldCurPos + 7) / 8; + + // reset bitpacking state. + bitfieldCurPos = ~0U; } // if we succeeded parsing but didn't get any root elements, use the last defined struct as the @@ -1023,8 +1246,7 @@ static void FillShaderVarData(ShaderVariable &var, const ShaderConstant &elem, c std::swap(outerCount, innerCount); } - QVariantList objs = - GetVariants(GetInterpretedResourceFormat(elem), elem.type.descriptor, data, end); + QVariantList objs = GetVariants(GetInterpretedResourceFormat(elem), elem, data, end); if(objs.isEmpty()) { @@ -1042,7 +1264,7 @@ static void FillShaderVarData(ShaderVariable &var, const ShaderConstant &elem, c if(colMajor) dst = inner * elem.type.descriptor.columns + outer; - const QVariant &o = objs[src]; + QVariant o = objs[src]; src++; @@ -1258,9 +1480,11 @@ inline T readObj(const byte *&data, const byte *end, bool &ok) return ret; } -QVariantList GetVariants(ResourceFormat format, const ShaderConstantDescriptor &varDesc, - const byte *&data, const byte *end) +QVariantList GetVariants(ResourceFormat format, const ShaderConstant &var, const byte *&data, + const byte *end) { + const ShaderConstantDescriptor &varDesc = var.type.descriptor; + QVariantList ret; bool ok = true; @@ -1488,25 +1712,90 @@ QVariantList GetVariants(ResourceFormat format, const ShaderConstantDescriptor & } else if(format.compType == CompType::SInt) { - if(format.compByteWidth == 8) - ret.push_back((qlonglong)readObj(data, end, ok)); - else if(format.compByteWidth == 4) - ret.push_back((int)readObj(data, end, ok)); - else if(format.compByteWidth == 2) - ret.push_back((int)readObj(data, end, ok)); - else if(format.compByteWidth == 1) - ret.push_back((int)readObj(data, end, ok)); + if(var.bitFieldSize == 0) + { + if(format.compByteWidth == 8) + ret.push_back((qlonglong)readObj(data, end, ok)); + else if(format.compByteWidth == 4) + ret.push_back((int)readObj(data, end, ok)); + else if(format.compByteWidth == 2) + ret.push_back((int)readObj(data, end, ok)); + else if(format.compByteWidth == 1) + ret.push_back((int)readObj(data, end, ok)); + } + else + { + uint64_t uval = 0; + if(format.compByteWidth == 8) + uval = readObj(data, end, ok); + else if(format.compByteWidth == 4) + uval = readObj(data, end, ok); + else if(format.compByteWidth == 2) + uval = readObj(data, end, ok); + else if(format.compByteWidth == 1) + uval = readObj(data, end, ok); + + int64_t val = 0; + + if(ok) + { + // shift by the offset + uval >>= var.bitFieldOffset; + + // mask by the size + const uint64_t mask = ((1ULL << var.bitFieldSize) - 1ULL); + uval &= mask; + + // sign extend by hand + if(uval & (1ULL << (var.bitFieldSize - 1))) + uval |= ~0ULL ^ mask; + + memcpy(&val, &uval, sizeof(uval)); + } + + ret.push_back(val); + } } else if(format.compType == CompType::UInt) { - if(format.compByteWidth == 8) - ret.push_back((qulonglong)readObj(data, end, ok)); - else if(format.compByteWidth == 4) - ret.push_back((uint32_t)readObj(data, end, ok)); - else if(format.compByteWidth == 2) - ret.push_back((uint32_t)readObj(data, end, ok)); - else if(format.compByteWidth == 1) - ret.push_back((uint32_t)readObj(data, end, ok)); + if(var.bitFieldSize == 0) + { + if(format.compByteWidth == 8) + ret.push_back((qulonglong)readObj(data, end, ok)); + else if(format.compByteWidth == 4) + ret.push_back((uint32_t)readObj(data, end, ok)); + else if(format.compByteWidth == 2) + ret.push_back((uint32_t)readObj(data, end, ok)); + else if(format.compByteWidth == 1) + ret.push_back((uint32_t)readObj(data, end, ok)); + } + else + { + uint64_t val = 0; + if(format.compByteWidth == 8) + val = readObj(data, end, ok); + else if(format.compByteWidth == 4) + val = readObj(data, end, ok); + else if(format.compByteWidth == 2) + val = readObj(data, end, ok); + else if(format.compByteWidth == 1) + val = readObj(data, end, ok); + + if(ok) + { + // shift by the offset + val >>= var.bitFieldOffset; + + // mask by the size + val &= ((1ULL << var.bitFieldSize) - 1ULL); + } + else + { + val = 0; + } + + ret.push_back(val); + } } else if(format.compType == CompType::UScaled) { diff --git a/qrenderdoc/Code/QRDUtils.h b/qrenderdoc/Code/QRDUtils.h index 20d084879..5de874b74 100644 --- a/qrenderdoc/Code/QRDUtils.h +++ b/qrenderdoc/Code/QRDUtils.h @@ -76,6 +76,7 @@ inline QMetaType::Type GetVariantMetatype(const QVariant &v) struct BufferFormatter { +private: Q_DECLARE_TR_FUNCTIONS(BufferFormatter); static GraphicsAPI m_API; @@ -105,8 +106,8 @@ public: static QString DeclarePaddingBytes(uint32_t bytes); }; -QVariantList GetVariants(ResourceFormat format, const ShaderConstantDescriptor &varDesc, - const byte *&data, const byte *end); +QVariantList GetVariants(ResourceFormat format, const ShaderConstant &var, const byte *&data, + const byte *end); ResourceFormat GetInterpretedResourceFormat(const ShaderConstant &elem); void SetInterpretedResourceFormat(ShaderConstant &elem, ResourceFormatType interpretType, CompType interpretCompType); diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index f8b11f340..878fb5d03 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -994,7 +994,7 @@ public: // only slightly wasteful, we need to fetch all variants together // since some formats are packed and can't be read individually - QVariantList list = GetVariants(prop.format, el.type.descriptor, data, end); + QVariantList list = GetVariants(prop.format, el, data, end); if(!list.isEmpty()) { @@ -1147,7 +1147,7 @@ public: // only slightly wasteful, we need to fetch all variants together // since some formats are packed and can't be read individually - QVariantList list = GetVariants(prop.format, el.type.descriptor, data, end); + QVariantList list = GetVariants(prop.format, el, data, end); int comp = componentForIndex(col); @@ -2720,11 +2720,7 @@ void BufferViewer::OnEventChanged(uint32_t eventId) buf = new BufferData; // calculate tight stride - buf->stride = 0; - for(int i = 0; i < bufdata->vsinConfig.columns.count(); i++) - buf->stride += bufdata->vsinConfig.columns[i].type.descriptor.arrayByteStride; - - buf->stride = qMax((size_t)1, buf->stride); + buf->stride = qMax(1U, BufferFormatter::GetStructVarSize(bufdata->vsinConfig.columns)); // the "permanent" range starts at ByteOffset and goes for m_ByteSize uint64_t rangeStart = m_ByteOffset; @@ -3056,7 +3052,7 @@ void BufferViewer::calcBoundingData(CalcBoundingBoxData &bbox) if(!prop->perinstance) bytes += d.stride * idx; - QVariantList list = GetVariants(prop->format, el->type.descriptor, bytes, d.end); + QVariantList list = GetVariants(prop->format, *el, bytes, d.end); for(int comp = 0; comp < 4 && comp < list.count(); comp++) { @@ -4421,7 +4417,7 @@ void BufferViewer::exportData(const BufferExport ¶ms) // only slightly wasteful, we need to fetch all variants together // since some formats are packed and can't be read individually - QVariantList list = GetVariants(prop->format, el->type.descriptor, data, end); + QVariantList list = GetVariants(prop->format, *el, data, end); for(int v = 0; v < list.count(); v++) { diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index 7a65478d3..6e41dc6db 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -1069,6 +1069,29 @@ struct ShaderConstant rdcstr name; DOCUMENT("The byte offset of this constant relative to the parent structure"); uint32_t byteOffset = 0; + DOCUMENT(R"(If the variable is bitfield packed, the bit offset from :data:`byteOffset` above where +this variable starts. + +If the variable is not a bitfield, this value will be 0. Only integer scalars will have bitfield +packing. + +.. note:: + Although the offset specified in :data:`byteOffset` is in bytes, this bitfield offset may be + larger than 0 depending on the surrounding values and their types and packing. However it is + guaranteed that the offset and the size (from :data:`bitFieldSize`) will be contained within the + normal bit size for the variable type. For example if the variable type is a 32-bit integer, the + offsets may range from 0 to 31 and the sum of offset and size will be no more than 32. If the + variable is an 8-bit integer, similarly the offset will be 0 to 7 and the sum will be no more + than 8. +)"); + uint16_t bitFieldOffset = 0; + DOCUMENT(R"(If the variable is bitfield packed, the number of bits this variable spans starting +from :data:`bitFieldOffset` into memory. + +If the variable is not a bitfield, this value will be 0. Only integer scalars will have bitfield +packing. +)"); + uint16_t bitFieldSize = 0; DOCUMENT("If this constant is no larger than a 64-bit constant, gives a default value for it."); uint64_t defaultValue = 0; DOCUMENT(R"(The type information for this constant. diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index eefc781ac..ee4fb76eb 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -190,6 +190,8 @@ void DoSerialise(SerialiserType &ser, ShaderConstant &el) { SERIALISE_MEMBER(name); SERIALISE_MEMBER(byteOffset); + SERIALISE_MEMBER(bitFieldOffset); + SERIALISE_MEMBER(bitFieldSize); SERIALISE_MEMBER(defaultValue); SERIALISE_MEMBER(type);