From cb49f21f2a0e988bfe551b296aa823abfae527d4 Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 22 Feb 2022 15:35:10 +0000 Subject: [PATCH] Support enums in buffer formatter --- qrenderdoc/Code/BufferFormatter.cpp | 660 +++++++++++------- qrenderdoc/Windows/BufferViewer.cpp | 13 +- .../D3D11PipelineStateViewer.cpp | 4 +- .../D3D12PipelineStateViewer.cpp | 4 +- .../PipelineState/PipelineStateViewer.cpp | 2 +- qrenderdoc/Windows/ShaderViewer.cpp | 2 + renderdoc/api/replay/replay_enums.h | 11 +- renderdoc/driver/gl/gl_replay.cpp | 2 + .../shaders/spirv/spirv_disassemble.cpp | 3 + 9 files changed, 441 insertions(+), 260 deletions(-) diff --git a/qrenderdoc/Code/BufferFormatter.cpp b/qrenderdoc/Code/BufferFormatter.cpp index 7760b341e..31edbc499 100644 --- a/qrenderdoc/Code/BufferFormatter.cpp +++ b/qrenderdoc/Code/BufferFormatter.cpp @@ -36,6 +36,118 @@ struct StructFormatData GraphicsAPI BufferFormatter::m_API; +static bool MatchBaseTypeDeclaration(QString basetype, ShaderConstant &el, const bool isUnsigned, + CompType &interpretCompType, ResourceFormatType &interpretType) +{ + if(basetype == lit("bool")) + { + el.type.descriptor.type = VarType::Bool; + } + 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")) + { + el.type.descriptor.type = VarType::UByte; + } + 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")) + { + el.type.descriptor.type = VarType::UShort; + } + 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")) + { + el.type.descriptor.type = VarType::ULong; + } + 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")) + { + el.type.descriptor.type = VarType::UInt; + } + else if(basetype == lit("half")) + { + el.type.descriptor.type = VarType::Half; + } + else if(basetype == lit("float") || basetype == lit("vec") || basetype == lit("mat")) + { + el.type.descriptor.type = VarType::Float; + } + else if(basetype == lit("double") || basetype == lit("dvec") || basetype == lit("dmat")) + { + el.type.descriptor.type = VarType::Double; + } + else if(basetype == lit("unormh")) + { + el.type.descriptor.type = VarType::UShort; + interpretCompType = CompType::UNorm; + } + else if(basetype == lit("unormb")) + { + el.type.descriptor.type = VarType::UByte; + interpretCompType = CompType::UNorm; + } + else if(basetype == lit("snormh")) + { + el.type.descriptor.type = VarType::SShort; + interpretCompType = CompType::SNorm; + } + else if(basetype == lit("snormb")) + { + el.type.descriptor.type = VarType::SByte; + interpretCompType = CompType::SNorm; + } + else if(basetype == lit("uintten")) + { + el.type.descriptor.type = VarType::UInt; + interpretType = ResourceFormatType::R10G10B10A2; + el.type.descriptor.columns = 4; + } + else if(basetype == lit("unormten")) + { + el.type.descriptor.type = VarType::UInt; + interpretCompType = CompType::UNorm; + interpretType = ResourceFormatType::R10G10B10A2; + el.type.descriptor.columns = 4; + } + else if(basetype == lit("floateleven")) + { + el.type.descriptor.type = VarType::Float; + interpretCompType = CompType::Float; + interpretType = ResourceFormatType::R11G11B10; + el.type.descriptor.columns = 3; + } + else + { + return false; + } + + return true; +} + ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, uint64_t maxLen, bool tightPacking, QString &errors) { @@ -88,14 +200,24 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u text = text.replace(c_comments, QString()).replace(cpp_comments, QString()); // ensure braces are forced onto separate lines so we can parse them text = text.replace(QLatin1Char('{'), lit("\n{\n")).replace(QLatin1Char('}'), lit("\n}\n")); + // treat commas as semi-colons for simplicity of parsing enum declarations and struct declarations + text = text.replace(QLatin1Char(','), QLatin1Char(';')); - QRegularExpression structDeclRegex(lit("^struct\\s+([A-Za-z_][A-Za-z0-9_]*)$")); + 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 - "(\\*)?" // maybe a pointer + "\\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 + "([A-Za-z_][A-Za-z0-9_]*)" // value name + "\\s*=\\s*" // maybe a pointer + "(0x[0-9a-fA-F]+|[0-9]+)" // numerical value "$")); QRegularExpression bitfieldSkipRegex( @@ -146,42 +268,121 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u bitfieldCurPos = ~0U; } - cur->structDef.type.descriptor.arrayByteStride = cur->offset; + if(cur->structDef.type.descriptor.type == VarType::Struct) + { + cur->structDef.type.descriptor.arrayByteStride = cur->offset; - // struct strides are aligned up to float4 boundary - if(!tightPacking) - cur->structDef.type.descriptor.arrayByteStride = (cur->offset + 0xFU) & (~0xFU); + // struct strides are aligned up to float4 boundary + if(!tightPacking) + cur->structDef.type.descriptor.arrayByteStride = (cur->offset + 0xFU) & (~0xFU); - cur->pointerTypeId = PointerTypeRegistry::GetTypeID(cur->structDef.type); + cur->pointerTypeId = PointerTypeRegistry::GetTypeID(cur->structDef.type); + } cur = &root; continue; } } - if(line.contains(lit("struct"))) + if(line.startsWith(lit("struct")) || line.startsWith(lit("enum"))) { QRegularExpressionMatch match = structDeclRegex.match(line); if(match.hasMatch()) { - lastStruct = match.captured(1); + QString name = match.captured(2); - if(structelems.contains(lastStruct)) + if(structelems.contains(name)) { - errors = tr("Duplicate struct definition: %1\n").arg(lastStruct); + errors = tr("Duplicate struct/enum definition: %1\n").arg(name); success = false; break; } - cur = &structelems[lastStruct]; - cur->structDef.type.descriptor.name = lastStruct; + cur = &structelems[name]; + cur->structDef.type.descriptor.name = name; bitfieldCurPos = ~0U; + + if(match.captured(1) == lit("struct")) + { + lastStruct = name; + cur->structDef.type.descriptor.type = VarType::Struct; + } + else + { + cur->structDef.type.descriptor.type = VarType::Enum; + + QString baseType = match.captured(4); + + if(baseType.isEmpty()) + { + errors = tr("Enum declarations require sized base type, see line: %1\n").arg(name); + success = false; + break; + } + + ShaderConstant tmp; + CompType dummyCompType; + ResourceFormatType dummyType; + + bool matched = MatchBaseTypeDeclaration(baseType, tmp, true, dummyCompType, dummyType); + + if(!matched) + { + errors = tr("Unknown enum base type on line: %1\n").arg(line); + success = false; + break; + } + + cur->structDef.type.descriptor.arrayByteStride = VarTypeByteSize(tmp.type.descriptor.type); + } + continue; } } ShaderConstant el; + ResourceFormatType interpretType = ResourceFormatType::Regular; + CompType interpretCompType = CompType::Typeless; + + if(cur->structDef.type.descriptor.type == VarType::Enum) + { + QRegularExpressionMatch enumMatch = enumValueRegex.match(line); + + if(!enumMatch.hasMatch()) + { + errors = tr("Couldn't parse enum value declaration on line: %1\n").arg(line); + success = false; + break; + } + + bool ok = false; + uint64_t val = enumMatch.captured(2).toULongLong(&ok, 0); + + if(!ok) + { + errors = tr("Couldn't parse enum numerical value on line: %1\n").arg(line); + success = false; + break; + } + + el.name = enumMatch.captured(1); + el.defaultValue = val; + + cur->structDef.type.members.push_back(el); + + continue; + } + + QRegularExpressionMatch bitfieldSkipMatch = bitfieldSkipRegex.match(line); + + if(bitfieldSkipMatch.hasMatch()) + { + if(bitfieldCurPos == ~0U) + bitfieldCurPos = 0; + bitfieldCurPos += bitfieldSkipMatch.captured(3).toUInt(); + continue; + } QRegularExpressionMatch structMatch = structUseRegex.match(line); @@ -204,8 +405,17 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u arrayCount = 1; } + QString bitfield = structMatch.captured(6).trimmed(); + if(isPointer) { + if(!bitfield.isEmpty()) + { + errors = tr("Bitfield packing is not allowed on pointers on line: %1\n").arg(line); + success = false; + break; + } + // if not tight packing, align up to pointer size if(!tightPacking) cur->offset = (cur->offset + 0x7) & (~0x7); @@ -220,9 +430,40 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u cur->offset += 8; cur->structDef.type.members.push_back(el); + + continue; + } + else if(structContext.structDef.type.descriptor.type == VarType::Enum) + { + if(!bitfield.isEmpty() && !arrayDim.isEmpty()) + { + errors = tr("Bitfield packing is not allowed on arrays on line: %1\n").arg(line); + success = false; + break; + } + + el = structContext.structDef; + el.name = varName; + el.byteOffset = cur->offset; + el.type.descriptor.elements = arrayCount; + + bool ok = false; + el.bitFieldSize = qMax(1U, bitfield.toUInt(&ok)); + if(!ok) + el.bitFieldSize = 0; + + // don't continue here - we will go through and handle bitfield packing like any other + // scalar } else { + if(!bitfield.isEmpty()) + { + errors = tr("Bitfield packing is not allowed on structs on line: %1\n").arg(line); + success = false; + break; + } + // cbuffer packing rules, structs are always float4 base aligned if(!tightPacking) cur->offset = (cur->offset + 0xFU) & (~0xFU); @@ -238,265 +479,158 @@ ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, u uint32_t padding = el.type.descriptor.arrayByteStride - structContext.offset; cur->offset += el.type.descriptor.elements * el.type.descriptor.arrayByteStride - padding; + + continue; } - - 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()) - { - errors = tr("Couldn't parse line: %1\n").arg(line); - success = false; - break; - } - - el.name = !match.captured(lit("name")).isEmpty() ? match.captured(lit("name")).trimmed() - : lit("data"); - - QString basetype = match.captured(lit("type")); - if(match.captured(lit("major")).trimmed() == lit("row_major")) - el.type.descriptor.flags |= ShaderVariableFlags::RowMajorMatrix; - if(!match.captured(lit("rgb")).isEmpty()) - el.type.descriptor.flags |= ShaderVariableFlags::RGBDisplay; - 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); - - const bool isUnsigned = match.captured(lit("sign")).trimmed() == lit("unsigned"); - - 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")); - } + QRegularExpressionMatch match = regExpr.match(line); - // 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(lit("mat")).isEmpty()) - secondDim = firstDim; - - // calculate format - { - bool ok = false; - - el.type.descriptor.columns = firstDim.toUInt(&ok); - if(!ok) + if(!match.hasMatch()) { - errors = tr("Invalid vector dimension on line: %1\n").arg(line); + errors = tr("Couldn't parse line: %1\n").arg(line); success = false; break; } - el.type.descriptor.elements = qMax(1U, arrayDim.toUInt(&ok)); - if(!ok) - el.type.descriptor.elements = 1; + el.name = !match.captured(lit("name")).isEmpty() ? match.captured(lit("name")).trimmed() + : lit("data"); - el.type.descriptor.rows = qMax(1U, secondDim.toUInt(&ok)); - if(!ok) - { - errors = tr("Invalid matrix second dimension on line: %1\n").arg(line); - success = false; - 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) + QString basetype = match.captured(lit("type")); + if(match.captured(lit("major")).trimmed() == lit("row_major")) el.type.descriptor.flags |= ShaderVariableFlags::RowMajorMatrix; + if(!match.captured(lit("rgb")).isEmpty()) + el.type.descriptor.flags |= ShaderVariableFlags::RGBDisplay; + 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(basetype == lit("bool")) - { - el.type.descriptor.type = VarType::Bool; - } - else if(basetype == lit("byte") || basetype == lit("char")) - { - el.type.descriptor.type = VarType::SByte; + const bool isUnsigned = match.captured(lit("sign")).trimmed() == lit("unsigned"); - if(isUnsigned) - el.type.descriptor.type = VarType::UByte; - } - else if(basetype == lit("ubyte") || basetype == lit("xbyte")) - { - el.type.descriptor.type = VarType::UByte; - } - else if(basetype == lit("short")) - { - el.type.descriptor.type = VarType::SShort; + QString bitfield = match.captured(lit("bitfield")); - if(isUnsigned) - el.type.descriptor.type = VarType::UShort; - } - else if(basetype == lit("ushort") || basetype == lit("xshort")) + if(!bitfield.isEmpty() && !arrayDim.isEmpty()) { - el.type.descriptor.type = VarType::UShort; + errors = tr("Bitfield packing is not allowed on arrays on line: %1\n").arg(line); + success = false; + break; } - 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")) - { - el.type.descriptor.type = VarType::ULong; - } - else if(basetype == lit("int") || basetype == lit("ivec") || basetype == lit("imat")) - { - el.type.descriptor.type = VarType::SInt; + QString vecMatSizeSuffix; - if(isUnsigned) - el.type.descriptor.type = VarType::UInt; - } - else if(basetype == lit("uint") || basetype == lit("xint") || basetype == lit("uvec") || - basetype == lit("umat")) + // 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")) { - el.type.descriptor.type = VarType::UInt; - } - else if(basetype == lit("half")) - { - el.type.descriptor.type = VarType::Half; - } - else if(basetype == lit("float") || basetype == lit("vec") || basetype == lit("mat")) - { - el.type.descriptor.type = VarType::Float; - } - else if(basetype == lit("double") || basetype == lit("dvec") || basetype == lit("dmat")) - { - el.type.descriptor.type = VarType::Double; - } - else if(basetype == lit("unormh")) - { - el.type.descriptor.type = VarType::UShort; - interpretCompType = CompType::UNorm; - } - else if(basetype == lit("unormb")) - { - el.type.descriptor.type = VarType::UByte; - interpretCompType = CompType::UNorm; - } - else if(basetype == lit("snormh")) - { - el.type.descriptor.type = VarType::SShort; - interpretCompType = CompType::SNorm; - } - else if(basetype == lit("snormb")) - { - el.type.descriptor.type = VarType::SByte; - interpretCompType = CompType::SNorm; - } - else if(basetype == lit("uintten")) - { - el.type.descriptor.type = VarType::UInt; - interpretType = ResourceFormatType::R10G10B10A2; - el.type.descriptor.columns = 4; - } - else if(basetype == lit("unormten")) - { - el.type.descriptor.type = VarType::UInt; - interpretCompType = CompType::UNorm; - interpretType = ResourceFormatType::R10G10B10A2; - el.type.descriptor.columns = 4; - } - else if(basetype == lit("floateleven")) - { - el.type.descriptor.type = VarType::Float; - interpretCompType = CompType::Float; - interpretType = ResourceFormatType::R11G11B10; - el.type.descriptor.columns = 3; + vecMatSizeSuffix = match.captured(lit("vec")) + match.captured(lit("mat")); + firstDim.swap(secondDim); } else { - errors = tr("Unrecognised type on line: %1\n").arg(line); - success = false; - break; + 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; + } + + // check for square matrix declarations like 'mat4' and 'mat3' + if(basetype == lit("mat") && match.captured(lit("mat")).isEmpty()) + secondDim = firstDim; + + // calculate format + { + bool ok = false; + + el.type.descriptor.columns = firstDim.toUInt(&ok); + if(!ok) + { + errors = tr("Invalid vector dimension on line: %1\n").arg(line); + success = false; + break; + } + + el.type.descriptor.elements = qMax(1U, arrayDim.toUInt(&ok)); + if(!ok) + el.type.descriptor.elements = 1; + + el.type.descriptor.rows = qMax(1U, secondDim.toUInt(&ok)); + if(!ok) + { + errors = tr("Invalid matrix second dimension on line: %1\n").arg(line); + success = false; + 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; + + bool matched = + MatchBaseTypeDeclaration(basetype, el, isUnsigned, interpretCompType, interpretType); + + if(!matched) + { + 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; + } + } + + if(basetype == lit("xlong") || basetype == lit("xint") || basetype == lit("xshort") || + basetype == lit("xbyte")) + el.type.descriptor.flags |= ShaderVariableFlags::HexDisplay; } - 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; - } - } - - if(basetype == lit("xlong") || basetype == lit("xint") || basetype == lit("xshort") || - basetype == lit("xbyte")) - el.type.descriptor.flags |= ShaderVariableFlags::HexDisplay; - SetInterpretedResourceFormat(el, interpretType, interpretCompType); ResourceFormat fmt = GetInterpretedResourceFormat(el); @@ -981,6 +1115,9 @@ uint32_t BufferFormatter::GetVarSize(const ShaderConstant &var) if(typeSize > 1) size *= typeSize; + if(var.type.descriptor.type == VarType::Enum) + size = var.type.descriptor.arrayByteStride; + if(var.type.descriptor.rows > 1) { if(var.type.descriptor.RowMajor()) @@ -989,7 +1126,7 @@ uint32_t BufferFormatter::GetVarSize(const ShaderConstant &var) size = var.type.descriptor.matrixByteStride * var.type.descriptor.columns; } - if(!var.type.members.empty()) + if(var.type.descriptor.type != VarType::Enum && !var.type.members.empty()) size = GetStructVarSize(var.type.members); if(var.type.descriptor.elements > 1 && var.type.descriptor.elements != ~0U) @@ -1005,7 +1142,7 @@ uint32_t BufferFormatter::GetStructVarSize(const rdcarray &membe const ShaderConstant *lastChild = &members.back(); lastMemberStart += lastChild->byteOffset; - while(!lastChild->type.members.isEmpty()) + while(lastChild->type.descriptor.type != VarType::Enum && !lastChild->type.members.isEmpty()) { if(lastChild->type.descriptor.elements != ~0U) lastMemberStart += (qMax(lastChild->type.descriptor.elements, 1U) - 1) * @@ -1209,6 +1346,9 @@ ResourceFormat GetInterpretedResourceFormat(const ShaderConstant &elem) format.compByteWidth = VarTypeByteSize(elem.type.descriptor.type); + if(elem.type.descriptor.type == VarType::Enum) + format.compByteWidth = elem.type.descriptor.arrayByteStride; + if(elem.type.descriptor.RowMajor() || elem.type.descriptor.rows == 1) format.compCount = elem.type.descriptor.columns; else @@ -1286,6 +1426,7 @@ static void FillShaderVarData(ShaderVariable &var, const ShaderConstant &elem, c case VarType::SByte: var.value.u8v[dst] = (int8_t)qBound((int)INT8_MIN, o.toInt(), (int)INT8_MAX); break; + case VarType::Enum: case VarType::GPUPointer: // treat this as a 64-bit unsigned integer var.value.u64v[dst] = o.toULongLong(); @@ -1315,7 +1456,7 @@ ShaderVariable InterpretShaderVar(const ShaderConstant &elem, const byte *data, ret.flags = elem.type.descriptor.flags; - if(!elem.type.members.isEmpty()) + if(elem.type.descriptor.type != VarType::Enum && !elem.type.members.isEmpty()) { ret.rows = ret.columns = 0; @@ -1796,6 +1937,26 @@ QVariantList GetVariants(ResourceFormat format, const ShaderConstant &var, const ret.push_back(val); } + + if(var.type.descriptor.type == VarType::Enum) + { + uint64_t val = ret.back().toULongLong(); + + QString str = QApplication::translate("BufferFormatter", "Unknown %1 (%2)") + .arg(QString(var.type.descriptor.name)) + .arg(val); + + for(size_t i = 0; i < var.type.members.size(); i++) + { + if(val == var.type.members[i].defaultValue) + { + str = var.type.members[i].name; + break; + } + } + + ret.back() = str; + } } else if(format.compType == CompType::UScaled) { @@ -2016,6 +2177,7 @@ QString RowString(const ShaderVariable &v, uint32_t row, VarType type) v.value.s8v[row * v.columns + 1], v.value.s8v[row * v.columns + 2], v.value.s8v[row * v.columns + 3]); case VarType::GPUPointer: return ToQStr(v.GetPointer()); + case VarType::Enum: case VarType::ConstantBlock: case VarType::ReadOnlyResource: case VarType::ReadWriteResource: diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index 878fb5d03..5d7784463 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -1034,6 +1034,10 @@ public: rgb = QColor::fromRgb(r, g, b); } + else + { + return QVariant(); + } if(role == Qt::BackgroundRole) return QBrush(rgb); @@ -1989,8 +1993,11 @@ static int MaxNumRows(const ShaderConstant &c) { int ret = c.type.descriptor.rows; - for(const ShaderConstant &child : c.type.members) - ret = qMax(ret, MaxNumRows(child)); + if(c.type.descriptor.type != VarType::Enum) + { + for(const ShaderConstant &child : c.type.members) + ret = qMax(ret, MaxNumRows(child)); + } return ret; } @@ -2006,7 +2013,7 @@ static void UnrollConstant(rdcstr prefix, uint32_t baseOffset, const ShaderConst if(!prefix.isEmpty()) baseName = prefix + "." + baseName; - if(constant.type.members.isEmpty()) + if(constant.type.descriptor.type == VarType::Enum || constant.type.members.isEmpty()) { BufferElementProperties prop; prop.format = GetInterpretedResourceFormat(constant); diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp index a75d0415e..430743143 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp @@ -796,7 +796,7 @@ void D3D11PipelineStateViewer::addResourceRow(const D3D11ViewTag &view, { if(r.viewFormat.compType == CompType::Typeless) { - if(!shaderInput->variableType.members.empty()) + if(shaderInput->variableType.descriptor.type == VarType::Struct) format = lit("struct ") + shaderInput->variableType.descriptor.name; else format = shaderInput->variableType.descriptor.name; @@ -2497,7 +2497,7 @@ QVariantList D3D11PipelineStateViewer::exportViewHTML(const D3D11Pipe::View &vie { if(view.viewFormat.compType == CompType::Typeless) { - if(!shaderInput->variableType.members.isEmpty()) + if(shaderInput->variableType.descriptor.type == VarType::Struct) viewFormat = format = lit("struct ") + shaderInput->variableType.descriptor.name; else viewFormat = format = shaderInput->variableType.descriptor.name; diff --git a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp index 4dd7fc4ab..68e1a5486 100644 --- a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp @@ -832,7 +832,7 @@ void D3D12PipelineStateViewer::addResourceRow(const D3D12ViewTag &view, const Bi // get the buffer type, whether it's just a basic type or a complex struct if(shaderInput && !shaderInput->isTexture) { - if(!shaderInput->variableType.members.empty()) + if(shaderInput->variableType.descriptor.type == VarType::Struct) format = lit("struct ") + shaderInput->variableType.descriptor.name; else if(r.viewFormat.compType == CompType::Typeless) format = shaderInput->variableType.descriptor.name; @@ -2583,7 +2583,7 @@ QVariantList D3D12PipelineStateViewer::exportViewHTML(const D3D12Pipe::View &vie { if(view.viewFormat.compType == CompType::Typeless) { - if(!shaderInput->variableType.members.isEmpty()) + if(shaderInput->variableType.descriptor.type == VarType::Struct) viewFormat = format = lit("struct ") + shaderInput->variableType.descriptor.name; else viewFormat = format = shaderInput->variableType.descriptor.name; diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp index 68c35532c..f370f54bc 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp @@ -743,7 +743,7 @@ void PipelineStateViewer::MakeShaderVariablesHLSL(bool cbufferContents, { for(const ShaderConstant &v : vars) { - if(!v.type.members.isEmpty()) + if(v.type.descriptor.type == VarType::Struct) { QString def = lit("struct %1 {\n").arg(v.type.descriptor.name); diff --git a/qrenderdoc/Windows/ShaderViewer.cpp b/qrenderdoc/Windows/ShaderViewer.cpp index 93fd0f391..50f3bb017 100644 --- a/qrenderdoc/Windows/ShaderViewer.cpp +++ b/qrenderdoc/Windows/ShaderViewer.cpp @@ -4178,6 +4178,7 @@ bool ShaderViewer::updateWatchVariable(RDTreeWidgetItem *watchItem, const RDTree case VarType::SShort: case VarType::SByte: regcast = QLatin1Char('i'); break; case VarType::Struct: + case VarType::Enum: case VarType::GPUPointer: regcast = QLatin1Char('#'); dataSize = 8; @@ -4640,6 +4641,7 @@ RDTreeWidgetItem *ShaderViewer::makeSourceVariableNode(const SourceVariableMappi case VarType::ReadOnlyResource: case VarType::ReadWriteResource: case VarType::Sampler: + case VarType::Enum: case VarType::Struct: value += stringRep(*reg, 0); break; case VarType::Unknown: qCritical() << "Unexpected unknown variable" << (QString)l.name; diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 2dbc72209..9414c354e 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -225,6 +225,10 @@ DOCUMENT(R"(Represents the base type of a shader variable in debugging or consta A boolean value. +.. data:: Enum + + An enum - each member gives a named value, and the type itself is stored as an integer. + .. data:: Struct A structure with some number of members. @@ -272,6 +276,7 @@ enum class VarType : uint8_t SByte, UByte, Bool, + Enum, Struct, GPUPointer, ConstantBlock, @@ -296,7 +301,7 @@ constexpr uint32_t VarTypeByteSize(VarType type) // clang-format off return (type == VarType::UByte || type == VarType::SByte) ? 1 : (type == VarType::Half || type == VarType::UShort || type == VarType::SShort) ? 2 - : (type == VarType::Float || type == VarType::UInt || type == VarType::SInt || type == VarType::Bool) ? 4 + : (type == VarType::Float || type == VarType::UInt || type == VarType::SInt || type == VarType::Bool || type == VarType::Enum) ? 4 : (type == VarType::Double || type == VarType::ULong || type == VarType::SLong ) ? 8 : 0; // clang-format on @@ -388,8 +393,8 @@ constexpr CompType VarTypeCompType(VarType type) // clang-format off return (type == VarType::Double || type == VarType::Float || type == VarType::Half) ? CompType::Float - : (type == VarType::ULong || type == VarType::UInt || - type == VarType::UShort || type == VarType::UByte || type == VarType::Bool) ? CompType::UInt + : (type == VarType::ULong || type == VarType::UInt || type == VarType::UShort || + type == VarType::UByte || type == VarType::Bool || type == VarType::Enum) ? CompType::UInt : (type == VarType::SLong || type == VarType::SInt || type == VarType::SShort || type == VarType::SByte) ? CompType::SInt diff --git a/renderdoc/driver/gl/gl_replay.cpp b/renderdoc/driver/gl/gl_replay.cpp index 360274afa..7832894db 100644 --- a/renderdoc/driver/gl/gl_replay.cpp +++ b/renderdoc/driver/gl/gl_replay.cpp @@ -2205,6 +2205,7 @@ void GLReplay::OpenGLFillCBufferVariables(ResourceId shader, GLuint prog, bool b case VarType::UByte: case VarType::Half: case VarType::Struct: + case VarType::Enum: RDCERR("Unexpected base variable type %s, treating as float", ToStr(var.type).c_str()); DELIBERATE_FALLTHROUGH(); @@ -2257,6 +2258,7 @@ void GLReplay::OpenGLFillCBufferVariables(ResourceId shader, GLuint prog, bool b case VarType::UByte: case VarType::Half: case VarType::Struct: + case VarType::Enum: RDCERR("Unexpected base variable type %s, treating as float", ToStr(var.type).c_str()); DELIBERATE_FALLTHROUGH(); diff --git a/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp b/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp index 354b699c0..f3e66750a 100644 --- a/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp +++ b/renderdoc/driver/shaders/spirv/spirv_disassemble.cpp @@ -451,6 +451,7 @@ rdcstr Reflector::Disassemble(const rdcstr &entryPoint, break; // none of these types are expected, either because they're opaque or (for struct) // because ConstantComposite should have been used + case VarType::Enum: case VarType::Struct: case VarType::Unknown: case VarType::GPUPointer: @@ -1680,6 +1681,7 @@ rdcstr Reflector::StringiseConstant(rdcspv::Id id) const case VarType::UByte: return ToStr(value.value.u8v[0]); case VarType::SLong: return ToStr(value.value.s64v[0]); case VarType::ULong: return ToStr(value.value.u64v[0]); + case VarType::Enum: case VarType::Struct: case VarType::Unknown: case VarType::GPUPointer: @@ -1708,6 +1710,7 @@ rdcstr Reflector::StringiseConstant(rdcspv::Id id) const case VarType::UByte: ret += ToStr(value.value.u8v[i]); break; case VarType::SLong: ret += ToStr(value.value.s64v[i]); break; case VarType::ULong: ret += ToStr(value.value.u64v[i]); break; + case VarType::Enum: case VarType::Struct: case VarType::Unknown: case VarType::GPUPointer: