From b3979262a5e3579e721458d580c3ce8bc4ce183c Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 28 Nov 2019 14:29:00 +0000 Subject: [PATCH] Replace FormatElement with ShaderConstant/ShaderVariableType * FormatElement is now a static BufferFormatter class to help generate and parse buffer formatting strings --- ...{FormatElement.cpp => BufferFormatter.cpp} | 1013 +++++++++++------ qrenderdoc/Code/CaptureContext.cpp | 2 + qrenderdoc/Code/QRDUtils.cpp | 2 +- qrenderdoc/Code/QRDUtils.h | 42 +- qrenderdoc/Windows/BufferViewer.cpp | 271 +++-- .../Windows/ConstantBufferPreviewer.cpp | 22 +- qrenderdoc/Windows/ConstantBufferPreviewer.h | 4 +- .../D3D11PipelineStateViewer.cpp | 4 +- .../D3D12PipelineStateViewer.cpp | 4 +- .../PipelineState/GLPipelineStateViewer.cpp | 4 +- .../PipelineState/PipelineStateViewer.cpp | 163 --- .../PipelineState/PipelineStateViewer.h | 3 - .../VulkanPipelineStateViewer.cpp | 4 +- qrenderdoc/Windows/ResourceInspector.cpp | 2 +- qrenderdoc/Windows/TextureViewer.cpp | 2 +- qrenderdoc/qrenderdoc.pro | 2 +- qrenderdoc/qrenderdoc_local.vcxproj | 2 +- qrenderdoc/qrenderdoc_local.vcxproj.filters | 6 +- renderdoc/api/replay/replay_enums.h | 23 +- renderdoc/api/replay/shader_types.h | 18 +- renderdoc/replay/renderdoc_serialise.inl | 2 + 21 files changed, 929 insertions(+), 666 deletions(-) rename qrenderdoc/Code/{FormatElement.cpp => BufferFormatter.cpp} (50%) diff --git a/qrenderdoc/Code/FormatElement.cpp b/qrenderdoc/Code/BufferFormatter.cpp similarity index 50% rename from qrenderdoc/Code/FormatElement.cpp rename to qrenderdoc/Code/BufferFormatter.cpp index 2a5d95921..62417c4c3 100644 --- a/qrenderdoc/Code/FormatElement.cpp +++ b/qrenderdoc/Code/BufferFormatter.cpp @@ -29,33 +29,14 @@ struct StructFormatData { - QList elems; + ShaderConstant structDef; uint32_t offset = 0; }; -FormatElement::FormatElement() -{ - offset = 0; - rowmajor = false; - matrixdim = 0; - hex = false; - rgb = false; -} +GraphicsAPI BufferFormatter::m_API; -FormatElement::FormatElement(const QString &Name, uint offs, bool rowMat, uint matDim, - ResourceFormat f, bool hexDisplay, bool rgbDisplay) -{ - name = Name; - offset = offs; - format = f; - rowmajor = rowMat; - matrixdim = matDim; - hex = hexDisplay; - rgb = rgbDisplay; -} - -QList FormatElement::ParseFormatString(const QString &formatString, uint64_t maxLen, - bool tightPacking, QString &errors) +ShaderConstant BufferFormatter::ParseFormatString(const QString &formatString, uint64_t maxLen, + bool tightPacking, QString &errors) { StructFormatData root; StructFormatData *cur = &root; @@ -67,7 +48,7 @@ QList FormatElement::ParseFormatString(const QString &formatStrin QRegularExpression regExpr( lit("^" // start of the line - "(row_major\\s+)?" // row_major matrix + "(row_major\\s+|column_major\\s+)?" // row_major matrix "(rgb\\s+)?" // rgb element colourising "(" // group the options for the type "uintten|unormten" // R10G10B10A2 types @@ -96,7 +77,10 @@ QList FormatElement::ParseFormatString(const QString &formatStrin QRegularExpression c_comments(lit("/\\*[^*]*\\*+(?:[^*/][^*]*\\*+)*/")); QRegularExpression cpp_comments(lit("//.*")); + // remove all comments 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")); QRegularExpression structDeclRegex(lit("^struct\\s+([A-Za-z_][A-Za-z0-9_]*)$")); QRegularExpression structUseRegex( @@ -129,6 +113,12 @@ QList FormatElement::ParseFormatString(const QString &formatStrin if(line == lit("}")) { + 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); + cur = &root; continue; } @@ -154,11 +144,13 @@ QList FormatElement::ParseFormatString(const QString &formatStrin } } + ShaderConstant el; + QRegularExpressionMatch structMatch = structUseRegex.match(line); if(structMatch.hasMatch() && structelems.contains(structMatch.captured(1))) { - StructFormatData &structDef = structelems[structMatch.captured(1)]; + StructFormatData &structContext = structelems[structMatch.captured(1)]; QString varName = structMatch.captured(2); @@ -173,21 +165,21 @@ QList FormatElement::ParseFormatString(const QString &formatStrin 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; + // cbuffer packing rules, structs are always float4 base aligned + if(!tightPacking) + cur->offset = (cur->offset + 0xFU) & (~0xFU); - cur->elems.push_back(el); - } + el = structContext.structDef; + el.name = varName; + el.byteOffset = cur->offset; + el.type.descriptor.elements = arrayCount; - cur->offset += structDef.offset; - } + cur->structDef.type.members.push_back(el); + + // undo the padding after the last struct + uint32_t padding = el.type.descriptor.arrayByteStride - structContext.offset; + + cur->offset += el.type.descriptor.elements * el.type.descriptor.arrayByteStride - padding; continue; } @@ -201,41 +193,34 @@ QList FormatElement::ParseFormatString(const QString &formatStrin break; } + el.name = !match.captured(6).isEmpty() ? match.captured(6).trimmed() : lit("data"); + QString basetype = match.captured(3); - bool row_major = !match.captured(1).isEmpty(); - bool rgb = !match.captured(2).isEmpty(); - QString vectorDim = !match.captured(4).isEmpty() ? match.captured(4) : lit("1"); - QString matrixDim = !match.captured(5).isEmpty() ? match.captured(5).mid(1) : lit("1"); - QString name = !match.captured(6).isEmpty() ? match.captured(6).trimmed() : lit("data"); + el.type.descriptor.rowMajorStorage = match.captured(1).trimmed() == lit("row_major"); + el.type.descriptor.displayAsRGB = !match.captured(2).isEmpty(); + 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]"); arrayDim = arrayDim.mid(1, arrayDim.count() - 2); if(!match.captured(5).isEmpty()) - { - vectorDim.swap(matrixDim); - } + firstDim.swap(secondDim); - ResourceFormat fmt; - fmt.type = ResourceFormatType::Regular; - fmt.compType = CompType::Typeless; + el.type.descriptor.name = match.captured(1) + match.captured(2) + match.captured(3) + + match.captured(4) + match.captured(5); - bool hex = false; - - CompType type = CompType::Float; - uint32_t count = 0; - uint32_t arrayCount = 1; - uint32_t matrixCount = 0; - uint32_t width = 0; + 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()) - matrixDim = vectorDim; + secondDim = firstDim; // calculate format { bool ok = false; - count = vectorDim.toUInt(&ok); + el.type.descriptor.columns = firstDim.toUInt(&ok); if(!ok) { errors = tr("Invalid vector dimension on line: %1\n").arg(line); @@ -243,14 +228,11 @@ QList FormatElement::ParseFormatString(const QString &formatStrin break; } - arrayCount = arrayDim.toUInt(&ok); + el.type.descriptor.elements = qMax(1U, arrayDim.toUInt(&ok)); if(!ok) - { - arrayCount = 1; - } - arrayCount = qMax(0U, arrayCount); + el.type.descriptor.elements = 1; - matrixCount = matrixDim.toUInt(&ok); + el.type.descriptor.rows = qMax(1U, secondDim.toUInt(&ok)); if(!ok) { errors = tr("Invalid matrix second dimension on line: %1\n").arg(line); @@ -258,107 +240,95 @@ QList FormatElement::ParseFormatString(const QString &formatStrin break; } + // vectors are never marked as row major + if(el.type.descriptor.rows == 1) + el.type.descriptor.rowMajorStorage = false; + if(basetype == lit("bool")) { - type = CompType::UInt; - width = 4; + el.type.descriptor.type = VarType::UInt; } else if(basetype == lit("byte")) { - type = CompType::SInt; - width = 1; + el.type.descriptor.type = VarType::SByte; } else if(basetype == lit("ubyte") || basetype == lit("xbyte")) { - type = CompType::UInt; - width = 1; + el.type.descriptor.type = VarType::UByte; } else if(basetype == lit("short")) { - type = CompType::SInt; - width = 2; + el.type.descriptor.type = VarType::SShort; } else if(basetype == lit("ushort") || basetype == lit("xshort")) { - type = CompType::UInt; - width = 2; + el.type.descriptor.type = VarType::UShort; } else if(basetype == lit("long")) { - type = CompType::SInt; - width = 8; + el.type.descriptor.type = VarType::SLong; } else if(basetype == lit("ulong") || basetype == lit("xlong")) { - type = CompType::UInt; - width = 8; + el.type.descriptor.type = VarType::ULong; } else if(basetype == lit("int") || basetype == lit("ivec") || basetype == lit("imat")) { - type = CompType::SInt; - width = 4; + el.type.descriptor.type = VarType::SInt; } else if(basetype == lit("uint") || basetype == lit("xint") || basetype == lit("uvec") || basetype == lit("umat")) { - type = CompType::UInt; - width = 4; + el.type.descriptor.type = VarType::UInt; } else if(basetype == lit("half")) { - type = CompType::Float; - width = 2; + el.type.descriptor.type = VarType::Half; } else if(basetype == lit("float") || basetype == lit("vec") || basetype == lit("mat")) { - type = CompType::Float; - width = 4; + el.type.descriptor.type = VarType::Float; } else if(basetype == lit("double")) { - type = CompType::Double; - width = 8; + el.type.descriptor.type = VarType::Double; } else if(basetype == lit("unormh")) { - type = CompType::UNorm; - width = 2; + el.type.descriptor.type = VarType::UShort; + interpretCompType = CompType::UNorm; } else if(basetype == lit("unormb")) { - type = CompType::UNorm; - width = 1; + el.type.descriptor.type = VarType::UByte; + interpretCompType = CompType::UNorm; } else if(basetype == lit("snormh")) { - type = CompType::SNorm; - width = 2; + el.type.descriptor.type = VarType::SShort; + interpretCompType = CompType::SNorm; } else if(basetype == lit("snormb")) { - type = CompType::SNorm; - width = 1; + el.type.descriptor.type = VarType::SByte; + interpretCompType = CompType::SNorm; } else if(basetype == lit("uintten")) { - fmt.compType = CompType::UInt; - fmt.compCount = 4 * count; - fmt.compByteWidth = 1; - fmt.type = ResourceFormatType::R10G10B10A2; + el.type.descriptor.type = VarType::UInt; + interpretType = ResourceFormatType::R10G10B10A2; } else if(basetype == lit("unormten")) { - fmt.compType = CompType::UNorm; - fmt.compCount = 4 * count; - fmt.compByteWidth = 1; - fmt.type = ResourceFormatType::R10G10B10A2; + el.type.descriptor.type = VarType::UInt; + interpretCompType = CompType::UNorm; + interpretType = ResourceFormatType::R10G10B10A2; } else if(basetype == lit("floateleven")) { - fmt.compType = CompType::Float; - fmt.compCount = 3 * count; - fmt.compByteWidth = 1; - fmt.type = ResourceFormatType::R11G11B10; + el.type.descriptor.type = VarType::Float; + interpretCompType = CompType::Float; + interpretType = ResourceFormatType::R11G11B10; } else { @@ -370,106 +340,108 @@ QList FormatElement::ParseFormatString(const QString &formatStrin if(basetype == lit("xlong") || basetype == lit("xint") || basetype == lit("xshort") || basetype == lit("xbyte")) - hex = true; + el.type.descriptor.displayAsHex = true; - if(fmt.compType == CompType::Typeless) + SetInterpretedResourceFormat(el, interpretType, interpretCompType); + + ResourceFormat fmt = GetInterpretedResourceFormat(el); + + // normally the array stride is the size of an element + el.type.descriptor.arrayByteStride = el.type.descriptor.matrixByteStride = fmt.ElementSize(); + + uint32_t padding = 0; + + // for matrices, it's the size of an element times the number of rows + if(el.type.descriptor.rows > 1) { - fmt.compType = type; - fmt.compCount = count; - fmt.compByteWidth = width; + // if we're cbuffer packing, matrix row/columns are always 16-bytes apart + if(!tightPacking) + { + padding = 16 - el.type.descriptor.matrixByteStride; + el.type.descriptor.matrixByteStride = 16; + } + + uint8_t majorDim = + el.type.descriptor.rowMajorStorage ? el.type.descriptor.rows : el.type.descriptor.columns; + + // total matrix size is + el.type.descriptor.arrayByteStride = el.type.descriptor.matrixByteStride * majorDim; } - uint32_t advance = fmt.ElementSize() * matrixCount; - if(!tightPacking && matrixCount > 1) + // cbuffer packing rules + if(!tightPacking) { - if(!row_major) + if(el.type.descriptor.elements == 1) { - advance = fmt.ElementSize() * 4; + // always float aligned + el.type.descriptor.arrayByteStride = (el.type.descriptor.arrayByteStride + 3U) & (~3U); + + // elements can't cross float4 boundaries, nudge up if this was the case + if(cur->offset / 16 != (cur->offset + el.type.descriptor.arrayByteStride - 1) / 16) + { + cur->offset = (cur->offset + 0xFU) & (~0xFU); + } } else { - ResourceFormat fmtpadded = fmt; - fmtpadded.compCount = 4; - advance = fmtpadded.ElementSize() * matrixCount; - } - } + // arrays always have elements float4 aligned + uint32_t paddedStride = (el.type.descriptor.arrayByteStride + 0xFU) & (~0xFU); - if(arrayCount == 1) - { - FormatElement elem(name, cur->offset, row_major, matrixCount, fmt, hex, rgb); + padding += paddedStride - el.type.descriptor.arrayByteStride; - if(!tightPacking) - { - // cbuffer packing always works in floats - advance = (advance + 3U) & (~3U); + el.type.descriptor.arrayByteStride = paddedStride; - // cbuffer packing doesn't allow elements to cross float4 boundaries, nudge up if this was - // the case - if(cur->offset / 16 != (cur->offset + fmt.ElementSize() * matrixCount - 1) / 16) - { - elem.offset = cur->offset = (cur->offset + 0xFU) & (~0xFU); - } - } - - cur->elems.push_back(elem); - - cur->offset += advance; - } - else - { - // when cbuffer packing, arrays are always aligned at float4 boundary - if(!tightPacking) - { + // and always aligned at float4 boundary if(cur->offset % 16 != 0) { cur->offset = (cur->offset + 0xFU) & (~0xFU); } } - - for(uint a = 0; a < arrayCount; a++) - { - FormatElement elem(QFormatStr("%1[%2]").arg(name).arg(a), cur->offset, row_major, - matrixCount, fmt, hex, rgb); - - cur->elems.push_back(elem); - - // cbuffer packing each array element is always float4 aligned - if(!tightPacking) - { - advance = (advance + 0xFU) & (~0xFU); - } - - cur->offset += advance; - } } + + el.byteOffset = cur->offset; + + cur->structDef.type.members.push_back(el); + + cur->offset += el.type.descriptor.arrayByteStride * el.type.descriptor.elements - padding; } // 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()) + if(success && root.structDef.type.members.isEmpty() && !lastStruct.isEmpty()) root = structelems[lastStruct]; - if(!success || root.elems.isEmpty()) - { - root.elems.clear(); + root.structDef.type.descriptor.arrayByteStride = root.offset; - ResourceFormat fmt; - fmt.compType = CompType::UInt; - fmt.compByteWidth = 4; - fmt.compCount = 4; + // struct strides are aligned up to float4 boundary + if(!tightPacking) + root.structDef.type.descriptor.arrayByteStride = (root.offset + 0xFU) & (~0xFU); + + if(!success || root.structDef.type.members.isEmpty()) + { + root.structDef.type.members.clear(); + + ShaderConstant el; + el.byteOffset = 0; + el.type.descriptor.displayAsHex = true; + el.name = "data"; + el.type.descriptor.type = VarType::UInt; + el.type.descriptor.columns = 4; if(maxLen > 0 && maxLen < 16) - fmt.compCount = 1; + el.type.descriptor.columns = 1; if(maxLen > 0 && maxLen < 4) - fmt.compByteWidth = 1; + el.type.descriptor.type = VarType::UByte; - root.elems.push_back(FormatElement(lit("data"), 0, false, 1, fmt, true, false)); + SetInterpretedResourceFormat(el, ResourceFormatType::Regular, CompType::Typeless); + + root.structDef.type.members.push_back(el); } - return root.elems; + return root.structDef; } -QString FormatElement::GenerateTextureBufferFormat(const TextureDescription &tex) +QString BufferFormatter::GetTextureFormatString(const TextureDescription &tex) { QString baseType; @@ -587,107 +559,459 @@ QString FormatElement::GenerateTextureBufferFormat(const TextureDescription &tex return QFormatStr("%1 %2[%3];").arg(baseType).arg(varName).arg(w); } -ShaderVariable FormatElement::GetShaderVar(const byte *&data, const byte *end) const +QString BufferFormatter::GetBufferFormatString(const ShaderResource &res, + const ResourceFormat &viewFormat, + uint64_t &baseByteOffset) { - QVariantList objs = GetVariants(format, matrixdim, data, end); + QString format; - ShaderVariable ret; - - ret.name = name.toUtf8().data(); - ret.type = VarType::Float; - if(format.compType == CompType::UInt) + if(!res.variableType.members.empty()) { - if(format.compByteWidth == 8) - ret.type = VarType::ULong; - else if(format.compByteWidth == 4) - ret.type = VarType::UInt; - else if(format.compByteWidth == 2) - ret.type = VarType::UShort; - else if(format.compByteWidth == 1) - ret.type = VarType::UByte; + if(m_API == GraphicsAPI::Vulkan || m_API == GraphicsAPI::OpenGL) + { + const rdcarray &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 += 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()) + { + 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 += + QFormatStr(" %1%2 %3%4;\n").arg(comment).arg(varTypeName).arg(members[i].name).arg(arraySize); + } + + format += lit("}"); + + // if the last member is a struct, declare it + if(!members.back().type.members.isEmpty()) + { + format = DeclareStruct(varTypeName, members.back().type.members, + members.back().type.descriptor.arrayByteStride) + + lit("\n") + format; + } + } else - qCritical() << "Unexpeted component bytewidth for uint: " << format.compByteWidth; - } - else if(format.compType == CompType::SInt) - { - if(format.compByteWidth == 8) - ret.type = VarType::SLong; - else if(format.compByteWidth == 4) - ret.type = VarType::SInt; - else if(format.compByteWidth == 2) - ret.type = VarType::SShort; - else if(format.compByteWidth == 1) - ret.type = VarType::SByte; - else - qCritical() << "Unexpeted component bytewidth for sint: " << format.compByteWidth; - } - else if(format.compType == CompType::Double) - { - ret.type = VarType::Double; - - if(format.compByteWidth != 8) - qCritical() << "Unexpeted component bytewidth for double: " << format.compByteWidth; + { + format = DeclareStruct(res.variableType.descriptor.name, res.variableType.members, 0); + } } else { - // assume float/double - if(format.compByteWidth == 8) - ret.type = VarType::Double; - else if(format.compByteWidth == 4) - ret.type = VarType::Float; - else if(format.compByteWidth == 2) - ret.type = VarType::Half; + const auto &desc = res.variableType.descriptor; + + if(viewFormat.type == ResourceFormatType::Undefined) + { + if(desc.type == VarType::Unknown) + { + format = desc.name; + } + else + { + if(desc.rowMajorStorage) + format += lit("row_major "); + + format += ToQStr(desc.type); + if(desc.rows > 1 && desc.columns > 1) + format += QFormatStr("%1x%2").arg(desc.rows).arg(desc.columns); + else if(desc.columns > 1) + format += QString::number(desc.columns); + + if(!desc.name.empty()) + format += lit(" ") + desc.name; + + if(desc.elements > 1) + format += QFormatStr("[%1]").arg(desc.elements); + } + } else - qCritical() << "Unexpeted component bytewidth for float: " << format.compByteWidth; + { + if(viewFormat.type == ResourceFormatType::R10G10B10A2) + { + if(viewFormat.compType == CompType::UInt) + format = lit("uintten"); + if(viewFormat.compType == CompType::UNorm) + format = lit("unormten"); + } + else if(viewFormat.type == ResourceFormatType::R11G11B10) + { + format = lit("floateleven"); + } + else + { + switch(viewFormat.compByteWidth) + { + case 1: + { + if(viewFormat.compType == CompType::UNorm || viewFormat.compType == CompType::UNormSRGB) + format = lit("unormb"); + if(viewFormat.compType == CompType::SNorm) + format = lit("snormb"); + if(viewFormat.compType == CompType::UInt) + format = lit("ubyte"); + if(viewFormat.compType == CompType::SInt) + format = lit("byte"); + break; + } + case 2: + { + if(viewFormat.compType == CompType::UNorm || viewFormat.compType == CompType::UNormSRGB) + format = lit("unormh"); + if(viewFormat.compType == CompType::SNorm) + format = lit("snormh"); + if(viewFormat.compType == CompType::UInt) + format = lit("ushort"); + if(viewFormat.compType == CompType::SInt) + format = lit("short"); + if(viewFormat.compType == CompType::Float) + format = lit("half"); + break; + } + case 4: + { + if(viewFormat.compType == CompType::UNorm || viewFormat.compType == CompType::UNormSRGB) + format = lit("unormf"); + if(viewFormat.compType == CompType::SNorm) + format = lit("snormf"); + if(viewFormat.compType == CompType::UInt) + format = lit("uint"); + if(viewFormat.compType == CompType::SInt) + format = lit("int"); + if(viewFormat.compType == CompType::Float) + format = lit("float"); + break; + } + } + + format += QString::number(viewFormat.compCount); + } + } } - ret.columns = qMin(format.compCount, uint8_t(4)); - ret.rows = qMin(matrixdim, 4U); + return format; +} - ret.displayAsHex = hex; - ret.rowMajor = rowmajor; +QString BufferFormatter::DeclarePaddingBytes(uint32_t bytes) +{ + if(bytes == 0) + return QString(); - memset(ret.value.dv, 0, sizeof(ret.value.dv)); + QString ret; + if(bytes > 4) + { + ret += lit("xint pad[%1];").arg(bytes / 4); + + bytes = bytes % 4; + } + + if(bytes == 4) + ret += lit("xint pad;"); + else if(bytes == 3) + ret += lit("xshort pad; xbyte pad;"); + else if(bytes == 2) + ret += lit("xshort pad;"); + else if(bytes == 1) + ret += lit("xbyte pad;"); + + return ret + lit("\n"); +} + +QString BufferFormatter::DeclareStruct(QList &declaredStructs, const QString &name, + const rdcarray &members, + uint32_t requiredByteStride) +{ + QString ret; + + ret = lit("struct %1\n{\n").arg(name); + + 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); + + QString varTypeName = members[i].type.descriptor.name; + + if(!members[i].type.members.isEmpty()) + { + // 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; + } + } + + ret += QFormatStr(" %1 %2%3;\n").arg(varTypeName).arg(members[i].name).arg(arraySize); + } + + if(requiredByteStride > 0) + { + uint32_t structStart = 0; + + const ShaderConstant *lastChild = &members.back(); + + structStart += lastChild->byteOffset; + structStart += (qMax(lastChild->type.descriptor.elements, 1U) - 1) * + lastChild->type.descriptor.arrayByteStride; + while(!lastChild->type.members.isEmpty()) + { + lastChild = &lastChild->type.members.back(); + structStart += lastChild->byteOffset; + structStart += (qMax(lastChild->type.descriptor.elements, 1U) - 1) * + lastChild->type.descriptor.arrayByteStride; + } + + uint32_t size = lastChild->type.descriptor.rows * lastChild->type.descriptor.columns; + if(lastChild->type.descriptor.type == VarType::Double || + lastChild->type.descriptor.type == VarType::ULong || + lastChild->type.descriptor.type == VarType::SLong) + size *= 8; + else if(lastChild->type.descriptor.type == VarType::Float || + lastChild->type.descriptor.type == VarType::UInt || + lastChild->type.descriptor.type == VarType::SInt) + size *= 4; + else if(lastChild->type.descriptor.type == VarType::Half || + lastChild->type.descriptor.type == VarType::UShort || + lastChild->type.descriptor.type == VarType::SShort) + size *= 2; + + if(lastChild->type.descriptor.elements > 1) + size *= lastChild->type.descriptor.elements; + + uint32_t padBytes = requiredByteStride - (structStart + size); + + if(padBytes > 0) + ret += lit(" ") + DeclarePaddingBytes(padBytes); + } + + ret += lit("}\n"); + + return ret; +} + +QString BufferFormatter::DeclareStruct(const QString &name, const rdcarray &members, + uint32_t requiredByteStride) +{ + QList declaredStructs; + return DeclareStruct(declaredStructs, name, members, requiredByteStride); +} + +void SetInterpretedResourceFormat(ShaderConstant &elem, ResourceFormatType interpretType, + CompType interpretCompType) +{ + static_assert(sizeof(elem.defaultValue) > 2, + "ShaderConstant::defaultValue has changed, check packing"); + + // packing must match GetInterpretedResourceFormat below + elem.defaultValue = (uint64_t(interpretType) << 8) | (uint64_t(interpretCompType) << 0); +} + +ResourceFormat GetInterpretedResourceFormat(const ShaderConstant &elem) +{ + // packing must match SetInterpretedResourceFormat above + ResourceFormatType interpretType = ResourceFormatType((elem.defaultValue >> 8) & 0xff); + CompType interpretCompType = CompType((elem.defaultValue >> 0) & 0xff); + + ResourceFormat format; + format.type = interpretType; + + switch(elem.type.descriptor.type) + { + case VarType::Half: + case VarType::Float: format.compType = CompType::Float; break; + case VarType::Double: format.compType = CompType::Double; break; + case VarType::SInt: + case VarType::SShort: + case VarType::SLong: + case VarType::SByte: format.compType = CompType::SInt; break; + case VarType::UInt: + case VarType::UShort: + case VarType::ULong: + case VarType::UByte: format.compType = CompType::UInt; break; + default: format.compType = CompType::UInt; break; + } + + if(interpretCompType != CompType::Typeless) + format.compType = interpretCompType; + + format.compByteWidth = VarTypeByteSize(elem.type.descriptor.type); + + if(elem.type.descriptor.rowMajorStorage || elem.type.descriptor.rows == 1) + format.compCount = elem.type.descriptor.columns; + else + format.compCount = elem.type.descriptor.rows; + + return format; +} + +static void FillShaderVarData(ShaderVariable &var, const ShaderConstant &elem, const byte *data, + const byte *end) +{ int src = 0; - for(uint32_t row = 0; row < ret.rows; row++) + uint32_t outerCount = elem.type.descriptor.rows; + uint32_t innerCount = elem.type.descriptor.columns; + + bool colMajor = false; + + if(!elem.type.descriptor.rowMajorStorage && outerCount > 1) { - for(uint32_t col = 0; col < ret.columns; col++) + colMajor = true; + std::swap(outerCount, innerCount); + } + + QVariantList objs = GetVariants(GetInterpretedResourceFormat(elem), outerCount, + elem.type.descriptor.matrixByteStride, data, end); + + if(objs.isEmpty()) + { + var.name = "-"; + memset(var.value.dv, 0, sizeof(var.value.dv)); + return; + } + + for(uint32_t outer = 0; outer < outerCount; outer++) + { + for(uint32_t inner = 0; inner < innerCount; inner++) { - uint32_t dst = row * ret.columns + col; + uint32_t dst = outer * elem.type.descriptor.columns + inner; - if(!ret.rowMajor && ret.rows > 1) - dst = col * ret.columns + row; - - // if we are about to read out of bounds, reset the variable and return - if(src >= objs.size()) - { - ret.name = "-"; - memset(ret.value.dv, 0, sizeof(ret.value.dv)); - return ret; - } + if(colMajor) + dst = inner * elem.type.descriptor.columns + outer; const QVariant &o = objs[src]; src++; - if(ret.type == VarType::Double) - ret.value.dv[dst] = o.toDouble(); - if(ret.type == VarType::Float || ret.type == VarType::Half) - ret.value.fv[dst] = o.toFloat(); - else if(ret.type == VarType::ULong) - ret.value.u64v[dst] = o.toULongLong(); - else if(ret.type == VarType::SLong) - ret.value.s64v[dst] = o.toLongLong(); - else if(ret.type == VarType::UInt || ret.type == VarType::UShort || ret.type == VarType::UByte) - ret.value.uv[dst] = o.toUInt(); - else if(ret.type == VarType::SInt || ret.type == VarType::SShort || ret.type == VarType::SByte) - ret.value.iv[dst] = o.toInt(); + if(var.type == VarType::Double) + var.value.dv[dst] = o.toDouble(); + if(var.type == VarType::Float || var.type == VarType::Half) + var.value.fv[dst] = o.toFloat(); + else if(var.type == VarType::ULong) + var.value.u64v[dst] = o.toULongLong(); + else if(var.type == VarType::SLong) + var.value.s64v[dst] = o.toLongLong(); + else if(var.type == VarType::UInt || var.type == VarType::UShort || var.type == VarType::UByte) + var.value.uv[dst] = o.toUInt(); + else if(var.type == VarType::SInt || var.type == VarType::SShort || var.type == VarType::SByte) + var.value.iv[dst] = o.toInt(); else - ret.value.fv[dst] = o.toFloat(); + var.value.fv[dst] = o.toFloat(); } } +} + +ShaderVariable InterpretShaderVar(const ShaderConstant &elem, const byte *data, const byte *end) +{ + ShaderVariable ret; + + ret.name = elem.name; + ret.type = elem.type.descriptor.type; + ret.columns = qMin(elem.type.descriptor.columns, uint8_t(4)); + ret.rows = qMin(elem.type.descriptor.rows, uint8_t(4)); + ret.isStruct = !elem.type.members.isEmpty(); + + ret.displayAsHex = elem.type.descriptor.displayAsHex; + ret.rowMajor = elem.type.descriptor.rowMajorStorage; + + if(!elem.type.members.isEmpty()) + { + ret.rows = ret.columns = 0; + + if(elem.type.descriptor.elements > 1) + { + rdcarray arrayElements; + + for(uint32_t a = 0; a < elem.type.descriptor.elements; a++) + { + rdcarray members; + + for(size_t m = 0; m < elem.type.members.size(); m++) + { + const ShaderConstant &member = elem.type.members[m]; + + members.push_back(InterpretShaderVar(member, data + member.byteOffset, end)); + } + + arrayElements.push_back(ret); + arrayElements.back().name = QFormatStr("%1[%2]").arg(ret.name).arg(a); + arrayElements.back().members = members; + + data += elem.type.descriptor.arrayByteStride; + } + + ret.isStruct = false; + ret.members = arrayElements; + } + else + { + rdcarray members; + + for(size_t m = 0; m < elem.type.members.size(); m++) + { + const ShaderConstant &member = elem.type.members[m]; + + members.push_back(InterpretShaderVar(member, data + member.byteOffset, end)); + } + + ret.members = members; + } + } + else if(elem.type.descriptor.elements > 1) + { + rdcarray arrayElements; + + for(uint32_t a = 0; a < elem.type.descriptor.elements; a++) + { + arrayElements.push_back(ret); + arrayElements.back().name = QFormatStr("%1[%2]").arg(ret.name).arg(a); + FillShaderVarData(arrayElements.back(), elem, data, end); + data += elem.type.descriptor.arrayByteStride; + } + + ret.rows = ret.columns = 0; + ret.members = arrayElements; + } + else + { + FillShaderVarData(ret, elem, data, end); + } return ret; } @@ -792,8 +1116,8 @@ inline T readObj(const byte *&data, const byte *end, bool &ok) return ret; } -QVariantList GetVariants(ResourceFormat rowFormat, uint32_t rowCount, const byte *&data, - const byte *end) +QVariantList GetVariants(ResourceFormat rowFormat, uint32_t rowCount, uint32_t rowByteStride, + const byte *&data, const byte *end) { QVariantList ret; @@ -997,101 +1321,109 @@ QVariantList GetVariants(ResourceFormat rowFormat, uint32_t rowCount, const byte } else { - int dim = (int)(qMax(rowCount, 1U) * rowFormat.compCount); + const byte *base = data; - for(int i = 0; i < dim; i++) + for(uint32_t row = 0; row < qMax(rowCount, 1U); row++) { - if(rowFormat.compType == CompType::Float) - { - if(rowFormat.compByteWidth == 8) - ret.push_back(readObj(data, end, ok)); - else if(rowFormat.compByteWidth == 4) - ret.push_back(readObj(data, end, ok)); - else if(rowFormat.compByteWidth == 2) - ret.push_back(RENDERDOC_HalfToFloat(readObj(data, end, ok))); - } - else if(rowFormat.compType == CompType::SInt) - { - if(rowFormat.compByteWidth == 8) - ret.push_back((qlonglong)readObj(data, end, ok)); - else if(rowFormat.compByteWidth == 4) - ret.push_back((int)readObj(data, end, ok)); - else if(rowFormat.compByteWidth == 2) - ret.push_back((int)readObj(data, end, ok)); - else if(rowFormat.compByteWidth == 1) - ret.push_back((int)readObj(data, end, ok)); - } - else if(rowFormat.compType == CompType::UInt) - { - if(rowFormat.compByteWidth == 8) - ret.push_back((qulonglong)readObj(data, end, ok)); - else if(rowFormat.compByteWidth == 4) - ret.push_back((uint32_t)readObj(data, end, ok)); - else if(rowFormat.compByteWidth == 2) - ret.push_back((uint32_t)readObj(data, end, ok)); - else if(rowFormat.compByteWidth == 1) - ret.push_back((uint32_t)readObj(data, end, ok)); - } - else if(rowFormat.compType == CompType::UScaled) - { - if(rowFormat.compByteWidth == 4) - ret.push_back((float)readObj(data, end, ok)); - else if(rowFormat.compByteWidth == 2) - ret.push_back((float)readObj(data, end, ok)); - else if(rowFormat.compByteWidth == 1) - ret.push_back((float)readObj(data, end, ok)); - } - else if(rowFormat.compType == CompType::SScaled) - { - if(rowFormat.compByteWidth == 4) - ret.push_back((float)readObj(data, end, ok)); - else if(rowFormat.compByteWidth == 2) - ret.push_back((float)readObj(data, end, ok)); - else if(rowFormat.compByteWidth == 1) - ret.push_back((float)readObj(data, end, ok)); - } - else if(rowFormat.compType == CompType::Depth) - { - if(rowFormat.compByteWidth == 4) - { - // 32-bit depth is native floats - ret.push_back(readObj(data, end, ok)); - } - else if(rowFormat.compByteWidth == 3) - { - // 32-bit depth is normalised, masked against non-stencil bits - uint32_t f = readObj(data, end, ok); - f &= 0x00ffffff; - ret.push_back((float)f / (float)0x00ffffff); - } - else if(rowFormat.compByteWidth == 2) - { - // 16-bit depth is normalised - float f = (float)readObj(data, end, ok); - ret.push_back(f / (float)0x0000ffff); - } - } - else if(rowFormat.compType == CompType::Double) - { - ret.push_back(readObj(data, end, ok)); - } - else - { - // unorm/snorm + const byte *rowStart = base + row * rowByteStride; - if(rowFormat.compByteWidth == 4) + if(data < rowStart) + data = rowStart; + + for(int i = 0; i < rowFormat.compCount; i++) + { + if(rowFormat.compType == CompType::Float) { - // should never hit this - no 32bit unorm/snorm type - qCritical() << "Unexpected 4-byte unorm/snorm value"; - ret.push_back((float)readObj(data, end, ok) / (float)0xffffffff); + if(rowFormat.compByteWidth == 8) + ret.push_back(readObj(data, end, ok)); + else if(rowFormat.compByteWidth == 4) + ret.push_back(readObj(data, end, ok)); + else if(rowFormat.compByteWidth == 2) + ret.push_back(RENDERDOC_HalfToFloat(readObj(data, end, ok))); } - else if(rowFormat.compByteWidth == 2) + else if(rowFormat.compType == CompType::SInt) { - ret.push_back(interpret(rowFormat, readObj(data, end, ok))); + if(rowFormat.compByteWidth == 8) + ret.push_back((qlonglong)readObj(data, end, ok)); + else if(rowFormat.compByteWidth == 4) + ret.push_back((int)readObj(data, end, ok)); + else if(rowFormat.compByteWidth == 2) + ret.push_back((int)readObj(data, end, ok)); + else if(rowFormat.compByteWidth == 1) + ret.push_back((int)readObj(data, end, ok)); } - else if(rowFormat.compByteWidth == 1) + else if(rowFormat.compType == CompType::UInt) { - ret.push_back(interpret(rowFormat, readObj(data, end, ok))); + if(rowFormat.compByteWidth == 8) + ret.push_back((qulonglong)readObj(data, end, ok)); + else if(rowFormat.compByteWidth == 4) + ret.push_back((uint32_t)readObj(data, end, ok)); + else if(rowFormat.compByteWidth == 2) + ret.push_back((uint32_t)readObj(data, end, ok)); + else if(rowFormat.compByteWidth == 1) + ret.push_back((uint32_t)readObj(data, end, ok)); + } + else if(rowFormat.compType == CompType::UScaled) + { + if(rowFormat.compByteWidth == 4) + ret.push_back((float)readObj(data, end, ok)); + else if(rowFormat.compByteWidth == 2) + ret.push_back((float)readObj(data, end, ok)); + else if(rowFormat.compByteWidth == 1) + ret.push_back((float)readObj(data, end, ok)); + } + else if(rowFormat.compType == CompType::SScaled) + { + if(rowFormat.compByteWidth == 4) + ret.push_back((float)readObj(data, end, ok)); + else if(rowFormat.compByteWidth == 2) + ret.push_back((float)readObj(data, end, ok)); + else if(rowFormat.compByteWidth == 1) + ret.push_back((float)readObj(data, end, ok)); + } + else if(rowFormat.compType == CompType::Depth) + { + if(rowFormat.compByteWidth == 4) + { + // 32-bit depth is native floats + ret.push_back(readObj(data, end, ok)); + } + else if(rowFormat.compByteWidth == 3) + { + // 32-bit depth is normalised, masked against non-stencil bits + uint32_t f = readObj(data, end, ok); + f &= 0x00ffffff; + ret.push_back((float)f / (float)0x00ffffff); + } + else if(rowFormat.compByteWidth == 2) + { + // 16-bit depth is normalised + float f = (float)readObj(data, end, ok); + ret.push_back(f / (float)0x0000ffff); + } + } + else if(rowFormat.compType == CompType::Double) + { + ret.push_back(readObj(data, end, ok)); + } + else + { + // unorm/snorm + + if(rowFormat.compByteWidth == 4) + { + // should never hit this - no 32bit unorm/snorm type + qCritical() << "Unexpected 4-byte unorm/snorm value"; + ret.push_back((float)readObj(data, end, ok) / (float)0xffffffff); + } + else if(rowFormat.compByteWidth == 2) + { + ret.push_back(interpret(rowFormat, readObj(data, end, ok))); + } + else if(rowFormat.compByteWidth == 1) + { + ret.push_back(interpret(rowFormat, readObj(data, end, ok))); + } } } } @@ -1144,8 +1476,7 @@ QString TypeString(const ShaderVariable &v) .arg(typeStr) .arg(v.rows) .arg(v.columns) - .arg(v.rowMajor ? QApplication::tr("row major", "FormatElement") - : QApplication::tr("column major", "FormatElement")); + .arg(v.rowMajor ? lit("row_major") : lit("column_major")); } template diff --git a/qrenderdoc/Code/CaptureContext.cpp b/qrenderdoc/Code/CaptureContext.cpp index c6c5dbbb7..f014dfdfe 100644 --- a/qrenderdoc/Code/CaptureContext.cpp +++ b/qrenderdoc/Code/CaptureContext.cpp @@ -736,6 +736,8 @@ void CaptureContext::LoadCapture(const rdcstr &captureFile, const ReplayOptions { m_CaptureTemporary = temporary; + BufferFormatter::Init(m_APIProps.pipelineType); + m_CaptureMods = CaptureModifications::NoModifications; rdcarray viewers(m_CaptureViewers); diff --git a/qrenderdoc/Code/QRDUtils.cpp b/qrenderdoc/Code/QRDUtils.cpp index a2c4c2ae1..672d88a4d 100644 --- a/qrenderdoc/Code/QRDUtils.cpp +++ b/qrenderdoc/Code/QRDUtils.cpp @@ -2191,4 +2191,4 @@ void *AccessWaylandPlatformInterface(const QByteArray &resource, QWindow *window { QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface(); return native->nativeResourceForWindow(resource, window); -} \ No newline at end of file +} diff --git a/qrenderdoc/Code/QRDUtils.h b/qrenderdoc/Code/QRDUtils.h index 8d45de7c3..95c0b803f 100644 --- a/qrenderdoc/Code/QRDUtils.h +++ b/qrenderdoc/Code/QRDUtils.h @@ -74,33 +74,37 @@ inline QMetaType::Type GetVariantMetatype(const QVariant &v) return (QMetaType::Type)v.type(); } -struct FormatElement +struct BufferFormatter { - Q_DECLARE_TR_FUNCTIONS(FormatElement); + Q_DECLARE_TR_FUNCTIONS(BufferFormatter); + + static GraphicsAPI m_API; + + static QString DeclareStruct(QList &declaredStructs, const QString &name, + const rdcarray &members, uint32_t requiredByteStride); public: - FormatElement(); - FormatElement(const QString &Name, uint offs, bool rowMat, uint matDim, ResourceFormat f, - bool hexDisplay, bool rgbDisplay); + BufferFormatter() = default; - static QList ParseFormatString(const QString &formatString, uint64_t maxLen, - bool tightPacking, QString &errors); + static void Init(GraphicsAPI api) { m_API = api; } + static ShaderConstant ParseFormatString(const QString &formatString, uint64_t maxLen, + bool tightPacking, QString &errors); - static QString GenerateTextureBufferFormat(const TextureDescription &tex); + static QString GetTextureFormatString(const TextureDescription &tex); + static QString GetBufferFormatString(const ShaderResource &res, const ResourceFormat &viewFormat, + uint64_t &baseByteOffset); - ShaderVariable GetShaderVar(const byte *&data, const byte *end) const; - - QString name; - ResourceFormat format; - uint32_t offset; - uint32_t matrixdim; - bool rowmajor; - - bool hex, rgb; + static QString DeclareStruct(const QString &name, const rdcarray &members, + uint32_t requiredByteStride); + static QString DeclarePaddingBytes(uint32_t bytes); }; -QVariantList GetVariants(ResourceFormat rowFormat, uint32_t rowCount, const byte *&data, - const byte *end); +QVariantList GetVariants(ResourceFormat rowFormat, uint32_t rowCount, uint32_t rowByteStride, + const byte *&data, const byte *end); +ResourceFormat GetInterpretedResourceFormat(const ShaderConstant &elem); +void SetInterpretedResourceFormat(ShaderConstant &elem, ResourceFormatType interpretType, + CompType interpretCompType); +ShaderVariable InterpretShaderVar(const ShaderConstant &elem, const byte *data, const byte *end); QString TypeString(const ShaderVariable &v); QString RowString(const ShaderVariable &v, uint32_t row, VarType type = VarType::Unknown); diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index d41841ebb..b767e515b 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -356,8 +356,8 @@ struct BufferConfiguration BufferData *indices = NULL; int32_t baseVertex = 0; - QList columns; - QList props; + rdcarray columns; + rdcarray props; QVector generics; QVector genericsEnabled; @@ -450,12 +450,12 @@ struct BufferConfiguration // look for an exact match for(int i = 0; posEl == -1 && i < columns.count(); i++) { - const FormatElement &el = columns[i]; + const ShaderConstant &el = columns[i]; - if(el.name.compare(lit("POSITION"), Qt::CaseInsensitive) == 0 || - el.name.compare(lit("POSITION0"), Qt::CaseInsensitive) == 0 || - el.name.compare(lit("POS"), Qt::CaseInsensitive) == 0 || - el.name.compare(lit("POS0"), Qt::CaseInsensitive) == 0) + if(QString(el.name).compare(lit("POSITION"), Qt::CaseInsensitive) == 0 || + QString(el.name).compare(lit("POSITION0"), Qt::CaseInsensitive) == 0 || + QString(el.name).compare(lit("POS"), Qt::CaseInsensitive) == 0 || + QString(el.name).compare(lit("POS0"), Qt::CaseInsensitive) == 0) { posEl = i; break; @@ -465,9 +465,9 @@ struct BufferConfiguration // try anything containing position for(int i = 0; posEl == -1 && i < columns.count(); i++) { - const FormatElement &el = columns[i]; + const ShaderConstant &el = columns[i]; - if(el.name.contains(lit("POSITION"), Qt::CaseInsensitive)) + if(QString(el.name).contains(lit("POSITION"), Qt::CaseInsensitive)) { posEl = i; break; @@ -477,9 +477,9 @@ struct BufferConfiguration // OK last resort, just look for 'pos' for(int i = 0; posEl == -1 && i < columns.count(); i++) { - const FormatElement &el = columns[i]; + const ShaderConstant &el = columns[i]; - if(el.name.contains(lit("POS"), Qt::CaseInsensitive)) + if(QString(el.name).contains(lit("POS"), Qt::CaseInsensitive)) { posEl = i; break; @@ -505,14 +505,14 @@ struct BufferConfiguration // prioritise TEXCOORD over general COLOR for(int i = 0; i < columns.count(); i++) { - const FormatElement &el = columns[i]; + const ShaderConstant &el = columns[i]; - if(el.name.compare(lit("TEXCOORD"), Qt::CaseInsensitive) == 0 || - el.name.compare(lit("TEXCOORD0"), Qt::CaseInsensitive) == 0 || - el.name.compare(lit("TEX"), Qt::CaseInsensitive) == 0 || - el.name.compare(lit("TEX0"), Qt::CaseInsensitive) == 0 || - el.name.compare(lit("UV"), Qt::CaseInsensitive) == 0 || - el.name.compare(lit("UV0"), Qt::CaseInsensitive) == 0) + if(QString(el.name).compare(lit("TEXCOORD"), Qt::CaseInsensitive) == 0 || + QString(el.name).compare(lit("TEXCOORD0"), Qt::CaseInsensitive) == 0 || + QString(el.name).compare(lit("TEX"), Qt::CaseInsensitive) == 0 || + QString(el.name).compare(lit("TEX0"), Qt::CaseInsensitive) == 0 || + QString(el.name).compare(lit("UV"), Qt::CaseInsensitive) == 0 || + QString(el.name).compare(lit("UV0"), Qt::CaseInsensitive) == 0) { secondEl = i; break; @@ -521,12 +521,12 @@ struct BufferConfiguration for(int i = 0; secondEl == -1 && i < columns.count(); i++) { - const FormatElement &el = columns[i]; + const ShaderConstant &el = columns[i]; - if(el.name.compare(lit("COLOR"), Qt::CaseInsensitive) == 0 || - el.name.compare(lit("COLOR0"), Qt::CaseInsensitive) == 0 || - el.name.compare(lit("COL"), Qt::CaseInsensitive) == 0 || - el.name.compare(lit("COL0"), Qt::CaseInsensitive) == 0) + if(QString(el.name).compare(lit("COLOR"), Qt::CaseInsensitive) == 0 || + QString(el.name).compare(lit("COLOR0"), Qt::CaseInsensitive) == 0 || + QString(el.name).compare(lit("COL"), Qt::CaseInsensitive) == 0 || + QString(el.name).compare(lit("COL0"), Qt::CaseInsensitive) == 0) { secondEl = i; break; @@ -631,7 +631,7 @@ public: } else { - const FormatElement &el = elementForColumn(section); + const ShaderConstant &el = elementForColumn(section); const BufferElementProperties &prop = propForColumn(section); if(prop.format.compCount == 1 || role == columnGroupRole) @@ -731,20 +731,21 @@ public: } else { - const FormatElement &el = elementForColumn(col); + const ShaderConstant &el = elementForColumn(col); const BufferElementProperties &prop = propForColumn(col); - if(el.rgb && prop.buffer < config.buffers.size()) + if(el.type.descriptor.displayAsRGB && prop.buffer < config.buffers.size()) { const byte *data = config.buffers[prop.buffer]->data(); const byte *end = config.buffers[prop.buffer]->end(); data += config.buffers[prop.buffer]->stride * row; - data += el.offset; + data += el.byteOffset; // 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.matrixdim, data, end); + QVariantList list = GetVariants(prop.format, el.type.descriptor.rows, + el.type.descriptor.matrixByteStride, data, end); if(!list.isEmpty()) { @@ -835,7 +836,7 @@ public: return idx; } - const FormatElement &el = elementForColumn(col); + const ShaderConstant &el = elementForColumn(col); const BufferElementProperties &prop = propForColumn(col); if(useGenerics(col)) @@ -855,24 +856,25 @@ public: else data += config.buffers[prop.buffer]->stride * instIdx; - data += el.offset; + data += el.byteOffset; // 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.matrixdim, data, end); + QVariantList list = GetVariants(prop.format, el.type.descriptor.rows, + el.type.descriptor.matrixByteStride, data, end); int comp = componentForIndex(col); if(comp < list.count()) { - uint32_t rowdim = el.matrixdim; - uint32_t coldim = prop.format.compCount; + uint32_t rowdim = el.type.descriptor.rows; + uint32_t coldim = el.type.descriptor.columns; if(rowdim == 1) { QVariant v; - if(el.rowmajor) + if(el.type.descriptor.rowMajorStorage) v = list[comp]; else v = list[comp * rowdim]; @@ -893,7 +895,7 @@ public: if(r > 0) ret += lit("\n"); - if(el.rowmajor) + if(el.type.descriptor.rowMajorStorage) ret += interpretVariant(list[comp + r * coldim], el, prop); else ret += interpretVariant(list[r + comp * rowdim], el, prop); @@ -969,7 +971,7 @@ public: return columnLookup[col - reservedColumnCount()]; } - const FormatElement &elementForColumn(int col) const + const ShaderConstant &elementForColumn(int col) const { return config.columns[columnLookup[col - reservedColumnCount()]]; } @@ -1065,7 +1067,7 @@ private: } QString outOfBounds() const { return lit("---"); } - QString interpretGeneric(int col, const FormatElement &el, const BufferElementProperties &prop) const + QString interpretGeneric(int col, const ShaderConstant &el, const BufferElementProperties &prop) const { int comp = componentForIndex(col); @@ -1090,7 +1092,7 @@ private: return outOfBounds(); } - QString interpretVariant(const QVariant &v, const FormatElement &el, + QString interpretVariant(const QVariant &v, const ShaderConstant &el, const BufferElementProperties &prop) const { QString ret; @@ -1128,10 +1130,10 @@ private: else if(vt == QMetaType::UInt || vt == QMetaType::UShort || vt == QMetaType::UChar) { uint u = v.toUInt(); - if(el.hex && prop.format.type == ResourceFormatType::Regular) + if(el.type.descriptor.displayAsHex && prop.format.type == ResourceFormatType::Regular) ret = Formatter::HexFormat(u, prop.format.compByteWidth); else - ret = Formatter::Format(u, el.hex); + ret = Formatter::Format(u, el.type.descriptor.displayAsHex); } else if(vt == QMetaType::Int || vt == QMetaType::Short || vt == QMetaType::SChar) { @@ -1143,7 +1145,7 @@ private: } else if(vt == QMetaType::ULongLong) { - ret = Formatter::Format((uint64_t)v.toULongLong(), el.hex); + ret = Formatter::Format((uint64_t)v.toULongLong(), el.type.descriptor.displayAsHex); } else if(vt == QMetaType::LongLong) { @@ -1164,7 +1166,7 @@ private: struct CachedElData { - const FormatElement *el = NULL; + const ShaderConstant *el = NULL; const BufferElementProperties *prop = NULL; const byte *data = NULL; @@ -1203,15 +1205,15 @@ struct CalcBoundingBoxData BBoxData output; }; -void CacheDataForIteration(QVector &cache, const QList &columns, - const QList &props, +void CacheDataForIteration(QVector &cache, const rdcarray &columns, + const rdcarray &props, const QList buffers, uint32_t inst) { cache.reserve(columns.count()); for(int col = 0; col < columns.count(); col++) { - const FormatElement &el = columns[col]; + const ShaderConstant &el = columns[col]; const BufferElementProperties &prop = props[col]; CachedElData d; @@ -1219,7 +1221,7 @@ void CacheDataForIteration(QVector &cache, const QList 0) @@ -1232,7 +1234,7 @@ void CacheDataForIteration(QVector &cache, const QListstride; - d.data += el.offset; + d.data += el.byteOffset; if(prop.perinstance) d.data += d.stride * d.instIdx; @@ -1243,8 +1245,8 @@ void CacheDataForIteration(QVector &cache, const QList &columns, - QList &props) + rdcarray &columns, + rdcarray &props) { if(!shader) return; @@ -1255,12 +1257,11 @@ static void ConfigureColumnsForShader(ICaptureContext &ctx, const ShaderReflecti int i = 0, posidx = -1; for(const SigParameter &sig : shader->outputSignature) { - FormatElement f; + ShaderConstant f; BufferElementProperties p; f.name = !sig.varName.isEmpty() ? sig.varName : sig.semanticIdxName; - f.rowmajor = false; - f.matrixdim = 1; + f.type.descriptor.columns = sig.compCount; p.buffer = 0; p.perinstance = false; @@ -1293,7 +1294,7 @@ static void ConfigureColumnsForShader(ICaptureContext &ctx, const ShaderReflecti for(i = 0; i < columns.count(); i++) { BufferElementProperties &prop = props[i]; - FormatElement &sig = columns[i]; + ShaderConstant &el = columns[i]; uint numComps = prop.format.compCount; uint elemSize = prop.format.compType == CompType::Double ? 8U : 4U; @@ -1307,7 +1308,7 @@ static void ConfigureColumnsForShader(ICaptureContext &ctx, const ShaderReflecti offset = AlignUp(offset, 4U * elemSize); } - sig.offset = offset; + el.byteOffset = offset; offset += numComps * elemSize; } @@ -1331,10 +1332,9 @@ static void ConfigureMeshColumns(ICaptureContext &ctx, PopulateBufferData *bufda if(!a.used) continue; - FormatElement f(a.name, a.byteOffset, - false, // row major matrix - 1, // matrix dimension - a.format, false, false); + ShaderConstant f; + f.name = a.name; + f.byteOffset = a.byteOffset; BufferElementProperties p; p.buffer = a.vertexBuffer; @@ -1342,12 +1342,12 @@ static void ConfigureMeshColumns(ICaptureContext &ctx, PopulateBufferData *bufda p.instancerate = a.instanceRate; p.format = a.format; - bufdata->vsinConfig.genericsEnabled[bufdata->vsinConfig.columns.size()] = false; + bufdata->vsinConfig.genericsEnabled[bufdata->vsinConfig.columns.count()] = false; if(a.genericEnabled) { - bufdata->vsinConfig.genericsEnabled[bufdata->vsinConfig.columns.size()] = true; - bufdata->vsinConfig.generics[bufdata->vsinConfig.columns.size()] = a.genericValue; + bufdata->vsinConfig.genericsEnabled[bufdata->vsinConfig.columns.count()] = true; + bufdata->vsinConfig.generics[bufdata->vsinConfig.columns.count()] = a.genericValue; } bufdata->vsinConfig.columns.push_back(f); @@ -1514,16 +1514,16 @@ static void RT_FetchMeshData(IReplayController *r, ICaptureContext &ctx, Populat uint32_t maxAttrOffset = 0; - for(int c = 0; c < data->vsinConfig.columns.size(); c++) + for(int c = 0; c < data->vsinConfig.columns.count(); c++) { - const FormatElement &col = data->vsinConfig.columns[c]; + const ShaderConstant &col = data->vsinConfig.columns[c]; const BufferElementProperties &prop = data->vsinConfig.props[c]; if(prop.buffer == vbIdx) { used = true; - maxAttrOffset = qMax(maxAttrOffset, col.offset); + maxAttrOffset = qMax(maxAttrOffset, col.byteOffset); if(prop.perinstance) pi = true; @@ -1647,6 +1647,70 @@ static void RT_FetchMeshData(IReplayController *r, ICaptureContext &ctx, Populat } } +static int MaxNumRows(const ShaderConstant &c) +{ + int ret = c.type.descriptor.rows; + + for(const ShaderConstant &child : c.type.members) + ret = qMax(ret, MaxNumRows(child)); + + return ret; +} + +static void UnrollConstant(rdcstr prefix, const ShaderConstant &constant, + rdcarray &columns, + rdcarray &props) +{ + bool isArray = constant.type.descriptor.elements > 1; + + rdcstr baseName = constant.name; + + if(!prefix.isEmpty()) + baseName = prefix + "." + baseName; + + if(constant.type.members.isEmpty()) + { + BufferElementProperties prop; + prop.format = GetInterpretedResourceFormat(constant); + + ShaderConstant c = constant; + + if(isArray) + { + for(uint32_t a = 0; a < constant.type.descriptor.elements; a++) + { + c.name = QFormatStr("%1[%2]").arg(baseName).arg(a); + columns.push_back(c); + props.push_back(prop); + } + } + else + { + c.name = baseName; + columns.push_back(c); + props.push_back(prop); + } + + return; + } + + // struct, expand by members + for(uint32_t a = 0; a < qMax(1U, constant.type.descriptor.elements); a++) + { + for(const ShaderConstant &child : constant.type.members) + { + UnrollConstant(isArray ? QFormatStr("%1[%2]").arg(baseName).arg(a) : QString(baseName), child, + columns, props); + } + } +} + +static void UnrollConstant(const ShaderConstant &constant, rdcarray &columns, + rdcarray &props) +{ + UnrollConstant("", constant, columns, props); +} + BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) : QFrame(parent), ui(new Ui::BufferViewer), m_Ctx(ctx) { @@ -2158,9 +2222,9 @@ void BufferViewer::OnEventChanged(uint32_t eventId) len = 0; QString errors; - bufdata->vsinConfig.columns = FormatElement::ParseFormatString(m_Format, len, true, errors); - for(int i = 0; i < bufdata->vsinConfig.columns.size(); i++) - bufdata->vsinConfig.props.push_back({}); + ShaderConstant constant = BufferFormatter::ParseFormatString(m_Format, len, true, errors); + + UnrollConstant(constant, bufdata->vsinConfig.columns, bufdata->vsinConfig.props); ClearModels(); } @@ -2241,7 +2305,7 @@ void BufferViewer::OnEventChanged(uint32_t eventId) buf->stride = 0; for(int i = 0; i < bufdata->vsinConfig.props.count(); i++) buf->stride += bufdata->vsinConfig.props[i].format.ElementSize() * - bufdata->vsinConfig.columns[i].matrixdim; + bufdata->vsinConfig.columns[i].type.descriptor.rows; buf->stride = qMax((size_t)1, buf->stride); @@ -2432,7 +2496,7 @@ void BufferViewer::calcBoundingData(CalcBoundingBoxData &bbox) for(int col = 0; col < s.columns.count(); col++) { const CachedElData &d = cache[col]; - const FormatElement *el = d.el; + const ShaderConstant *el = d.el; const BufferElementProperties *prop = d.prop; float *minOut = (float *)&minOutputList[col]; @@ -2445,7 +2509,8 @@ void BufferViewer::calcBoundingData(CalcBoundingBoxData &bbox) if(!prop->perinstance) bytes += d.stride * idx; - QVariantList list = GetVariants(prop->format, el->matrixdim, bytes, d.end); + QVariantList list = GetVariants(prop->format, el->type.descriptor.rows, + el->type.descriptor.matrixByteStride, bytes, d.end); for(int comp = 0; comp < 4 && comp < list.count(); comp++) { @@ -2622,7 +2687,7 @@ void BufferViewer::UI_CalculateMeshFormats() m_VSInPosition.indexByteStride = 4U; { - const FormatElement &el = vsinConfig.columns[elIdx]; + const ShaderConstant &el = vsinConfig.columns[elIdx]; const BufferElementProperties &prop = vsinConfig.props[elIdx]; m_VSInPosition.instanced = prop.perinstance; @@ -2632,7 +2697,7 @@ void BufferViewer::UI_CalculateMeshFormats() { m_VSInPosition.vertexResourceId = vbs[prop.buffer].resourceId; m_VSInPosition.vertexByteStride = vbs[prop.buffer].byteStride; - m_VSInPosition.vertexByteOffset = vbs[prop.buffer].byteOffset + el.offset + + m_VSInPosition.vertexByteOffset = vbs[prop.buffer].byteOffset + el.byteOffset + draw->vertexOffset * m_VSInPosition.vertexByteStride; } else @@ -2649,7 +2714,7 @@ void BufferViewer::UI_CalculateMeshFormats() if(elIdx >= 0 && elIdx < vsinConfig.columns.count()) { - const FormatElement &el = vsinConfig.columns[elIdx]; + const ShaderConstant &el = vsinConfig.columns[elIdx]; const BufferElementProperties &prop = vsinConfig.props[elIdx]; m_VSInSecondary.instanced = prop.perinstance; @@ -2659,7 +2724,7 @@ void BufferViewer::UI_CalculateMeshFormats() { m_VSInSecondary.vertexResourceId = vbs[prop.buffer].resourceId; m_VSInSecondary.vertexByteStride = vbs[prop.buffer].byteStride; - m_VSInSecondary.vertexByteOffset = vbs[prop.buffer].byteOffset + el.offset + + m_VSInSecondary.vertexByteOffset = vbs[prop.buffer].byteOffset + el.byteOffset + draw->vertexOffset * m_VSInSecondary.vertexByteStride; } else @@ -2685,11 +2750,11 @@ void BufferViewer::UI_CalculateMeshFormats() if(elIdx < 0 || elIdx >= vsoutConfig.columns.count()) elIdx = 0; - const FormatElement &el = vsoutConfig.columns[elIdx]; + const ShaderConstant &el = vsoutConfig.columns[elIdx]; const BufferElementProperties &prop = vsoutConfig.props[elIdx]; m_PostVSPosition = m_PostVS; - m_PostVSPosition.vertexByteOffset += el.offset; + m_PostVSPosition.vertexByteOffset += el.byteOffset; m_PostVSPosition.unproject = prop.systemValue == ShaderBuiltin::Position; // if geometry/tessellation is enabled, don't unproject VS output data @@ -2702,7 +2767,7 @@ void BufferViewer::UI_CalculateMeshFormats() if(elIdx >= 0 && elIdx < vsoutConfig.columns.count()) { m_PostVSSecondary = m_PostVS; - m_PostVSSecondary.vertexByteOffset += vsoutConfig.columns[elIdx].offset; + m_PostVSSecondary.vertexByteOffset += vsoutConfig.columns[elIdx].byteOffset; m_PostVSSecondary.showAlpha = m_ModelVSOut->secondaryAlpha(); } } @@ -2721,11 +2786,11 @@ void BufferViewer::UI_CalculateMeshFormats() if(elIdx < 0 || elIdx >= gsoutConfig.columns.count()) elIdx = 0; - const FormatElement &el = gsoutConfig.columns[elIdx]; + const ShaderConstant &el = gsoutConfig.columns[elIdx]; const BufferElementProperties &prop = gsoutConfig.props[elIdx]; m_PostGSPosition = m_PostGS; - m_PostGSPosition.vertexByteOffset += el.offset; + m_PostGSPosition.vertexByteOffset += el.byteOffset; m_PostGSPosition.unproject = prop.systemValue == ShaderBuiltin::Position; elIdx = m_ModelGSOut->secondaryColumn(); @@ -2733,7 +2798,7 @@ void BufferViewer::UI_CalculateMeshFormats() if(elIdx >= 0 && elIdx < gsoutConfig.columns.count()) { m_PostGSSecondary = m_PostGS; - m_PostGSSecondary.vertexByteOffset += gsoutConfig.columns[elIdx].offset; + m_PostGSSecondary.vertexByteOffset += gsoutConfig.columns[elIdx].byteOffset; m_PostGSSecondary.showAlpha = m_ModelGSOut->secondaryAlpha(); } } @@ -3190,16 +3255,35 @@ void BufferViewer::CalcColumnWidth(int maxNumRows) floatProp.format = floatFmt; intProp.format = intFmt; + ShaderConstant elem; + elem.name = headerText; + elem.byteOffset = 0; + elem.type.descriptor.rows = maxNumRows; + bufconfig.columns.clear(); - bufconfig.columns.push_back(FormatElement(headerText, 0, false, maxNumRows, floatFmt, false, false)); + + bufconfig.columns.push_back(elem); bufconfig.props.push_back(floatProp); - bufconfig.columns.push_back(FormatElement(headerText, 4, false, 1, floatFmt, false, false)); + + elem.type.descriptor.rows = 1; + elem.byteOffset = 4; + + bufconfig.columns.push_back(elem); bufconfig.props.push_back(floatProp); - bufconfig.columns.push_back(FormatElement(headerText, 8, false, 1, floatFmt, false, false)); + + elem.byteOffset = 8; + + bufconfig.columns.push_back(elem); bufconfig.props.push_back(floatProp); - bufconfig.columns.push_back(FormatElement(headerText, 12, false, 1, intFmt, true, false)); + + elem.byteOffset = 12; + + bufconfig.columns.push_back(elem); bufconfig.props.push_back(intProp); - bufconfig.columns.push_back(FormatElement(headerText, 16, false, 1, intFmt, false, false)); + + elem.byteOffset = 16; + + bufconfig.columns.push_back(elem); bufconfig.props.push_back(intProp); bufconfig.numRows = 2; @@ -3359,14 +3443,9 @@ void BufferViewer::processFormat(const QString &format) if(len == UINT64_MAX) len = 0; - QList cols = FormatElement::ParseFormatString(format, len, true, errors); + ShaderConstant cols = BufferFormatter::ParseFormatString(format, len, true, errors); - int maxNumRows = 1; - - for(const FormatElement &c : cols) - maxNumRows = qMax(maxNumRows, (int)c.matrixdim); - - CalcColumnWidth(maxNumRows); + CalcColumnWidth(MaxNumRows(cols)); ClearModels(); @@ -3374,11 +3453,7 @@ void BufferViewer::processFormat(const QString &format) ui->formatSpecifier->setFormat(format); - uint32_t stride = 0; - for(const FormatElement &el : cols) - stride += el.format.ElementSize() * el.matrixdim; - - stride = qMax(1U, stride); + uint32_t stride = qMax(1U, cols.type.descriptor.arrayByteStride); ui->byteRangeStart->setSingleStep((int)stride); ui->byteRangeLength->setSingleStep((int)stride); @@ -3496,7 +3571,7 @@ void BufferViewer::exportData(const BufferExport ¶ms) for(int col = 0; col < cache.count(); col++) { const CachedElData &d = cache[col]; - const FormatElement *el = d.el; + const ShaderConstant *el = d.el; const BufferElementProperties *prop = d.prop; if(d.data) diff --git a/qrenderdoc/Windows/ConstantBufferPreviewer.cpp b/qrenderdoc/Windows/ConstantBufferPreviewer.cpp index 91559791d..7e2f21fe5 100644 --- a/qrenderdoc/Windows/ConstantBufferPreviewer.cpp +++ b/qrenderdoc/Windows/ConstantBufferPreviewer.cpp @@ -122,7 +122,7 @@ void ConstantBufferPreviewer::OnEventChanged(uint32_t eventId) if(reflection == NULL || m_slot >= reflection->constantBlocks.size()) { // save expansion before clearing - if(m_formatOverride.empty()) + if(m_formatOverride.type.members.empty()) { RDTreeViewExpansionState &prevShaderExpansionState = ui->variables->getInternalExpansion(qHash(ToQStr(prevShader))); @@ -133,7 +133,7 @@ void ConstantBufferPreviewer::OnEventChanged(uint32_t eventId) return; } - if(!m_formatOverride.empty()) + if(!m_formatOverride.type.members.empty()) { m_Ctx.Replay().AsyncInvoke([this, offs, size, wasEmpty](IReplayController *r) { bytebuf data = r->GetBufferData(m_cbuffer, offs, size); @@ -265,14 +265,14 @@ void ConstantBufferPreviewer::processFormat(const QString &format) { if(format.isEmpty()) { - m_formatOverride.clear(); + m_formatOverride = ShaderConstant(); ui->formatSpecifier->setErrors(QString()); } else { QString errors; - m_formatOverride = FormatElement::ParseFormatString(format, 0, false, errors); + m_formatOverride = BufferFormatter::ParseFormatString(format, 0, false, errors); ui->formatSpecifier->setErrors(errors); } @@ -348,17 +348,7 @@ void ConstantBufferPreviewer::updateLabels() rdcarray ConstantBufferPreviewer::applyFormatOverride(const bytebuf &bytes) { - QVector variables; + ShaderVariable var = InterpretShaderVar(m_formatOverride, bytes.begin(), bytes.end()); - variables.resize(m_formatOverride.length()); - - for(int i = 0; i < m_formatOverride.length(); i++) - { - const byte *b = bytes.begin() + m_formatOverride[i].offset; - variables[i] = m_formatOverride[i].GetShaderVar(b, bytes.end()); - } - - rdcarray ret; - ret = variables.toStdVector(); - return ret; + return var.members; } diff --git a/qrenderdoc/Windows/ConstantBufferPreviewer.h b/qrenderdoc/Windows/ConstantBufferPreviewer.h index ce5f7dde7..8e4f24c81 100644 --- a/qrenderdoc/Windows/ConstantBufferPreviewer.h +++ b/qrenderdoc/Windows/ConstantBufferPreviewer.h @@ -34,7 +34,7 @@ class ConstantBufferPreviewer; class QTextStream; class RDTreeWidgetItem; -struct FormatElement; +struct BufferFormatter; class ConstantBufferPreviewer : public QFrame, public IConstantBufferPreviewer, public ICaptureViewer { @@ -89,5 +89,5 @@ private: static QList m_Previews; - QList m_formatOverride; + ShaderConstant m_formatOverride; }; diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp index 317c4eced..025edad4d 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp @@ -1958,7 +1958,7 @@ void D3D11PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, in if(tex->type == TextureType::Buffer) { IBufferViewer *viewer = m_Ctx.ViewTextureAsBuffer( - tex->resourceId, Subresource(), FormatElement::GenerateTextureBufferFormat(*tex)); + tex->resourceId, Subresource(), BufferFormatter::GetTextureFormatString(*tex)); m_Ctx.AddDockWindow(viewer->Widget(), DockReference::AddTo, this); } @@ -2067,7 +2067,7 @@ void D3D11PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, in if(shaderRes) { - format = m_Common.GenerateBufferFormatter(*shaderRes, view.res.viewFormat, offs); + format = BufferFormatter::GetBufferFormatString(*shaderRes, view.res.viewFormat, offs); if(view.res.bufferFlags & D3DBufferViewFlags::Raw) format = lit("xint"); diff --git a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp index d5fa2a53f..ec007fd75 100644 --- a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp @@ -1845,7 +1845,7 @@ void D3D12PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, in if(tex->type == TextureType::Buffer) { IBufferViewer *viewer = m_Ctx.ViewTextureAsBuffer( - tex->resourceId, Subresource(), FormatElement::GenerateTextureBufferFormat(*tex)); + tex->resourceId, Subresource(), BufferFormatter::GetTextureFormatString(*tex)); m_Ctx.AddDockWindow(viewer->Widget(), DockReference::AddTo, this); } @@ -1920,7 +1920,7 @@ void D3D12PipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, in if(shaderRes) { - format = m_Common.GenerateBufferFormatter(*shaderRes, view.res.viewFormat, offs); + format = BufferFormatter::GetBufferFormatString(*shaderRes, view.res.viewFormat, offs); if(view.res.bufferFlags & D3DBufferViewFlags::Raw) format = lit("xint"); diff --git a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp index 2a11df4de..86cc0ee77 100644 --- a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp @@ -2144,7 +2144,7 @@ void GLPipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, int c if(tex->type == TextureType::Buffer) { IBufferViewer *viewer = m_Ctx.ViewTextureAsBuffer( - tex->resourceId, Subresource(), FormatElement::GenerateTextureBufferFormat(*tex)); + tex->resourceId, Subresource(), BufferFormatter::GetTextureFormatString(*tex)); m_Ctx.AddDockWindow(viewer->Widget(), DockReference::AddTo, this); } @@ -2165,7 +2165,7 @@ void GLPipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, int c const ShaderResource &shaderRes = stage->reflection->readWriteResources[buf.bindPoint]; - QString format = m_Common.GenerateBufferFormatter(shaderRes, ResourceFormat(), buf.offset); + QString format = BufferFormatter::GetBufferFormatString(shaderRes, ResourceFormat(), buf.offset); if(buf.ID != ResourceId()) { diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp index 8ba5766fc..bb3447d8d 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp @@ -694,169 +694,6 @@ QString PipelineStateViewer::declareStruct(QList &declaredStructs, cons return ret; } -QString PipelineStateViewer::GenerateBufferFormatter(const ShaderResource &res, - const ResourceFormat &viewFormat, - uint64_t &baseByteOffset) -{ - QString format; - - if(!res.variableType.members.empty()) - { - if(m_Ctx.APIProps().pipelineType == GraphicsAPI::Vulkan || - m_Ctx.APIProps().pipelineType == GraphicsAPI::OpenGL) - { - const rdcarray &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 += 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()) - { - 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 += - QFormatStr(" %1%2 %3%4;\n").arg(comment).arg(varTypeName).arg(members[i].name).arg(arraySize); - } - - format += lit("}"); - - // if the last member is a struct, declare it - if(!members.back().type.members.isEmpty()) - { - QList declaredStructs; - format = declareStruct(declaredStructs, varTypeName, members.back().type.members, - members.back().type.descriptor.arrayByteStride) + - lit("\n") + format; - } - } - else - { - QList declaredStructs; - format = declareStruct(declaredStructs, res.variableType.descriptor.name, - res.variableType.members, 0); - } - } - else - { - const auto &desc = res.variableType.descriptor; - - if(viewFormat.type == ResourceFormatType::Undefined) - { - if(desc.type == VarType::Unknown) - { - format = desc.name; - } - else - { - if(desc.rowMajorStorage) - format += lit("row_major "); - - format += ToQStr(desc.type); - if(desc.rows > 1 && desc.columns > 1) - format += QFormatStr("%1x%2").arg(desc.rows).arg(desc.columns); - else if(desc.columns > 1) - format += QString::number(desc.columns); - - if(!desc.name.empty()) - format += lit(" ") + desc.name; - - if(desc.elements > 1) - format += QFormatStr("[%1]").arg(desc.elements); - } - } - else - { - if(viewFormat.type == ResourceFormatType::R10G10B10A2) - { - if(viewFormat.compType == CompType::UInt) - format = lit("uintten"); - if(viewFormat.compType == CompType::UNorm) - format = lit("unormten"); - } - else if(viewFormat.type == ResourceFormatType::R11G11B10) - { - format = lit("floateleven"); - } - else - { - switch(viewFormat.compByteWidth) - { - case 1: - { - if(viewFormat.compType == CompType::UNorm || viewFormat.compType == CompType::UNormSRGB) - format = lit("unormb"); - if(viewFormat.compType == CompType::SNorm) - format = lit("snormb"); - if(viewFormat.compType == CompType::UInt) - format = lit("ubyte"); - if(viewFormat.compType == CompType::SInt) - format = lit("byte"); - break; - } - case 2: - { - if(viewFormat.compType == CompType::UNorm || viewFormat.compType == CompType::UNormSRGB) - format = lit("unormh"); - if(viewFormat.compType == CompType::SNorm) - format = lit("snormh"); - if(viewFormat.compType == CompType::UInt) - format = lit("ushort"); - if(viewFormat.compType == CompType::SInt) - format = lit("short"); - if(viewFormat.compType == CompType::Float) - format = lit("half"); - break; - } - case 4: - { - if(viewFormat.compType == CompType::UNorm || viewFormat.compType == CompType::UNormSRGB) - format = lit("unormf"); - if(viewFormat.compType == CompType::SNorm) - format = lit("snormf"); - if(viewFormat.compType == CompType::UInt) - format = lit("uint"); - if(viewFormat.compType == CompType::SInt) - format = lit("int"); - if(viewFormat.compType == CompType::Float) - format = lit("float"); - break; - } - } - - format += QString::number(viewFormat.compCount); - } - } - } - - return format; -} - void PipelineStateViewer::MakeShaderVariablesHLSL(bool cbufferContents, const rdcarray &vars, QString &struct_contents, QString &struct_defs) diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h index 77ce00a65..343374c86 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h @@ -71,9 +71,6 @@ public: const ShaderBindpointMapping &bindpointMapping, const ShaderReflection *shaderDetails); - QString GenerateBufferFormatter(const ShaderResource &res, const ResourceFormat &viewFormat, - uint64_t &baseByteOffset); - QString GetVBufferFormatString(uint32_t slot); void setTopologyDiagram(QLabel *diagram, Topology topo); diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index 699104789..767f5f467 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -2540,7 +2540,7 @@ void VulkanPipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, i if(tex->type == TextureType::Buffer) { IBufferViewer *viewer = m_Ctx.ViewTextureAsBuffer( - tex->resourceId, Subresource(), FormatElement::GenerateTextureBufferFormat(*tex)); + tex->resourceId, Subresource(), BufferFormatter::GetTextureFormatString(*tex)); m_Ctx.AddDockWindow(viewer->Widget(), DockReference::AddTo, this); } @@ -2569,7 +2569,7 @@ void VulkanPipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, i ? stage->reflection->readWriteResources[buf.bindPoint] : stage->reflection->readOnlyResources[buf.bindPoint]; - format = m_Common.GenerateBufferFormatter(shaderRes, buf.fmt, buf.offset); + format = BufferFormatter::GetBufferFormatString(shaderRes, buf.fmt, buf.offset); } if(buf.ID != ResourceId()) diff --git a/qrenderdoc/Windows/ResourceInspector.cpp b/qrenderdoc/Windows/ResourceInspector.cpp index b67e81770..3c420e060 100644 --- a/qrenderdoc/Windows/ResourceInspector.cpp +++ b/qrenderdoc/Windows/ResourceInspector.cpp @@ -451,7 +451,7 @@ void ResourceInspector::on_viewContents_clicked() if(tex->type == TextureType::Buffer) { IBufferViewer *viewer = m_Ctx.ViewTextureAsBuffer( - tex->resourceId, Subresource(), FormatElement::GenerateTextureBufferFormat(*tex)); + tex->resourceId, Subresource(), BufferFormatter::GetTextureFormatString(*tex)); m_Ctx.AddDockWindow(viewer->Widget(), DockReference::AddTo, this); } diff --git a/qrenderdoc/Windows/TextureViewer.cpp b/qrenderdoc/Windows/TextureViewer.cpp index dec15a701..3235c7b58 100644 --- a/qrenderdoc/Windows/TextureViewer.cpp +++ b/qrenderdoc/Windows/TextureViewer.cpp @@ -3622,7 +3622,7 @@ void TextureViewer::on_viewTexBuffer_clicked() { IBufferViewer *viewer = m_Ctx.ViewTextureAsBuffer(texptr->resourceId, m_TexDisplay.subresource, - FormatElement::GenerateTextureBufferFormat(*texptr)); + BufferFormatter::GetTextureFormatString(*texptr)); m_Ctx.AddDockWindow(viewer->Widget(), DockReference::AddTo, this); } diff --git a/qrenderdoc/qrenderdoc.pro b/qrenderdoc/qrenderdoc.pro index 334d1d1b9..0bcc422b5 100644 --- a/qrenderdoc/qrenderdoc.pro +++ b/qrenderdoc/qrenderdoc.pro @@ -168,7 +168,7 @@ SOURCES += Code/qrenderdoc.cpp \ Code/CaptureContext.cpp \ Code/ScintillaSyntax.cpp \ Code/QRDUtils.cpp \ - Code/FormatElement.cpp \ + Code/BufferFormatter.cpp \ Code/Resources.cpp \ Code/RGPInterop.cpp \ Code/pyrenderdoc/PythonContext.cpp \ diff --git a/qrenderdoc/qrenderdoc_local.vcxproj b/qrenderdoc/qrenderdoc_local.vcxproj index b5de61ded..e1532dd2b 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj +++ b/qrenderdoc/qrenderdoc_local.vcxproj @@ -574,7 +574,7 @@ - + diff --git a/qrenderdoc/qrenderdoc_local.vcxproj.filters b/qrenderdoc/qrenderdoc_local.vcxproj.filters index 0149a492c..2afc0973b 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj.filters +++ b/qrenderdoc/qrenderdoc_local.vcxproj.filters @@ -369,9 +369,6 @@ Windows\Dialogs - - Code - Windows @@ -729,6 +726,9 @@ Generated Files + + Code + diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index 180273e47..79e7aa97f 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -186,7 +186,7 @@ DOCUMENT(R"(Represents the base type of a shader variable in debugging or consta An unknown type. )"); -enum class VarType : uint32_t +enum class VarType : uint8_t { Float = 0, Double, @@ -199,11 +199,30 @@ enum class VarType : uint32_t ULong, SByte, UByte, - Unknown = ~0U, + Unknown = 0xFF, }; DECLARE_REFLECTION_ENUM(VarType); +DOCUMENT(R"(Get the byte size of a variable type. + +:param VarType type: The variable type +:return: The size in bytes of this type +:rtype: int +)"); +constexpr uint32_t VarTypeByteSize(VarType type) +{ + // temporarily disable clang-format to make this more readable. + // Ideally we'd use a simple switch() but VS2015 doesn't support that :(. + // 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 ) ? 4 + : (type == VarType::Double || type == VarType::ULong || type == VarType::SLong ) ? 8 + : 0; + // clang-format on +} + DOCUMENT(R"(Represents the component type of a channel in a texture or element in a structure. .. data:: Typeless diff --git a/renderdoc/api/replay/shader_types.h b/renderdoc/api/replay/shader_types.h index 39334ab2a..26c3a91b8 100644 --- a/renderdoc/api/replay/shader_types.h +++ b/renderdoc/api/replay/shader_types.h @@ -635,6 +635,12 @@ struct ShaderVariableDescriptor return name < o.name; return false; } + DOCUMENT("The name of the type of this constant, e.g. a ``struct`` name."); + rdcstr name; + DOCUMENT("The number of elements in the array, or 1 if it's not an array."); + uint32_t elements = 1; + DOCUMENT("The number of bytes between the start of one element in the array and the next."); + uint32_t arrayByteStride = 0; DOCUMENT("The :class:`VarType` that this basic constant stores."); VarType type = VarType::Unknown; DOCUMENT("The number of rows in this matrix."); @@ -645,12 +651,12 @@ struct ShaderVariableDescriptor uint8_t matrixByteStride = 0; DOCUMENT("``True`` if the matrix is stored as row major instead of column major."); bool rowMajorStorage = false; - DOCUMENT("The number of elements in the array, or 1 if it's not an array."); - uint32_t elements = 1; - DOCUMENT("The number of bytes between the start of one element in the array and the next."); - uint32_t arrayByteStride = 0; - DOCUMENT("The name of the type of this constant, e.g. a ``struct`` name."); - rdcstr name; + DOCUMENT("``True`` if the contents of this variable should be displayed as hex."); + bool displayAsHex = false; + DOCUMENT(R"(``True`` if the contents of this variable should be displayed as RGB color (where +possible). +)"); + bool displayAsRGB = false; }; DECLARE_REFLECTION_STRUCT(ShaderVariableDescriptor); diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index f893fb3bc..482422ad5 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -181,6 +181,8 @@ void DoSerialise(SerialiserType &ser, ShaderVariableDescriptor &el) SERIALISE_MEMBER(elements); SERIALISE_MEMBER(arrayByteStride); SERIALISE_MEMBER(name); + SERIALISE_MEMBER(displayAsHex); + SERIALISE_MEMBER(displayAsRGB); SIZE_CHECK(40); }