From 4bb54d00dd37ca7e93031744347ac1c069e1d6b6 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 11 May 2022 13:48:37 +0100 Subject: [PATCH] Show visible offsets and (optionally) padding in buffers. Closes #1664 --- qrenderdoc/Code/BufferFormatter.cpp | 89 ++-- qrenderdoc/Code/QRDUtils.h | 17 +- qrenderdoc/Code/Resources.h | 1 + qrenderdoc/Resources/align.png | Bin 0 -> 1488 bytes qrenderdoc/Resources/align@2x.png | Bin 0 -> 1809 bytes qrenderdoc/Resources/resources.qrc | 2 + qrenderdoc/Windows/BufferViewer.cpp | 443 +++++++++++++++++--- qrenderdoc/Windows/BufferViewer.h | 8 +- qrenderdoc/Windows/BufferViewer.ui | 23 + qrenderdoc/qrenderdoc_local.vcxproj | 2 + qrenderdoc/qrenderdoc_local.vcxproj.filters | 18 +- 11 files changed, 494 insertions(+), 109 deletions(-) create mode 100644 qrenderdoc/Resources/align.png create mode 100644 qrenderdoc/Resources/align@2x.png diff --git a/qrenderdoc/Code/BufferFormatter.cpp b/qrenderdoc/Code/BufferFormatter.cpp index 9247995a6..eb7842ff5 100644 --- a/qrenderdoc/Code/BufferFormatter.cpp +++ b/qrenderdoc/Code/BufferFormatter.cpp @@ -412,8 +412,8 @@ bool BufferFormatter::CheckInvalidUnbounded(const ShaderConstant &structDef, QSt return true; } -rdcpair BufferFormatter::ParseFormatString( - const QString &formatString, uint64_t maxLen, bool cbuffer, QString &errors) +ParsedFormat BufferFormatter::ParseFormatString(const QString &formatString, uint64_t maxLen, + bool cbuffer, QString &errors) { StructFormatData root; StructFormatData *cur = &root; @@ -1615,21 +1615,7 @@ rdcpair BufferFormatter::ParseFormatString( // if we're bitfield packing don't advance offset, otherwise advance to the end of this element if(bitfieldCurPos == ~0U) - { - // advance by the struct including any trailing padding - cur->offset += GetVarSize(el); - - // if we allow trailing overlap in arrays/matrices, remove the padding. This is only possible - // with non-tight arrays - if(pack.trailing_overlap && !pack.tight_arrays && - (el.type.descriptor.type == VarType::Struct || el.type.descriptor.elements > 1 || - el.type.descriptor.rows > 1)) - { - // the padding is the stride (which is rounded up to 16 for non-tight arrays) minus the size - // of the last vector (whether or not this is an array of scalars, vectors or matrices - cur->offset -= 16 - elSize; - } - } + cur->offset += GetVarAdvance(pack, el); } if(bitfieldCurPos != ~0U) @@ -1834,7 +1820,7 @@ rdcpair BufferFormatter::ParseFormatString( } } - return {root.structDef, repeat}; + return {root.structDef, repeat, pack}; } QString BufferFormatter::GetTextureFormatString(const TextureDescription &tex) @@ -2097,7 +2083,7 @@ uint32_t BufferFormatter::GetVarStraddleSize(const ShaderConstant &var) return VarTypeByteSize(var.type.descriptor.type) * var.type.descriptor.columns; } -uint32_t BufferFormatter::GetVarSize(const ShaderConstant &var) +uint32_t BufferFormatter::GetVarSizeAndTrail(const ShaderConstant &var) { if(var.type.descriptor.elements > 1 && var.type.descriptor.elements != ~0U) return var.type.descriptor.arrayByteStride * var.type.descriptor.elements; @@ -2119,6 +2105,37 @@ uint32_t BufferFormatter::GetVarSize(const ShaderConstant &var) return VarTypeByteSize(var.type.descriptor.type) * var.type.descriptor.columns; } +uint32_t BufferFormatter::GetVarAdvance(Packing::Rules pack, const ShaderConstant &var) +{ + uint32_t ret = GetVarSizeAndTrail(var); + + // if we allow trailing overlap, remove the padding at the end of the struct/array + if(pack.trailing_overlap) + { + if(var.type.descriptor.type == VarType::Struct) + { + ret -= (var.type.descriptor.arrayByteStride - GetUnpaddedStructAdvance(pack, var.type.members)); + } + else if((var.type.descriptor.elements > 1 || var.type.descriptor.rows > 1) && !pack.tight_arrays) + { + uint8_t vecSize = var.type.descriptor.columns; + + if(var.type.descriptor.rows > 1 && var.type.descriptor.ColMajor()) + vecSize = var.type.descriptor.rows; + + uint32_t elSize = GetAlignment(pack, var); + if(pack.vector_align_component) + elSize *= vecSize; + + // the padding is the stride (which is rounded up to 16 for non-tight arrays) minus the size + // of the last vector (whether or not this is an array of scalars, vectors or matrices + ret -= 16 - elSize; + } + } + + return ret; +} + uint32_t BufferFormatter::GetAlignment(Packing::Rules pack, const ShaderConstant &c) { uint32_t ret = 1; @@ -2162,7 +2179,8 @@ uint32_t BufferFormatter::GetAlignment(Packing::Rules pack, const ShaderConstant return ret; } -uint32_t BufferFormatter::GetUnpaddedStructSize(const rdcarray &members) +uint32_t BufferFormatter::GetUnpaddedStructAdvance(Packing::Rules pack, + const rdcarray &members) { uint32_t lastMemberStart = 0; @@ -2181,7 +2199,7 @@ uint32_t BufferFormatter::GetUnpaddedStructSize(const rdcarray & lastMemberStart += lastChild->byteOffset; } - return lastMemberStart + GetVarSize(*lastChild); + return lastMemberStart + GetVarAdvance(pack, *lastChild); } QString BufferFormatter::DeclareStruct(Packing::Rules pack, QList &declaredStructs, @@ -2204,7 +2222,6 @@ QString BufferFormatter::DeclareStruct(Packing::Rules pack, QList &decl { const uint32_t alignment = GetAlignment(pack, members[i]); const uint32_t vecsize = GetVarStraddleSize(members[i]); - const uint32_t size = GetVarSize(members[i]); structAlignment = std::max(structAlignment, alignment); offset = AlignUp(offset, alignment); @@ -2233,33 +2250,7 @@ QString BufferFormatter::DeclareStruct(Packing::Rules pack, QList &decl offset = members[i].byteOffset; - offset += size; - - // if we allow trailing overlap, remove the padding at the end of the struct/array - if(pack.trailing_overlap) - { - if(members[i].type.descriptor.type == VarType::Struct) - { - offset -= (members[i].type.descriptor.arrayByteStride - - GetUnpaddedStructSize(members[i].type.members)); - } - else if((members[i].type.descriptor.elements > 1 || members[i].type.descriptor.rows > 1) && - !pack.tight_arrays) - { - uint8_t vecSize = members[i].type.descriptor.columns; - - if(members[i].type.descriptor.rows > 1 && members[i].type.descriptor.ColMajor()) - vecSize = members[i].type.descriptor.rows; - - uint32_t elSize = GetAlignment(pack, members[i]); - if(pack.vector_align_component) - elSize *= vecSize; - - // the padding is the stride (which is rounded up to 16 for non-tight arrays) minus the size - // of the last vector (whether or not this is an array of scalars, vectors or matrices - offset -= 16 - elSize; - } - } + offset += GetVarAdvance(pack, members[i]); QString arraySize; if(members[i].type.descriptor.elements > 1) diff --git a/qrenderdoc/Code/QRDUtils.h b/qrenderdoc/Code/QRDUtils.h index 3e99b04aa..ba8604b01 100644 --- a/qrenderdoc/Code/QRDUtils.h +++ b/qrenderdoc/Code/QRDUtils.h @@ -175,6 +175,12 @@ struct Rules }; // namespace Packing +struct ParsedFormat +{ + ShaderConstant fixed, repeating; + Packing::Rules packing; +}; + struct BufferFormatter { private: @@ -190,8 +196,10 @@ private: uint32_t requiredByteStride, QString innerSkippedPrefixString); static uint32_t GetAlignment(Packing::Rules pack, const ShaderConstant &constant); - static uint32_t GetUnpaddedStructSize(const rdcarray &members); + static uint32_t GetUnpaddedStructAdvance(Packing::Rules pack, + const rdcarray &members); static uint32_t GetVarStraddleSize(const ShaderConstant &var); + static uint32_t GetVarSizeAndTrail(const ShaderConstant &var); static void EstimatePackingRules(Packing::Rules &pack, const ShaderConstant &constant); static QString DeclarePacking(Packing::Rules pack); @@ -200,10 +208,9 @@ public: BufferFormatter() = default; static void Init(GraphicsAPI api) { m_API = api; } - static rdcpair ParseFormatString(const QString &formatString, - uint64_t maxLen, bool cbuffer, - QString &errors); - static uint32_t GetVarSize(const ShaderConstant &var); + static ParsedFormat ParseFormatString(const QString &formatString, uint64_t maxLen, bool cbuffer, + QString &errors); + static uint32_t GetVarAdvance(Packing::Rules pack, const ShaderConstant &var); static Packing::Rules EstimatePackingRules(const rdcarray &members); diff --git a/qrenderdoc/Code/Resources.h b/qrenderdoc/Code/Resources.h index 3b6f3b493..e25eabb88 100644 --- a/qrenderdoc/Code/Resources.h +++ b/qrenderdoc/Code/Resources.h @@ -30,6 +30,7 @@ #define RESOURCE_LIST() \ RESOURCE_DEF(add, "add.png") \ + RESOURCE_DEF(align, "align.png") \ RESOURCE_DEF(arrow_in, "arrow_in.png") \ RESOURCE_DEF(arrow_join, "arrow_join.png") \ RESOURCE_DEF(arrow_left, "arrow_left.png") \ diff --git a/qrenderdoc/Resources/align.png b/qrenderdoc/Resources/align.png new file mode 100644 index 0000000000000000000000000000000000000000..5377a845aa5d580c077bc4610f4409cd7cf423d2 GIT binary patch literal 1488 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxKsVXI%uvD1M9IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr4|esh*)icxGNo zet9uiy|1s8XI^nhVqS8pr;Du;&;-5A%oHn2OEV{PV`FDS14Cm&Lsw%96LU95Lt_J1 zS7%FOHw#0UUYGpj(%jU%5}4i;gkBe%dO=Acw*Y9fOKMSOS!#+~QGTuh*vnR#xZUE2 z(>$o&6x?oc!l_pu=oo!a#3DsBObD2IKumbD1#;jCKQ#}S+KYh6x;XR8KL!RS7f%<* zkcwMVf_*(g0!99v+gmr^^#HeMWak{*WFD~ zTRO}nJ$kOGo#H52K4IJBf~!u8W?h_h|NA@JxjT)scotu1Jihz=oZq$2Yo5<>4|j9> z@#}?#X!p^R23>4(iy4gf2s&!AE-rPu_xvd1>ZBDj{Jzxg>aYKM&YM-5RlurjtEbQ5zy}9wy{EI>;al$0 z+rZ+X*0=T0uiu^9<1d zY`WS0JA75B#j4bA6^iB)SIn^c%$Tjtf1g|Xp@H@7DT+L6HMCE!eA5^pwWPUcqYua2 zrrtLN*UxGytjh6Cv|cSVL#(F0L@`%N=|pYxm-lluA|9Qrb>G9axKr>l&&$I-bv75W zV>q-oN&J!*YFcfcd-%Jv+UM0v>}EPx#>{zr^hx(}^RkRbWz&oeH@yf*m645)=w#>0 z{n((JdFt<(Yu}i;Qu`7s-!nMnxlW80*m!fz2@T6*OEzl6rfHPrcr)pVE;zdSWZ~82iIxi{2(M~b5_Npx z)lAQjn>m};Y&x`YJ^zO%KUZH_c{}*?)dP3_maGbV^V0ggK){oOPEq0tEN}iZ_s-kB znS1kN+m*t}!k<@eoX-9B&pN(Pm&p00i_ I>zopr0D-VH{r~^~ literal 0 HcmV?d00001 diff --git a/qrenderdoc/Resources/align@2x.png b/qrenderdoc/Resources/align@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..405597bd2b31c2eb2c999d345602d5975cc0b667 GIT binary patch literal 1809 zcmaJ?c~sMO91jINst68LWE`P*F=>;u9JW%CHmww`L+AuAq_zP{X;Yg(LuGRxPNOgnCmA+r6_{<4scOim88S; z?>KI88R21bV$8^u6pQPXr~NO2Sg_WSq}DJ z2R*PwyT>vev@N|DG(M)M-EO39?J==(5`9}TWoWR1y5A7_V6GzADTwz*ye#|Jo#?h! zPji-eTh`jECGLrsmOY~8;-2nwFr(y$ag&%ES)y+1(BCId`Xy{_g{?Rf%z7dD`pOpn z2<5W-M^+u=5znGck&zD`Xop|B*0y^lu{!Q)(XT?WvugH;n@vskw_aam4upETqv3Rb zCqJG1ndee(+ZdnJDb9g8X}qxgWydP>bZ&b`)(m4v*cm0g_Y|cULTyVLlX-87E}v+R z$je__F(bF#HD6e7`nmU#5x6IMxv8o(uag1XD7O|Jw+VHqy^(GmGw!I>b}w5i1(^P~ zXQq|nxz_x=&B2ocY{_j5r;IvEu3e9& z%q$tP*>S@C>(LjEC)Yl1beun{I#s_2;4TahevY^AW3H%yf1GCYW+YyXI=!-dwo6UN zrq<=Qga8;lWr}tOQ`;GB&J=ss9sd3Rm9IS{K#OhMo+^k>ojxg8vnOtA2hnhMoY?p)DyxRi2yop2`F}Z=875QkOet<`bqpBHrK31(;)j zEAd5WlmM=ykb#C;X7Vr6IY&%H96d zGu6u5f7u-#9-=otuibrQTfnZ(=^L)gv+HW?*dfouJ4;vHr0#lq1iJRvMY=R>k(#UQ zGSjg9c&DE21&<2Gq{*KA$ppB0&z6ZBd(KFy?nm~NkQvz10GO5(-T;Te?+oW$%BvPo z@QfG5-bY;_6m4Gp+fApkLwk4D&PyHDJ#~E7B@Uxpq)hN!VB~*0I$c)rs;$y-eS4yd zyYQQKV0F#t5~^#WvxoSL(D4(-wu)(AzP$#cfbN;?ah zrWU50Xr+lem@`t)W9uxHhUSe+eXuiNV7 zC%DcF$^aM-GDT!HxII2-pRj*yVIsBTq+4yYzPCl{kZU(PAKi9s-x6W#Q&-c5jYLZ^ ZU=46)2ZWy57i?qsNy#Mh(JE0)#y>Idv3URh literal 0 HcmV?d00001 diff --git a/qrenderdoc/Resources/resources.qrc b/qrenderdoc/Resources/resources.qrc index 05048cef5..e95f7dd93 100644 --- a/qrenderdoc/Resources/resources.qrc +++ b/qrenderdoc/Resources/resources.qrc @@ -17,6 +17,8 @@ action_hover@2x.png add.png add@2x.png + align.png + align@2x.png arrow_in.png arrow_in@2x.png arrow_join.png diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index 5029c15f3..e504fe98b 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include "Code/QRDUtils.h" #include "Code/Resources.h" @@ -43,6 +44,28 @@ #include "Windows/Dialogs/AxisMappingDialog.h" #include "ui_BufferViewer.h" +struct FixedVarTag +{ + FixedVarTag() = default; + FixedVarTag(uint32_t size) : valid(true), padding(true), byteSize(size) {} + FixedVarTag(rdcstr varName, uint32_t offset) + : valid(true), padding(false), name(varName), byteOffset(offset) + { + } + bool valid = false; + bool padding = false; + bool matrix = false; + bool rowmajor = false; + rdcstr name; + union + { + uint32_t byteOffset; + uint32_t byteSize; + }; +}; + +Q_DECLARE_METATYPE(FixedVarTag); + static const uint32_t MaxVisibleRows = 10000; namespace NativeScanCode @@ -470,6 +493,7 @@ struct BufferConfiguration uint32_t numRows = 0, unclampedNumRows = 0; uint32_t pagingOffset = 0; + Packing::Rules packing; ShaderConstant fixedVars; rdcarray evalVars; uint32_t repeatStride = 1; @@ -508,6 +532,7 @@ struct BufferConfiguration unclampedNumRows = o.unclampedNumRows; pagingOffset = o.pagingOffset; + packing = o.packing; fixedVars = o.fixedVars; evalVars = o.evalVars; repeatStride = o.repeatStride; @@ -2288,6 +2313,10 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) QObject::connect(ui->render, &CustomPaintWidget::mouseWheel, this, &BufferViewer::render_mouseWheel); + // event filter to pick up tooltip events + ui->fixedVars->setTooltipElidedItems(false); + ui->fixedVars->installEventFilter(this); + Reset(); m_Ctx.AddCaptureViewer(this); @@ -2329,7 +2358,7 @@ void BufferViewer::SetupRawView() processFormat(format); }); - ui->fixedVars->setColumns({tr("Name"), tr("Value"), tr("Type")}); + ui->fixedVars->setColumns({tr("Name"), tr("Value"), tr("Byte Offset"), tr("Type")}); { ui->fixedVars->header()->setSectionResizeMode(0, QHeaderView::Interactive); ui->fixedVars->header()->setSectionResizeMode(1, QHeaderView::Interactive); @@ -2412,6 +2441,7 @@ void BufferViewer::SetupMeshView() byteRangeLength->setVisible(false); ui->fixedVars->setVisible(false); + ui->showPadding->setVisible(false); ui->resourceDetails->setVisible(false); ui->formatSpecifier->setVisible(false); @@ -2564,25 +2594,33 @@ void BufferViewer::fixedVars_contextMenu(const QPoint &pos) QMenu contextMenu(this); QAction expandAll(tr("&Expand All"), this); - QAction collapseAll(tr("&Collapse All"), this); + QAction collapseAll(tr("C&ollapse All"), this); QAction copy(tr("&Copy"), this); + QAction showPadding(tr("&Show Padding"), this); expandAll.setIcon(Icons::arrow_out()); collapseAll.setIcon(Icons::arrow_in()); copy.setIcon(Icons::copy()); + showPadding.setIcon(Icons::align()); + showPadding.setCheckable(true); + showPadding.setChecked(ui->showPadding->isChecked()); expandAll.setEnabled(item && item->childCount() > 0); collapseAll.setEnabled(expandAll.isEnabled()); - contextMenu.addAction(m_ExportCSV); - contextMenu.addAction(m_ExportBytes); - - contextMenu.addSeparator(); - contextMenu.addAction(&expandAll); contextMenu.addAction(&collapseAll); contextMenu.addAction(©); + contextMenu.addSeparator(); + + contextMenu.addAction(&showPadding); + + contextMenu.addSeparator(); + + contextMenu.addAction(m_ExportCSV); + contextMenu.addAction(m_ExportBytes); + QObject::connect(&expandAll, &QAction::triggered, [this, item]() { ui->fixedVars->expandAllItems(item); }); @@ -2590,6 +2628,8 @@ void BufferViewer::fixedVars_contextMenu(const QPoint &pos) [this, item]() { ui->fixedVars->collapseAllItems(item); }); QObject::connect(©, &QAction::triggered, [this, item, pos]() { ui->fixedVars->copyItem(pos, item); }); + QObject::connect(&showPadding, &QAction::triggered, + [this]() { ui->showPadding->setChecked(!ui->showPadding->isChecked()); }); RDDialog::show(&contextMenu, ui->fixedVars->viewport()->mapToGlobal(pos)); } @@ -2855,16 +2895,36 @@ void BufferViewer::OnEventChanged(uint32_t eventId) } QString errors; - ShaderConstant repeating; - rdctie(bufdata->vsinConfig.fixedVars, repeating) = + ParsedFormat parsed = BufferFormatter::ParseFormatString(m_Format, m_ByteSize, IsCBufferView(), errors); - if(repeating.type.descriptor.type != VarType::Unknown) - { - bufdata->vsinConfig.repeatStride = repeating.type.descriptor.arrayByteStride; - bufdata->vsinConfig.repeatOffset = repeating.byteOffset; + bufdata->vsinConfig.fixedVars = parsed.fixed; + bufdata->vsinConfig.packing = parsed.packing; - UnrollConstant(repeating, bufdata->vsinConfig.columns, bufdata->vsinConfig.props); + if(parsed.repeating.type.descriptor.type != VarType::Unknown) + { + bufdata->vsinConfig.repeatStride = parsed.repeating.type.descriptor.arrayByteStride; + bufdata->vsinConfig.repeatOffset = parsed.repeating.byteOffset; + + UnrollConstant(parsed.repeating, bufdata->vsinConfig.columns, bufdata->vsinConfig.props); + } + + if((m_Format.isEmpty() || !bufdata->cb.bufferBacked) && IsCBufferView()) + { + if(bufdata->cb.valid) + { + const ShaderReflection *reflection = + m_Ctx.CurPipelineState().GetShaderReflection(m_CBufferSlot.stage); + + bufdata->vsinConfig.fixedVars.type.members = + reflection->constantBlocks[m_CBufferSlot.slot].variables; + + if(IsD3D(m_Ctx.APIProps().pipelineType)) + bufdata->vsinConfig.packing = Packing::D3DCB; + else + bufdata->vsinConfig.packing = + BufferFormatter::EstimatePackingRules(bufdata->vsinConfig.fixedVars.type.members); + } } ClearModels(); @@ -3132,7 +3192,13 @@ void BufferViewer::OnEventChanged(uint32_t eventId) ui->fixedVars->clear(); if(!vars.isEmpty()) - UI_AddFixedVariables(ui->fixedVars->invisibleRootItem(), vars); + { + UI_AddFixedVariables(ui->fixedVars->invisibleRootItem(), 0, + bufdata->vsinConfig.fixedVars.type.members, vars); + + if(!bufdata->cb.bufferBacked) + UI_RemoveOffsets(ui->fixedVars->invisibleRootItem()); + } ui->fixedVars->endUpdate(); @@ -3286,23 +3352,168 @@ void BufferViewer::setPersistData(const QVariant &persistData) } } -void BufferViewer::UI_AddFixedVariables(RDTreeWidgetItem *root, const rdcarray &vars) +void BufferViewer::UI_FixedAddMatrixRows(RDTreeWidgetItem *n, const ShaderConstant &c, + const ShaderVariable &v) { - for(const ShaderVariable &v : vars) + const bool showPadding = ui->showPadding->isChecked() && m_CurCBuffer.bufferBacked; + + if(v.rows > 1) { - RDTreeWidgetItem *n = new RDTreeWidgetItem({v.name, VarString(v), TypeString(v)}); + uint32_t vecSize = VarTypeByteSize(v.type) * v.columns; + + FixedVarTag tag = n->tag().value(); + tag.matrix = true; + tag.rowmajor = v.RowMajor(); + n->setTag(QVariant::fromValue(tag)); + + if(v.ColMajor()) + vecSize = VarTypeByteSize(v.type) * v.rows; + + for(uint32_t r = 0; r < v.rows; r++) + { + n->addChild(new RDTreeWidgetItem({QFormatStr("%1.row%2").arg(v.name).arg(r), RowString(v, r), + QString(), RowTypeString(v)})); + + if(showPadding && v.RowMajor() && c.type.descriptor.matrixByteStride > vecSize) + { + uint32_t size = c.type.descriptor.matrixByteStride - vecSize; + + RDTreeWidgetItem *pad = new RDTreeWidgetItem( + {tr(""), QFormatStr("%1 bytes").arg(size), QString(), tr("Padding")}); + + pad->setItalic(true); + pad->setTag(QVariant::fromValue(FixedVarTag(size))); + + n->addChild(pad); + } + } + + if(showPadding && v.ColMajor() && c.type.descriptor.matrixByteStride > vecSize) + { + uint32_t size = c.type.descriptor.matrixByteStride - vecSize; + + RDTreeWidgetItem *pad = new RDTreeWidgetItem( + {tr(""), QFormatStr("%1 bytes each column").arg(size), QString(), tr("Padding")}); + + pad->setItalic(true); + pad->setTag(QVariant::fromValue(FixedVarTag(size))); + + n->addChild(pad); + } + } +} + +void BufferViewer::UI_AddFixedVariables(RDTreeWidgetItem *root, uint32_t baseOffset, + const rdcarray &consts, + const rdcarray &vars) +{ + const bool showPadding = ui->showPadding->isChecked() && m_CurCBuffer.bufferBacked; + + if(consts.size() != vars.size()) + qCritical() << "Shader variable mismatch"; + + uint32_t offset = 0; + + for(size_t idx = 0; idx < consts.size() && idx < vars.size(); idx++) + { + const ShaderConstant &c = consts[idx]; + const ShaderVariable &v = vars[idx]; + + if(showPadding && c.byteOffset > offset) + { + uint32_t size = c.byteOffset - offset; + + RDTreeWidgetItem *pad = new RDTreeWidgetItem( + {QString(), QFormatStr("%1 bytes").arg(size), QString(), tr("Padding")}); + + pad->setItalic(true); + pad->setTag(QVariant::fromValue(FixedVarTag(size))); + + root->addChild(pad); + + offset = c.byteOffset; + } + + RDTreeWidgetItem *n = + new RDTreeWidgetItem({v.name, VarString(v), baseOffset + c.byteOffset, TypeString(v)}); + + n->setTag(QVariant::fromValue(FixedVarTag(v.name, baseOffset + c.byteOffset))); root->addChild(n); - if(v.rows > 1) + UI_FixedAddMatrixRows(n, c, v); + + // if it's an array the value (v) will be expanded with one element in each of v.members, but + // the constant (c) will just have the type with a number of elements + if(c.type.descriptor.elements > 1) { - for(uint32_t i = 0; i < v.rows; i++) - n->addChild(new RDTreeWidgetItem( - {QFormatStr("%1.row%2").arg(v.name).arg(i), RowString(v, i), RowTypeString(v)})); + ShaderConstant noarray = c; + noarray.type.descriptor.elements = 1; + + // calculate the tight scalar-packed advance, so we can detect padding + uint32_t elSize = BufferFormatter::GetVarAdvance(Packing::Scalar, noarray); + + for(uint32_t e = 0; e < v.members.size(); e++) + { + const uint32_t elOffset = baseOffset + c.byteOffset + c.type.descriptor.arrayByteStride * e; + + RDTreeWidgetItem *el = new RDTreeWidgetItem( + {v.members[e].name, VarString(v.members[e]), elOffset, TypeString(v.members[e])}); + + el->setTag(QVariant::fromValue(FixedVarTag(v.members[e].name, elOffset))); + + // if it's an array of structs we can recurse, just need to do the outer iteration here + // because v.members[...].members will be the actual struct members because of the expansion + if(c.type.descriptor.type == VarType::Struct) + { + UI_AddFixedVariables(el, elOffset, c.type.members, v.members[e].members); + } + else + { + // otherwise just expand by hand since there will be no more members in c.type.members for + // us to recurse with + UI_FixedAddMatrixRows(el, c, v.members[e]); + } + + n->addChild(el); + + // don't count the padding in the last struct in an array of structs, it will be handled as + // padding after the array + if(c.type.descriptor.type == VarType::Struct && e + 1 == v.members.size()) + break; + + if(showPadding && c.type.descriptor.arrayByteStride > elSize) + { + uint32_t size = c.type.descriptor.arrayByteStride - elSize; + + RDTreeWidgetItem *pad = new RDTreeWidgetItem( + {QString(), QFormatStr("%1 bytes").arg(size), QString(), tr("Padding")}); + + pad->setItalic(true); + pad->setTag(QVariant::fromValue(FixedVarTag(size))); + + n->addChild(pad); + } + } + } + // for single structs, recurse + else if(v.type == VarType::Struct) + { + UI_AddFixedVariables(n, c.byteOffset, c.type.members, v.members); } - if(!v.members.isEmpty()) - UI_AddFixedVariables(n, v.members); + // advance by the tight scalar-packed advance, so we can detect padding + offset += BufferFormatter::GetVarAdvance(Packing::Scalar, c); + } +} + +void BufferViewer::UI_RemoveOffsets(RDTreeWidgetItem *root) +{ + for(int i = 0; i < root->childCount(); i++) + { + RDTreeWidgetItem *item = root->child(i); + item->setText(2, QVariant()); + UI_RemoveOffsets(item); } } @@ -4055,19 +4266,143 @@ void BufferViewer::ScrollToColumn(int32_t column, MeshDataStage stage) bool BufferViewer::eventFilter(QObject *watched, QEvent *event) { - if(!m_MeshView && watched == ui->vsinData->viewport() && event->type() == QEvent::MouseMove) + if(event->type() == QEvent::ToolTip) { - bool ret = QObject::eventFilter(watched, event); + RDTreeWidget *tree = qobject_cast(watched); + if(tree) + { + RDTreeWidgetItem *item = tree->itemAt(tree->viewport()->mapFromGlobal(QCursor::pos())); + if(item) + { + FixedVarTag tag = item->tag().value(); - QMouseEvent *mouseEvent = (QMouseEvent *)event; + QString tooltip; - if(m_delegate->linkHover(mouseEvent, font(), - ui->vsinData->indexAt(mouseEvent->localPos().toPoint()))) - ui->vsinData->setCursor(QCursor(Qt::PointingHandCursor)); - else - ui->vsinData->unsetCursor(); + Packing::Rules pack = m_ModelVSIn->getConfig().packing; - return ret; + if(tag.valid && tag.padding) + { + tooltip = tr("%1 bytes of padding. Packing rules in effect:\n\n").arg(tag.byteSize); + + if(pack == Packing::D3DCB) + tooltip += tr("Standard D3D constant buffer packing.\n\n"); + else if(pack == Packing::std140) + tooltip += tr("Standard std140 buffer packing.\n\n"); + else if(pack == Packing::std430) + tooltip += tr("Standard std430 buffer packing.\n\n"); + else if(pack == Packing::C) + tooltip += tr("Standard C / D3D UAV packing.\n\n"); + else if(pack == Packing::Scalar) + tooltip += tr("Scalar packing.\n\n"); + + if(pack.vector_align_component) + tooltip += + tr("- Vectors are only aligned to their component (float4 to 4-byte boundary)\n"); + else + tooltip += + tr("- 3- and 4-wide vectors must be aligned to a 4-wide boundary\n" + " (vec3 and vec4 to 16-byte boundary)\n"); + + if(pack.tight_arrays) + tooltip += tr("- Arrays are tightly packed to each element\n"); + else + tooltip += tr("- Arrays have a stride of a 16 bytes\n"); + + if(pack.trailing_overlap) + tooltip += tr("- Variables can overlap the trailing padding in arrays or structs.\n"); + else + tooltip += + tr("- Variables must not overlap the trailing padding in arrays or structs.\n"); + + if(pack.vector_straddle_16b) + tooltip += tr("- Vectors can straddle 16-byte boundaries.\n"); + else + tooltip += tr("- Vectors must not straddle 16-byte boundaries.\n"); + } + else if(tag.valid && !tag.padding) + { + tooltip = tr("Variable %1 is at byte offset %2").arg(tag.name).arg(tag.byteOffset); + + if(!IsCBufferView()) + tooltip += tr(", not including overall base byte offset %1 in buffer").arg(m_ByteOffset); + + tooltip += lit("."); + + if(tag.matrix) + { + tooltip += tr("\n\nMatrix stored "); + if(tag.rowmajor) + tooltip += tr("row-major."); + else + tooltip += tr("column-major."); + } + } + + if(!tooltip.isEmpty()) + { + QPoint pos = QCursor::pos(); + pos.setX(pos.x() + 10); + pos.setY(pos.y() + 10); + QToolTip::showText(pos, tooltip.trimmed()); + + return true; + } + } + } + else if(!m_MeshView && watched == ui->vsinData->viewport()) + { + QModelIndex index = + ui->vsinData->indexAt(ui->vsinData->viewport()->mapFromGlobal(QCursor::pos())); + + if(index.isValid()) + { + const ShaderConstant &c = m_ModelVSIn->elementForColumn(index.column()); + + QModelIndex rowidx = m_ModelVSIn->index(index.row(), 0, index.parent()); + int row = m_ModelVSIn->data(rowidx).toInt(); + + size_t stride = m_ModelVSIn->getConfig().buffers[0]->stride; + + QString tooltip; + + tooltip = tr("%1 at overall byte offset %2").arg(c.name).arg(stride * row + c.byteOffset); + tooltip += tr(", not including overall base byte offset %1 in buffer").arg(m_ByteOffset); + + tooltip += lit(".\n\n"); + + tooltip += + tr("Row %1 begins at offset %2 (stride of %3 bytes)\n%4 is at offset %5 in each row.") + .arg(row) + .arg(stride * row) + .arg(stride) + .arg(c.name) + .arg(c.byteOffset); + + QPoint pos = QCursor::pos(); + pos.setX(pos.x() + 10); + pos.setY(pos.y() + 10); + QToolTip::showText(pos, tooltip.trimmed()); + + return true; + } + } + } + else if(!m_MeshView && watched == ui->vsinData->viewport()) + { + if(event->type() == QEvent::MouseMove) + { + bool ret = QObject::eventFilter(watched, event); + + QMouseEvent *mouseEvent = (QMouseEvent *)event; + + if(m_delegate->linkHover(mouseEvent, font(), + ui->vsinData->indexAt(mouseEvent->localPos().toPoint()))) + ui->vsinData->setCursor(QCursor(Qt::PointingHandCursor)); + else + ui->vsinData->unsetCursor(); + + return ret; + } } return QObject::eventFilter(watched, event); @@ -4619,22 +4954,21 @@ void BufferViewer::processFormat(const QString &format) BufferConfiguration bufconfig; - ShaderConstant fixed, repeating; + ParsedFormat parsed; if(IsCBufferView() && format.isEmpty()) { // insert a dummy member so we get identified as plain fixed vars - we will automatically // evaluate ignoring the format - fixed.type.members.push_back(ShaderConstant()); + parsed.fixed.type.members.push_back(ShaderConstant()); } else { - rdctie(fixed, repeating) = - BufferFormatter::ParseFormatString(format, m_ByteSize, IsCBufferView(), errors); + parsed = BufferFormatter::ParseFormatString(format, m_ByteSize, IsCBufferView(), errors); } - const bool repeatedVars = repeating.type.descriptor.type != VarType::Unknown; - const bool fixedVars = !fixed.type.members.empty(); + const bool repeatedVars = parsed.repeating.type.descriptor.type != VarType::Unknown; + const bool fixedVars = !parsed.fixed.type.members.empty(); if(fixedVars && repeatedVars) { @@ -4657,6 +4991,8 @@ void BufferViewer::processFormat(const QString &format) ui->fixedVars->setVisible(true); ui->vsinData->setVisible(true); + ui->showPadding->setVisible(true); + m_InnerSplitter->setVisible(true); if(m_CurView == NULL && !m_CurFixed) @@ -4675,6 +5011,8 @@ void BufferViewer::processFormat(const QString &format) ui->fixedVars->setVisible(true); ui->vsinData->setVisible(false); + ui->showPadding->setVisible(true); + m_InnerSplitter->setVisible(false); m_CurView = NULL; @@ -4711,13 +5049,15 @@ void BufferViewer::processFormat(const QString &format) ui->fixedVars->setVisible(false); ui->vsinData->setVisible(true); + ui->showPadding->setVisible(false); + m_InnerSplitter->setVisible(false); m_CurView = ui->vsinData; m_CurFixed = false; } - CalcColumnWidth(MaxNumRows(repeating)); + CalcColumnWidth(MaxNumRows(parsed.repeating)); ClearModels(); @@ -4739,7 +5079,7 @@ void BufferViewer::processFormat(const QString &format) } else { - qulonglong stride = qMax(1U, repeating.type.descriptor.arrayByteStride); + qulonglong stride = qMax(1U, parsed.repeating.type.descriptor.arrayByteStride); byteRangeStart->setSingleStep(stride); byteRangeLength->setSingleStep(stride); @@ -4833,11 +5173,15 @@ void BufferViewer::exportCSV(QTextStream &ts, const QString &prefix, RDTreeWidge { if(item->childCount() == 0) { - ts << QFormatStr("%1,\"%2\",%3\n").arg(item->text(0)).arg(item->text(1)).arg(item->text(2)); + ts << QFormatStr("%1,\"%2\",%3,%4\n") + .arg(item->text(0)) + .arg(item->text(1)) + .arg(item->text(2)) + .arg(item->text(3)); } else { - ts << QFormatStr("%1,,%2\n").arg(item->text(0)).arg(item->text(2)); + ts << QFormatStr("%1,,%2,%3\n").arg(item->text(0)).arg(item->text(2)).arg(item->text(3)); for(int i = 0; i < item->childCount(); i++) exportCSV(ts, item->text(0) + lit("."), item->child(i)); } @@ -5114,12 +5458,10 @@ void BufferViewer::exportData(const BufferExport ¶ms) BufferItemModel *model = (BufferItemModel *)ui->vsinData->model(); const BufferConfiguration &config = model->getConfig(); - const ShaderConstant &fixedVars = config.fixedVars; - size_t byteSize = 0; - if(!fixedVars.type.members.empty()) - byteSize = BufferFormatter::GetVarSize(fixedVars.type.members.back()); + if(!config.fixedVars.type.members.empty()) + byteSize = BufferFormatter::GetVarAdvance(config.packing, config.fixedVars); const bytebuf &bufdata = config.buffers[0]->storage; @@ -5137,7 +5479,7 @@ void BufferViewer::exportData(const BufferExport ¶ms) { QTextStream ts(f); - ts << tr("Name,Value,Type\n"); + ts << tr("Name,Value,Byte Offset,Type\n"); for(int i = 0; i < ui->fixedVars->topLevelItemCount(); i++) exportCSV(ts, QString(), ui->fixedVars->topLevelItem(i)); @@ -5361,6 +5703,11 @@ void BufferViewer::on_syncViews_toggled(bool checked) SyncViews(NULL, true, true); } +void BufferViewer::on_showPadding_toggled(bool checked) +{ + OnEventChanged(m_Ctx.CurEvent()); +} + void BufferViewer::on_highlightVerts_toggled(bool checked) { UpdateHighlightVerts(); diff --git a/qrenderdoc/Windows/BufferViewer.h b/qrenderdoc/Windows/BufferViewer.h index e9ffa6778..785c1f437 100644 --- a/qrenderdoc/Windows/BufferViewer.h +++ b/qrenderdoc/Windows/BufferViewer.h @@ -124,6 +124,7 @@ private slots: void on_autofitCamera_clicked(); void on_toggleControls_toggled(bool checked); void on_syncViews_toggled(bool checked); + void on_showPadding_toggled(bool checked); void on_resourceDetails_clicked(); void on_highlightVerts_toggled(bool checked); void on_wireframeRender_toggled(bool checked); @@ -196,7 +197,12 @@ private: void UI_UpdateBoundingBox(const CalcBoundingBoxData &bbox); void UI_UpdateBoundingBoxLabels(int compCount = 0); - void UI_AddFixedVariables(RDTreeWidgetItem *root, const rdcarray &vars); + void UI_AddFixedVariables(RDTreeWidgetItem *root, uint32_t baseOffset, + const rdcarray &consts, + const rdcarray &vars); + void UI_RemoveOffsets(RDTreeWidgetItem *root); + void UI_FixedAddMatrixRows(RDTreeWidgetItem *n, const ShaderConstant &c, const ShaderVariable &v); + void exportCSV(QTextStream &ts, const QString &prefix, RDTreeWidgetItem *item); void FillScrolls(PopulateBufferData *bufdata); diff --git a/qrenderdoc/Windows/BufferViewer.ui b/qrenderdoc/Windows/BufferViewer.ui index 8dd47056e..ff98a2c1e 100644 --- a/qrenderdoc/Windows/BufferViewer.ui +++ b/qrenderdoc/Windows/BufferViewer.ui @@ -846,6 +846,29 @@ Enter 0.0 to use automatic/guessed value derived from data. + + + + true + + + Show visible padding + + + + :/align.png:/align.png + + + true + + + false + + + true + + + diff --git a/qrenderdoc/qrenderdoc_local.vcxproj b/qrenderdoc/qrenderdoc_local.vcxproj index 41bcba3ed..25cbea7a0 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj +++ b/qrenderdoc/qrenderdoc_local.vcxproj @@ -1914,6 +1914,8 @@ + + diff --git a/qrenderdoc/qrenderdoc_local.vcxproj.filters b/qrenderdoc/qrenderdoc_local.vcxproj.filters index 0f8d601be..44558565f 100644 --- a/qrenderdoc/qrenderdoc_local.vcxproj.filters +++ b/qrenderdoc/qrenderdoc_local.vcxproj.filters @@ -411,9 +411,9 @@ Generated Files - - Generated Files - + + Generated Files + Generated Files @@ -974,9 +974,9 @@ Generated Files - - Generated Files - + + Generated Files + Generated Files @@ -1957,6 +1957,12 @@ Resources\Files + + Resources\Files + + + Resources\Files +