From b91d19e3165d49bcf3d946f2db9f9a971bdcb2d7 Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 9 Oct 2018 15:41:21 +0100 Subject: [PATCH] Generate better buffer format strings when opening VB or IB buffer views --- .../D3D11PipelineStateViewer.cpp | 53 ++++- .../D3D12PipelineStateViewer.cpp | 53 ++++- .../PipelineState/GLPipelineStateViewer.cpp | 45 +++- .../PipelineState/PipelineStateViewer.cpp | 205 ++++++++++++++++++ .../PipelineState/PipelineStateViewer.h | 2 + .../VulkanPipelineStateViewer.cpp | 42 +++- 6 files changed, 366 insertions(+), 34 deletions(-) diff --git a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp index 10683bd48..a906ac407 100644 --- a/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D11PipelineStateViewer.cpp @@ -37,14 +37,16 @@ struct D3D11VBIBTag { D3D11VBIBTag() { offset = 0; } - D3D11VBIBTag(ResourceId i, uint64_t offs) + D3D11VBIBTag(ResourceId i, uint64_t offs, QString f = QString()) { id = i; offset = offs; + format = f; } ResourceId id; uint64_t offset; + QString format; }; Q_DECLARE_METATYPE(D3D11VBIBTag); @@ -1348,10 +1350,24 @@ void D3D11PipelineStateViewer::setState() {tr("Index"), state.inputAssembly.indexBuffer.resourceId, draw ? draw->indexByteWidth : 0, state.inputAssembly.indexBuffer.byteOffset, (qulonglong)length, QString()}); - node->setTag(QVariant::fromValue( - D3D11VBIBTag(state.inputAssembly.indexBuffer.resourceId, - state.inputAssembly.indexBuffer.byteOffset + - (draw ? draw->indexOffset * draw->indexByteWidth : 0)))); + QString iformat; + if(draw) + { + if(draw->indexByteWidth == 1) + iformat = lit("ubyte"); + else if(draw->indexByteWidth == 2) + iformat = lit("ushort"); + else if(draw->indexByteWidth == 4) + iformat = lit("uint"); + + iformat += lit(" indices[%1]").arg(RENDERDOC_NumVerticesPerPrimitive(draw->topology)); + } + + node->setTag( + QVariant::fromValue(D3D11VBIBTag(state.inputAssembly.indexBuffer.resourceId, + state.inputAssembly.indexBuffer.byteOffset + + (draw ? draw->indexOffset * draw->indexByteWidth : 0), + iformat))); if(!ibufferUsed) setInactiveRow(node); @@ -1372,10 +1388,24 @@ void D3D11PipelineStateViewer::setState() RDTreeWidgetItem *node = new RDTreeWidgetItem( {tr("Index"), tr("No Buffer Set"), lit("-"), lit("-"), lit("-"), QString()}); - node->setTag(QVariant::fromValue( - D3D11VBIBTag(state.inputAssembly.indexBuffer.resourceId, - state.inputAssembly.indexBuffer.byteOffset + - (draw ? draw->indexOffset * draw->indexByteWidth : 0)))); + QString iformat; + if(draw) + { + if(draw->indexByteWidth == 1) + iformat = lit("ubyte"); + else if(draw->indexByteWidth == 2) + iformat = lit("ushort"); + else if(draw->indexByteWidth == 4) + iformat = lit("uint"); + + iformat += lit(" indices[%1]").arg(RENDERDOC_NumVerticesPerPrimitive(draw->topology)); + } + + node->setTag( + QVariant::fromValue(D3D11VBIBTag(state.inputAssembly.indexBuffer.resourceId, + state.inputAssembly.indexBuffer.byteOffset + + (draw ? draw->indexOffset * draw->indexByteWidth : 0), + iformat))); setEmptyRow(node); m_EmptyNodes.push_back(node); @@ -1410,7 +1440,8 @@ void D3D11PipelineStateViewer::setState() node = new RDTreeWidgetItem({i, tr("No Buffer Set"), lit("-"), lit("-"), lit("-"), QString()}); - node->setTag(QVariant::fromValue(D3D11VBIBTag(v.resourceId, v.byteOffset))); + node->setTag(QVariant::fromValue( + D3D11VBIBTag(v.resourceId, v.byteOffset, m_Common.GetVBufferFormatString(i)))); if(!filledSlot) { @@ -2174,7 +2205,7 @@ void D3D11PipelineStateViewer::on_iaBuffers_itemActivated(RDTreeWidgetItem *item if(buf.id != ResourceId()) { - IBufferViewer *viewer = m_Ctx.ViewBuffer(buf.offset, UINT64_MAX, buf.id); + IBufferViewer *viewer = m_Ctx.ViewBuffer(buf.offset, UINT64_MAX, buf.id, buf.format); m_Ctx.AddDockWindow(viewer->Widget(), DockReference::AddTo, this); } diff --git a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp index 7c54f8b21..39b6ea46c 100644 --- a/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/D3D12PipelineStateViewer.cpp @@ -37,14 +37,16 @@ struct D3D12VBIBTag { D3D12VBIBTag() { offset = 0; } - D3D12VBIBTag(ResourceId i, uint64_t offs) + D3D12VBIBTag(ResourceId i, uint64_t offs, QString f = QString()) { id = i; offset = offs; + format = f; } ResourceId id; uint64_t offset; + QString format; }; Q_DECLARE_METATYPE(D3D12VBIBTag); @@ -1366,10 +1368,24 @@ void D3D12PipelineStateViewer::setState() {tr("Index"), state.inputAssembly.indexBuffer.resourceId, draw ? draw->indexByteWidth : 0, (qulonglong)state.inputAssembly.indexBuffer.byteOffset, (qulonglong)length, QString()}); - node->setTag(QVariant::fromValue( - D3D12VBIBTag(state.inputAssembly.indexBuffer.resourceId, - state.inputAssembly.indexBuffer.byteOffset + - (draw ? draw->indexOffset * draw->indexByteWidth : 0)))); + QString iformat; + if(draw) + { + if(draw->indexByteWidth == 1) + iformat = lit("ubyte"); + else if(draw->indexByteWidth == 2) + iformat = lit("ushort"); + else if(draw->indexByteWidth == 4) + iformat = lit("uint"); + + iformat += lit(" indices[%1]").arg(RENDERDOC_NumVerticesPerPrimitive(draw->topology)); + } + + node->setTag( + QVariant::fromValue(D3D12VBIBTag(state.inputAssembly.indexBuffer.resourceId, + state.inputAssembly.indexBuffer.byteOffset + + (draw ? draw->indexOffset * draw->indexByteWidth : 0), + iformat))); if(!ibufferUsed) setInactiveRow(node); @@ -1390,10 +1406,24 @@ void D3D12PipelineStateViewer::setState() RDTreeWidgetItem *node = new RDTreeWidgetItem( {tr("Index"), tr("No Buffer Set"), lit("-"), lit("-"), lit("-"), QString()}); - node->setTag(QVariant::fromValue( - D3D12VBIBTag(state.inputAssembly.indexBuffer.resourceId, - state.inputAssembly.indexBuffer.byteOffset + - (draw ? draw->indexOffset * draw->indexByteWidth : 0)))); + QString iformat; + if(draw) + { + if(draw->indexByteWidth == 1) + iformat = lit("ubyte"); + else if(draw->indexByteWidth == 2) + iformat = lit("ushort"); + else if(draw->indexByteWidth == 4) + iformat = lit("uint"); + + iformat += lit(" indices[%1]").arg(RENDERDOC_NumVerticesPerPrimitive(draw->topology)); + } + + node->setTag( + QVariant::fromValue(D3D12VBIBTag(state.inputAssembly.indexBuffer.resourceId, + state.inputAssembly.indexBuffer.byteOffset + + (draw ? draw->indexOffset * draw->indexByteWidth : 0), + iformat))); setEmptyRow(node); m_EmptyNodes.push_back(node); @@ -1449,7 +1479,8 @@ void D3D12PipelineStateViewer::setState() node = new RDTreeWidgetItem({i, tr("No Buffer Set"), lit("-"), lit("-"), lit("-"), QString()}); - node->setTag(QVariant::fromValue(D3D12VBIBTag(v.resourceId, v.byteOffset))); + node->setTag(QVariant::fromValue( + D3D12VBIBTag(v.resourceId, v.byteOffset, m_Common.GetVBufferFormatString(i)))); if(!filledSlot) { @@ -2033,7 +2064,7 @@ void D3D12PipelineStateViewer::on_iaBuffers_itemActivated(RDTreeWidgetItem *item if(buf.id != ResourceId()) { - IBufferViewer *viewer = m_Ctx.ViewBuffer(buf.offset, UINT64_MAX, buf.id); + IBufferViewer *viewer = m_Ctx.ViewBuffer(buf.offset, UINT64_MAX, buf.id, buf.format); m_Ctx.AddDockWindow(viewer->Widget(), DockReference::AddTo, this); } diff --git a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp index 9f87a7e2e..f834ff84b 100644 --- a/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/GLPipelineStateViewer.cpp @@ -36,14 +36,16 @@ struct GLVBIBTag { GLVBIBTag() { offset = 0; } - GLVBIBTag(ResourceId i, uint64_t offs) + GLVBIBTag(ResourceId i, uint64_t offs, QString f = QString()) { id = i; offset = offs; + format = f; } ResourceId id; uint64_t offset; + QString format; }; Q_DECLARE_METATYPE(GLVBIBTag); @@ -1293,8 +1295,22 @@ void GLPipelineStateViewer::setState() draw ? draw->indexByteWidth : 0, 0, 0, (qulonglong)length, QString()}); - node->setTag(QVariant::fromValue(GLVBIBTag( - state.vertexInput.indexBuffer, draw ? draw->indexOffset * draw->indexByteWidth : 0))); + QString iformat; + if(draw) + { + if(draw->indexByteWidth == 1) + iformat = lit("ubyte"); + else if(draw->indexByteWidth == 2) + iformat = lit("ushort"); + else if(draw->indexByteWidth == 4) + iformat = lit("uint"); + + iformat += lit(" indices[%1]").arg(RENDERDOC_NumVerticesPerPrimitive(draw->topology)); + } + + node->setTag(QVariant::fromValue(GLVBIBTag(state.vertexInput.indexBuffer, + draw ? draw->indexOffset * draw->indexByteWidth : 0, + iformat))); if(!ibufferUsed) setInactiveRow(node); @@ -1315,8 +1331,22 @@ void GLPipelineStateViewer::setState() RDTreeWidgetItem *node = new RDTreeWidgetItem( {tr("Element"), tr("No Buffer Set"), lit("-"), lit("-"), lit("-"), lit("-"), QString()}); - node->setTag(QVariant::fromValue(GLVBIBTag( - state.vertexInput.indexBuffer, draw ? draw->indexOffset * draw->indexByteWidth : 0))); + QString iformat; + if(draw) + { + if(draw->indexByteWidth == 1) + iformat = lit("ubyte"); + else if(draw->indexByteWidth == 2) + iformat = lit("ushort"); + else if(draw->indexByteWidth == 4) + iformat = lit("uint"); + + iformat += lit(" indices[%1]").arg(RENDERDOC_NumVerticesPerPrimitive(draw->topology)); + } + + node->setTag(QVariant::fromValue(GLVBIBTag(state.vertexInput.indexBuffer, + draw ? draw->indexOffset * draw->indexByteWidth : 0, + iformat))); setEmptyRow(node); m_EmptyNodes.push_back(node); @@ -1348,7 +1378,8 @@ void GLPipelineStateViewer::setState() new RDTreeWidgetItem({i, v.resourceId, v.byteStride, (qulonglong)offset, v.instanceDivisor, (qulonglong)length, QString()}); - node->setTag(QVariant::fromValue(GLVBIBTag(v.resourceId, v.byteOffset))); + node->setTag(QVariant::fromValue( + GLVBIBTag(v.resourceId, v.byteOffset, m_Common.GetVBufferFormatString(i)))); if(!filledSlot) { @@ -2185,7 +2216,7 @@ void GLPipelineStateViewer::on_viBuffers_itemActivated(RDTreeWidgetItem *item, i if(buf.id != ResourceId()) { - IBufferViewer *viewer = m_Ctx.ViewBuffer(buf.offset, UINT64_MAX, buf.id); + IBufferViewer *viewer = m_Ctx.ViewBuffer(buf.offset, UINT64_MAX, buf.id, buf.format); m_Ctx.AddDockWindow(viewer->Widget(), DockReference::AddTo, this); } diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp index 597eae5be..ef8a93816 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp @@ -912,6 +912,211 @@ void PipelineStateViewer::SetupShaderEditButton(QToolButton *button, ResourceId button->setMenu(menu); } +static uint32_t byteSize(const ResourceFormat &fmt) +{ + if(fmt.Special()) + { + switch(fmt.type) + { + case ResourceFormatType::R9G9B9E5: + case ResourceFormatType::R5G6B5: + case ResourceFormatType::R5G5B5A1: + case ResourceFormatType::R4G4B4A4: + case ResourceFormatType::R4G4: + case ResourceFormatType::BC1: + case ResourceFormatType::BC2: + case ResourceFormatType::BC3: + case ResourceFormatType::BC4: + case ResourceFormatType::BC5: + case ResourceFormatType::BC6: + case ResourceFormatType::BC7: + case ResourceFormatType::ETC2: + case ResourceFormatType::EAC: + case ResourceFormatType::ASTC: + case ResourceFormatType::D16S8: + case ResourceFormatType::D24S8: + case ResourceFormatType::D32S8: + case ResourceFormatType::S8: + case ResourceFormatType::YUV: + case ResourceFormatType::PVRTC: return ~0U; + case ResourceFormatType::R10G10B10A2: + case ResourceFormatType::R11G11B10: return 4; + } + } + + return fmt.compByteWidth * fmt.compCount; +} + +static QString padding(uint32_t bytes) +{ + if(bytes == 0) + return QString(); + + QString ret; + + if(bytes > 4) + { + ret += lit("xint pad[%1];").arg(bytes / 4); + + bytes = bytes % 4; + } + + 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 PipelineStateViewer::GetVBufferFormatString(uint32_t slot) +{ + rdcarray vbs = m_Ctx.CurPipelineState().GetVBuffers(); + rdcarray attrs = m_Ctx.CurPipelineState().GetVertexInputs(); + + if(slot >= vbs.size()) + return tr("// Unbound vertex buffer slot %1").arg(slot); + + uint32_t stride = vbs[slot].byteStride; + + // filter attributes to only the ones enabled and using this slot + for(size_t i = 0; i < attrs.size();) + { + if(!attrs[i].used || attrs[i].vertexBuffer != (int)slot) + { + attrs.erase(i); + // continue with same i + } + else + { + // move to next i + i++; + } + } + + // we now have all attributes in this buffer. Sort by offset + std::sort(attrs.begin(), attrs.end(), + [](const VertexInputAttribute &a, const VertexInputAttribute &b) { + return a.byteOffset < b.byteOffset; + }); + + // ensure we don't have any overlap between attributes or with the stride + for(size_t i = 0; i < attrs.size(); i++) + { + uint32_t cursz = byteSize(attrs[i].format); + if(cursz == 0) + return tr("// Unhandled vertex attribute type '%1'").arg(ToQStr(attrs[i].format.type)); + + // for all but the first attribute, ensure no overlaps with previous. We allow identical + // elements + if(i > 0) + { + uint32_t prevsz = byteSize(attrs[i - 1].format); + + if((attrs[i - 1].byteOffset != attrs[i].byteOffset || cursz != prevsz) && + attrs[i - 1].byteOffset + prevsz > attrs[i].byteOffset) + return tr("// vertex attributes overlapping, no automatic format available"); + } + + if(i + 1 == attrs.size()) + { + // for the last attribute, ensure the total size doesn't overlap stride + if(attrs[i].byteOffset + cursz > stride && stride > 0) + return tr("// vertex stride %1 less than total data fetched %1") + .arg(stride) + .arg(attrs[i].byteOffset + cursz); + } + } + + QString format; + + uint32_t offset = 0; + + for(size_t i = 0; i < attrs.size(); i++) + { + // we disallowed overlaps above, but we do allow *duplicates*. So if our offset has already + // passed, silently skip this element. + if(attrs[i].byteOffset < offset) + continue; + + // declare any padding from previous element to this one + format += padding(attrs[i].byteOffset - offset); + + const ResourceFormat &fmt = attrs[i].format; + + offset += byteSize(fmt); + + if(fmt.Special()) + { + switch(fmt.type) + { + case ResourceFormatType::R10G10B10A2: + if(fmt.compType == CompType::UInt) + format += lit("uintten"); + if(fmt.compType == CompType::UNorm) + format += lit("unormten"); + break; + case ResourceFormatType::R11G11B10: format += lit("floateleven"); break; + default: format += tr("// unknown type "); break; + } + } + else + { + QChar widthchar[] = { + QLatin1Char('?'), QLatin1Char('b'), QLatin1Char('h'), QLatin1Char('?'), QLatin1Char('f'), + }; + + if(fmt.compType == CompType::UNorm) + { + format += lit("unorm%1").arg(widthchar[fmt.compByteWidth]); + } + else if(fmt.compType == CompType::SNorm) + { + format += lit("snorm%1").arg(widthchar[fmt.compByteWidth]); + } + else + { + if(fmt.compType == CompType::UInt) + format += lit("u"); + + if(fmt.compByteWidth == 1) + { + format += lit("byte"); + } + else if(fmt.compByteWidth == 2) + { + if(fmt.compType == CompType::Float) + format += lit("half"); + else + format += lit("short"); + } + else if(fmt.compByteWidth == 4) + { + if(fmt.compType == CompType::Float) + format += lit("float"); + else + format += lit("int"); + } + else if(fmt.compByteWidth == 8) + { + format += lit("double"); + } + } + + format += QString::number(fmt.compCount); + } + + format += QFormatStr(" %1;\n").arg(QString(attrs[i].name)); + } + + if(stride > 0) + format += padding(stride - offset); + + return format; +} + bool PipelineStateViewer::SaveShaderFile(const ShaderReflection *shader) { if(!shader) diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h index 852e463a6..df86be267 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.h @@ -70,6 +70,8 @@ public: void SetupShaderEditButton(QToolButton *button, ResourceId pipelineId, ResourceId shaderId, const ShaderReflection *shaderDetails); + QString GetVBufferFormatString(uint32_t slot); + void setTopologyDiagram(QLabel *diagram, Topology topo); void setMeshViewPixmap(RDLabel *meshView); diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp index 49f8e12fe..973593ec4 100644 --- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp @@ -38,14 +38,16 @@ Q_DECLARE_METATYPE(SamplerData); struct VulkanVBIBTag { VulkanVBIBTag() { offset = 0; } - VulkanVBIBTag(ResourceId i, uint64_t offs) + VulkanVBIBTag(ResourceId i, uint64_t offs, QString f = QString()) { id = i; offset = offs; + format = f; } ResourceId id; uint64_t offset; + QString format; }; Q_DECLARE_METATYPE(VulkanVBIBTag); @@ -1615,10 +1617,24 @@ void VulkanPipelineStateViewer::setState() (qulonglong)state.inputAssembly.indexBuffer.byteOffset, draw != NULL ? draw->indexByteWidth : 0, (qulonglong)length, QString()}); + QString iformat; + if(draw) + { + if(draw->indexByteWidth == 1) + iformat = lit("ubyte"); + else if(draw->indexByteWidth == 2) + iformat = lit("ushort"); + else if(draw->indexByteWidth == 4) + iformat = lit("uint"); + + iformat += lit(" indices[%1]").arg(RENDERDOC_NumVerticesPerPrimitive(draw->topology)); + } + node->setTag(QVariant::fromValue( VulkanVBIBTag(state.inputAssembly.indexBuffer.resourceId, state.inputAssembly.indexBuffer.byteOffset + - (draw ? draw->indexOffset * draw->indexByteWidth : 0)))); + (draw ? draw->indexOffset * draw->indexByteWidth : 0), + iformat))); if(!ibufferUsed) setInactiveRow(node); @@ -1639,9 +1655,24 @@ void VulkanPipelineStateViewer::setState() RDTreeWidgetItem *node = new RDTreeWidgetItem({tr("Index"), ResourceId(), tr("Index"), lit("-"), lit("-"), lit("-"), lit("-"), QString()}); + QString iformat; + if(draw) + { + if(draw->indexByteWidth == 1) + iformat = lit("ubyte"); + else if(draw->indexByteWidth == 2) + iformat = lit("ushort"); + else if(draw->indexByteWidth == 4) + iformat = lit("uint"); + + iformat += lit(" indices[%1]").arg(RENDERDOC_NumVerticesPerPrimitive(draw->topology)); + } + node->setTag(QVariant::fromValue( VulkanVBIBTag(state.inputAssembly.indexBuffer.resourceId, - draw != NULL ? draw->indexOffset * draw->indexByteWidth : 0))); + state.inputAssembly.indexBuffer.byteOffset + + (draw ? draw->indexOffset * draw->indexByteWidth : 0), + iformat))); setEmptyRow(node); m_EmptyNodes.push_back(node); @@ -1709,7 +1740,8 @@ void VulkanPipelineStateViewer::setState() {i, tr("No Binding"), lit("-"), lit("-"), lit("-"), lit("-"), lit("-"), QString()}); node->setTag(QVariant::fromValue(VulkanVBIBTag( - vbuff != NULL ? vbuff->resourceId : ResourceId(), vbuff != NULL ? vbuff->byteOffset : 0))); + vbuff != NULL ? vbuff->resourceId : ResourceId(), vbuff != NULL ? vbuff->byteOffset : 0, + m_Common.GetVBufferFormatString(i)))); if(!filledSlot || bind == NULL || vbuff == NULL) { @@ -2280,7 +2312,7 @@ void VulkanPipelineStateViewer::on_viBuffers_itemActivated(RDTreeWidgetItem *ite if(buf.id != ResourceId()) { - IBufferViewer *viewer = m_Ctx.ViewBuffer(buf.offset, UINT64_MAX, buf.id); + IBufferViewer *viewer = m_Ctx.ViewBuffer(buf.offset, UINT64_MAX, buf.id, buf.format); m_Ctx.AddDockWindow(viewer->Widget(), DockReference::AddTo, this); }