Mesh exploder

New 'Exploded' visualisation mode in BufferViewer with new exploder controls
hidden when not in 'Exploded' mode.

Change 'solidShading' and 'solidShadeMode' to 'visualisation' and
'visualisationMode'.

Hide the 'highlightVerts' widget when using 'Exploded' vis for both
real-estate and practical implementation reasons.
This commit is contained in:
Dan Hawson
2023-12-09 11:40:30 +00:00
committed by Baldur Karlsson
parent da3e2366ac
commit 442b48bb77
23 changed files with 634 additions and 197 deletions
+1 -1
View File
@@ -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.
Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

+1 -1
View File
@@ -61,7 +61,7 @@ Mesh View
.. autoclass:: MeshFormat
:members:
.. autoclass:: SolidShade
.. autoclass:: Visualisation
:members:
.. autoclass:: Camera
+10 -1
View File
@@ -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.
+42
View File
@@ -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<const QAbstractSlider *>(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<const QStyleOptionComboBox *>(opt);
+66 -27
View File
@@ -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);
+4 -1
View File
@@ -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);
+130 -5
View File
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>788</width>
<height>537</height>
<width>1300</width>
<height>837</height>
</rect>
</property>
<property name="windowTitle">
@@ -307,14 +307,14 @@
</widget>
</item>
<item>
<widget class="QLabel" name="solidShadingLabel">
<widget class="QLabel" name="visualisationLabel">
<property name="text">
<string>Solid Shading</string>
<string>Visualisation</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="solidShading"/>
<widget class="QComboBox" name="visualisation"/>
</item>
<item>
<widget class="QToolButton" name="wireframeRender">
@@ -382,6 +382,131 @@
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="vtxExploderLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Exploder</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="vtxExploderSlider">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="baseSize">
<size>
<width>90</width>
<height>30</height>
</size>
</property>
<property name="mouseTracking">
<bool>true</bool>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="toolTip">
<string>Explode vertices</string>
</property>
<property name="accessibleName">
<string>slider name</string>
</property>
<property name="accessibleDescription">
<string>slider description</string>
</property>
<property name="autoFillBackground">
<bool>true</bool>
</property>
<property name="minimum">
<number>-100</number>
</property>
<property name="maximum">
<number>100</number>
</property>
<property name="sliderPosition">
<number>0</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
<property name="invertedControls">
<bool>false</bool>
</property>
<property name="tickPosition">
<enum>QSlider::NoTicks</enum>
</property>
<property name="tickInterval">
<number>20</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="exploderReset">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Reset/zero exploder slider</string>
</property>
<property name="text">
<string>Reset</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="exploderScaleLabel">
<property name="text">
<string>Scale</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="exploderScale">
<property name="toolTip">
<string>Exploder scaling</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.100000000000000</double>
</property>
<property name="maximum">
<double>99.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
+7 -2
View File
@@ -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 <SolidShade>` to use when rendering the current mesh.");
SolidShade solidShadeMode = SolidShade::NoSolid;
DOCUMENT(
"The :class:`visualisation mode <Visualisation>` 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;
};
+8 -3
View File
@@ -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.
+4 -3
View File
@@ -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)
+8 -1
View File
@@ -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);
+6
View File
@@ -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);
+68 -1
View File
@@ -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
+9 -5
View File
@@ -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)
+71
View File
@@ -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<uint4> 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);
}
+29 -13
View File
@@ -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<MeshFormat> &secondaryDraws,
const MeshDisplay &cfg)
{
@@ -58,6 +71,12 @@ void D3D11Replay::RenderMesh(uint32_t eventId, const rdcarray<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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);
+40 -18
View File
@@ -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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &secon
pixelData.MeshDisplayFormat = MESHDISPLAY_SOLID;
vertexData.homogenousInput = 0U;
vertexData.vtxExploderSNorm = 0.0f;
vertexData.exploderScale = 0.0f;
vsCB = GetDebugManager()->UploadConstants(&vertexData, sizeof(vertexData));
+31 -16
View File
@@ -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<MeshFormat> &secondaryDraws,
const MeshDisplay &cfg)
{
@@ -113,6 +123,12 @@ void GLReplay::RenderMesh(uint32_t eventId, const rdcarray<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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);
+3 -1
View File
@@ -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;
+91 -93
View File
@@ -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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<VkBuffer>(cfg.second.vertexResourceId);
@@ -687,53 +707,49 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const rdcarray<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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<MeshFormat> &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,
+5 -5
View File
@@ -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")