diff --git a/docs/getting_started/quick_start.rst b/docs/getting_started/quick_start.rst index ecbf9941b..714ce6a95 100644 --- a/docs/getting_started/quick_start.rst +++ b/docs/getting_started/quick_start.rst @@ -184,7 +184,7 @@ More details can be found on the :doc:`../window/mesh_viewer` page. Mesh Viewer allows you to inspect the geometry data as it passes through the pipeline. Both raw data in a grid view and 3D inspection is possible. The tabs in the preview window allow you to choose at which part of the pipeline you'd like to visualise the data. -By default the preview shows a wireframe rendering of the mesh, but you can choose solid shading options. This can either be simple shading or use a secondary attribute as color. Right clicking on any column allows you to choose the secondary attribute for rendering. +By default the preview shows a wireframe rendering of the mesh, but you can choose different visualisations. This can either be simple shading, a vertex exploder utility, or using a secondary attribute as color. Right clicking on any column allows you to choose the secondary attribute for rendering. You can also select which attribute is the position, in case either the auto-detection failed or you want to visualise another attribute like texture co-ordinates in 3D space. diff --git a/docs/imgs/Screenshots/ExploderBadShoes.png b/docs/imgs/Screenshots/ExploderBadShoes.png new file mode 100644 index 000000000..92b18e1dd Binary files /dev/null and b/docs/imgs/Screenshots/ExploderBadShoes.png differ diff --git a/docs/imgs/Screenshots/ExploderGoodShoes.png b/docs/imgs/Screenshots/ExploderGoodShoes.png new file mode 100644 index 000000000..364375cbc Binary files /dev/null and b/docs/imgs/Screenshots/ExploderGoodShoes.png differ diff --git a/docs/python_api/renderdoc/outputs.rst b/docs/python_api/renderdoc/outputs.rst index 07b209c1c..d10d17ada 100644 --- a/docs/python_api/renderdoc/outputs.rst +++ b/docs/python_api/renderdoc/outputs.rst @@ -61,7 +61,7 @@ Mesh View .. autoclass:: MeshFormat :members: -.. autoclass:: SolidShade +.. autoclass:: Visualisation :members: .. autoclass:: Camera diff --git a/docs/window/mesh_viewer.rst b/docs/window/mesh_viewer.rst index 35057d7c3..9ea6bb144 100644 --- a/docs/window/mesh_viewer.rst +++ b/docs/window/mesh_viewer.rst @@ -47,12 +47,21 @@ Page up and Page down will move vertically 'up' and 'down' relative to the camer Mesh Preview ------------ -In the 3D Mesh preview, you have the option to display the mesh with some solid shading modes, not just as a wireframe mesh. When solid shading you can toggle the wireframe on and off. +In the 3D Mesh preview, you have the option to display the mesh with some colored visualisations, not just as a wireframe mesh. When using one of the visualisations, you can toggle the wireframe on and off. * Solid Color simply displays a solid color for each triangle. * Flat Shaded will perform basic flat lighting calculations based on triangle normals to give a better idea of the topology of the mesh. * Secondary will display the selected secondary mesh element. * Meshlet is only relevant when using mesh shaders, and will colourise each meshlet with a different colour. +* Exploded will display a shaded mesh where vertex colors are a function of the vertex index (SV_VertexID or gl_VertexID). This means adjacent primitives that do not share the exact same vertices can be more easily seen by a hard break in coloring. Additionally, a slider allows displacing vertices along an explosion direction by an amount based on the vertex index, which also helps better visualise shared or disjoint vertices and can also help in visualising other mesh problems (e.g. hidden or duplicate vertices/primitives). + +.. figure:: ../imgs/Screenshots/ExploderBadShoes.png + + Exploded: Visualising a mesh with no vertex sharing. + +.. figure:: ../imgs/Screenshots/ExploderGoodShoes.png + + Exploded: Visualising a mesh with widely shared vertices. To select which element will be displayed as secondary, simply right click on the column you would like to use. This can be done on the input and output separately, and 4-component columns have the option to either show RGB as color, or alpha as grayscale. diff --git a/qrenderdoc/Styles/RDStyle/RDStyle.cpp b/qrenderdoc/Styles/RDStyle/RDStyle.cpp index 15a652721..5d0b26226 100644 --- a/qrenderdoc/Styles/RDStyle/RDStyle.cpp +++ b/qrenderdoc/Styles/RDStyle/RDStyle.cpp @@ -37,6 +37,9 @@ namespace Constants { +static const int SliderHandleHalfWidth = 4; +static const int SliderGrooveHeight = 4; + static const int ButtonMargin = 6; static const int ButtonBorder = 1; @@ -470,6 +473,32 @@ QRect RDStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, return opt->rect; } + else if(cc == QStyle::CC_Slider) + { + QRect ret = opt->rect; + ret.adjust(1, 1, -1, -1); + if(sc == QStyle::SC_SliderGroove) + { + int toGrooveHeightHalfReduction = (ret.height() - Constants::SliderGrooveHeight) / 2; + ret.adjust(Constants::SliderHandleHalfWidth, toGrooveHeightHalfReduction, + -Constants::SliderHandleHalfWidth, -toGrooveHeightHalfReduction); + } + else if(sc == QStyle::SC_SliderHandle) + { + const QAbstractSlider *slider = qobject_cast(widget); + int sliderMin = slider->minimum(); + int sliderMax = slider->maximum(); + int sliderPos = slider->sliderPosition(); + qreal posUNorm = (qreal)(sliderPos - sliderMin) / (qreal)(sliderMax - sliderMin); + int grooveLeft = ret.left() + Constants::SliderHandleHalfWidth; + int grooveRight = ret.right() - Constants::SliderHandleHalfWidth; + int grooveWidth = grooveRight - grooveLeft; + int sliderX = (int)(posUNorm * (qreal)grooveWidth) + grooveLeft; + ret.setLeft(sliderX - Constants::SliderHandleHalfWidth); + ret.setRight(sliderX + Constants::SliderHandleHalfWidth); + } + return ret; + } else if(cc == QStyle::CC_ComboBox) { QRect rect = opt->rect; @@ -1118,6 +1147,19 @@ void RDStyle::drawComplexControl(ComplexControl control, const QStyleOptionCompl return; } + else if(control == QStyle::CC_Slider) + { + QRect grooveRect = subControlRect(control, opt, QStyle::SC_SliderGroove, widget); + p->drawLine(QLine(grooveRect.x(), grooveRect.y() + grooveRect.height() / 2, grooveRect.right(), + grooveRect.y() + grooveRect.height() / 2)); + + QRect handleRect = subControlRect(control, opt, QStyle::SC_SliderHandle, widget); + QBrush handleBrush = (m_Scheme == Light) ? opt->palette.brush(QPalette::Dark) + : opt->palette.brush(QPalette::Text); + p->fillRect(handleRect, handleBrush); + + return; + } else if(control == QStyle::CC_ComboBox) { const QStyleOptionComboBox *combo = qstyleoption_cast(opt); diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index 01f636b06..59b1a5fc7 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -2402,6 +2402,7 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) memset(&m_Config, 0, sizeof(m_Config)); m_Config.type = MeshDataStage::VSIn; m_Config.wireframeDraw = true; + m_Config.exploderScale = 1.0f; ui->outputTabs->setCurrentIndex(0); m_CurStage = MeshDataStage::VSIn; @@ -2537,10 +2538,11 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) configureDrawRange(); - ui->solidShading->clear(); - ui->solidShading->addItems({tr("None"), tr("Solid Colour"), tr("Flat Shaded"), tr("Secondary")}); - ui->solidShading->adjustSize(); - ui->solidShading->setCurrentIndex(0); + ui->visualisation->clear(); + ui->visualisation->addItems( + {tr("None"), tr("Solid Colour"), tr("Flat Shaded"), tr("Secondary"), tr("Exploded")}); + ui->visualisation->adjustSize(); + ui->visualisation->setCurrentIndex(0); ui->matrixType->addItems({tr("Perspective"), tr("Orthographic")}); @@ -2843,7 +2845,7 @@ void BufferViewer::SetupMeshView() BufferItemModel *model = (BufferItemModel *)m_CurView->model(); model->setPosColumn(-1); - model->setSecondaryColumn(-1, m_Config.solidShadeMode == SolidShade::Secondary, false); + model->setSecondaryColumn(-1, m_Config.visualisationMode == Visualisation::Secondary, false); UI_ConfigureFormats(); on_resetCamera_clicked(); @@ -2863,8 +2865,8 @@ void BufferViewer::SetupMeshView() QObject::connect(m_SelectSecondColumn, &QAction::triggered, [this]() { BufferItemModel *model = (BufferItemModel *)m_CurView->model(); - model->setSecondaryColumn(m_ContextColumn, m_Config.solidShadeMode == SolidShade::Secondary, - false); + model->setSecondaryColumn(m_ContextColumn, + m_Config.visualisationMode == Visualisation::Secondary, false); UI_ConfigureFormats(); UpdateCurrentMeshConfig(); @@ -2873,8 +2875,8 @@ void BufferViewer::SetupMeshView() QObject::connect(m_SelectSecondAlphaColumn, &QAction::triggered, [this]() { BufferItemModel *model = (BufferItemModel *)m_CurView->model(); - model->setSecondaryColumn(m_ContextColumn, m_Config.solidShadeMode == SolidShade::Secondary, - true); + model->setSecondaryColumn(m_ContextColumn, + m_Config.visualisationMode == Visualisation::Secondary, true); UI_ConfigureFormats(); UpdateCurrentMeshConfig(); INVOKE_MEMFN(RT_UpdateAndDisplay); @@ -3674,7 +3676,8 @@ void BufferViewer::OnEventChanged(uint32_t eventId) // similarly for secondary columns if(m_ModelIn->secondaryColumn() == -1 || bufdata->highlightNames[1] != bufdata->inConfig.columnName(m_ModelIn->secondaryColumn())) - m_ModelIn->setSecondaryColumn(-1, m_Config.solidShadeMode == SolidShade::Secondary, false); + m_ModelIn->setSecondaryColumn(-1, m_Config.visualisationMode == Visualisation::Secondary, + false); // and as above for VS Out / GS Out if(m_ModelOut1->posColumn() == -1 || @@ -3682,14 +3685,16 @@ void BufferViewer::OnEventChanged(uint32_t eventId) m_ModelOut1->setPosColumn(-1); if(m_ModelOut1->secondaryColumn() == -1 || bufdata->highlightNames[3] != bufdata->out1Config.columnName(m_ModelOut1->secondaryColumn())) - m_ModelOut1->setSecondaryColumn(-1, m_Config.solidShadeMode == SolidShade::Secondary, false); + m_ModelOut1->setSecondaryColumn(-1, m_Config.visualisationMode == Visualisation::Secondary, + false); if(m_ModelOut2->posColumn() == -1 || bufdata->highlightNames[4] != bufdata->out2Config.columnName(m_ModelOut2->posColumn())) m_ModelOut2->setPosColumn(-1); if(m_ModelOut2->secondaryColumn() == -1 || bufdata->highlightNames[5] != bufdata->out2Config.columnName(m_ModelOut2->secondaryColumn())) - m_ModelOut2->setSecondaryColumn(-1, m_Config.solidShadeMode == SolidShade::Secondary, false); + m_ModelOut2->setSecondaryColumn(-1, m_Config.visualisationMode == Visualisation::Secondary, + false); EnableCameraGuessControls(); @@ -5306,9 +5311,9 @@ void BufferViewer::updateLabelsAndLayout() ui->outputTabs->setTabText(0, tr("Mesh Input")); ui->outputTabs->setTabText(1, tr("Mesh Out")); - if(ui->solidShading->itemText(ui->solidShading->count() - 1) != tr("Meshlet")) - ui->solidShading->addItem(tr("Meshlet")); - ui->solidShading->adjustSize(); + if(ui->visualisation->itemText(ui->visualisation->count() - 1) != tr("Meshlet")) + ui->visualisation->addItem(tr("Meshlet")); + ui->visualisation->adjustSize(); } else { @@ -5334,9 +5339,9 @@ void BufferViewer::updateLabelsAndLayout() ui->outputTabs->setTabText(1, tr("VS Out")); ui->outputTabs->setTabText(2, tr("GS/DS Out")); - if(ui->solidShading->itemText(ui->solidShading->count() - 1) == tr("Meshlet")) - ui->solidShading->removeItem(ui->solidShading->count() - 1); - ui->solidShading->adjustSize(); + if(ui->visualisation->itemText(ui->visualisation->count() - 1) == tr("Meshlet")) + ui->visualisation->removeItem(ui->visualisation->count() - 1); + ui->visualisation->adjustSize(); } } else @@ -5363,9 +5368,9 @@ void BufferViewer::updateLabelsAndLayout() ui->outputTabs->setTabText(1, tr("VS Out")); ui->outputTabs->setTabText(2, tr("GS/DS Out")); - if(ui->solidShading->itemText(ui->solidShading->count() - 1) == tr("Meshlet")) - ui->solidShading->removeItem(ui->solidShading->count() - 1); - ui->solidShading->adjustSize(); + if(ui->visualisation->itemText(ui->visualisation->count() - 1) == tr("Meshlet")) + ui->visualisation->removeItem(ui->visualisation->count() - 1); + ui->visualisation->adjustSize(); } } else @@ -6678,7 +6683,7 @@ void BufferViewer::UpdateHighlightVerts() { m_Config.highlightVert = ~0U; - if(!ui->highlightVerts->isChecked()) + if(ui->highlightVerts->isHidden() || !ui->highlightVerts->isChecked()) return; RDTableView *table = currentTable(); @@ -6777,6 +6782,25 @@ void BufferViewer::on_highlightVerts_toggled(bool checked) INVOKE_MEMFN(RT_UpdateAndDisplay); } +void BufferViewer::on_vtxExploderSlider_valueChanged(int value) +{ + m_Config.vtxExploderSliderSNorm = (float)value / 100.0f; + + INVOKE_MEMFN(RT_UpdateAndDisplay); +} + +void BufferViewer::on_exploderReset_clicked() +{ + ui->vtxExploderSlider->setSliderPosition(0); +} + +void BufferViewer::on_exploderScale_valueChanged(double value) +{ + m_Config.exploderScale = (float)value; + + INVOKE_MEMFN(RT_UpdateAndDisplay); +} + void BufferViewer::on_wireframeRender_toggled(bool checked) { m_Config.wireframeDraw = checked; @@ -6784,7 +6808,7 @@ void BufferViewer::on_wireframeRender_toggled(bool checked) INVOKE_MEMFN(RT_UpdateAndDisplay); } -void BufferViewer::on_solidShading_currentIndexChanged(int index) +void BufferViewer::on_visualisation_currentIndexChanged(int index) { ui->wireframeRender->setEnabled(index > 0); @@ -6794,16 +6818,31 @@ void BufferViewer::on_solidShading_currentIndexChanged(int index) m_Config.wireframeDraw = true; } - m_Config.solidShadeMode = (SolidShade)qMax(0, index); + bool explodeHidden = (index != (int)Visualisation::Explode); + ui->vtxExploderLabel->setHidden(explodeHidden); + ui->vtxExploderSlider->setHidden(explodeHidden); + ui->exploderReset->setHidden(explodeHidden); + ui->exploderScaleLabel->setHidden(explodeHidden); + ui->exploderScale->setHidden(explodeHidden); + // Because the vertex/prim highlights draw from a new, temporary vertex buffer, + // those vertex IDs (which determine the explode displacement) won't necessarily + // match the original mesh's IDs and exploded vertices. Because of this, it seems + // cleanest to just avoid drawing the highlighted vert/prim with the explode + // visualisation (while also getting back a little room on the toolbar used by + // the extra exploder controls). + ui->highlightVerts->setHidden(!explodeHidden); + UpdateHighlightVerts(); + + m_Config.visualisationMode = (Visualisation)qMax(0, index); m_ModelIn->setSecondaryColumn(m_ModelIn->secondaryColumn(), - m_Config.solidShadeMode == SolidShade::Secondary, + m_Config.visualisationMode == Visualisation::Secondary, m_ModelIn->secondaryAlpha()); m_ModelOut1->setSecondaryColumn(m_ModelOut1->secondaryColumn(), - m_Config.solidShadeMode == SolidShade::Secondary, + m_Config.visualisationMode == Visualisation::Secondary, m_ModelOut1->secondaryAlpha()); m_ModelOut2->setSecondaryColumn(m_ModelOut2->secondaryColumn(), - m_Config.solidShadeMode == SolidShade::Secondary, + m_Config.visualisationMode == Visualisation::Secondary, m_ModelOut2->secondaryAlpha()); INVOKE_MEMFN(RT_UpdateAndDisplay); diff --git a/qrenderdoc/Windows/BufferViewer.h b/qrenderdoc/Windows/BufferViewer.h index 35069eb10..b9c58e259 100644 --- a/qrenderdoc/Windows/BufferViewer.h +++ b/qrenderdoc/Windows/BufferViewer.h @@ -128,8 +128,11 @@ private slots: void on_showPadding_toggled(bool checked); void on_resourceDetails_clicked(); void on_highlightVerts_toggled(bool checked); + void on_vtxExploderSlider_valueChanged(int value); + void on_exploderReset_clicked(); + void on_exploderScale_valueChanged(double value); void on_wireframeRender_toggled(bool checked); - void on_solidShading_currentIndexChanged(int index); + void on_visualisation_currentIndexChanged(int index); void on_drawRange_currentIndexChanged(int index); void on_controlType_currentIndexChanged(int index); void on_camSpeed_valueChanged(double value); diff --git a/qrenderdoc/Windows/BufferViewer.ui b/qrenderdoc/Windows/BufferViewer.ui index 0e2e3b48d..cab4612ac 100644 --- a/qrenderdoc/Windows/BufferViewer.ui +++ b/qrenderdoc/Windows/BufferViewer.ui @@ -6,8 +6,8 @@ 0 0 - 788 - 537 + 1300 + 837 @@ -307,14 +307,14 @@ - + - Solid Shading + Visualisation - + @@ -382,6 +382,131 @@ + + + + true + + + + 0 + 0 + + + + Exploder + + + + + + + true + + + + 0 + 0 + + + + + 90 + 30 + + + + true + + + Qt::StrongFocus + + + Qt::NoContextMenu + + + Explode vertices + + + slider name + + + slider description + + + true + + + -100 + + + 100 + + + 0 + + + Qt::Horizontal + + + false + + + false + + + QSlider::NoTicks + + + 20 + + + + + + + + 0 + 0 + + + + Reset/zero exploder slider + + + Reset + + + + + + + Scale + + + + + + + Exploder scaling + + + 1 + + + 0.100000000000000 + + + 99.000000000000000 + + + 0.100000000000000 + + + 1.000000000000000 + + + diff --git a/renderdoc/api/replay/control_types.h b/renderdoc/api/replay/control_types.h index 5a01c89a0..0b665c933 100644 --- a/renderdoc/api/replay/control_types.h +++ b/renderdoc/api/replay/control_types.h @@ -309,10 +309,15 @@ struct MeshDisplay DOCUMENT("``True`` if the bounding box around the mesh should be rendered."); bool showBBox = false; - DOCUMENT("The :class:`solid shading mode ` to use when rendering the current mesh."); - SolidShade solidShadeMode = SolidShade::NoSolid; + DOCUMENT( + "The :class:`visualisation mode ` to use when rendering the current mesh."); + Visualisation visualisationMode = Visualisation::NoSolid; DOCUMENT("``True`` if the wireframe of the mesh should be rendered as well as solid shading."); bool wireframeDraw = true; + DOCUMENT("Displace/explode vertices to help visualise vertex reuse vs disjointedness."); + float vtxExploderSliderSNorm = 0.0f; + DOCUMENT("Scales the exploded vertex displacement."); + float exploderScale = 1.0f; static const uint32_t NoHighlight = ~0U; }; diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index edc604d49..7316ff91b 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -2895,7 +2895,7 @@ constexpr inline ResourceUsage RWResUsage(ShaderStage stage) return RWResUsage(uint32_t(stage)); } -DOCUMENT(R"(What kind of solid shading to use when rendering a mesh. +DOCUMENT(R"(What kind of visualisation to use when rendering a mesh. .. data:: NoSolid @@ -2913,22 +2913,27 @@ DOCUMENT(R"(What kind of solid shading to use when rendering a mesh. The mesh should be rendered using the secondary element as color. +.. data:: Explode + + The mesh should be rendered with vertices displaced and coloured by vertex ID. + .. data:: Meshlet The mesh should be rendered colorising each meshlet differently. )"); -enum class SolidShade : uint32_t +enum class Visualisation : uint32_t { NoSolid = 0, Solid, Lit, Secondary, + Explode, Meshlet, Count, }; -DECLARE_REFLECTION_ENUM(SolidShade); +DECLARE_REFLECTION_ENUM(Visualisation); DOCUMENT(R"(The fill mode for polygons. diff --git a/renderdoc/data/glsl/glsl_globals.h b/renderdoc/data/glsl/glsl_globals.h index 7ef861019..b4a9c0af2 100644 --- a/renderdoc/data/glsl/glsl_globals.h +++ b/renderdoc/data/glsl/glsl_globals.h @@ -172,14 +172,15 @@ vec3 CalcCubeCoord(vec2 uv, int face) #endif -// first few match SolidShade enum +// first few match Visualisation enum #define MESHDISPLAY_SOLID 0x1 #define MESHDISPLAY_FACELIT 0x2 #define MESHDISPLAY_SECONDARY 0x3 -#define MESHDISPLAY_MESHLET 0x4 +#define MESHDISPLAY_EXPLODE 0x4 +#define MESHDISPLAY_MESHLET 0x5 // extra values below -#define MESHDISPLAY_SECONDARY_ALPHA 0x5 +#define MESHDISPLAY_SECONDARY_ALPHA 0x6 #define MAX_NUM_MESHLETS (512 * 1024) diff --git a/renderdoc/data/glsl/glsl_ubos.h b/renderdoc/data/glsl/glsl_ubos.h index ddee1b380..c8399f272 100644 --- a/renderdoc/data/glsl/glsl_ubos.h +++ b/renderdoc/data/glsl/glsl_ubos.h @@ -35,12 +35,19 @@ BINDING(0) uniform MeshUBOData mat4 mvp; mat4 invProj; vec4 color; + int displayFormat; uint homogenousInput; vec2 pointSpriteSize; + uint rawoutput; uint flipY; - vec2 padding; + float vtxExploderSNorm; + float exploderScale; + + vec3 exploderCentre; + float padding; + uvec4 meshletColours[12]; } INST_NAME(Mesh); diff --git a/renderdoc/data/glsl/mesh.frag b/renderdoc/data/glsl/mesh.frag index 4afd34702..12050d353 100644 --- a/renderdoc/data/glsl/mesh.frag +++ b/renderdoc/data/glsl/mesh.frag @@ -60,6 +60,12 @@ void main(void) color_out = vec4(Mesh.color.xyz * abs(dot(lightDir, NORM_NAME.xyz)), 1); } + else if(type == MESHDISPLAY_EXPLODE) + { + vec3 lightDir = normalize(vec3(0, -0.3f, -1)); + + color_out = vec4(SECONDARY_NAME.xyz * abs(dot(lightDir, NORM_NAME.xyz)), 1); + } else // if(type == MESHDISPLAY_SOLID) { color_out = vec4(Mesh.color.xyz, 1); diff --git a/renderdoc/data/glsl/mesh.vert b/renderdoc/data/glsl/mesh.vert index 5d29c30f1..aec9432cf 100644 --- a/renderdoc/data/glsl/mesh.vert +++ b/renderdoc/data/glsl/mesh.vert @@ -36,6 +36,70 @@ #define SECONDARY_TYPE vec4 #endif +// This function is mostly duplicated between 'mesh.hlsl' and 'mesh.vert'. +// Without a convenient shared common source, changes to one should be +// reflected in the other. +void vtxExploder(in int vtxID, inout vec3 pos, inout vec3 secondary) +{ + if(Mesh.exploderScale > 0.0f) + { + float nonLinearVtxExplodeScale = 4.0f * Mesh.exploderScale * Mesh.vtxExploderSNorm * + Mesh.vtxExploderSNorm * Mesh.vtxExploderSNorm; + + // A vertex might be coincident with our 'exploderCentre' so that, when normalized, + // can give us INFs/NaNs that, even if multiplied by a zero 'exploderScale', can + // leave us with bad numbers (as seems to be the case with glsl/vulkan, but not hlsl). + // Still, we should make this case safe for when we have a non-zero 'exploderScale' - + vec3 offset = pos - Mesh.exploderCentre; + float offsetDistSquared = dot(offset, offset); + vec3 safeExplodeDir = offset * inversesqrt(max(offsetDistSquared, FLT_EPSILON)); + + float displacement = + nonLinearVtxExplodeScale * ((float((vtxID >> 1) & 0xf) / 15.0f) * 1.5f - 0.5f); + pos += (safeExplodeDir * displacement); + + // For the exploder visualisation, colour verts based on vertex ID, which we + // store in secondary. + // + // Interpolate a colour gradient from 0.0 to 1.0 and back to 0.0 for vertex IDs + // 0 to 16 to 32 respectively - + // 1 - | .`. + // | .` `. + // | .` | `. + // 0.5-| .` `. + // | .` | `. . + // |.` `. .` + // 0.0-+-----------+-----------+--- vtx IDs + // 0 16 32 + + float vtxIDMod32Div16 = float(vtxID % 32) / 16.0f; // 0: 0.0 16: 1.0 31: 1.94 + float descending = floor(vtxIDMod32Div16); // 0..15: 0.0 16..31: 1.0 + float gradientVal = abs(vtxIDMod32Div16 - (2.0f * descending)); // 0.0..1.0 + + // Use a hopefully fairly intuitive temperature gradient scheme to help visualise + // contiguous/nearby sequences of vertices, which should also show up breaks in + // colour where verts aren't shared between adjacent primitives. + const vec3 gradientColours[5] = + vec3[](vec3(0.004f, 0.002f, 0.025f), // 0.0..0.25: Dark blue + vec3(0.305f, 0.001f, 0.337f), // 0.25..0.5: Purple + vec3(0.665f, 0.033f, 0.133f), // 0.5..0.75: Purple orange + vec3(1.000f, 0.468f, 0.000f), // 0.75..1.0: Orange + vec3(1.000f, 1.000f, 1.000f) // 1.0: White + ); + + uint gradientSectionStartIdx = uint(gradientVal * 4.0f); + uint gradientSectionEndIdx = min(gradientSectionStartIdx + 1u, 4u); + vec3 gradSectionStartCol = gradientColours[gradientSectionStartIdx]; + vec3 gradSectionEndCol = gradientColours[gradientSectionEndIdx]; + + float sectionLerp = gradientVal - float(gradientSectionStartIdx) * 0.25f; + vec3 gradCol = mix(gradientColours[gradientSectionStartIdx], + gradientColours[gradientSectionEndIdx], sectionLerp); + + secondary = gradCol; + } +} + IO_LOCATION(0) in POSITION_TYPE vsin_position; IO_LOCATION(1) in SECONDARY_TYPE vsin_secondary; @@ -111,6 +175,9 @@ void main(void) vec2[](vec2(-1.0f, -1.0f), vec2(-1.0f, 1.0f), vec2(1.0f, -1.0f), vec2(1.0f, 1.0f)); vec4 pos = vec4(vsin_position); + vec4 secondary = vec4(vsin_secondary); + vtxExploder(VERTEX_ID, pos.xyz, secondary.xyz); + if(Mesh.homogenousInput == 0u) { pos = vec4(pos.xyz, 1); @@ -124,7 +191,7 @@ void main(void) gl_Position = Mesh.mvp * pos; gl_Position.xy += Mesh.pointSpriteSize.xy * 0.01f * psprite[VERTEX_ID % 4] * gl_Position.w; - vsout_secondary = vec4(vsin_secondary); + vsout_secondary = vec4(secondary); vsout_norm = vec4(0, 0, 1, 1); #ifdef VULKAN diff --git a/renderdoc/data/hlsl/hlsl_cbuffers.h b/renderdoc/data/hlsl/hlsl_cbuffers.h index 0e7d32d03..90cbda75d 100644 --- a/renderdoc/data/hlsl/hlsl_cbuffers.h +++ b/renderdoc/data/hlsl/hlsl_cbuffers.h @@ -107,12 +107,15 @@ cbuffer MeshVertexCBuffer REG(b0) float2 SpriteSize; uint homogenousInput; - uint vertMeshDisplayFormat; + float vtxExploderSNorm; + float3 exploderCentre; + float exploderScale; // Non-zero values imply use of the exploder visualisation. + + uint vertMeshDisplayFormat; uint meshletOffset; uint meshletCount; uint padding1; - uint padding2; uint4 meshletColours[12]; }; @@ -250,14 +253,15 @@ cbuffer DebugSampleOperation REG(b0) #define RESTYPE_DEPTH_STENCIL_MS 0x7 #define RESTYPE_TEX2D_MS 0x9 -// first few match SolidShade enum +// first few match Visualisation enum #define MESHDISPLAY_SOLID 0x1 #define MESHDISPLAY_FACELIT 0x2 #define MESHDISPLAY_SECONDARY 0x3 -#define MESHDISPLAY_MESHLET 0x4 +#define MESHDISPLAY_EXPLODE 0x4 +#define MESHDISPLAY_MESHLET 0x5 // extra values below -#define MESHDISPLAY_SECONDARY_ALPHA 0x5 +#define MESHDISPLAY_SECONDARY_ALPHA 0x6 #define MAX_NUM_MESHLETS (512 * 1024) diff --git a/renderdoc/data/hlsl/mesh.hlsl b/renderdoc/data/hlsl/mesh.hlsl index 9da7b5def..19eba41af 100644 --- a/renderdoc/data/hlsl/mesh.hlsl +++ b/renderdoc/data/hlsl/mesh.hlsl @@ -37,6 +37,70 @@ struct meshA2V float4 secondary : sec; }; +// This function is mostly duplicated between 'mesh.hlsl' and 'mesh.vert'. +// Without a convenient shared common source, changes to one should be +// reflected in the other. +void vtxExploder(in uint vtxID, inout float3 pos, inout float3 secondary) +{ + if(exploderScale > 0.0f) + { + float nonLinearVtxExplodeScale = + 4.0f * exploderScale * vtxExploderSNorm * vtxExploderSNorm * vtxExploderSNorm; + + // A vertex might be coincident with our 'exploderCentre' so that, when normalized, + // can give us INFs/NaNs that, even if multiplied by a zero 'exploderScale', can + // leave us with bad numbers (as seems to be the case with glsl/vulkan, but not hlsl). + // Still, we should make this case safe for when we have a non-zero 'exploderScale' - + float3 offset = pos - exploderCentre; + float offsetDistSquared = dot(offset, offset); + float3 safeExplodeDir = offset * rsqrt(max(offsetDistSquared, FLT_EPSILON)); + + float displacement = + nonLinearVtxExplodeScale * ((float((vtxID >> 1u) & 0xfu) / 15.0f) * 1.5f - 0.5f); + pos += (safeExplodeDir * displacement); + + // For the exploder visualisation, colour verts based on vertex ID, which we + // store in secondary. + // + // Interpolate a colour gradient from 0.0 to 1.0 and back to 0.0 for vertex IDs + // 0 to 16 to 32 respectively - + // 1 - | .`. + // | .` `. + // | .` | `. + // 0.5-| .` `. + // | .` | `. . + // |.` `. .` + // 0.0-+-----------+-----------+--- vtx IDs + // 0 16 32 + + float vtxIDMod32Div16 = (float)(vtxID % 32u) / 16.0f; // 0: 0.0 16: 1.0 31: 1.94 + float descending = floor(vtxIDMod32Div16); // 0..15: 0.0 16..31: 1.0 + float gradientVal = abs(vtxIDMod32Div16 - (2.0f * descending)); // 0.0..1.0 + + // Use a hopefully fairly intuitive temperature gradient scheme to help visualise + // contiguous/nearby sequences of vertices, which should also show up breaks in + // colour where verts aren't shared between adjacent primitives. + const float3 gradientColours[5] = { + float3(0.004f, 0.002f, 0.025f), // 0.0..0.25: Dark blue + float3(0.305f, 0.001f, 0.337f), // 0.25..0.5: Purple + float3(0.665f, 0.033f, 0.133f), // 0.5..0.75: Purple orange + float3(1.000f, 0.468f, 0.000f), // 0.75..1.0: Orange + float3(1.000f, 1.000f, 1.000f) // 1.0: White + }; + + uint gradientSectionStartIdx = (uint)(gradientVal * 4.0f); + uint gradientSectionEndIdx = min(gradientSectionStartIdx + 1u, 4u); + float3 gradSectionStartCol = gradientColours[gradientSectionStartIdx]; + float3 gradSectionEndCol = gradientColours[gradientSectionEndIdx]; + + float sectionLerp = gradientVal - (float)gradientSectionStartIdx * 0.25f; + float3 gradCol = lerp(gradientColours[gradientSectionStartIdx], + gradientColours[gradientSectionEndIdx], sectionLerp); + + secondary = gradCol; + } +} + StructuredBuffer meshletSizesBuf : register(t0); float4 unpackUnorm4x8(uint value) @@ -118,6 +182,7 @@ meshV2F RENDERDOC_MeshVS(meshA2V IN, uint vid : SV_VertexID) float2(1.0f, 1.0f)}; float4 pos = IN.pos; + vtxExploder(vid, pos.xyz, IN.secondary.xyz); if(homogenousInput == 0u) { @@ -214,6 +279,12 @@ float4 RENDERDOC_MeshPS(meshV2F IN) return float4(MeshColour.xyz * abs(dot(lightDir, IN.norm)), 1); } + else if(type == MESHDISPLAY_EXPLODE) + { + float3 lightDir = normalize(float3(0, -0.3f, -1)); + + return float4(IN.secondary.xyz * abs(dot(lightDir, IN.norm)), 1); + } else // if(type == MESHDISPLAY_SOLID) return float4(MeshColour.xyz, 1); } diff --git a/renderdoc/driver/d3d11/d3d11_rendermesh.cpp b/renderdoc/driver/d3d11/d3d11_rendermesh.cpp index 1e6085f4a..9e0c63562 100644 --- a/renderdoc/driver/d3d11/d3d11_rendermesh.cpp +++ b/renderdoc/driver/d3d11/d3d11_rendermesh.cpp @@ -33,6 +33,19 @@ #include "data/hlsl/hlsl_cbuffers.h" +static uint32_t VisModeToMeshDisplayFormat(const MeshDisplay &cfg) +{ + switch(cfg.visualisationMode) + { + default: return (uint32_t)cfg.visualisationMode; + case Visualisation::Secondary: + return cfg.second.showAlpha ? MESHDISPLAY_SECONDARY_ALPHA : MESHDISPLAY_SECONDARY; + case Visualisation::Meshlet: + RDCERR("D3D11 does not support meshlet rendering"); + return MESHDISPLAY_SOLID; + } +} + void D3D11Replay::RenderMesh(uint32_t eventId, const rdcarray &secondaryDraws, const MeshDisplay &cfg) { @@ -58,6 +71,12 @@ void D3D11Replay::RenderMesh(uint32_t eventId, const rdcarray &secon vertexData.ModelViewProj = projMat.Mul(camMat.Mul(axisMapMat)); vertexData.SpriteSize = Vec2f(); vertexData.homogenousInput = cfg.position.unproject; + vertexData.vtxExploderSNorm = cfg.vtxExploderSliderSNorm; + vertexData.exploderCentre = + Vec3f((cfg.minBounds.x + cfg.maxBounds.x) * 0.5f, (cfg.minBounds.y + cfg.maxBounds.y) * 0.5f, + (cfg.minBounds.z + cfg.maxBounds.z) * 0.5f); + vertexData.exploderScale = + (cfg.visualisationMode == Visualisation::Explode) ? cfg.exploderScale : 0.0f; Vec4f col(0.0f, 0.0f, 0.0f, 1.0f); ID3D11Buffer *psCBuf = GetDebugManager()->MakeCBuffer(&col, sizeof(col)); @@ -260,27 +279,21 @@ void D3D11Replay::RenderMesh(uint32_t eventId, const rdcarray &secon m_pImmediateContext->IASetIndexBuffer(NULL, DXGI_FORMAT_UNKNOWN, NULL); // draw solid shaded mode - if(cfg.solidShadeMode != SolidShade::NoSolid && cfg.position.topology < Topology::PatchList_1CPs) + if(cfg.visualisationMode != Visualisation::NoSolid && + cfg.position.topology < Topology::PatchList_1CPs) { m_pImmediateContext->RSSetState(m_General.RasterState); m_pImmediateContext->IASetPrimitiveTopology(topo); - pixelData.MeshDisplayFormat = (int)cfg.solidShadeMode; - if(cfg.solidShadeMode == SolidShade::Meshlet) - { - RDCERR("D3D11 does not support mesh rendering"); - pixelData.MeshDisplayFormat = (int)SolidShade::Solid; - } - - if(cfg.solidShadeMode == SolidShade::Secondary && cfg.second.showAlpha) - pixelData.MeshDisplayFormat = MESHDISPLAY_SECONDARY_ALPHA; + pixelData.MeshDisplayFormat = VisModeToMeshDisplayFormat(cfg); pixelData.MeshColour = Vec3f(0.8f, 0.8f, 0.0f); GetDebugManager()->FillCBuffer(psCBuf, &pixelData, sizeof(pixelData)); m_pImmediateContext->PSSetConstantBuffers(0, 1, &psCBuf); - if(cfg.solidShadeMode == SolidShade::Lit) + if(cfg.visualisationMode == Visualisation::Lit || + cfg.visualisationMode == Visualisation::Explode) { MeshGeometryCBuffer geomData; @@ -298,12 +311,13 @@ void D3D11Replay::RenderMesh(uint32_t eventId, const rdcarray &secon else m_pImmediateContext->Draw(cfg.position.numIndices, 0); - if(cfg.solidShadeMode == SolidShade::Lit) + if(cfg.visualisationMode == Visualisation::Lit || + cfg.visualisationMode == Visualisation::Explode) m_pImmediateContext->GSSetShader(NULL, NULL, 0); } // draw wireframe mode - if(cfg.solidShadeMode == SolidShade::NoSolid || cfg.wireframeDraw || + if(cfg.visualisationMode == Visualisation::NoSolid || cfg.wireframeDraw || cfg.position.topology >= Topology::PatchList_1CPs) { m_pImmediateContext->RSSetState(m_MeshRender.WireframeRasterState); @@ -335,6 +349,8 @@ void D3D11Replay::RenderMesh(uint32_t eventId, const rdcarray &secon // set up state for drawing helpers { vertexData.ModelViewProj = projMat.Mul(camMat.Mul(axisMapMat)); + vertexData.vtxExploderSNorm = 0.0f; + vertexData.exploderScale = 0.0f; GetDebugManager()->FillCBuffer(vsCBuf, &vertexData, sizeof(vertexData)); m_pImmediateContext->RSSetState(m_MeshRender.SolidRasterState); diff --git a/renderdoc/driver/d3d12/d3d12_rendermesh.cpp b/renderdoc/driver/d3d12/d3d12_rendermesh.cpp index fb27983dd..ee0bd9902 100644 --- a/renderdoc/driver/d3d12/d3d12_rendermesh.cpp +++ b/renderdoc/driver/d3d12/d3d12_rendermesh.cpp @@ -38,6 +38,16 @@ RDOC_EXTERN_CONFIG(bool, D3D12_Debug_SingleSubmitFlushing); +static uint32_t VisModeToMeshDisplayFormat(const MeshDisplay &cfg) +{ + switch(cfg.visualisationMode) + { + default: return (uint32_t)cfg.visualisationMode; + case Visualisation::Secondary: + return cfg.second.showAlpha ? MESHDISPLAY_SECONDARY_ALPHA : MESHDISPLAY_SECONDARY; + } +} + MeshDisplayPipelines D3D12DebugManager::CacheMeshDisplayPipelines(const MeshFormat &primary, const MeshFormat &secondary) { @@ -93,7 +103,7 @@ MeshDisplayPipelines D3D12DebugManager::CacheMeshDisplayPipelines(const MeshForm MeshDisplayPipelines &cache = m_CachedMeshPipelines[key]; - if(cache.pipes[(uint32_t)SolidShade::NoSolid] != NULL) + if(cache.pipes[(uint32_t)Visualisation::NoSolid] != NULL) return cache; cache.rootsig = m_MeshRootSig; @@ -276,6 +286,12 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon vertexData.ModelViewProj = projMat.Mul(camMat.Mul(axisMapMat)); vertexData.SpriteSize = Vec2f(); vertexData.homogenousInput = cfg.position.unproject; + vertexData.vtxExploderSNorm = cfg.vtxExploderSliderSNorm; + vertexData.exploderCentre = + Vec3f((cfg.minBounds.x + cfg.maxBounds.x) * 0.5f, (cfg.minBounds.y + cfg.maxBounds.y) * 0.5f, + (cfg.minBounds.z + cfg.maxBounds.z) * 0.5f); + vertexData.exploderScale = + (cfg.visualisationMode == Visualisation::Explode) ? cfg.exploderScale : 0.0f; vertexData.vertMeshDisplayFormat = MESHDISPLAY_SOLID; MeshPixelCBuffer pixelData; @@ -425,13 +441,13 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon list->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_POINTLIST); } - SolidShade solidShadeMode = cfg.solidShadeMode; - // can't support secondary shading without a buffer - no pipeline will have been created - if(solidShadeMode == SolidShade::Secondary && cfg.second.vertexResourceId == ResourceId()) - solidShadeMode = SolidShade::NoSolid; + const Visualisation finalVisualisation = (cfg.visualisationMode == Visualisation::Secondary && + cfg.second.vertexResourceId == ResourceId()) + ? Visualisation::NoSolid + : cfg.visualisationMode; - if(solidShadeMode == SolidShade::Secondary) + if(finalVisualisation == Visualisation::Secondary) { D3D12MarkerRegion::Set(list, "Secondary"); @@ -454,28 +470,32 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon } // solid render - if(solidShadeMode != SolidShade::NoSolid && cfg.position.topology < Topology::PatchList) + if(finalVisualisation != Visualisation::NoSolid && cfg.position.topology < Topology::PatchList) { D3D12MarkerRegion region(list, "Solid render"); ID3D12PipelineState *pipe = NULL; - switch(solidShadeMode) + switch(finalVisualisation) { default: - case SolidShade::Solid: pipe = cache.pipes[MeshDisplayPipelines::ePipe_SolidDepth]; break; - case SolidShade::Lit: + case Visualisation::Solid: pipe = cache.pipes[MeshDisplayPipelines::ePipe_SolidDepth]; break; + case Visualisation::Lit: + case Visualisation::Explode: pipe = cache.pipes[MeshDisplayPipelines::ePipe_Lit]; // point list topologies don't have lighting obvious, just render them as solid if(!pipe) pipe = cache.pipes[MeshDisplayPipelines::ePipe_SolidDepth]; break; - case SolidShade::Secondary: pipe = cache.pipes[MeshDisplayPipelines::ePipe_Secondary]; break; - case SolidShade::Meshlet: pipe = cache.pipes[MeshDisplayPipelines::ePipe_SolidDepth]; break; + case Visualisation::Secondary: + pipe = cache.pipes[MeshDisplayPipelines::ePipe_Secondary]; + break; + case Visualisation::Meshlet: + pipe = cache.pipes[MeshDisplayPipelines::ePipe_SolidDepth]; + break; } - pixelData.MeshDisplayFormat = (int)cfg.solidShadeMode; - if(cfg.solidShadeMode == SolidShade::Secondary && cfg.second.showAlpha) - pixelData.MeshDisplayFormat = MESHDISPLAY_SECONDARY_ALPHA; + pixelData.MeshDisplayFormat = VisModeToMeshDisplayFormat(cfg); + pixelData.MeshColour = Vec3f(0.8f, 0.8f, 0.0f); list->SetPipelineState(pipe); @@ -483,7 +503,7 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon size_t numMeshlets = RDCMIN(cfg.position.meshletSizes.size(), (size_t)MAX_NUM_MESHLETS); - if(cfg.solidShadeMode == SolidShade::Meshlet) + if(finalVisualisation == Visualisation::Meshlet) { vertexData.meshletCount = (uint32_t)numMeshlets; vertexData.meshletOffset = (uint32_t)cfg.position.meshletOffset; @@ -496,7 +516,7 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon list->SetGraphicsRootConstantBufferView(0, vsCBSolid); - if(solidShadeMode == SolidShade::Lit) + if(finalVisualisation == Visualisation::Lit || finalVisualisation == Visualisation::Explode) { MeshGeometryCBuffer geomData; geomData.InvProj = projMat.Inverse(); @@ -540,7 +560,7 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon } // wireframe render - if(solidShadeMode == SolidShade::NoSolid || cfg.wireframeDraw || + if(finalVisualisation == Visualisation::NoSolid || cfg.wireframeDraw || cfg.position.topology >= Topology::PatchList) { D3D12MarkerRegion region(list, "Wireframe render"); @@ -603,6 +623,8 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const rdcarray &secon pixelData.MeshDisplayFormat = MESHDISPLAY_SOLID; vertexData.homogenousInput = 0U; + vertexData.vtxExploderSNorm = 0.0f; + vertexData.exploderScale = 0.0f; vsCB = GetDebugManager()->UploadConstants(&vertexData, sizeof(vertexData)); diff --git a/renderdoc/driver/gl/gl_rendermesh.cpp b/renderdoc/driver/gl/gl_rendermesh.cpp index ed4efcb02..a017fdfc6 100644 --- a/renderdoc/driver/gl/gl_rendermesh.cpp +++ b/renderdoc/driver/gl/gl_rendermesh.cpp @@ -33,6 +33,16 @@ #define OPENGL 1 #include "data/glsl/glsl_ubos_cpp.h" +static int VisModeToMeshDisplayFormat(const MeshDisplay &cfg) +{ + switch(cfg.visualisationMode) + { + default: return (int)cfg.visualisationMode; + case Visualisation::Secondary: + return cfg.second.showAlpha ? MESHDISPLAY_SECONDARY_ALPHA : MESHDISPLAY_SECONDARY; + } +} + void GLReplay::RenderMesh(uint32_t eventId, const rdcarray &secondaryDraws, const MeshDisplay &cfg) { @@ -113,6 +123,12 @@ void GLReplay::RenderMesh(uint32_t eventId, const rdcarray &secondar uboParams.mvp = ModelViewProj; uboParams.homogenousInput = cfg.position.unproject; uboParams.pointSpriteSize = Vec2f(0.0f, 0.0f); + uboParams.vtxExploderSNorm = cfg.vtxExploderSliderSNorm; + uboParams.exploderScale = + (cfg.visualisationMode == Visualisation::Explode) ? cfg.exploderScale : 0.0f; + uboParams.exploderCentre = + Vec3f((cfg.minBounds.x + cfg.maxBounds.x) * 0.5f, (cfg.minBounds.y + cfg.maxBounds.y) * 0.5f, + (cfg.minBounds.z + cfg.maxBounds.z) * 0.5f); if(!secondaryDraws.empty()) { @@ -333,13 +349,15 @@ void GLReplay::RenderMesh(uint32_t eventId, const rdcarray &secondar drv.glEnable(eGL_DEPTH_TEST); // solid render - if(cfg.solidShadeMode != SolidShade::NoSolid && topo != eGL_PATCHES) + if(cfg.visualisationMode != Visualisation::NoSolid && topo != eGL_PATCHES) { drv.glDepthFunc(eGL_LESS); GLuint solidProg = prog; - if(cfg.solidShadeMode == SolidShade::Lit && DebugData.meshgsProg[0]) + if((cfg.visualisationMode == Visualisation::Lit || + cfg.visualisationMode == Visualisation::Explode) && + DebugData.meshgsProg[0]) { // pick program with GS for per-face lighting solidProg = DebugData.meshgsProg[progidx]; @@ -363,18 +381,11 @@ void GLReplay::RenderMesh(uint32_t eventId, const rdcarray &secondar return; } - soliddata->mvp = ModelViewProj; - soliddata->pointSpriteSize = Vec2f(0.0f, 0.0f); - soliddata->homogenousInput = cfg.position.unproject; + uboParams.color = Vec4f(0.8f, 0.8f, 0.0f, 1.0f); + uboParams.displayFormat = VisModeToMeshDisplayFormat(cfg); + *soliddata = uboParams; - soliddata->color = Vec4f(0.8f, 0.8f, 0.0f, 1.0f); - - uint32_t OutputDisplayFormat = (uint32_t)cfg.solidShadeMode; - if(cfg.solidShadeMode == SolidShade::Secondary && cfg.second.showAlpha) - OutputDisplayFormat = MESHDISPLAY_SECONDARY_ALPHA; - soliddata->displayFormat = OutputDisplayFormat; - - if(cfg.solidShadeMode == SolidShade::Lit) + if(cfg.visualisationMode == Visualisation::Lit || cfg.visualisationMode == Visualisation::Explode) soliddata->invProj = projMat.Inverse(); drv.glUnmapBuffer(eGL_UNIFORM_BUFFER); @@ -417,14 +428,14 @@ void GLReplay::RenderMesh(uint32_t eventId, const rdcarray &secondar drv.glDepthFunc(eGL_ALWAYS); + uboParams.displayFormat = MESHDISPLAY_SOLID; + // wireframe render - if(cfg.solidShadeMode == SolidShade::NoSolid || cfg.wireframeDraw || topo == eGL_PATCHES) + if(cfg.visualisationMode == Visualisation::NoSolid || cfg.wireframeDraw || topo == eGL_PATCHES) { uboParams.color = Vec4f(cfg.position.meshColor.x, cfg.position.meshColor.y, cfg.position.meshColor.z, cfg.position.meshColor.w); - uboParams.displayFormat = MESHDISPLAY_SOLID; - if(!IsGLES) drv.glPolygonMode(eGL_FRONT_AND_BACK, eGL_LINE); @@ -469,6 +480,10 @@ void GLReplay::RenderMesh(uint32_t eventId, const rdcarray &secondar // helpers always use basic float-input program drv.glUseProgram(DebugData.meshProg[0]); + uboParams.vtxExploderSNorm = 0.0f; + uboParams.exploderScale = 0.0f; + uboParams.exploderCentre = Vec3f(); + if(cfg.showBBox) { Vec4f a = Vec4f(cfg.minBounds.x, cfg.minBounds.y, cfg.minBounds.z, cfg.minBounds.w); diff --git a/renderdoc/driver/vulkan/vk_overlay.cpp b/renderdoc/driver/vulkan/vk_overlay.cpp index 0eddfe387..278c2f275 100644 --- a/renderdoc/driver/vulkan/vk_overlay.cpp +++ b/renderdoc/driver/vulkan/vk_overlay.cpp @@ -2732,7 +2732,9 @@ ResourceId VulkanReplay::RenderOverlay(ResourceId texid, FloatVector clearCol, D data->displayFormat = 0; data->rawoutput = 1; data->flipY = 0; - data->padding = Vec2f(); + data->vtxExploderSNorm = 0.0f; + data->exploderScale = 0.0f; + data->exploderCentre = Vec3f(); m_MeshRender.UBO.Unmap(); uint32_t viewOffs = 0; diff --git a/renderdoc/driver/vulkan/vk_rendermesh.cpp b/renderdoc/driver/vulkan/vk_rendermesh.cpp index 7df16e401..1c18077ad 100644 --- a/renderdoc/driver/vulkan/vk_rendermesh.cpp +++ b/renderdoc/driver/vulkan/vk_rendermesh.cpp @@ -37,6 +37,16 @@ RDOC_EXTERN_CONFIG(bool, Vulkan_Debug_SingleSubmitFlushing); +static int VisModeToMeshDisplayFormat(Visualisation vis, bool showAlpha) +{ + switch(vis) + { + default: return (int)vis; + case Visualisation::Secondary: + return showAlpha ? MESHDISPLAY_SECONDARY_ALPHA : MESHDISPLAY_SECONDARY; + } +} + VKMeshDisplayPipelines VulkanDebugManager::CacheMeshDisplayPipelines(VkPipelineLayout pipeLayout, const MeshFormat &primary, const MeshFormat &secondary) @@ -138,7 +148,7 @@ VKMeshDisplayPipelines VulkanDebugManager::CacheMeshDisplayPipelines(VkPipelineL VKMeshDisplayPipelines &cache = m_CachedMeshPipelines[key]; - if(cache.pipes[(uint32_t)SolidShade::NoSolid] != VK_NULL_HANDLE) + if(cache.pipes[(uint32_t)Visualisation::NoSolid] != VK_NULL_HANDLE) return cache; const VkDevDispatchTable *vt = ObjDisp(m_Device); @@ -526,6 +536,25 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco ModelViewProj = projMat.Mul(camMat.Mul(guessProjInv)); } + // can't support secondary shading without a buffer - no pipeline will have been created + const Visualisation finalVisualisation = (cfg.visualisationMode == Visualisation::Secondary && + cfg.second.vertexResourceId == ResourceId()) + ? Visualisation::NoSolid + : cfg.visualisationMode; + + MeshUBOData meshUniforms = {}; + meshUniforms.mvp = ModelViewProj; + meshUniforms.displayFormat = MESHDISPLAY_SOLID; + meshUniforms.homogenousInput = cfg.position.unproject; + meshUniforms.pointSpriteSize = Vec2f(0.0f, 0.0f); + meshUniforms.rawoutput = 0; + meshUniforms.vtxExploderSNorm = cfg.vtxExploderSliderSNorm; + meshUniforms.exploderScale = + (finalVisualisation == Visualisation::Explode) ? cfg.exploderScale : 0.0f; + meshUniforms.exploderCentre = + Vec3f((cfg.minBounds.x + cfg.maxBounds.x) * 0.5f, (cfg.minBounds.y + cfg.maxBounds.y) * 0.5f, + (cfg.minBounds.z + cfg.maxBounds.z) * 0.5f); + uint32_t dynOffs[2] = {}; if(!secondaryDraws.empty()) @@ -543,13 +572,10 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco if(!data) return; - data->mvp = ModelViewProj; - data->color = Vec4f(fmt.meshColor.x, fmt.meshColor.y, fmt.meshColor.z, fmt.meshColor.w); - data->homogenousInput = cfg.position.unproject; - data->pointSpriteSize = Vec2f(0.0f, 0.0f); - data->displayFormat = MESHDISPLAY_SOLID; - data->rawoutput = 0; - data->flipY = (cfg.position.flipY == fmt.flipY) ? 0 : 1; + meshUniforms.color = + Vec4f(fmt.meshColor.x, fmt.meshColor.y, fmt.meshColor.z, fmt.meshColor.w); + meshUniforms.flipY = (cfg.position.flipY == fmt.flipY) ? 0 : 1; + *data = meshUniforms; m_MeshRender.UBO.Unmap(); @@ -663,13 +689,7 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco vt->CmdBindVertexBuffers(Unwrap(cmd), 0, 1, UnwrapPtr(vb), &offs); } - SolidShade solidShadeMode = cfg.solidShadeMode; - - // can't support secondary shading without a buffer - no pipeline will have been created - if(solidShadeMode == SolidShade::Secondary && cfg.second.vertexResourceId == ResourceId()) - solidShadeMode = SolidShade::NoSolid; - - if(solidShadeMode == SolidShade::Secondary) + if(finalVisualisation == Visualisation::Secondary) { VkBuffer vb = m_pDriver->GetResourceManager()->GetCurrentHandle(cfg.second.vertexResourceId); @@ -687,53 +707,49 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco } // solid render - if(solidShadeMode != SolidShade::NoSolid && cfg.position.topology < Topology::PatchList) + if(finalVisualisation != Visualisation::NoSolid && cfg.position.topology < Topology::PatchList) { VkPipeline pipe = VK_NULL_HANDLE; - switch(solidShadeMode) + switch(finalVisualisation) { default: - case SolidShade::Solid: pipe = cache.pipes[VKMeshDisplayPipelines::ePipe_SolidDepth]; break; - case SolidShade::Lit: + case Visualisation::Solid: + pipe = cache.pipes[VKMeshDisplayPipelines::ePipe_SolidDepth]; + break; + case Visualisation::Lit: + case Visualisation::Explode: pipe = cache.pipes[VKMeshDisplayPipelines::ePipe_Lit]; // point list topologies don't have lighting obvious, just render them as solid + // Also, can't support lit rendering without the pipeline - maybe geometry shader wasn't supported. if(pipe == VK_NULL_HANDLE) pipe = cache.pipes[VKMeshDisplayPipelines::ePipe_SolidDepth]; break; - case SolidShade::Secondary: + case Visualisation::Secondary: pipe = cache.pipes[VKMeshDisplayPipelines::ePipe_Secondary]; break; - case SolidShade::Meshlet: pipe = cache.pipes[VKMeshDisplayPipelines::ePipe_SolidDepth]; break; + case Visualisation::Meshlet: + pipe = cache.pipes[VKMeshDisplayPipelines::ePipe_SolidDepth]; + break; } - // can't support lit rendering without the pipeline - maybe geometry shader wasn't supported. - if(solidShadeMode == SolidShade::Lit && pipe == VK_NULL_HANDLE) - pipe = cache.pipes[VKMeshDisplayPipelines::ePipe_SolidDepth]; - MeshUBOData *data = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!data) return; - if(solidShadeMode == SolidShade::Lit) - data->invProj = projMat.Inverse(); + if(finalVisualisation == Visualisation::Lit || finalVisualisation == Visualisation::Explode) + meshUniforms.invProj = projMat.Inverse(); - data->mvp = ModelViewProj; - data->color = Vec4f(0.8f, 0.8f, 0.0f, 1.0f); - data->homogenousInput = cfg.position.unproject; - data->pointSpriteSize = Vec2f(0.0f, 0.0f); - data->displayFormat = (uint32_t)solidShadeMode; - data->rawoutput = 0; + meshUniforms.color = Vec4f(0.8f, 0.8f, 0.0f, 1.0f); + meshUniforms.displayFormat = VisModeToMeshDisplayFormat(finalVisualisation, cfg.second.showAlpha); + meshUniforms.flipY = 0; - if(solidShadeMode == SolidShade::Secondary && cfg.second.showAlpha) - data->displayFormat = MESHDISPLAY_SECONDARY_ALPHA; - - if(solidShadeMode == SolidShade::Meshlet) + if(finalVisualisation == Visualisation::Meshlet) { size_t numMeshlets = RDCMIN(cfg.position.meshletSizes.size(), (size_t)MAX_NUM_MESHLETS); uint32_t *meshletCounts = (uint32_t *)m_MeshRender.MeshletSSBO.Map( &dynOffs[1], AlignUp4(numMeshlets + 4) * sizeof(uint32_t)); - if(!data) + if(!meshletCounts) return; if(cfg.position.meshletSizes.size() > MAX_NUM_MESHLETS) @@ -751,13 +767,15 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco meshletCounts[i] = prefixCount; } - memcpy(&data->meshletColours[0].x, uniqueColors, sizeof(uniqueColors)); - RDCCOMPILE_ASSERT(sizeof(data->meshletColours) == sizeof(uniqueColors), + memcpy(&meshUniforms.meshletColours[0].x, uniqueColors, sizeof(uniqueColors)); + RDCCOMPILE_ASSERT(sizeof(meshUniforms.meshletColours) == sizeof(uniqueColors), "Unique colors array is wrongly sized"); m_MeshRender.MeshletSSBO.Unmap(); } + *data = meshUniforms; + m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, @@ -789,8 +807,10 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco } } + meshUniforms.displayFormat = MESHDISPLAY_SOLID; + // wireframe render - if(solidShadeMode == SolidShade::NoSolid || cfg.wireframeDraw || + if(finalVisualisation == Visualisation::NoSolid || cfg.wireframeDraw || cfg.position.topology >= Topology::PatchList) { Vec4f wireCol = @@ -800,12 +820,8 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco if(!data) return; - data->mvp = ModelViewProj; - data->color = wireCol; - data->displayFormat = (uint32_t)SolidShade::Solid; - data->homogenousInput = cfg.position.unproject; - data->pointSpriteSize = Vec2f(0.0f, 0.0f); - data->rawoutput = 0; + meshUniforms.color = wireCol; + *data = meshUniforms; m_MeshRender.UBO.Unmap(); @@ -851,6 +867,11 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco helper.vertexByteStride = sizeof(Vec4f); + meshUniforms.homogenousInput = 0; + meshUniforms.vtxExploderSNorm = 0.0f; + meshUniforms.exploderScale = 0.0f; + meshUniforms.exploderCentre = Vec3f(); + // cache pipelines for use in drawing wireframe helpers cache = GetDebugManager()->CacheMeshDisplayPipelines(m_MeshRender.PipeLayout, helper, helper); @@ -893,12 +914,8 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco if(!data) return; - data->mvp = ModelViewProj; - data->color = Vec4f(0.2f, 0.2f, 1.0f, 1.0f); - data->displayFormat = (uint32_t)SolidShade::Solid; - data->homogenousInput = 0; - data->pointSpriteSize = Vec2f(0.0f, 0.0f); - data->rawoutput = 0; + meshUniforms.color = Vec4f(0.2f, 0.2f, 1.0f, 1.0f); + *data = meshUniforms; m_MeshRender.UBO.Unmap(); @@ -922,12 +939,8 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco if(!data) return; - data->mvp = ModelViewProj; - data->color = Vec4f(1.0f, 0.0f, 0.0f, 1.0f); - data->displayFormat = (uint32_t)SolidShade::Solid; - data->homogenousInput = 0; - data->pointSpriteSize = Vec2f(0.0f, 0.0f); - data->rawoutput = 0; + meshUniforms.color = Vec4f(1.0f, 0.0f, 0.0f, 1.0f); + *data = meshUniforms; m_MeshRender.UBO.Unmap(); @@ -945,12 +958,8 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco if(!data) return; - data->mvp = ModelViewProj; - data->color = Vec4f(0.0f, 1.0f, 0.0f, 1.0f); - data->displayFormat = (uint32_t)SolidShade::Solid; - data->homogenousInput = 0; - data->pointSpriteSize = Vec2f(0.0f, 0.0f); - data->rawoutput = 0; + meshUniforms.color = Vec4f(0.0f, 1.0f, 0.0f, 1.0f); + *data = meshUniforms; m_MeshRender.UBO.Unmap(); @@ -963,12 +972,8 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco if(!data) return; - data->mvp = ModelViewProj; - data->color = Vec4f(0.0f, 0.0f, 1.0f, 1.0f); - data->displayFormat = (uint32_t)SolidShade::Solid; - data->homogenousInput = 0; - data->pointSpriteSize = Vec2f(0.0f, 0.0f); - data->rawoutput = 0; + meshUniforms.color = Vec4f(0.0f, 0.0f, 1.0f, 1.0f); + *data = meshUniforms; m_MeshRender.UBO.Unmap(); @@ -988,12 +993,8 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco if(!data) return; - data->mvp = ModelViewProj; - data->color = Vec4f(1.0f, 1.0f, 1.0f, 1.0f); - data->displayFormat = (uint32_t)SolidShade::Solid; - data->homogenousInput = 0; - data->pointSpriteSize = Vec2f(0.0f, 0.0f); - data->rawoutput = 0; + meshUniforms.color = Vec4f(1.0f, 1.0f, 1.0f, 1.0f); + *data = meshUniforms; m_MeshRender.UBO.Unmap(); @@ -1088,17 +1089,14 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco else ModelViewProj = projMat.Mul(camMat.Mul(axisMapMat)); - MeshUBOData uniforms = {}; - uniforms.mvp = ModelViewProj; - uniforms.color = Vec4f(1.0f, 1.0f, 1.0f, 1.0f); - uniforms.displayFormat = (uint32_t)SolidShade::Solid; - uniforms.homogenousInput = cfg.position.unproject; - uniforms.pointSpriteSize = Vec2f(0.0f, 0.0f); + meshUniforms.mvp = ModelViewProj; + meshUniforms.color = Vec4f(1.0f, 1.0f, 1.0f, 1.0f); + meshUniforms.homogenousInput = cfg.position.unproject; MeshUBOData *ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!ubodata) return; - *ubodata = uniforms; + *ubodata = meshUniforms; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, @@ -1112,12 +1110,12 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco // render primitives // Draw active primitive (red) - uniforms.color = Vec4f(1.0f, 0.0f, 0.0f, 1.0f); + meshUniforms.color = Vec4f(1.0f, 0.0f, 0.0f, 1.0f); // poke the color (this would be a good candidate for a push constant) ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!ubodata) return; - *ubodata = uniforms; + *ubodata = meshUniforms; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, @@ -1140,12 +1138,12 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco } // Draw adjacent primitives (green) - uniforms.color = Vec4f(0.0f, 1.0f, 0.0f, 1.0f); + meshUniforms.color = Vec4f(0.0f, 1.0f, 0.0f, 1.0f); // poke the color (this would be a good candidate for a push constant) ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!ubodata) return; - *ubodata = uniforms; + *ubodata = meshUniforms; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, @@ -1173,15 +1171,15 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco float scale = 800.0f / float(m_DebugHeight); float asp = float(m_DebugWidth) / float(m_DebugHeight); - uniforms.pointSpriteSize = Vec2f(scale / asp, scale); + meshUniforms.pointSpriteSize = Vec2f(scale / asp, scale); // Draw active vertex (blue) - uniforms.color = Vec4f(0.0f, 0.0f, 1.0f, 1.0f); + meshUniforms.color = Vec4f(0.0f, 0.0f, 1.0f, 1.0f); // poke the color (this would be a good candidate for a push constant) ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!ubodata) return; - *ubodata = uniforms; + *ubodata = meshUniforms; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, @@ -1221,12 +1219,12 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray &seco } // Draw inactive vertices (green) - uniforms.color = Vec4f(0.0f, 1.0f, 0.0f, 1.0f); + meshUniforms.color = Vec4f(0.0f, 1.0f, 0.0f, 1.0f); // poke the color (this would be a good candidate for a push constant) ubodata = (MeshUBOData *)m_MeshRender.UBO.Map(&dynOffs[0]); if(!ubodata) return; - *ubodata = uniforms; + *ubodata = meshUniforms; m_MeshRender.UBO.Unmap(); vt->CmdBindDescriptorSets(Unwrap(cmd), VK_PIPELINE_BIND_POINT_GRAPHICS, Unwrap(m_MeshRender.PipeLayout), 0, 1, diff --git a/util/test/rdtest/shared/Mesh_Zoo.py b/util/test/rdtest/shared/Mesh_Zoo.py index 7f194602c..1fbfb3a43 100644 --- a/util/test/rdtest/shared/Mesh_Zoo.py +++ b/util/test/rdtest/shared/Mesh_Zoo.py @@ -118,7 +118,7 @@ class Mesh_Zoo(): rdtest.log.success("Base rendering is as expected") - self.cfg.solidShadeMode = rd.SolidShade.Secondary + self.cfg.visualisationMode = rd.Visualisation.Secondary self.cfg.wireframeDraw = False # allow for blending with white for the frustum @@ -244,7 +244,7 @@ class Mesh_Zoo(): rdtest.log.success("Rendering of float2 color secondary in instance 1 is as expected") - self.cfg.solidShadeMode = rd.SolidShade.NoSolid + self.cfg.visualisationMode = rd.Visualisation.NoSolid self.cfg.showAllInstances = True self.cache_output() @@ -370,10 +370,10 @@ class Mesh_Zoo(): rdtest.log.success("Point picking is as expected") self.cfg.highlightVert = rd.MeshDisplay.NoHighlight - self.cfg.solidShadeMode = rd.SolidShade.Solid + self.cfg.visualisationMode = rd.Visualisation.Solid self.cache_output() - self.cfg.solidShadeMode = rd.SolidShade.Lit + self.cfg.visualisationMode = rd.Visualisation.Lit self.cache_output() rdtest.log.success("Point solid and lit rendering works as expected") @@ -381,7 +381,7 @@ class Mesh_Zoo(): self.controller.SetFrameEvent(self.find_action("Lines").next.eventId, False) self.cache_output() - self.cfg.solidShadeMode = rd.SolidShade.Lit + self.cfg.visualisationMode = rd.Visualisation.Lit self.cache_output() rdtest.log.success("Lines solid and lit rendering works as expected")