diff --git a/qrenderdoc/Code/Resources.h b/qrenderdoc/Code/Resources.h
index 1d538fafb..9e7d68757 100644
--- a/qrenderdoc/Code/Resources.h
+++ b/qrenderdoc/Code/Resources.h
@@ -61,6 +61,7 @@
RESOURCE_DEF(cut, "cut.png") \
RESOURCE_DEF(del, "del.png") \
RESOURCE_DEF(disconnect, "disconnect.png") \
+ RESOURCE_DEF(draw_vertex, "draw_vertex.png") \
RESOURCE_DEF(find, "find.png") \
RESOURCE_DEF(filter, "filter.png") \
RESOURCE_DEF(filter_reapply, "filter_reapply.png") \
diff --git a/qrenderdoc/Resources/draw_vertex.png b/qrenderdoc/Resources/draw_vertex.png
new file mode 100644
index 000000000..bf4d3a37f
Binary files /dev/null and b/qrenderdoc/Resources/draw_vertex.png differ
diff --git a/qrenderdoc/Resources/draw_vertex@2x.png b/qrenderdoc/Resources/draw_vertex@2x.png
new file mode 100644
index 000000000..cc824cddc
Binary files /dev/null and b/qrenderdoc/Resources/draw_vertex@2x.png differ
diff --git a/qrenderdoc/Resources/resources.qrc b/qrenderdoc/Resources/resources.qrc
index 28b44e26a..3860d68eb 100644
--- a/qrenderdoc/Resources/resources.qrc
+++ b/qrenderdoc/Resources/resources.qrc
@@ -81,6 +81,8 @@
del@2x.png
disconnect.png
disconnect@2x.png
+ draw_vertex.png
+ draw_vertex@2x.png
filter.png
filter@2x.png
filter_reapply.png
diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp
index afdd54188..c697630e3 100644
--- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp
+++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.cpp
@@ -152,27 +152,32 @@ VulkanPipelineStateViewer::VulkanPipelineStateViewer(ICaptureContext &ctx,
const QIcon &action_hover = Icons::action_hover();
RDLabel *shaderLabels[] = {
- ui->vsShader, ui->tcsShader, ui->tesShader, ui->gsShader, ui->fsShader, ui->csShader,
+ ui->tsShader, ui->msShader, ui->vsShader, ui->tcsShader,
+ ui->tesShader, ui->gsShader, ui->fsShader, ui->csShader,
};
QToolButton *viewButtons[] = {
- ui->vsShaderViewButton, ui->tcsShaderViewButton, ui->tesShaderViewButton,
- ui->gsShaderViewButton, ui->fsShaderViewButton, ui->csShaderViewButton,
+ ui->tsShaderViewButton, ui->msShaderViewButton, ui->vsShaderViewButton,
+ ui->tcsShaderViewButton, ui->tesShaderViewButton, ui->gsShaderViewButton,
+ ui->fsShaderViewButton, ui->csShaderViewButton,
};
QToolButton *editButtons[] = {
- ui->vsShaderEditButton, ui->tcsShaderEditButton, ui->tesShaderEditButton,
- ui->gsShaderEditButton, ui->fsShaderEditButton, ui->csShaderEditButton,
+ ui->tsShaderEditButton, ui->msShaderEditButton, ui->vsShaderEditButton,
+ ui->tcsShaderEditButton, ui->tesShaderEditButton, ui->gsShaderEditButton,
+ ui->fsShaderEditButton, ui->csShaderEditButton,
};
QToolButton *saveButtons[] = {
- ui->vsShaderSaveButton, ui->tcsShaderSaveButton, ui->tesShaderSaveButton,
- ui->gsShaderSaveButton, ui->fsShaderSaveButton, ui->csShaderSaveButton,
+ ui->tsShaderSaveButton, ui->msShaderSaveButton, ui->vsShaderSaveButton,
+ ui->tcsShaderSaveButton, ui->tesShaderSaveButton, ui->gsShaderSaveButton,
+ ui->fsShaderSaveButton, ui->csShaderSaveButton,
};
QToolButton *messageButtons[] = {
- ui->vsShaderMessagesButton, ui->tcsShaderMessagesButton, ui->tesShaderMessagesButton,
- ui->gsShaderMessagesButton, ui->fsShaderMessagesButton, ui->csShaderMessagesButton,
+ ui->tsShaderMessagesButton, ui->msShaderMessagesButton, ui->vsShaderMessagesButton,
+ ui->tcsShaderMessagesButton, ui->tesShaderMessagesButton, ui->gsShaderMessagesButton,
+ ui->fsShaderMessagesButton, ui->csShaderMessagesButton,
};
QToolButton *viewPredicateBufferButtons[] = {
@@ -181,12 +186,13 @@ VulkanPipelineStateViewer::VulkanPipelineStateViewer(ICaptureContext &ctx,
};
RDTreeWidget *resources[] = {
- ui->vsResources, ui->tcsResources, ui->tesResources,
- ui->gsResources, ui->fsResources, ui->csResources,
+ ui->tsResources, ui->msResources, ui->vsResources, ui->tcsResources,
+ ui->tesResources, ui->gsResources, ui->fsResources, ui->csResources,
};
RDTreeWidget *ubos[] = {
- ui->vsUBOs, ui->tcsUBOs, ui->tesUBOs, ui->gsUBOs, ui->fsUBOs, ui->csUBOs,
+ ui->tsUBOs, ui->msUBOs, ui->vsUBOs, ui->tcsUBOs,
+ ui->tesUBOs, ui->gsUBOs, ui->fsUBOs, ui->csUBOs,
};
// setup FlowLayout for CS shader group, with debugging controls
@@ -440,38 +446,19 @@ VulkanPipelineStateViewer::VulkanPipelineStateViewer(ICaptureContext &ctx,
ui->stagesTabs->tabBar()->setVisible(false);
- ui->pipeFlow->setStages(
- {
- lit("VTX"),
- lit("VS"),
- lit("TCS"),
- lit("TES"),
- lit("GS"),
- lit("RS"),
- lit("FS"),
- lit("FB"),
- lit("CS"),
- },
- {
- tr("Vertex Input"),
- tr("Vertex Shader"),
- tr("Tess. Control Shader"),
- tr("Tess. Eval. Shader"),
- tr("Geometry Shader"),
- tr("Rasterizer"),
- tr("Fragment Shader"),
- tr("Framebuffer Output"),
- tr("Compute Shader"),
- });
-
- ui->pipeFlow->setIsolatedStage(8); // compute shader isolated
-
+ setOldMeshPipeFlow();
ui->pipeFlow->setStagesEnabled({true, true, true, true, true, true, true, true, true});
m_Common.setMeshViewPixmap(ui->meshView);
ui->viAttrs->setFont(Formatter::PreferredFont());
ui->viBuffers->setFont(Formatter::PreferredFont());
+ ui->tsShader->setFont(Formatter::PreferredFont());
+ ui->tsResources->setFont(Formatter::PreferredFont());
+ ui->tsUBOs->setFont(Formatter::PreferredFont());
+ ui->msShader->setFont(Formatter::PreferredFont());
+ ui->msResources->setFont(Formatter::PreferredFont());
+ ui->msUBOs->setFont(Formatter::PreferredFont());
ui->vsShader->setFont(Formatter::PreferredFont());
ui->vsResources->setFont(Formatter::PreferredFont());
ui->vsUBOs->setFont(Formatter::PreferredFont());
@@ -536,6 +523,7 @@ void VulkanPipelineStateViewer::OnCaptureLoaded()
void VulkanPipelineStateViewer::OnCaptureClosed()
{
+ setOldMeshPipeFlow();
ui->pipeFlow->setStagesEnabled({true, true, true, true, true, true, true, true, true});
clearState();
@@ -872,6 +860,10 @@ const VKPipe::Shader *VulkanPipelineStateViewer::stageForSender(QWidget *widget)
return &m_Ctx.CurVulkanPipelineState()->fragmentShader;
if(widget == ui->stagesTabs->widget(8))
return &m_Ctx.CurVulkanPipelineState()->computeShader;
+ if(widget == ui->stagesTabs->widget(9))
+ return &m_Ctx.CurVulkanPipelineState()->taskShader;
+ if(widget == ui->stagesTabs->widget(10))
+ return &m_Ctx.CurVulkanPipelineState()->meshShader;
widget = widget->parentWidget();
}
@@ -881,6 +873,62 @@ const VKPipe::Shader *VulkanPipelineStateViewer::stageForSender(QWidget *widget)
return NULL;
}
+void VulkanPipelineStateViewer::setOldMeshPipeFlow()
+{
+ m_MeshPipe = false;
+
+ ui->pipeFlow->setStages(
+ {
+ lit("VTX"),
+ lit("VS"),
+ lit("TCS"),
+ lit("TES"),
+ lit("GS"),
+ lit("RS"),
+ lit("FS"),
+ lit("FB"),
+ lit("CS"),
+ },
+ {
+ tr("Vertex Input"),
+ tr("Vertex Shader"),
+ tr("Tess. Control Shader"),
+ tr("Tess. Eval. Shader"),
+ tr("Geometry Shader"),
+ tr("Rasterizer"),
+ tr("Fragment Shader"),
+ tr("Framebuffer Output"),
+ tr("Compute Shader"),
+ });
+
+ ui->pipeFlow->setIsolatedStage(8); // compute shader isolated
+}
+
+void VulkanPipelineStateViewer::setNewMeshPipeFlow()
+{
+ m_MeshPipe = true;
+
+ ui->pipeFlow->setStages(
+ {
+ lit("TS"),
+ lit("MS"),
+ lit("RS"),
+ lit("FS"),
+ lit("FB"),
+ lit("CS"),
+ },
+ {
+ tr("Task Shader"),
+ tr("Mesh Shader"),
+ tr("Rasterizer"),
+ tr("Fragment Shader"),
+ tr("Framebuffer Output"),
+ tr("Compute Shader"),
+ });
+
+ ui->pipeFlow->setIsolatedStage(5); // compute shader isolated
+}
+
void VulkanPipelineStateViewer::clearShaderState(RDLabel *shader, RDTreeWidget *resources,
RDTreeWidget *cbuffers)
{
@@ -903,6 +951,8 @@ void VulkanPipelineStateViewer::clearState()
ui->primRestart->setVisible(false);
ui->topologyDiagram->setPixmap(QPixmap());
+ clearShaderState(ui->tsShader, ui->tsResources, ui->tsUBOs);
+ clearShaderState(ui->msShader, ui->msResources, ui->msUBOs);
clearShaderState(ui->vsShader, ui->vsResources, ui->vsUBOs);
clearShaderState(ui->tcsShader, ui->tcsResources, ui->tcsUBOs);
clearShaderState(ui->tesShader, ui->tesResources, ui->tesUBOs);
@@ -913,20 +963,42 @@ void VulkanPipelineStateViewer::clearState()
ui->xfbBuffers->clear();
QToolButton *shaderButtons[] = {
- ui->vsShaderViewButton, ui->tcsShaderViewButton, ui->tesShaderViewButton,
- ui->gsShaderViewButton, ui->fsShaderViewButton, ui->csShaderViewButton,
- ui->vsShaderEditButton, ui->tcsShaderEditButton, ui->tesShaderEditButton,
- ui->gsShaderEditButton, ui->fsShaderEditButton, ui->csShaderEditButton,
- ui->vsShaderSaveButton, ui->tcsShaderSaveButton, ui->tesShaderSaveButton,
- ui->gsShaderSaveButton, ui->fsShaderSaveButton, ui->csShaderSaveButton,
+ // view buttons
+ ui->tsShaderViewButton,
+ ui->msShaderViewButton,
+ ui->vsShaderViewButton,
+ ui->tcsShaderViewButton,
+ ui->tesShaderViewButton,
+ ui->gsShaderViewButton,
+ ui->fsShaderViewButton,
+ ui->csShaderViewButton,
+ // edit buttons
+ ui->tsShaderEditButton,
+ ui->msShaderEditButton,
+ ui->vsShaderEditButton,
+ ui->tcsShaderEditButton,
+ ui->tesShaderEditButton,
+ ui->gsShaderEditButton,
+ ui->fsShaderEditButton,
+ ui->csShaderEditButton,
+ // save buttons
+ ui->tsShaderSaveButton,
+ ui->msShaderSaveButton,
+ ui->vsShaderSaveButton,
+ ui->tcsShaderSaveButton,
+ ui->tesShaderSaveButton,
+ ui->gsShaderSaveButton,
+ ui->fsShaderSaveButton,
+ ui->csShaderSaveButton,
};
for(QToolButton *b : shaderButtons)
b->setEnabled(false);
QToolButton *messageButtons[] = {
- ui->vsShaderMessagesButton, ui->tcsShaderMessagesButton, ui->tesShaderMessagesButton,
- ui->gsShaderMessagesButton, ui->fsShaderMessagesButton, ui->csShaderMessagesButton,
+ ui->tsShaderMessagesButton, ui->msShaderMessagesButton, ui->vsShaderMessagesButton,
+ ui->tcsShaderMessagesButton, ui->tesShaderMessagesButton, ui->gsShaderMessagesButton,
+ ui->fsShaderMessagesButton, ui->csShaderMessagesButton,
};
for(QToolButton *b : messageButtons)
@@ -2173,287 +2245,367 @@ void VulkanPipelineStateViewer::setState()
bool usedBindings[128] = {};
+ // highlight the appropriate stages in the flowchart
+ if(action == NULL)
+ {
+ setOldMeshPipeFlow();
+ ui->pipeFlow->setStagesEnabled({true, true, true, true, true, true, true, true, true});
+ }
+ else if(action->flags & ActionFlags::Dispatch)
+ {
+ setOldMeshPipeFlow();
+ ui->pipeFlow->setStagesEnabled({false, false, false, false, false, false, false, false, true});
+ }
+ else if(action->flags & ActionFlags::MeshDispatch)
+ {
+ setNewMeshPipeFlow();
+ ui->pipeFlow->setStagesEnabled(
+ {state.taskShader.resourceId != ResourceId(), true, true, true, true, false});
+ }
+ else
+ {
+ bool xfbActive = !state.transformFeedback.buffers.isEmpty();
+
+ bool raster = true;
+
+ if(state.rasterizer.rasterizerDiscardEnable)
+ {
+ raster = false;
+ }
+
+ if(state.geometryShader.resourceId == ResourceId() && xfbActive)
+ {
+ ui->pipeFlow->setStageName(4, lit("XFB"), tr("Transform Feedback"));
+ }
+ else
+ {
+ ui->pipeFlow->setStageName(4, lit("GS"), tr("Geometry Shader"));
+ }
+
+ setOldMeshPipeFlow();
+ ui->pipeFlow->setStagesEnabled(
+ {true, true, state.tessControlShader.resourceId != ResourceId(),
+ state.tessEvalShader.resourceId != ResourceId(),
+ state.geometryShader.resourceId != ResourceId() || xfbActive, raster,
+ raster && state.fragmentShader.resourceId != ResourceId(), raster, false});
+ }
+
////////////////////////////////////////////////
// Vertex Input
int vs = 0;
- vs = ui->viAttrs->verticalScrollBar()->value();
- ui->viAttrs->beginUpdate();
- ui->viAttrs->clear();
+ if(m_MeshPipe)
{
- int i = 0;
- for(const VKPipe::VertexAttribute &a : state.vertexInput.attributes)
+ setShaderState(state.taskShader, state.graphics, ui->tsShader, ui->tsResources, ui->tsUBOs);
+ setShaderState(state.meshShader, state.graphics, ui->msShader, ui->msResources, ui->msUBOs);
+
+ ui->msTopology->setText(ToQStr(state.meshShader.reflection->outputTopology));
+ }
+ else
+ {
+ vs = ui->viAttrs->verticalScrollBar()->value();
+ ui->viAttrs->beginUpdate();
+ ui->viAttrs->clear();
{
- bool usedSlot = false;
-
- QString name = tr("Attribute %1").arg(i);
-
- if(state.vertexShader.resourceId != ResourceId())
+ int i = 0;
+ for(const VKPipe::VertexAttribute &a : state.vertexInput.attributes)
{
- int attrib = -1;
- if((int32_t)a.location < state.vertexShader.bindpointMapping.inputAttributes.count())
- attrib = state.vertexShader.bindpointMapping.inputAttributes[a.location];
+ bool usedSlot = false;
- if(attrib >= 0 && attrib < state.vertexShader.reflection->inputSignature.count())
+ QString name = tr("Attribute %1").arg(i);
+
+ if(state.vertexShader.resourceId != ResourceId())
{
- name = state.vertexShader.reflection->inputSignature[attrib].varName;
- usedSlot = true;
+ int attrib = -1;
+ if((int32_t)a.location < state.vertexShader.bindpointMapping.inputAttributes.count())
+ attrib = state.vertexShader.bindpointMapping.inputAttributes[a.location];
+
+ if(attrib >= 0 && attrib < state.vertexShader.reflection->inputSignature.count())
+ {
+ name = state.vertexShader.reflection->inputSignature[attrib].varName;
+ usedSlot = true;
+ }
}
+
+ if(showNode(usedSlot, /*filledSlot*/ true))
+ {
+ RDTreeWidgetItem *node = new RDTreeWidgetItem(
+ {i, name, a.location, a.binding, a.format.Name(), a.byteOffset, QString()});
+
+ node->setTag(i);
+
+ usedBindings[a.binding] = true;
+
+ if(!usedSlot)
+ setInactiveRow(node);
+
+ ui->viAttrs->addTopLevelItem(node);
+ }
+
+ i++;
}
-
- if(showNode(usedSlot, /*filledSlot*/ true))
- {
- RDTreeWidgetItem *node = new RDTreeWidgetItem(
- {i, name, a.location, a.binding, a.format.Name(), a.byteOffset, QString()});
-
- node->setTag(i);
-
- usedBindings[a.binding] = true;
-
- if(!usedSlot)
- setInactiveRow(node);
-
- ui->viAttrs->addTopLevelItem(node);
- }
-
- i++;
}
- }
- ui->viAttrs->clearSelection();
- ui->viAttrs->endUpdate();
- ui->viAttrs->verticalScrollBar()->setValue(vs);
+ ui->viAttrs->clearSelection();
+ ui->viAttrs->endUpdate();
+ ui->viAttrs->verticalScrollBar()->setValue(vs);
- m_BindNodes.clear();
- m_VBNodes.clear();
- m_EmptyNodes.clear();
+ m_BindNodes.clear();
+ m_VBNodes.clear();
+ m_EmptyNodes.clear();
- int numCPs = PatchList_Count(state.inputAssembly.topology);
- if(numCPs > 0)
- {
- ui->topology->setText(tr("PatchList (%1 Control Points)").arg(numCPs));
- }
- else
- {
- ui->topology->setText(ToQStr(state.inputAssembly.topology));
- }
-
- m_Common.setTopologyDiagram(ui->topologyDiagram, state.inputAssembly.topology);
-
- ui->primRestart->setVisible(state.inputAssembly.primitiveRestartEnable);
-
- vs = ui->viBuffers->verticalScrollBar()->value();
- ui->viBuffers->beginUpdate();
- ui->viBuffers->clear();
-
- bool ibufferUsed = action != NULL && (action->flags & ActionFlags::Indexed);
-
- if(state.inputAssembly.indexBuffer.resourceId != ResourceId())
- {
- if(ibufferUsed || showUnused)
+ int numCPs = PatchList_Count(state.inputAssembly.topology);
+ if(numCPs > 0)
{
- uint64_t length = 1;
-
- if(!ibufferUsed)
- length = 0;
-
- BufferDescription *buf = m_Ctx.GetBuffer(state.inputAssembly.indexBuffer.resourceId);
-
- if(buf)
- length = buf->length;
-
- RDTreeWidgetItem *node = new RDTreeWidgetItem(
- {tr("Index"), state.inputAssembly.indexBuffer.resourceId, tr("Index"), lit("-"),
- (qulonglong)state.inputAssembly.indexBuffer.byteOffset,
- (qulonglong)state.inputAssembly.indexBuffer.byteStride, (qulonglong)length, QString()});
-
- QString iformat;
-
- if(state.inputAssembly.indexBuffer.byteStride == 1)
- iformat = lit("ubyte");
- else if(state.inputAssembly.indexBuffer.byteStride == 2)
- iformat = lit("ushort");
- else if(state.inputAssembly.indexBuffer.byteStride == 4)
- iformat = lit("uint");
-
- iformat +=
- lit(" indices[%1]").arg(RENDERDOC_NumVerticesPerPrimitive(state.inputAssembly.topology));
-
- node->setTag(QVariant::fromValue(VulkanVBIBTag(
- state.inputAssembly.indexBuffer.resourceId,
- state.inputAssembly.indexBuffer.byteOffset +
- (action ? action->indexOffset * state.inputAssembly.indexBuffer.byteStride : 0),
- iformat)));
-
- if(!ibufferUsed)
- setInactiveRow(node);
-
- if(state.inputAssembly.indexBuffer.resourceId == ResourceId())
- {
- setEmptyRow(node);
- m_EmptyNodes.push_back(node);
- }
-
- ui->viBuffers->addTopLevelItem(node);
+ ui->topology->setText(tr("PatchList (%1 Control Points)").arg(numCPs));
}
- }
- else
- {
- if(ibufferUsed || showEmpty)
+ else
{
- RDTreeWidgetItem *node = new RDTreeWidgetItem({tr("Index"), ResourceId(), tr("Index"), lit("-"),
- lit("-"), lit("-"), lit("-"), QString()});
-
- QString iformat;
-
- if(state.inputAssembly.indexBuffer.byteStride == 1)
- iformat = lit("ubyte");
- else if(state.inputAssembly.indexBuffer.byteStride == 2)
- iformat = lit("ushort");
- else if(state.inputAssembly.indexBuffer.byteStride == 4)
- iformat = lit("uint");
-
- iformat +=
- lit(" indices[%1]").arg(RENDERDOC_NumVerticesPerPrimitive(state.inputAssembly.topology));
-
- node->setTag(QVariant::fromValue(VulkanVBIBTag(
- state.inputAssembly.indexBuffer.resourceId,
- state.inputAssembly.indexBuffer.byteOffset +
- (action ? action->indexOffset * state.inputAssembly.indexBuffer.byteStride : 0),
- iformat)));
-
- setEmptyRow(node);
- m_EmptyNodes.push_back(node);
-
- if(!ibufferUsed)
- setInactiveRow(node);
-
- ui->viBuffers->addTopLevelItem(node);
+ ui->topology->setText(ToQStr(state.inputAssembly.topology));
}
- }
- {
- int i = 0;
- for(; i < qMax(state.vertexInput.vertexBuffers.count(), state.vertexInput.bindings.count()); i++)
+ m_Common.setTopologyDiagram(ui->topologyDiagram, state.inputAssembly.topology);
+
+ ui->primRestart->setVisible(state.inputAssembly.primitiveRestartEnable);
+
+ vs = ui->viBuffers->verticalScrollBar()->value();
+ ui->viBuffers->beginUpdate();
+ ui->viBuffers->clear();
+
+ bool ibufferUsed = action != NULL && (action->flags & ActionFlags::Indexed);
+
+ if(state.inputAssembly.indexBuffer.resourceId != ResourceId())
{
- const VKPipe::VertexBuffer *vbuff =
- (i < state.vertexInput.vertexBuffers.count() ? &state.vertexInput.vertexBuffers[i] : NULL);
- const VKPipe::VertexBinding *bind = NULL;
-
- for(int b = 0; b < state.vertexInput.bindings.count(); b++)
+ if(ibufferUsed || showUnused)
{
- if(state.vertexInput.bindings[b].vertexBufferBinding == (uint32_t)i)
- bind = &state.vertexInput.bindings[b];
- }
-
- bool filledSlot = ((vbuff != NULL && vbuff->resourceId != ResourceId()) || bind != NULL);
- bool usedSlot = (usedBindings[i]);
-
- if(showNode(usedSlot, filledSlot))
- {
- QString rate = lit("-");
uint64_t length = 1;
- uint64_t offset = 0;
- uint32_t stride = 0;
- uint32_t divisor = 1;
- if(vbuff != NULL)
- {
- offset = vbuff->byteOffset;
- stride = vbuff->byteStride;
- length = vbuff->byteSize;
+ if(!ibufferUsed)
+ length = 0;
- BufferDescription *buf = m_Ctx.GetBuffer(vbuff->resourceId);
- if(buf && length >= ULONG_MAX)
- length = buf->length;
- }
+ BufferDescription *buf = m_Ctx.GetBuffer(state.inputAssembly.indexBuffer.resourceId);
- if(bind != NULL)
- {
- rate = bind->perInstance ? tr("Instance") : tr("Vertex");
- if(bind->perInstance)
- divisor = bind->instanceDivisor;
- }
- else
- {
- rate += tr("No Binding");
- }
+ if(buf)
+ length = buf->length;
- RDTreeWidgetItem *node = NULL;
+ RDTreeWidgetItem *node = new RDTreeWidgetItem(
+ {tr("Index"), state.inputAssembly.indexBuffer.resourceId, tr("Index"), lit("-"),
+ (qulonglong)state.inputAssembly.indexBuffer.byteOffset,
+ (qulonglong)state.inputAssembly.indexBuffer.byteStride, (qulonglong)length, QString()});
- if(filledSlot)
- node = new RDTreeWidgetItem({i, vbuff->resourceId, rate, divisor, (qulonglong)offset,
- stride, (qulonglong)length, QString()});
- else
- node = new RDTreeWidgetItem(
- {i, tr("No Binding"), lit("-"), lit("-"), lit("-"), lit("-"), lit("-"), QString()});
+ QString iformat;
+
+ if(state.inputAssembly.indexBuffer.byteStride == 1)
+ iformat = lit("ubyte");
+ else if(state.inputAssembly.indexBuffer.byteStride == 2)
+ iformat = lit("ushort");
+ else if(state.inputAssembly.indexBuffer.byteStride == 4)
+ iformat = lit("uint");
+
+ iformat +=
+ lit(" indices[%1]").arg(RENDERDOC_NumVerticesPerPrimitive(state.inputAssembly.topology));
node->setTag(QVariant::fromValue(VulkanVBIBTag(
- vbuff != NULL ? vbuff->resourceId : ResourceId(), vbuff != NULL ? vbuff->byteOffset : 0,
- m_Common.GetVBufferFormatString(i))));
+ state.inputAssembly.indexBuffer.resourceId,
+ state.inputAssembly.indexBuffer.byteOffset +
+ (action ? action->indexOffset * state.inputAssembly.indexBuffer.byteStride : 0),
+ iformat)));
- if(!filledSlot || bind == NULL || vbuff == NULL || vbuff->resourceId == ResourceId())
+ if(!ibufferUsed)
+ setInactiveRow(node);
+
+ if(state.inputAssembly.indexBuffer.resourceId == ResourceId())
{
setEmptyRow(node);
m_EmptyNodes.push_back(node);
}
- if(!usedSlot)
- setInactiveRow(node);
-
- m_VBNodes.push_back(node);
-
ui->viBuffers->addTopLevelItem(node);
}
- else
- {
- m_VBNodes.push_back(NULL);
- }
}
-
- for(; i < (int)ARRAY_COUNT(usedBindings); i++)
+ else
{
- if(usedBindings[i])
+ if(ibufferUsed || showEmpty)
{
- RDTreeWidgetItem *node = new RDTreeWidgetItem(
- {i, tr("No Binding"), lit("-"), lit("-"), lit("-"), lit("-"), lit("-"), QString()});
+ RDTreeWidgetItem *node =
+ new RDTreeWidgetItem({tr("Index"), ResourceId(), tr("Index"), lit("-"), lit("-"),
+ lit("-"), lit("-"), QString()});
- node->setTag(QVariant::fromValue(VulkanVBIBTag(ResourceId(), 0)));
+ QString iformat;
+
+ if(state.inputAssembly.indexBuffer.byteStride == 1)
+ iformat = lit("ubyte");
+ else if(state.inputAssembly.indexBuffer.byteStride == 2)
+ iformat = lit("ushort");
+ else if(state.inputAssembly.indexBuffer.byteStride == 4)
+ iformat = lit("uint");
+
+ iformat +=
+ lit(" indices[%1]").arg(RENDERDOC_NumVerticesPerPrimitive(state.inputAssembly.topology));
+
+ node->setTag(QVariant::fromValue(VulkanVBIBTag(
+ state.inputAssembly.indexBuffer.resourceId,
+ state.inputAssembly.indexBuffer.byteOffset +
+ (action ? action->indexOffset * state.inputAssembly.indexBuffer.byteStride : 0),
+ iformat)));
setEmptyRow(node);
m_EmptyNodes.push_back(node);
- setInactiveRow(node);
+ if(!ibufferUsed)
+ setInactiveRow(node);
ui->viBuffers->addTopLevelItem(node);
-
- m_VBNodes.push_back(node);
- }
- else
- {
- m_VBNodes.push_back(NULL);
}
}
- }
- ui->viBuffers->clearSelection();
- ui->viBuffers->endUpdate();
- ui->viBuffers->verticalScrollBar()->setValue(vs);
- setShaderState(state.vertexShader, state.graphics, ui->vsShader, ui->vsResources, ui->vsUBOs);
- setShaderState(state.geometryShader, state.graphics, ui->gsShader, ui->gsResources, ui->gsUBOs);
- setShaderState(state.tessControlShader, state.graphics, ui->tcsShader, ui->tcsResources,
- ui->tcsUBOs);
- setShaderState(state.tessEvalShader, state.graphics, ui->tesShader, ui->tesResources, ui->tesUBOs);
+ {
+ int i = 0;
+ for(; i < qMax(state.vertexInput.vertexBuffers.count(), state.vertexInput.bindings.count()); i++)
+ {
+ const VKPipe::VertexBuffer *vbuff =
+ (i < state.vertexInput.vertexBuffers.count() ? &state.vertexInput.vertexBuffers[i]
+ : NULL);
+ const VKPipe::VertexBinding *bind = NULL;
+
+ for(int b = 0; b < state.vertexInput.bindings.count(); b++)
+ {
+ if(state.vertexInput.bindings[b].vertexBufferBinding == (uint32_t)i)
+ bind = &state.vertexInput.bindings[b];
+ }
+
+ bool filledSlot = ((vbuff != NULL && vbuff->resourceId != ResourceId()) || bind != NULL);
+ bool usedSlot = (usedBindings[i]);
+
+ if(showNode(usedSlot, filledSlot))
+ {
+ QString rate = lit("-");
+ uint64_t length = 1;
+ uint64_t offset = 0;
+ uint32_t stride = 0;
+ uint32_t divisor = 1;
+
+ if(vbuff != NULL)
+ {
+ offset = vbuff->byteOffset;
+ stride = vbuff->byteStride;
+ length = vbuff->byteSize;
+
+ BufferDescription *buf = m_Ctx.GetBuffer(vbuff->resourceId);
+ if(buf && length >= ULONG_MAX)
+ length = buf->length;
+ }
+
+ if(bind != NULL)
+ {
+ rate = bind->perInstance ? tr("Instance") : tr("Vertex");
+ if(bind->perInstance)
+ divisor = bind->instanceDivisor;
+ }
+ else
+ {
+ rate += tr("No Binding");
+ }
+
+ RDTreeWidgetItem *node = NULL;
+
+ if(filledSlot)
+ node = new RDTreeWidgetItem({i, vbuff->resourceId, rate, divisor, (qulonglong)offset,
+ stride, (qulonglong)length, QString()});
+ else
+ node = new RDTreeWidgetItem(
+ {i, tr("No Binding"), lit("-"), lit("-"), lit("-"), lit("-"), lit("-"), QString()});
+
+ node->setTag(QVariant::fromValue(VulkanVBIBTag(
+ vbuff != NULL ? vbuff->resourceId : ResourceId(),
+ vbuff != NULL ? vbuff->byteOffset : 0, m_Common.GetVBufferFormatString(i))));
+
+ if(!filledSlot || bind == NULL || vbuff == NULL || vbuff->resourceId == ResourceId())
+ {
+ setEmptyRow(node);
+ m_EmptyNodes.push_back(node);
+ }
+
+ if(!usedSlot)
+ setInactiveRow(node);
+
+ m_VBNodes.push_back(node);
+
+ ui->viBuffers->addTopLevelItem(node);
+ }
+ else
+ {
+ m_VBNodes.push_back(NULL);
+ }
+ }
+
+ for(; i < (int)ARRAY_COUNT(usedBindings); i++)
+ {
+ if(usedBindings[i])
+ {
+ RDTreeWidgetItem *node = new RDTreeWidgetItem(
+ {i, tr("No Binding"), lit("-"), lit("-"), lit("-"), lit("-"), lit("-"), QString()});
+
+ node->setTag(QVariant::fromValue(VulkanVBIBTag(ResourceId(), 0)));
+
+ setEmptyRow(node);
+ m_EmptyNodes.push_back(node);
+
+ setInactiveRow(node);
+
+ ui->viBuffers->addTopLevelItem(node);
+
+ m_VBNodes.push_back(node);
+ }
+ else
+ {
+ m_VBNodes.push_back(NULL);
+ }
+ }
+ }
+ ui->viBuffers->clearSelection();
+ ui->viBuffers->endUpdate();
+ ui->viBuffers->verticalScrollBar()->setValue(vs);
+
+ setShaderState(state.vertexShader, state.graphics, ui->vsShader, ui->vsResources, ui->vsUBOs);
+ setShaderState(state.geometryShader, state.graphics, ui->gsShader, ui->gsResources, ui->gsUBOs);
+ setShaderState(state.tessControlShader, state.graphics, ui->tcsShader, ui->tcsResources,
+ ui->tcsUBOs);
+ setShaderState(state.tessEvalShader, state.graphics, ui->tesShader, ui->tesResources,
+ ui->tesUBOs);
+ }
+
setShaderState(state.fragmentShader, state.graphics, ui->fsShader, ui->fsResources, ui->fsUBOs);
setShaderState(state.computeShader, state.compute, ui->csShader, ui->csResources, ui->csUBOs);
QToolButton *shaderButtons[] = {
- ui->vsShaderViewButton, ui->tcsShaderViewButton, ui->tesShaderViewButton,
- ui->gsShaderViewButton, ui->fsShaderViewButton, ui->csShaderViewButton,
- ui->vsShaderEditButton, ui->tcsShaderEditButton, ui->tesShaderEditButton,
- ui->gsShaderEditButton, ui->fsShaderEditButton, ui->csShaderEditButton,
- ui->vsShaderSaveButton, ui->tcsShaderSaveButton, ui->tesShaderSaveButton,
- ui->gsShaderSaveButton, ui->fsShaderSaveButton, ui->csShaderSaveButton,
+ // view buttons
+ ui->tsShaderViewButton,
+ ui->msShaderViewButton,
+ ui->vsShaderViewButton,
+ ui->tcsShaderViewButton,
+ ui->tesShaderViewButton,
+ ui->gsShaderViewButton,
+ ui->fsShaderViewButton,
+ ui->csShaderViewButton,
+ // edit buttons
+ ui->tsShaderEditButton,
+ ui->msShaderEditButton,
+ ui->vsShaderEditButton,
+ ui->tcsShaderEditButton,
+ ui->tesShaderEditButton,
+ ui->gsShaderEditButton,
+ ui->fsShaderEditButton,
+ ui->csShaderEditButton,
+ // save buttons
+ ui->tsShaderSaveButton,
+ ui->msShaderSaveButton,
+ ui->vsShaderSaveButton,
+ ui->tcsShaderSaveButton,
+ ui->tesShaderSaveButton,
+ ui->gsShaderSaveButton,
+ ui->fsShaderSaveButton,
+ ui->csShaderSaveButton,
};
for(QToolButton *b : shaderButtons)
@@ -2475,9 +2627,10 @@ void VulkanPipelineStateViewer::setState()
QToolButton *messageButtons[] = {
ui->vsShaderMessagesButton, ui->tcsShaderMessagesButton, ui->tesShaderMessagesButton,
ui->gsShaderMessagesButton, ui->fsShaderMessagesButton, ui->csShaderMessagesButton,
+ ui->tsShaderMessagesButton, ui->msShaderMessagesButton,
};
- int numMessages[6] = {};
+ int numMessages[NumShaderStages] = {};
for(const ShaderMessage &msg : state.shaderMessages)
numMessages[(uint32_t)msg.stage]++;
@@ -3185,42 +3338,6 @@ void VulkanPipelineStateViewer::setState()
else
ui->computeDebugSelector->setToolTip(tr("Invalid dispatch/threadgroup dimensions."));
}
-
- // highlight the appropriate stages in the flowchart
- if(action == NULL)
- {
- ui->pipeFlow->setStagesEnabled({true, true, true, true, true, true, true, true, true});
- }
- else if(action->flags & ActionFlags::Dispatch)
- {
- ui->pipeFlow->setStagesEnabled({false, false, false, false, false, false, false, false, true});
- }
- else
- {
- bool xfbActive = !state.transformFeedback.buffers.isEmpty();
-
- bool raster = true;
-
- if(state.rasterizer.rasterizerDiscardEnable)
- {
- raster = false;
- }
-
- if(state.geometryShader.resourceId == ResourceId() && xfbActive)
- {
- ui->pipeFlow->setStageName(4, lit("XFB"), tr("Transform Feedback"));
- }
- else
- {
- ui->pipeFlow->setStageName(4, lit("GS"), tr("Geometry Shader"));
- }
-
- ui->pipeFlow->setStagesEnabled(
- {true, true, state.tessControlShader.resourceId != ResourceId(),
- state.tessEvalShader.resourceId != ResourceId(),
- state.geometryShader.resourceId != ResourceId() || xfbActive, raster,
- raster && state.fragmentShader.resourceId != ResourceId(), raster, false});
- }
}
void VulkanPipelineStateViewer::resource_itemActivated(RDTreeWidgetItem *item, int column)
@@ -3494,7 +3611,26 @@ void VulkanPipelineStateViewer::vertex_leave(QEvent *e)
void VulkanPipelineStateViewer::on_pipeFlow_stageSelected(int index)
{
- ui->stagesTabs->setCurrentIndex(index);
+ if(m_MeshPipe)
+ {
+ // remap since TS/MS are the last tabs but appear first in the flow
+ switch(index)
+ {
+ // TS
+ case 0: ui->stagesTabs->setCurrentIndex(9); break;
+ // MS
+ case 1: ui->stagesTabs->setCurrentIndex(10); break;
+ // raster onwards are the same, just skipping VTX,VS,TCS,TES,GS
+ case 2: ui->stagesTabs->setCurrentIndex(5); break;
+ case 3: ui->stagesTabs->setCurrentIndex(6); break;
+ case 4: ui->stagesTabs->setCurrentIndex(7); break;
+ case 5: ui->stagesTabs->setCurrentIndex(8); break;
+ }
+ }
+ else
+ {
+ ui->stagesTabs->setCurrentIndex(index);
+ }
}
void VulkanPipelineStateViewer::shaderView_clicked()
@@ -4573,6 +4709,12 @@ QString VulkanPipelineStateViewer::GetBufferForFossilize(const SDObject *obj)
case ShaderStageMask::Compute:
ret = ReconstructSpecializationData(pipe->computeShader, mapEntries);
break;
+ case ShaderStageMask::Task:
+ ret = ReconstructSpecializationData(pipe->taskShader, mapEntries);
+ break;
+ case ShaderStageMask::Mesh:
+ ret = ReconstructSpecializationData(pipe->meshShader, mapEntries);
+ break;
default: break;
}
}
@@ -5021,8 +5163,9 @@ void VulkanPipelineStateViewer::exportFOZ(QString dir, ResourceId pso)
// we don't care which reflection we get, as long as the ID matches
for(const VKPipe::Shader *sh :
- {&pipe->vertexShader, &pipe->tessControlShader, &pipe->tessEvalShader,
- &pipe->geometryShader, &pipe->fragmentShader, &pipe->computeShader})
+ {&pipe->taskShader, &pipe->meshShader, &pipe->vertexShader, &pipe->tessControlShader,
+ &pipe->tessEvalShader, &pipe->geometryShader, &pipe->fragmentShader,
+ &pipe->computeShader})
{
if(sh->resourceId == id)
spirv = &sh->reflection->rawBytes;
@@ -5073,7 +5216,7 @@ void VulkanPipelineStateViewer::exportFOZ_clicked()
if(m_Ctx.CurAction()->flags & ActionFlags::Dispatch)
pso = m_Ctx.CurVulkanPipelineState()->compute.pipelineResourceId;
- else if(m_Ctx.CurAction()->flags & ActionFlags::Drawcall)
+ else if(m_Ctx.CurAction()->flags & (ActionFlags::MeshDispatch | ActionFlags::Drawcall))
pso = m_Ctx.CurVulkanPipelineState()->graphics.pipelineResourceId;
if(pso == ResourceId())
@@ -5120,53 +5263,90 @@ void VulkanPipelineStateViewer::exportHTML_clicked()
xml.writeCharacters(sn);
xml.writeEndElement();
- switch(stage)
+ if(m_MeshPipe)
{
- case 0:
- // VTX
- xml.writeStartElement(lit("h2"));
- xml.writeCharacters(tr("Input Assembly"));
- xml.writeEndElement();
- exportHTML(xml, m_Ctx.CurVulkanPipelineState()->inputAssembly);
+ switch(stage)
+ {
+ case 0: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->taskShader); break;
+ case 1: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->meshShader); break;
+ case 2:
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->rasterizer);
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->conditionalRendering);
+ break;
+ case 3: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->fragmentShader); break;
+ case 4:
+ // FB
+ xml.writeStartElement(lit("h2"));
+ xml.writeCharacters(tr("Color Blend"));
+ xml.writeEndElement();
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->colorBlend);
- xml.writeStartElement(lit("h2"));
- xml.writeCharacters(tr("Vertex Input"));
- xml.writeEndElement();
- exportHTML(xml, m_Ctx.CurVulkanPipelineState()->vertexInput);
- break;
- case 1: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->vertexShader); break;
- case 2: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->tessControlShader); break;
- case 3: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->tessEvalShader); break;
- case 4:
- exportHTML(xml, m_Ctx.CurVulkanPipelineState()->geometryShader);
- exportHTML(xml, m_Ctx.CurVulkanPipelineState()->transformFeedback);
- break;
- case 5:
- exportHTML(xml, m_Ctx.CurVulkanPipelineState()->rasterizer);
- exportHTML(xml, m_Ctx.CurVulkanPipelineState()->conditionalRendering);
- break;
- case 6: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->fragmentShader); break;
- case 7:
- // FB
- xml.writeStartElement(lit("h2"));
- xml.writeCharacters(tr("Color Blend"));
- xml.writeEndElement();
- exportHTML(xml, m_Ctx.CurVulkanPipelineState()->colorBlend);
+ xml.writeStartElement(lit("h2"));
+ xml.writeCharacters(tr("Depth Stencil"));
+ xml.writeEndElement();
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->depthStencil);
- xml.writeStartElement(lit("h2"));
- xml.writeCharacters(tr("Depth Stencil"));
- xml.writeEndElement();
- exportHTML(xml, m_Ctx.CurVulkanPipelineState()->depthStencil);
+ xml.writeStartElement(lit("h2"));
+ xml.writeCharacters(tr("Current Pass"));
+ xml.writeEndElement();
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->currentPass);
+ break;
+ case 5:
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->computeShader);
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->conditionalRendering);
+ break;
+ }
+ }
+ else
+ {
+ switch(stage)
+ {
+ case 0:
+ // VTX
+ xml.writeStartElement(lit("h2"));
+ xml.writeCharacters(tr("Input Assembly"));
+ xml.writeEndElement();
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->inputAssembly);
- xml.writeStartElement(lit("h2"));
- xml.writeCharacters(tr("Current Pass"));
- xml.writeEndElement();
- exportHTML(xml, m_Ctx.CurVulkanPipelineState()->currentPass);
- break;
- case 8:
- exportHTML(xml, m_Ctx.CurVulkanPipelineState()->computeShader);
- exportHTML(xml, m_Ctx.CurVulkanPipelineState()->conditionalRendering);
- break;
+ xml.writeStartElement(lit("h2"));
+ xml.writeCharacters(tr("Vertex Input"));
+ xml.writeEndElement();
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->vertexInput);
+ break;
+ case 1: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->vertexShader); break;
+ case 2: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->tessControlShader); break;
+ case 3: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->tessEvalShader); break;
+ case 4:
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->geometryShader);
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->transformFeedback);
+ break;
+ case 5:
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->rasterizer);
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->conditionalRendering);
+ break;
+ case 6: exportHTML(xml, m_Ctx.CurVulkanPipelineState()->fragmentShader); break;
+ case 7:
+ // FB
+ xml.writeStartElement(lit("h2"));
+ xml.writeCharacters(tr("Color Blend"));
+ xml.writeEndElement();
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->colorBlend);
+
+ xml.writeStartElement(lit("h2"));
+ xml.writeCharacters(tr("Depth Stencil"));
+ xml.writeEndElement();
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->depthStencil);
+
+ xml.writeStartElement(lit("h2"));
+ xml.writeCharacters(tr("Current Pass"));
+ xml.writeEndElement();
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->currentPass);
+ break;
+ case 8:
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->computeShader);
+ exportHTML(xml, m_Ctx.CurVulkanPipelineState()->conditionalRendering);
+ break;
+ }
}
xml.writeEndElement();
@@ -5178,6 +5358,13 @@ void VulkanPipelineStateViewer::exportHTML_clicked()
}
}
+void VulkanPipelineStateViewer::on_msMeshButton_clicked()
+{
+ if(!m_Ctx.HasMeshPreview())
+ m_Ctx.ShowMeshPreview();
+ ToolWindowManager::raiseToolWindow(m_Ctx.GetMeshPreview()->Widget());
+}
+
void VulkanPipelineStateViewer::on_meshView_clicked()
{
if(!m_Ctx.HasMeshPreview())
diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h
index 40b5a8b03..db38b387a 100644
--- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h
+++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.h
@@ -71,6 +71,7 @@ private slots:
void on_showEmpty_toggled(bool checked);
void on_meshView_clicked();
+ void on_msMeshButton_clicked();
void on_viAttrs_itemActivated(RDTreeWidgetItem *item, int column);
void on_viBuffers_itemActivated(RDTreeWidgetItem *item, int column);
void on_viAttrs_mouseMove(QMouseEvent *event);
@@ -100,6 +101,9 @@ private:
PipelineStateViewer &m_Common;
ComputeDebugSelector *m_ComputeDebugSelector;
+ void setOldMeshPipeFlow();
+ void setNewMeshPipeFlow();
+
QVariantList makeSampler(const QString &bindset, const QString &slotname,
const VKPipe::BindingElement &descriptor);
void addResourceRow(ShaderReflection *shaderDetails, const VKPipe::Shader &stage, int bindset,
@@ -170,4 +174,6 @@ private:
// from an combined image to its sampler (since we de-duplicate)
QMap m_CombinedImageSamplers;
+
+ bool m_MeshPipe = false;
};
diff --git a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui
index 48d79433f..9d60a5e5e 100644
--- a/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui
+++ b/qrenderdoc/Windows/PipelineState/VulkanPipelineStateViewer.ui
@@ -4132,6 +4132,664 @@
+
+
+ Task Shader
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Shader
+
+
+
+ 0
+
+
+ 0
+
+
+ 4
+
+
-
+
+
+ QFrame::Box
+
+
+
+
+
+
+ -
+
+
+ PointingHandCursor
+
+
+ Open Shader Source
+
+
+ View
+
+
+
+ :/action.png:/action.png
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ true
+
+
+
+ -
+
+
+ Edit Shader
+
+
+ Edit
+
+
+
+ :/page_white_edit.png:/page_white_edit.png
+
+
+ QToolButton::MenuButtonPopup
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ true
+
+
+
+ -
+
+
+ Save Shader SPIR-V
+
+
+ Save
+
+
+
+ :/save.png:/save.png
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ true
+
+
+
+ -
+
+
+ View Shader Messages
+
+
+ Messages
+
+
+
+ :/text_add.png:/text_add.png
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 96
+ 172
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 1
+
+
+
+ Resources
+
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
-
+
+
+ QFrame::Box
+
+
+ QFrame::Plain
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ false
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 1
+
+
+
+ Uniform Buffers
+
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
-
+
+
+ QFrame::Box
+
+
+ QFrame::Plain
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ false
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::MinimumExpanding
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+ Mesh Shader
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ Shader
+
+
+
+ 0
+
+
+ 0
+
+
+ 4
+
+
-
+
+
+ QFrame::Box
+
+
+
+
+
+
+ -
+
+
+ PointingHandCursor
+
+
+ Open Shader Source
+
+
+ View
+
+
+
+ :/action.png:/action.png
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ true
+
+
+
+ -
+
+
+ Edit Shader
+
+
+ Edit
+
+
+
+ :/page_white_edit.png:/page_white_edit.png
+
+
+ QToolButton::MenuButtonPopup
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ true
+
+
+
+ -
+
+
+ Save Shader SPIR-V
+
+
+ Save
+
+
+
+ :/save.png:/save.png
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ true
+
+
+
+ -
+
+
+ View Shader Messages
+
+
+ Messages
+
+
+
+ :/text_add.png:/text_add.png
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ true
+
+
+
+ -
+
+
+ View Mesh
+
+
+ View Mesh
+
+
+
+ :/draw_vertex.png:/draw_vertex.png
+
+
+ Qt::ToolButtonTextBesideIcon
+
+
+ true
+
+
+
+ -
+
+
+ Output Topology:
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ true
+
+
+
+
+ 0
+ 0
+ 96
+ 172
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 1
+
+
+
+ Resources
+
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
-
+
+
+ QFrame::Box
+
+
+ QFrame::Plain
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ false
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 1
+
+
+
+ Uniform Buffers
+
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
+ 2
+
+
-
+
+
+ QFrame::Box
+
+
+ QFrame::Plain
+
+
+ QAbstractItemView::NoEditTriggers
+
+
+ false
+
+
+ true
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+ QSizePolicy::MinimumExpanding
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+
+
+
+
diff --git a/qrenderdoc/qrenderdoc_local.vcxproj b/qrenderdoc/qrenderdoc_local.vcxproj
index 69e70f376..6c4a83a92 100644
--- a/qrenderdoc/qrenderdoc_local.vcxproj
+++ b/qrenderdoc/qrenderdoc_local.vcxproj
@@ -1980,6 +1980,8 @@
+
+
diff --git a/qrenderdoc/qrenderdoc_local.vcxproj.filters b/qrenderdoc/qrenderdoc_local.vcxproj.filters
index 20bc3d922..d669a7451 100644
--- a/qrenderdoc/qrenderdoc_local.vcxproj.filters
+++ b/qrenderdoc/qrenderdoc_local.vcxproj.filters
@@ -1978,6 +1978,12 @@
Resources\Files
+
+ Resources\Files
+
+
+ Resources\Files
+
diff --git a/renderdoc/api/replay/pipestate.inl b/renderdoc/api/replay/pipestate.inl
index 5a272c16d..2083ae8bf 100644
--- a/renderdoc/api/replay/pipestate.inl
+++ b/renderdoc/api/replay/pipestate.inl
@@ -147,7 +147,9 @@ bool PipeState::IsVulkanStage(ShaderStage stage) const
case ShaderStage::Hull:
case ShaderStage::Geometry:
case ShaderStage::Pixel:
- case ShaderStage::Compute: return true;
+ case ShaderStage::Compute:
+ case ShaderStage::Task:
+ case ShaderStage::Mesh: return true;
default: return false;
}
}
@@ -223,6 +225,10 @@ const VKPipe::Shader &PipeState::GetVulkanStage(ShaderStage stage) const
return m_Vulkan->fragmentShader;
if(stage == ShaderStage::Compute)
return m_Vulkan->computeShader;
+ if(stage == ShaderStage::Task)
+ return m_Vulkan->taskShader;
+ if(stage == ShaderStage::Mesh)
+ return m_Vulkan->meshShader;
RENDERDOC_LogMessage(LogType::Error, "PIPE", __FILE__, __LINE__, "Error - invalid stage");
return m_Vulkan->computeShader;
@@ -335,6 +341,8 @@ const ShaderBindpointMapping &PipeState::GetBindpointMapping(ShaderStage stage)
case ShaderStage::Geometry: return m_Vulkan->geometryShader.bindpointMapping;
case ShaderStage::Fragment: return m_Vulkan->fragmentShader.bindpointMapping;
case ShaderStage::Compute: return m_Vulkan->computeShader.bindpointMapping;
+ case ShaderStage::Task: return m_Vulkan->taskShader.bindpointMapping;
+ case ShaderStage::Mesh: return m_Vulkan->meshShader.bindpointMapping;
default: break;
}
}
@@ -398,6 +406,8 @@ const ShaderReflection *PipeState::GetShaderReflection(ShaderStage stage) const
case ShaderStage::Geometry: return m_Vulkan->geometryShader.reflection;
case ShaderStage::Fragment: return m_Vulkan->fragmentShader.reflection;
case ShaderStage::Compute: return m_Vulkan->computeShader.reflection;
+ case ShaderStage::Task: return m_Vulkan->taskShader.reflection;
+ case ShaderStage::Mesh: return m_Vulkan->meshShader.reflection;
default: break;
}
}
@@ -456,6 +466,8 @@ rdcstr PipeState::GetShaderEntryPoint(ShaderStage stage) const
case ShaderStage::Geometry: return m_Vulkan->geometryShader.entryPoint;
case ShaderStage::Fragment: return m_Vulkan->fragmentShader.entryPoint;
case ShaderStage::Compute: return m_Vulkan->computeShader.entryPoint;
+ case ShaderStage::Task: return m_Vulkan->taskShader.entryPoint;
+ case ShaderStage::Mesh: return m_Vulkan->meshShader.entryPoint;
default: break;
}
}
@@ -516,6 +528,8 @@ ResourceId PipeState::GetShader(ShaderStage stage) const
case ShaderStage::Geometry: return m_Vulkan->geometryShader.resourceId;
case ShaderStage::Fragment: return m_Vulkan->fragmentShader.resourceId;
case ShaderStage::Compute: return m_Vulkan->computeShader.resourceId;
+ case ShaderStage::Task: return m_Vulkan->taskShader.resourceId;
+ case ShaderStage::Mesh: return m_Vulkan->meshShader.resourceId;
default: break;
}
}
@@ -1183,6 +1197,14 @@ BoundCBuffer PipeState::GetConstantBuffer(ShaderStage stage, uint32_t BufIdx, ui
ret.byteOffset = m_Vulkan->computeShader.pushConstantRangeByteOffset;
ret.byteSize = m_Vulkan->computeShader.pushConstantRangeByteSize;
break;
+ case ShaderStage::Task:
+ ret.byteOffset = m_Vulkan->taskShader.pushConstantRangeByteOffset;
+ ret.byteSize = m_Vulkan->taskShader.pushConstantRangeByteSize;
+ break;
+ case ShaderStage::Mesh:
+ ret.byteOffset = m_Vulkan->meshShader.pushConstantRangeByteOffset;
+ ret.byteSize = m_Vulkan->meshShader.pushConstantRangeByteSize;
+ break;
default: break;
}
}
diff --git a/renderdoc/driver/ihv/nv/nv_vk_counters.cpp b/renderdoc/driver/ihv/nv/nv_vk_counters.cpp
index 1e6ebfc52..019c2ad1d 100644
--- a/renderdoc/driver/ihv/nv/nv_vk_counters.cpp
+++ b/renderdoc/driver/ihv/nv/nv_vk_counters.cpp
@@ -233,34 +233,40 @@ struct VulkanNvidiaActionCallback final : public VulkanActionCallback
m_driver->SetActionCB(this);
}
~VulkanNvidiaActionCallback() { m_driver->SetActionCB(NULL); }
- void PreDraw(uint32_t eid, VkCommandBuffer cmd) final
+ void PreDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) final
{
rdcstr eidName = StringFormat::Fmt("%d", eid);
nv::perf::profiler::VulkanPushRange(Unwrap(cmd), eidName.c_str());
}
- bool PostDraw(uint32_t eid, VkCommandBuffer cmd) final
+ bool PostDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) final
{
nv::perf::profiler::VulkanPopRange(Unwrap(cmd));
return false;
}
- void PostRedraw(uint32_t eid, VkCommandBuffer cmd) final {}
- void PreDispatch(uint32_t eid, VkCommandBuffer cmd) final { PreDraw(eid, cmd); }
- bool PostDispatch(uint32_t eid, VkCommandBuffer cmd) final { return PostDraw(eid, cmd); }
- void PostRedispatch(uint32_t eid, VkCommandBuffer cmd) final {}
+ void PostRedraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) final {}
+ void PreDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) final
+ {
+ PreDraw(eid, flags, cmd);
+ }
+ bool PostDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) final
+ {
+ return PostDraw(eid, flags, cmd);
+ }
+ void PostRedispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) final {}
void PreMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) final
{
if(flags & ActionFlags::PassBoundary)
return;
- PreDraw(eid, cmd);
+ PreDraw(eid, flags, cmd);
}
bool PostMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) final
{
if(flags & ActionFlags::PassBoundary)
return false;
- return PostDraw(eid, cmd);
+ return PostDraw(eid, flags, cmd);
}
void PostRemisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) final {}
diff --git a/renderdoc/driver/vulkan/vk_common.cpp b/renderdoc/driver/vulkan/vk_common.cpp
index bd6e445b8..7ea7c3c5b 100644
--- a/renderdoc/driver/vulkan/vk_common.cpp
+++ b/renderdoc/driver/vulkan/vk_common.cpp
@@ -541,6 +541,8 @@ int StageIndex(VkShaderStageFlagBits stageFlag)
case VK_SHADER_STAGE_GEOMETRY_BIT: return 3;
case VK_SHADER_STAGE_FRAGMENT_BIT: return 4;
case VK_SHADER_STAGE_COMPUTE_BIT: return 5;
+ case VK_SHADER_STAGE_TASK_BIT_EXT: return 6;
+ case VK_SHADER_STAGE_MESH_BIT_EXT: return 7;
default: RDCERR("Unrecognised/not single flag %x", stageFlag); break;
}
@@ -556,6 +558,8 @@ VkShaderStageFlags ShaderMaskFromIndex(size_t index)
VK_SHADER_STAGE_GEOMETRY_BIT,
VK_SHADER_STAGE_FRAGMENT_BIT,
VK_SHADER_STAGE_COMPUTE_BIT,
+ VK_SHADER_STAGE_TASK_BIT_EXT,
+ VK_SHADER_STAGE_MESH_BIT_EXT,
};
if(index < ARRAY_COUNT(mask))
diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h
index 20b2c55f0..52fee2190 100644
--- a/renderdoc/driver/vulkan/vk_common.h
+++ b/renderdoc/driver/vulkan/vk_common.h
@@ -975,6 +975,9 @@ enum class VulkanChunk : uint32_t
vkCmdSetSampleLocationsEnableEXT,
vkCmdSetSampleMaskEXT,
vkCmdSetTessellationDomainOriginEXT,
+ vkCmdDrawMeshTasksEXT,
+ vkCmdDrawMeshTasksIndirectEXT,
+ vkCmdDrawMeshTasksIndirectCountEXT,
Max,
};
@@ -1248,6 +1251,8 @@ DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceMaintenance4Properties);
DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceMemoryBudgetPropertiesEXT);
DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceMemoryPriorityFeaturesEXT);
DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceMemoryProperties2);
+DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceMeshShaderFeaturesEXT);
+DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceMeshShaderPropertiesEXT);
DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT);
DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceMultiviewFeatures);
DECLARE_REFLECTION_STRUCT(VkPhysicalDeviceMultiviewProperties);
@@ -1658,6 +1663,8 @@ DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceMaintenance4Properties);
DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceMemoryBudgetPropertiesEXT);
DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceMemoryPriorityFeaturesEXT);
DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceMemoryProperties2);
+DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceMeshShaderFeaturesEXT);
+DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceMeshShaderPropertiesEXT);
DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT);
DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceMultiviewFeatures);
DECLARE_DESERIALISE_TYPE(VkPhysicalDeviceMultiviewProperties);
@@ -1872,6 +1879,7 @@ DECLARE_REFLECTION_STRUCT(VkDisplayPlanePropertiesKHR);
DECLARE_REFLECTION_STRUCT(VkDisplayPropertiesKHR);
DECLARE_REFLECTION_STRUCT(VkDrawIndexedIndirectCommand);
DECLARE_REFLECTION_STRUCT(VkDrawIndirectCommand);
+DECLARE_REFLECTION_STRUCT(VkDrawMeshTasksIndirectCommandEXT);
DECLARE_REFLECTION_STRUCT(VkExtent2D);
DECLARE_REFLECTION_STRUCT(VkExtent3D);
DECLARE_REFLECTION_STRUCT(VkExternalMemoryProperties);
diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp
index bd5db48db..167010a32 100644
--- a/renderdoc/driver/vulkan/vk_core.cpp
+++ b/renderdoc/driver/vulkan/vk_core.cpp
@@ -649,10 +649,10 @@ uint32_t WrappedVulkan::HandlePreCallback(VkCommandBuffer commandBuffer, ActionF
eventId += multiDrawOffset;
- if(type == ActionFlags::Drawcall)
- m_ActionCallback->PreDraw(eventId, commandBuffer);
+ if(type == ActionFlags::MeshDispatch || type == ActionFlags::Drawcall)
+ m_ActionCallback->PreDraw(eventId, type, commandBuffer);
else if(type == ActionFlags::Dispatch)
- m_ActionCallback->PreDispatch(eventId, commandBuffer);
+ m_ActionCallback->PreDispatch(eventId, type, commandBuffer);
else
m_ActionCallback->PreMisc(eventId, type, commandBuffer);
@@ -1094,6 +1094,10 @@ static const VkExtensionProperties supportedExtensions[] = {
VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME,
VK_EXT_MEMORY_PRIORITY_SPEC_VERSION,
},
+ {
+ VK_EXT_MESH_SHADER_EXTENSION_NAME,
+ VK_EXT_MESH_SHADER_SPEC_VERSION,
+ },
#ifdef VK_EXT_metal_surface
{
VK_EXT_METAL_SURFACE_EXTENSION_NAME,
@@ -3915,6 +3919,13 @@ bool WrappedVulkan::ProcessChunk(ReadSerialiser &ser, VulkanChunk chunk)
case VulkanChunk::vkCmdSetAttachmentFeedbackLoopEnableEXT:
return Serialise_vkCmdSetAttachmentFeedbackLoopEnableEXT(ser, VK_NULL_HANDLE,
VK_IMAGE_ASPECT_NONE);
+ case VulkanChunk::vkCmdDrawMeshTasksEXT:
+ return Serialise_vkCmdDrawMeshTasksEXT(ser, VK_NULL_HANDLE, 0, 0, 0);
+ case VulkanChunk::vkCmdDrawMeshTasksIndirectEXT:
+ return Serialise_vkCmdDrawMeshTasksIndirectEXT(ser, VK_NULL_HANDLE, VK_NULL_HANDLE, 0, 0, 0);
+ case VulkanChunk::vkCmdDrawMeshTasksIndirectCountEXT:
+ return Serialise_vkCmdDrawMeshTasksIndirectCountEXT(ser, VK_NULL_HANDLE, VK_NULL_HANDLE, 0,
+ VK_NULL_HANDLE, 0, 0, 0);
case VulkanChunk::vkCmdSetAlphaToCoverageEnableEXT:
return Serialise_vkCmdSetAlphaToCoverageEnableEXT(ser, VK_NULL_HANDLE, VK_FALSE);
@@ -4179,7 +4190,8 @@ void WrappedVulkan::ReplayLog(uint32_t startEventID, uint32_t endEventID, Replay
{
rpUnneeded = true;
}
- else if(!(action->flags & (ActionFlags::Drawcall | ActionFlags::Dispatch)))
+ else if(!(action->flags &
+ (ActionFlags::MeshDispatch | ActionFlags::Drawcall | ActionFlags::Dispatch)))
{
rpUnneeded = true;
}
@@ -4940,33 +4952,36 @@ void WrappedVulkan::AddUsage(VulkanActionTreeNode &actionNode, rdcarray shaderStages;
if(action.flags & ActionFlags::Dispatch)
{
- shaderStart = 5;
- shaderEnd = 6;
+ shaderStages = {5};
}
else if(action.flags & ActionFlags::Drawcall)
{
- shaderStart = 0;
- shaderEnd = 5;
+ shaderStages = {0, 1, 2, 3, 4, 5};
+ }
+ else if(action.flags & ActionFlags::MeshDispatch)
+ {
+ shaderStages = {4, 6, 7};
}
- for(int shad = shaderStart; shad < shaderEnd; shad++)
+ for(int shad : shaderStages)
{
bool compute = (shad == 5);
ResourceId pipe = (compute ? state.compute.pipeline : state.graphics.pipeline);
@@ -5152,7 +5168,8 @@ void WrappedVulkan::AddUsage(VulkanActionTreeNode &actionNode, rdcarrayCmdDrawMeshTasksEXT(Unwrap(cmd), action.dispatchDimension[0],
+ action.dispatchDimension[1], action.dispatchDimension[2]);
+ else if(action.flags & ActionFlags::Indexed)
ObjDisp(cmd)->CmdDrawIndexed(Unwrap(cmd), action.numIndices, action.numInstances,
action.indexOffset, action.baseVertex, action.instanceOffset);
else
@@ -5432,7 +5452,17 @@ void WrappedVulkan::ReplayDraw(VkCommandBuffer cmd, const ActionDescription &act
bytebuf params;
- if(action.flags & ActionFlags::Indexed)
+ if(action.flags & ActionFlags::MeshDispatch)
+ {
+ VkDrawMeshTasksIndirectCommandEXT drawParams;
+ drawParams.groupCountX = action.dispatchDimension[0];
+ drawParams.groupCountY = action.dispatchDimension[1];
+ drawParams.groupCountZ = action.dispatchDimension[2];
+
+ params.resize(sizeof(drawParams));
+ memcpy(params.data(), &drawParams, sizeof(drawParams));
+ }
+ else if(action.flags & ActionFlags::Indexed)
{
VkDrawIndexedIndirectCommand drawParams;
drawParams.indexCount = action.numIndices;
@@ -5498,7 +5528,11 @@ void WrappedVulkan::ReplayDraw(VkCommandBuffer cmd, const ActionDescription &act
DoPipelineBarrier(cmd, 1, &bufBarrier);
- if(action.flags & ActionFlags::Indexed)
+ if(action.flags & ActionFlags::MeshDispatch)
+ ObjDisp(cmd)->CmdDrawMeshTasksIndirectEXT(Unwrap(cmd), Unwrap(m_IndirectBuffer.buf),
+ m_IndirectBufferSize, action.drawIndex + 1,
+ (uint32_t)params.size());
+ else if(action.flags & ActionFlags::Indexed)
ObjDisp(cmd)->CmdDrawIndexedIndirect(Unwrap(cmd), Unwrap(m_IndirectBuffer.buf),
m_IndirectBufferSize, action.drawIndex + 1,
(uint32_t)params.size());
diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h
index 8f4c3b999..c65f55ad4 100644
--- a/renderdoc/driver/vulkan/vk_core.h
+++ b/renderdoc/driver/vulkan/vk_core.h
@@ -68,6 +68,8 @@ enum class VkIndirectPatchType
DrawIndirectCount,
DrawIndexedIndirectCount,
DrawIndirectByteCount,
+ MeshIndirect,
+ MeshIndirectCount,
};
enum class EventFlags : uint32_t
@@ -77,6 +79,8 @@ enum class EventFlags : uint32_t
TessControlRWUsage = 1 << uint32_t(ShaderStage::Tess_Control),
TessEvalRWUsage = 1 << uint32_t(ShaderStage::Tess_Eval),
GeometryRWUsage = 1 << uint32_t(ShaderStage::Geometry),
+ TaskRWUsage = 1 << uint32_t(ShaderStage::Task),
+ MeshRWUsage = 1 << uint32_t(ShaderStage::Mesh),
};
BITMASK_OPERATORS(EventFlags);
@@ -88,7 +92,8 @@ constexpr inline EventFlags PipeStageRWEventFlags(ShaderStage stage)
inline EventFlags PipeRWUsageEventFlags(ResourceUsage usage)
{
- if(usage >= ResourceUsage::VS_RWResource && usage <= ResourceUsage::GS_RWResource)
+ if((usage >= ResourceUsage::VS_RWResource && usage <= ResourceUsage::GS_RWResource) ||
+ usage == ResourceUsage::TS_RWResource || usage == ResourceUsage::MS_RWResource)
return PipeStageRWEventFlags(
ShaderStage(uint32_t(usage) - uint32_t(ResourceUsage::VS_RWResource)));
return EventFlags::NoFlags;
@@ -213,14 +218,14 @@ struct VulkanActionCallback
// and do the real draw by returning true. OR they can do nothing in PreDraw,
// do the real draw, then in PostDraw return true to apply the modifications
// which are then undone in PostRedraw.
- virtual void PreDraw(uint32_t eid, VkCommandBuffer cmd) = 0;
- virtual bool PostDraw(uint32_t eid, VkCommandBuffer cmd) = 0;
- virtual void PostRedraw(uint32_t eid, VkCommandBuffer cmd) = 0;
+ virtual void PreDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) = 0;
+ virtual bool PostDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) = 0;
+ virtual void PostRedraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) = 0;
// same principle as above, but for dispatch calls
- virtual void PreDispatch(uint32_t eid, VkCommandBuffer cmd) = 0;
- virtual bool PostDispatch(uint32_t eid, VkCommandBuffer cmd) = 0;
- virtual void PostRedispatch(uint32_t eid, VkCommandBuffer cmd) = 0;
+ virtual void PreDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) = 0;
+ virtual bool PostDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) = 0;
+ virtual void PostRedispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) = 0;
// finally, these are for copy/blit/resolve/clear/etc
virtual void PreMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) = 0;
@@ -468,6 +473,9 @@ private:
bool m_DynColorWrite = false;
bool m_DynVertexInput = false;
bool m_DynAttachmentLoop = false;
+ bool m_MeshQueries = false;
+ bool m_MeshShaders = false;
+ bool m_TaskShaders = false;
PFN_vkSetDeviceLoaderData m_SetDeviceLoaderData;
@@ -1262,6 +1270,9 @@ public:
bool DynamicColorWrite() const { return m_DynColorWrite; }
bool DynamicVertexInput() const { return m_DynVertexInput; }
bool DynamicAttachmentLoop() const { return m_DynAttachmentLoop; }
+ bool MeshQueries() const { return m_MeshQueries; }
+ bool TaskShaders() const { return m_TaskShaders; }
+ bool MeshShaders() const { return m_MeshShaders; }
VulkanRenderState &GetRenderState() { return m_RenderState; }
void SetActionCB(VulkanActionCallback *cb) { m_ActionCallback = cb; }
void SetSubmitChain(void *submitChain) { m_SubmitChain = submitChain; }
@@ -2775,4 +2786,15 @@ public:
const VkViewportSwizzleNV *pViewportSwizzles);
void vkCmdSetViewportWScalingEnableNV(VkCommandBuffer commandBuffer,
VkBool32 viewportWScalingEnable);
+
+ // VK_EXT_mesh_shader
+ IMPLEMENT_FUNCTION_SERIALISED(void, vkCmdDrawMeshTasksEXT, VkCommandBuffer commandBuffer,
+ uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
+ IMPLEMENT_FUNCTION_SERIALISED(void, vkCmdDrawMeshTasksIndirectEXT, VkCommandBuffer commandBuffer,
+ VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount,
+ uint32_t stride);
+ IMPLEMENT_FUNCTION_SERIALISED(void, vkCmdDrawMeshTasksIndirectCountEXT,
+ VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
+ VkBuffer countBuffer, VkDeviceSize countBufferOffset,
+ uint32_t maxDrawCount, uint32_t stride);
};
diff --git a/renderdoc/driver/vulkan/vk_counters.cpp b/renderdoc/driver/vulkan/vk_counters.cpp
index 3bdb1d76e..c6630931a 100644
--- a/renderdoc/driver/vulkan/vk_counters.cpp
+++ b/renderdoc/driver/vulkan/vk_counters.cpp
@@ -145,6 +145,14 @@ rdcarray VulkanReplay::EnumerateCounters()
ret.push_back(GPUCounter::GSInvocations);
ret.push_back(GPUCounter::PSInvocations);
ret.push_back(GPUCounter::CSInvocations);
+
+ if(m_pDriver->MeshQueries())
+ {
+ if(m_pDriver->TaskShaders())
+ ret.push_back(GPUCounter::TSInvocations);
+ if(m_pDriver->MeshShaders())
+ ret.push_back(GPUCounter::MSInvocations);
+ }
}
if(m_pDriver->GetPhysicalDevicePerformanceQueryFeatures().performanceCounterQueryPools)
@@ -339,6 +347,20 @@ CounterDescription VulkanReplay::DescribeCounter(GPUCounter counterID)
desc.resultType = CompType::UInt;
desc.unit = CounterUnit::Absolute;
break;
+ case GPUCounter::TSInvocations:
+ desc.name = "TS Invocations";
+ desc.description = "Number of times a task shader was invoked.";
+ desc.resultByteWidth = 8;
+ desc.resultType = CompType::UInt;
+ desc.unit = CounterUnit::Absolute;
+ break;
+ case GPUCounter::MSInvocations:
+ desc.name = "MS Invocations";
+ desc.description = "Number of times a mesh shader was invoked.";
+ desc.resultByteWidth = 8;
+ desc.resultType = CompType::UInt;
+ desc.unit = CounterUnit::Absolute;
+ break;
default:
desc.name = "Unknown";
desc.description = "Unknown counter ID";
@@ -361,7 +383,7 @@ struct VulkanAMDActionCallback : public VulkanActionCallback
}
virtual ~VulkanAMDActionCallback() { m_pDriver->SetActionCB(NULL); }
- void PreDraw(uint32_t eid, VkCommandBuffer cmd) override
+ void PreDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
{
m_pEventIds->push_back(eid);
@@ -379,7 +401,7 @@ struct VulkanAMDActionCallback : public VulkanActionCallback
++*m_pSampleId;
}
- bool PostDraw(uint32_t eid, VkCommandBuffer cmd) override
+ bool PostDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
{
VkCommandBuffer realCmdBuffer = Unwrap(cmd);
@@ -400,28 +422,37 @@ struct VulkanAMDActionCallback : public VulkanActionCallback
}
}
- void PostRedraw(uint32_t eid, VkCommandBuffer cmd) override {}
+ void PostRedraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override {}
// we don't need to distinguish, call the Draw functions
- void PreDispatch(uint32_t eid, VkCommandBuffer cmd) override { PreDraw(eid, cmd); }
- bool PostDispatch(uint32_t eid, VkCommandBuffer cmd) override { return PostDraw(eid, cmd); }
- void PostRedispatch(uint32_t eid, VkCommandBuffer cmd) override { PostRedraw(eid, cmd); }
+ void PreDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
+ {
+ PreDraw(eid, flags, cmd);
+ }
+ bool PostDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
+ {
+ return PostDraw(eid, flags, cmd);
+ }
+ void PostRedispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
+ {
+ PostRedraw(eid, flags, cmd);
+ }
void PreMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
{
if(flags & ActionFlags::PassBoundary)
return;
- PreDraw(eid, cmd);
+ PreDraw(eid, flags, cmd);
}
bool PostMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
{
if(flags & ActionFlags::PassBoundary)
return false;
- return PostDraw(eid, cmd);
+ return PostDraw(eid, flags, cmd);
}
void PostRemisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
{
if(flags & ActionFlags::PassBoundary)
return;
- PostRedraw(eid, cmd);
+ PostRedraw(eid, flags, cmd);
}
void AliasEvent(uint32_t primary, uint32_t alias) override
@@ -562,31 +593,43 @@ struct VulkanKHRCallback : public VulkanActionCallback
m_pDriver->SetActionCB(this);
}
~VulkanKHRCallback() { m_pDriver->SetActionCB(NULL); }
- void PreDraw(uint32_t eid, VkCommandBuffer cmd) override
+ void PreDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
{
ObjDisp(cmd)->CmdBeginQuery(Unwrap(cmd), m_QueryPool, (uint32_t)m_Results.size(), 0);
}
- bool PostDraw(uint32_t eid, VkCommandBuffer cmd) override
+ bool PostDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
{
ObjDisp(cmd)->CmdEndQuery(Unwrap(cmd), m_QueryPool, (uint32_t)m_Results.size());
m_Results.push_back(eid);
return false;
}
- void PostRedraw(uint32_t eid, VkCommandBuffer cmd) override {}
+ void PostRedraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override {}
// we don't need to distinguish, call the Draw functions
- void PreDispatch(uint32_t eid, VkCommandBuffer cmd) override { PreDraw(eid, cmd); }
- bool PostDispatch(uint32_t eid, VkCommandBuffer cmd) override { return PostDraw(eid, cmd); }
- void PostRedispatch(uint32_t eid, VkCommandBuffer cmd) override { PostRedraw(eid, cmd); }
- void PreMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override { PreDraw(eid, cmd); }
+ void PreDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
+ {
+ PreDraw(eid, flags, cmd);
+ }
+ bool PostDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
+ {
+ return PostDraw(eid, flags, cmd);
+ }
+ void PostRedispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
+ {
+ PostRedraw(eid, flags, cmd);
+ }
+ void PreMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
+ {
+ PreDraw(eid, flags, cmd);
+ }
bool PostMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
{
- return PostDraw(eid, cmd);
+ return PostDraw(eid, flags, cmd);
}
void PostRemisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
{
- PostRedraw(eid, cmd);
+ PostRedraw(eid, flags, cmd);
}
void AliasEvent(uint32_t primary, uint32_t alias) override
{
@@ -749,27 +792,36 @@ rdcarray VulkanReplay::FetchCountersKHR(const rdcarraySetActionCB(this);
}
~VulkanGPUTimerCallback() { m_pDriver->SetActionCB(NULL); }
- void PreDraw(uint32_t eid, VkCommandBuffer cmd) override
+ void PreDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
{
VkQueueFlags cmdType = m_pDriver->GetCommandType();
if(cmdType & VK_QUEUE_GRAPHICS_BIT)
{
if(m_OcclusionQueryPool != VK_NULL_HANDLE)
- ObjDisp(cmd)->CmdBeginQuery(Unwrap(cmd), m_OcclusionQueryPool, m_GraphicsQueries,
+ ObjDisp(cmd)->CmdBeginQuery(Unwrap(cmd), m_OcclusionQueryPool, m_OcclQueries,
VK_QUERY_CONTROL_PRECISE_BIT);
- if(m_PipeStatsQueryPool != VK_NULL_HANDLE)
- ObjDisp(cmd)->CmdBeginQuery(Unwrap(cmd), m_PipeStatsQueryPool, m_GraphicsQueries, 0);
+ if(flags & ActionFlags::MeshDispatch)
+ {
+ if(m_MeshStatsQueryPool != VK_NULL_HANDLE)
+ ObjDisp(cmd)->CmdBeginQuery(Unwrap(cmd), m_MeshStatsQueryPool, m_MeshQueries, 0);
+ }
+ else
+ {
+ if(m_PipeStatsQueryPool != VK_NULL_HANDLE)
+ ObjDisp(cmd)->CmdBeginQuery(Unwrap(cmd), m_PipeStatsQueryPool, m_GraphicsQueries, 0);
+ }
}
else if(cmdType & VK_QUEUE_COMPUTE_BIT)
{
@@ -780,7 +832,7 @@ struct VulkanGPUTimerCallback : public VulkanActionCallback
m_TimeStampQueryPool, (uint32_t)(m_Results.size() * 2 + 0));
}
- bool PostDraw(uint32_t eid, VkCommandBuffer cmd) override
+ bool PostDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
{
ObjDisp(cmd)->CmdWriteTimestamp(Unwrap(cmd), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
m_TimeStampQueryPool, (uint32_t)(m_Results.size() * 2 + 1));
@@ -789,10 +841,17 @@ struct VulkanGPUTimerCallback : public VulkanActionCallback
if(cmdType & VK_QUEUE_GRAPHICS_BIT)
{
if(m_OcclusionQueryPool != VK_NULL_HANDLE)
- ObjDisp(cmd)->CmdEndQuery(Unwrap(cmd), m_OcclusionQueryPool, m_GraphicsQueries);
- if(m_PipeStatsQueryPool != VK_NULL_HANDLE)
- ObjDisp(cmd)->CmdEndQuery(Unwrap(cmd), m_PipeStatsQueryPool, m_GraphicsQueries);
- m_GraphicsQueries++;
+ ObjDisp(cmd)->CmdEndQuery(Unwrap(cmd), m_OcclusionQueryPool, m_OcclQueries++);
+ if(flags & ActionFlags::MeshDispatch)
+ {
+ if(m_MeshStatsQueryPool != VK_NULL_HANDLE)
+ ObjDisp(cmd)->CmdEndQuery(Unwrap(cmd), m_MeshStatsQueryPool, m_MeshQueries++);
+ }
+ else
+ {
+ if(m_PipeStatsQueryPool != VK_NULL_HANDLE)
+ ObjDisp(cmd)->CmdEndQuery(Unwrap(cmd), m_PipeStatsQueryPool, m_GraphicsQueries++);
+ }
}
else if(cmdType & VK_QUEUE_COMPUTE_BIT)
{
@@ -800,32 +859,41 @@ struct VulkanGPUTimerCallback : public VulkanActionCallback
ObjDisp(cmd)->CmdEndQuery(Unwrap(cmd), m_ComputePipeStatsQueryPool, m_ComputeQueries);
m_ComputeQueries++;
}
- m_Results.push_back({eid, cmdType});
+ m_Results.push_back({eid, cmdType, flags});
return false;
}
- void PostRedraw(uint32_t eid, VkCommandBuffer cmd) override {}
+ void PostRedraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override {}
// we don't need to distinguish, call the Draw functions
- void PreDispatch(uint32_t eid, VkCommandBuffer cmd) override { PreDraw(eid, cmd); }
- bool PostDispatch(uint32_t eid, VkCommandBuffer cmd) override { return PostDraw(eid, cmd); }
- void PostRedispatch(uint32_t eid, VkCommandBuffer cmd) override { PostRedraw(eid, cmd); }
+ void PreDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
+ {
+ PreDraw(eid, flags, cmd);
+ }
+ bool PostDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
+ {
+ return PostDraw(eid, flags, cmd);
+ }
+ void PostRedispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
+ {
+ PostRedraw(eid, flags, cmd);
+ }
void PreMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
{
if(flags & ActionFlags::PassBoundary)
return;
- PreDraw(eid, cmd);
+ PreDraw(eid, flags, cmd);
}
bool PostMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
{
if(flags & ActionFlags::PassBoundary)
return false;
- return PostDraw(eid, cmd);
+ return PostDraw(eid, flags, cmd);
}
void PostRemisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) override
{
if(flags & ActionFlags::PassBoundary)
return;
- PostRedraw(eid, cmd);
+ PostRedraw(eid, flags, cmd);
}
void AliasEvent(uint32_t primary, uint32_t alias) override
{
@@ -847,9 +915,20 @@ struct VulkanGPUTimerCallback : public VulkanActionCallback
VulkanReplay *m_pReplay;
VkQueryPool m_TimeStampQueryPool;
VkQueryPool m_OcclusionQueryPool;
+ VkQueryPool m_MeshStatsQueryPool;
VkQueryPool m_PipeStatsQueryPool;
VkQueryPool m_ComputePipeStatsQueryPool;
- rdcarray> m_Results;
+
+ struct Result
+ {
+ uint32_t eid;
+ VkQueueFlags queue;
+ ActionFlags flags;
+ };
+
+ rdcarray m_Results;
+ uint32_t m_OcclQueries = 0;
+ uint32_t m_MeshQueries = 0;
uint32_t m_GraphicsQueries = 0;
uint32_t m_ComputeQueries = 0;
// events which are the 'same' from being the same command buffer resubmitted
@@ -928,10 +1007,35 @@ rdcarray VulkanReplay::FetchCounters(const rdcarray &
VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT |
VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT;
+ const uint32_t numPipeStats = 11;
+
VkQueryPoolCreateInfo pipeStatsPoolCreateInfo = {
VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, NULL, 0,
VK_QUERY_TYPE_PIPELINE_STATISTICS, maxEID, pipeStatsFlags};
+ pipeStatsFlags = VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT |
+ VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT |
+ VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT;
+
+ uint32_t numMeshStats = 3;
+
+ const uint32_t taskStatIdx = numMeshStats;
+ if(m_pDriver->TaskShaders())
+ {
+ pipeStatsFlags |= VK_QUERY_PIPELINE_STATISTIC_TASK_SHADER_INVOCATIONS_BIT_EXT;
+ numMeshStats++;
+ }
+ const uint32_t meshStatIdx = numMeshStats;
+ if(m_pDriver->MeshShaders())
+ {
+ pipeStatsFlags |= VK_QUERY_PIPELINE_STATISTIC_MESH_SHADER_INVOCATIONS_BIT_EXT;
+ numMeshStats++;
+ }
+
+ VkQueryPoolCreateInfo meshStatsPoolCreateInfo = {
+ VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, NULL, 0,
+ VK_QUERY_TYPE_PIPELINE_STATISTICS, maxEID, pipeStatsFlags};
+
VkQueryPool timeStampPool;
VkResult vkr =
ObjDisp(dev)->CreateQueryPool(Unwrap(dev), &timeStampPoolCreateInfo, NULL, &timeStampPool);
@@ -939,6 +1043,7 @@ rdcarray VulkanReplay::FetchCounters(const rdcarray &
bool occlNeeded = false;
bool statsNeeded = false;
+ bool meshNeeded = false;
for(size_t c = 0; c < vkCounters.size(); c++)
{
@@ -955,6 +1060,8 @@ rdcarray VulkanReplay::FetchCounters(const rdcarray &
case GPUCounter::GSInvocations:
case GPUCounter::PSInvocations:
case GPUCounter::CSInvocations: statsNeeded = true; break;
+ case GPUCounter::TSInvocations:
+ case GPUCounter::MSInvocations: meshNeeded = true; break;
case GPUCounter::SamplesPassed: occlNeeded = true; break;
default: break;
}
@@ -974,6 +1081,13 @@ rdcarray VulkanReplay::FetchCounters(const rdcarray &
CheckVkResult(vkr);
}
+ VkQueryPool meshStatsPool = VK_NULL_HANDLE;
+ if(m_pDriver->MeshQueries() && meshNeeded)
+ {
+ vkr = ObjDisp(dev)->CreateQueryPool(Unwrap(dev), &meshStatsPoolCreateInfo, NULL, &meshStatsPool);
+ CheckVkResult(vkr);
+ }
+
VkQueryPool compPipeStatsPool = VK_NULL_HANDLE;
if(availableFeatures.pipelineStatisticsQuery && statsNeeded)
{
@@ -1000,6 +1114,8 @@ rdcarray VulkanReplay::FetchCounters(const rdcarray &
ObjDisp(dev)->CmdResetQueryPool(Unwrap(cmd), occlusionPool, 0, maxEID);
if(pipeStatsPool != VK_NULL_HANDLE)
ObjDisp(dev)->CmdResetQueryPool(Unwrap(cmd), pipeStatsPool, 0, maxEID);
+ if(meshStatsPool != VK_NULL_HANDLE)
+ ObjDisp(dev)->CmdResetQueryPool(Unwrap(cmd), meshStatsPool, 0, maxEID);
if(compPipeStatsPool != VK_NULL_HANDLE)
ObjDisp(dev)->CmdResetQueryPool(Unwrap(cmd), compPipeStatsPool, 0, maxEID);
@@ -1009,8 +1125,8 @@ rdcarray VulkanReplay::FetchCounters(const rdcarray &
if(Vulkan_Debug_SingleSubmitFlushing())
m_pDriver->SubmitCmds();
- VulkanGPUTimerCallback cb(m_pDriver, this, timeStampPool, occlusionPool, pipeStatsPool,
- compPipeStatsPool);
+ VulkanGPUTimerCallback cb(m_pDriver, this, timeStampPool, occlusionPool, meshStatsPool,
+ pipeStatsPool, compPipeStatsPool);
// replay the events to perform all the queries
m_pDriver->ReplayLog(0, maxEID, eReplay_Full);
@@ -1029,35 +1145,51 @@ rdcarray VulkanReplay::FetchCounters(const rdcarray &
ObjDisp(dev)->DestroyQueryPool(Unwrap(dev), timeStampPool, NULL);
rdcarray occlusionData;
- occlusionData.resize(cb.m_GraphicsQueries);
+ occlusionData.resize(cb.m_OcclQueries);
if(occlusionPool != VK_NULL_HANDLE)
{
vkr = VK_SUCCESS;
- if(cb.m_GraphicsQueries > 0)
- vkr = ObjDisp(dev)->GetQueryPoolResults(Unwrap(dev), occlusionPool, 0, cb.m_GraphicsQueries,
- sizeof(uint64_t) * cb.m_GraphicsQueries,
- occlusionData.data(), sizeof(uint64_t),
- VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
+ if(cb.m_OcclQueries > 0)
+ vkr = ObjDisp(dev)->GetQueryPoolResults(
+ Unwrap(dev), occlusionPool, 0, cb.m_OcclQueries, sizeof(uint64_t) * cb.m_OcclQueries,
+ occlusionData.data(), sizeof(uint64_t), VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
CheckVkResult(vkr);
ObjDisp(dev)->DestroyQueryPool(Unwrap(dev), occlusionPool, NULL);
}
rdcarray pipeStatsData;
- pipeStatsData.resize(cb.m_GraphicsQueries * 11);
+
+ pipeStatsData.resize(cb.m_GraphicsQueries * numPipeStats);
if(pipeStatsPool != VK_NULL_HANDLE)
{
vkr = VK_SUCCESS;
if(cb.m_GraphicsQueries > 0)
vkr = ObjDisp(dev)->GetQueryPoolResults(Unwrap(dev), pipeStatsPool, 0, cb.m_GraphicsQueries,
- sizeof(uint64_t) * cb.m_GraphicsQueries * 11,
- pipeStatsData.data(), sizeof(uint64_t) * 11,
+ sizeof(uint64_t) * cb.m_GraphicsQueries * numPipeStats,
+ pipeStatsData.data(), sizeof(uint64_t) * numPipeStats,
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
CheckVkResult(vkr);
ObjDisp(dev)->DestroyQueryPool(Unwrap(dev), pipeStatsPool, NULL);
}
+ rdcarray meshStatsData;
+
+ meshStatsData.resize(cb.m_MeshQueries * numMeshStats);
+ if(meshStatsPool != VK_NULL_HANDLE)
+ {
+ vkr = VK_SUCCESS;
+ if(cb.m_MeshQueries > 0)
+ vkr = ObjDisp(dev)->GetQueryPoolResults(Unwrap(dev), meshStatsPool, 0, cb.m_MeshQueries,
+ sizeof(uint64_t) * cb.m_MeshQueries * numMeshStats,
+ meshStatsData.data(), sizeof(uint64_t) * numMeshStats,
+ VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WAIT_BIT);
+ CheckVkResult(vkr);
+
+ ObjDisp(dev)->DestroyQueryPool(Unwrap(dev), meshStatsPool, NULL);
+ }
+
rdcarray m_CompPipeStatsData;
m_CompPipeStatsData.resize(cb.m_ComputeQueries);
if(compPipeStatsPool != VK_NULL_HANDLE)
@@ -1073,23 +1205,35 @@ rdcarray VulkanReplay::FetchCounters(const rdcarray &
ObjDisp(dev)->DestroyQueryPool(Unwrap(dev), compPipeStatsPool, NULL);
}
- uint32_t graphicsIdx = 0, computeIdx = 0;
+ uint32_t occlIdx = 0, graphicsIdx = 0, meshIdx = 0, computeIdx = 0;
for(size_t i = 0; i < cb.m_Results.size(); i++)
{
- uint64_t pipeStats[11] = {};
+ rdcarray pipeStats;
+ pipeStats.resize(numPipeStats);
+ rdcarray meshStats;
+ meshStats.resize(numMeshStats);
uint64_t occl = 0;
- if(cb.m_Results[i].second & VK_QUEUE_GRAPHICS_BIT)
+ if(cb.m_Results[i].queue & VK_QUEUE_GRAPHICS_BIT)
{
- if(graphicsIdx < cb.m_GraphicsQueries)
+ if(occlIdx < cb.m_OcclQueries)
{
- occl = occlusionData[graphicsIdx];
- memcpy(pipeStats, &pipeStatsData[graphicsIdx * 11], sizeof(pipeStats));
+ occl = occlusionData[occlIdx++];
+ }
+ if(cb.m_Results[i].flags & ActionFlags::MeshDispatch)
+ {
+ if(meshIdx < cb.m_MeshQueries)
+ memcpy(meshStats.data(), &meshStatsData[meshIdx++ * numMeshStats], meshStats.byteSize());
+ }
+ else
+ {
+ if(graphicsIdx < cb.m_GraphicsQueries)
+ memcpy(pipeStats.data(), &pipeStatsData[graphicsIdx++ * numPipeStats],
+ pipeStats.byteSize());
}
- graphicsIdx++;
}
- else if(cb.m_Results[i].second & VK_QUEUE_COMPUTE_BIT)
+ else if(cb.m_Results[i].queue & VK_QUEUE_COMPUTE_BIT)
{
if(computeIdx < cb.m_ComputeQueries)
{
@@ -1102,7 +1246,7 @@ rdcarray VulkanReplay::FetchCounters(const rdcarray &
{
CounterResult result;
- result.eventId = cb.m_Results[i].first;
+ result.eventId = cb.m_Results[i].eid;
result.counter = vkCounters[c];
switch(vkCounters[c])
@@ -1115,18 +1259,35 @@ rdcarray VulkanReplay::FetchCounters(const rdcarray &
/ (1000.0 * 1000.0 * 1000.0); // to seconds
}
break;
+ case GPUCounter::SamplesPassed: result.value.u64 = occl; break;
case GPUCounter::InputVerticesRead: result.value.u64 = pipeStats[0]; break;
case GPUCounter::IAPrimitives: result.value.u64 = pipeStats[1]; break;
- case GPUCounter::GSPrimitives: result.value.u64 = pipeStats[4]; break;
- case GPUCounter::RasterizerInvocations: result.value.u64 = pipeStats[5]; break;
- case GPUCounter::RasterizedPrimitives: result.value.u64 = pipeStats[6]; break;
- case GPUCounter::SamplesPassed: result.value.u64 = occl; break;
case GPUCounter::VSInvocations: result.value.u64 = pipeStats[2]; break;
+ case GPUCounter::GSPrimitives: result.value.u64 = pipeStats[4]; break;
case GPUCounter::TCSInvocations: result.value.u64 = pipeStats[8]; break;
case GPUCounter::TESInvocations: result.value.u64 = pipeStats[9]; break;
case GPUCounter::GSInvocations: result.value.u64 = pipeStats[3]; break;
- case GPUCounter::PSInvocations: result.value.u64 = pipeStats[7]; break;
case GPUCounter::CSInvocations: result.value.u64 = pipeStats[10]; break;
+ case GPUCounter::RasterizerInvocations:
+ if(cb.m_Results[i].flags & ActionFlags::MeshDispatch)
+ result.value.u64 = meshStats[0];
+ else
+ result.value.u64 = pipeStats[5];
+ break;
+ case GPUCounter::RasterizedPrimitives:
+ if(cb.m_Results[i].flags & ActionFlags::MeshDispatch)
+ result.value.u64 = meshStats[1];
+ else
+ result.value.u64 = pipeStats[6];
+ break;
+ case GPUCounter::PSInvocations:
+ if(cb.m_Results[i].flags & ActionFlags::MeshDispatch)
+ result.value.u64 = meshStats[2];
+ else
+ result.value.u64 = pipeStats[7];
+ break;
+ case GPUCounter::TSInvocations: result.value.u64 = meshStats[taskStatIdx]; break;
+ case GPUCounter::MSInvocations: result.value.u64 = meshStats[meshStatIdx]; break;
default: break;
}
ret.push_back(result);
diff --git a/renderdoc/driver/vulkan/vk_debug.cpp b/renderdoc/driver/vulkan/vk_debug.cpp
index 9341948fa..26ffc924e 100644
--- a/renderdoc/driver/vulkan/vk_debug.cpp
+++ b/renderdoc/driver/vulkan/vk_debug.cpp
@@ -2794,7 +2794,7 @@ void VulkanReplay::PatchReservedDescriptors(const VulkanStatePipeline &pipe,
#define UPDATE_AND_CHECK_STAGE_LIMIT(maxLimit) \
if(!error) \
{ \
- for(uint32_t sbit = 0; sbit < 6; sbit++) \
+ for(uint32_t sbit = 0; sbit < NumShaderStages; sbit++) \
{ \
if(newBind.stageFlags & (1U << sbit)) \
{ \
diff --git a/renderdoc/driver/vulkan/vk_hookset_defs.h b/renderdoc/driver/vulkan/vk_hookset_defs.h
index a92be4187..3748d223a 100644
--- a/renderdoc/driver/vulkan/vk_hookset_defs.h
+++ b/renderdoc/driver/vulkan/vk_hookset_defs.h
@@ -549,7 +549,8 @@
DeclExt(EXT_swapchain_maintenance1); \
DeclExt(EXT_provoking_vertex); \
DeclExt(EXT_attachment_feedback_loop_dynamic_state); \
- DeclExt(EXT_extended_dynamic_state3);
+ DeclExt(EXT_extended_dynamic_state3); \
+ DeclExt(EXT_mesh_shader);
// for simplicity and since the check itself is platform agnostic,
// these aren't protected in platform defines
@@ -669,7 +670,8 @@
CheckExt(EXT_swapchain_maintenance1, VKXX); \
CheckExt(EXT_provoking_vertex, VKXX); \
CheckExt(EXT_attachment_feedback_loop_dynamic_state, VKXX); \
- CheckExt(EXT_extended_dynamic_state3, VKXX);
+ CheckExt(EXT_extended_dynamic_state3, VKXX); \
+ CheckExt(EXT_mesh_shader, VKXX);
#define HookInitVulkanInstanceExts_PhysDev() \
HookInitExtension(KHR_surface, GetPhysicalDeviceSurfaceSupportKHR); \
@@ -964,6 +966,9 @@
HookInitExtension(EXT_extended_dynamic_state3, CmdSetTessellationDomainOriginEXT); \
HookInitExtension(EXT_extended_dynamic_state3, CmdSetViewportSwizzleNV); \
HookInitExtension(EXT_extended_dynamic_state3, CmdSetViewportWScalingEnableNV); \
+ HookInitExtension(EXT_mesh_shader, CmdDrawMeshTasksEXT); \
+ HookInitExtension(EXT_mesh_shader, CmdDrawMeshTasksIndirectEXT); \
+ HookInitExtension(EXT_mesh_shader, CmdDrawMeshTasksIndirectCountEXT); \
HookInitExtension_Device_Win32(); \
HookInitExtension_Device_Linux(); \
HookInitExtension_Device_GGP(); \
@@ -1758,6 +1763,13 @@
pViewportSwizzles); \
HookDefine2(void, vkCmdSetViewportWScalingEnableNV, VkCommandBuffer, commandBuffer, VkBool32, \
viewportWScalingEnable); \
+ HookDefine4(void, vkCmdDrawMeshTasksEXT, VkCommandBuffer, commandBuffer, uint32_t, groupCountX, \
+ uint32_t, groupCountY, uint32_t, groupCountZ); \
+ HookDefine5(void, vkCmdDrawMeshTasksIndirectEXT, VkCommandBuffer, commandBuffer, VkBuffer, \
+ buffer, VkDeviceSize, offset, uint32_t, drawCount, uint32_t, stride); \
+ HookDefine7(void, vkCmdDrawMeshTasksIndirectCountEXT, VkCommandBuffer, commandBuffer, VkBuffer, \
+ buffer, VkDeviceSize, offset, VkBuffer, countBuffer, VkDeviceSize, \
+ countBufferOffset, uint32_t, maxDrawCount, uint32_t, stride); \
HookDefine_Win32(); \
HookDefine_Linux(); \
HookDefine_GGP(); \
diff --git a/renderdoc/driver/vulkan/vk_next_chains.cpp b/renderdoc/driver/vulkan/vk_next_chains.cpp
index 6214767dd..94435a295 100644
--- a/renderdoc/driver/vulkan/vk_next_chains.cpp
+++ b/renderdoc/driver/vulkan/vk_next_chains.cpp
@@ -339,6 +339,10 @@ static void AppendModifiedChainedStruct(byte *&tempMem, VkStruct *outputStruct,
VkPhysicalDeviceMemoryProperties2); \
COPY_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PRIORITY_FEATURES_EXT, \
VkPhysicalDeviceMemoryPriorityFeaturesEXT); \
+ COPY_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT, \
+ VkPhysicalDeviceMeshShaderFeaturesEXT); \
+ COPY_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_EXT, \
+ VkPhysicalDeviceMeshShaderPropertiesEXT); \
COPY_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTISAMPLED_RENDER_TO_SINGLE_SAMPLED_FEATURES_EXT, \
VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT); \
COPY_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, \
@@ -931,9 +935,7 @@ static void AppendModifiedChainedStruct(byte *&tempMem, VkStruct *outputStruct,
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LINEAR_COLOR_ATTACHMENT_FEATURES_NV: \
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_DECOMPRESSION_FEATURES_NV: \
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_DECOMPRESSION_PROPERTIES_NV: \
- case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT: \
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_NV: \
- case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_EXT: \
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_NV: \
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT: \
case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_PROPERTIES_EXT: \
diff --git a/renderdoc/driver/vulkan/vk_overlay.cpp b/renderdoc/driver/vulkan/vk_overlay.cpp
index 6393ae2a7..c624fe093 100644
--- a/renderdoc/driver/vulkan/vk_overlay.cpp
+++ b/renderdoc/driver/vulkan/vk_overlay.cpp
@@ -62,7 +62,7 @@ struct VulkanQuadOverdrawCallback : public VulkanActionCallback
m_pDriver->vkDestroyPipelineLayout(dev, it->second.pipeLayout, NULL);
}
}
- void PreDraw(uint32_t eid, VkCommandBuffer cmd)
+ void PreDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
if(!m_Events.contains(eid))
return;
@@ -236,7 +236,7 @@ struct VulkanQuadOverdrawCallback : public VulkanActionCallback
pipestate.BindPipeline(m_pDriver, cmd, VulkanRenderState::BindGraphics, false);
}
- bool PostDraw(uint32_t eid, VkCommandBuffer cmd)
+ bool PostDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
if(!m_Events.contains(eid))
return false;
@@ -251,15 +251,15 @@ struct VulkanQuadOverdrawCallback : public VulkanActionCallback
return true;
}
- void PostRedraw(uint32_t eid, VkCommandBuffer cmd)
+ void PostRedraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
// nothing to do
}
// Dispatches don't rasterize, so do nothing
- void PreDispatch(uint32_t eid, VkCommandBuffer cmd) {}
- bool PostDispatch(uint32_t eid, VkCommandBuffer cmd) { return false; }
- void PostRedispatch(uint32_t eid, VkCommandBuffer cmd) {}
+ void PreDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
+ bool PostDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
+ void PostRedispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
// Ditto copy/etc
void PreMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
bool PostMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
@@ -731,7 +731,7 @@ ResourceId VulkanReplay::RenderOverlay(ResourceId texid, FloatVector clearCol, D
const VulkanCreationInfo::Pipeline &pipeInfo =
m_pDriver->m_CreationInfo.m_Pipeline[state.graphics.pipeline];
- if((mainDraw && !(mainDraw->flags & ActionFlags::Drawcall)) ||
+ if((mainDraw && !(mainDraw->flags & (ActionFlags::MeshDispatch | ActionFlags::Drawcall))) ||
(!m_pDriver->m_Partial[WrappedVulkan::Primary].renderPassActive &&
!m_pDriver->m_Partial[WrappedVulkan::Secondary].renderPassActive))
{
@@ -2373,7 +2373,7 @@ ResourceId VulkanReplay::RenderOverlay(ResourceId texid, FloatVector clearCol, D
const ActionDescription *action = m_pDriver->GetAction(events[0]);
// remove any non-drawcalls, like the pass boundary.
- if(!action || !(action->flags & ActionFlags::Drawcall))
+ if(!action || !(action->flags & (ActionFlags::MeshDispatch | ActionFlags::Drawcall)))
events.erase(0);
else
break;
diff --git a/renderdoc/driver/vulkan/vk_pixelhistory.cpp b/renderdoc/driver/vulkan/vk_pixelhistory.cpp
index 0392db016..7d7e827e1 100644
--- a/renderdoc/driver/vulkan/vk_pixelhistory.cpp
+++ b/renderdoc/driver/vulkan/vk_pixelhistory.cpp
@@ -1510,7 +1510,7 @@ struct VulkanOcclusionCallback : public VulkanPixelHistoryCallback
m_pDriver->vkDestroyPipeline(m_pDriver->GetDev(), it->second, NULL);
}
- void PreDraw(uint32_t eid, VkCommandBuffer cmd)
+ void PreDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
if(!m_Events.contains(eid))
return;
@@ -1534,11 +1534,11 @@ struct VulkanOcclusionCallback : public VulkanPixelHistoryCallback
false);
}
- bool PostDraw(uint32_t eid, VkCommandBuffer cmd) { return false; }
- void PostRedraw(uint32_t eid, VkCommandBuffer cmd) {}
- void PreDispatch(uint32_t eid, VkCommandBuffer cmd) { return; }
- bool PostDispatch(uint32_t eid, VkCommandBuffer cmd) { return false; }
- void PostRedispatch(uint32_t eid, VkCommandBuffer cmd) {}
+ bool PostDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
+ void PostRedraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
+ void PreDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return; }
+ bool PostDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
+ void PostRedispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
void PreMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return; }
bool PostMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
void PostRemisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
@@ -1652,7 +1652,7 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback
}
}
- void PreDraw(uint32_t eid, VkCommandBuffer cmd)
+ void PreDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
if(!m_Events.contains(eid) || !m_pDriver->IsCmdPrimary())
return;
@@ -1731,7 +1731,7 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback
pipestate.BeginRenderPassAndApplyState(m_pDriver, cmd, VulkanRenderState::BindGraphics, true);
}
- bool PostDraw(uint32_t eid, VkCommandBuffer cmd)
+ bool PostDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
if(!m_Events.contains(eid) || !m_pDriver->IsCmdPrimary())
return false;
@@ -1763,7 +1763,7 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback
return false;
}
- void PostRedraw(uint32_t eid, VkCommandBuffer cmd)
+ void PostRedraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
// nothing to do
}
@@ -1869,14 +1869,14 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback
m_pDriver, cmd, VulkanRenderState::BindNone, true);
}
- void PreDispatch(uint32_t eid, VkCommandBuffer cmd)
+ void PreDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
if(!m_Events.contains(eid))
return;
size_t storeOffset = m_EventIndices.size() * sizeof(EventInfo);
CopyPixel(eid, cmd, storeOffset, false);
}
- bool PostDispatch(uint32_t eid, VkCommandBuffer cmd)
+ bool PostDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
if(!m_Events.contains(eid))
return false;
@@ -1885,7 +1885,7 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback
m_EventIndices.insert(std::make_pair(eid, m_EventIndices.size()));
return false;
}
- void PostRedispatch(uint32_t eid, VkCommandBuffer cmd) {}
+ void PostRedispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
void PreMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
if(!m_Events.contains(eid))
@@ -1899,7 +1899,7 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback
}
return;
}
- PreDispatch(eid, cmd);
+ PreDispatch(eid, flags, cmd);
}
bool PostMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
@@ -1923,7 +1923,7 @@ struct VulkanColorAndStencilCallback : public VulkanPixelHistoryCallback
m_pDriver->GetCmdRenderState().FinishSuspendedRenderPass(cmd);
}
- bool ret = PostDispatch(eid, cmd);
+ bool ret = PostDispatch(eid, flags, cmd);
if(flags & ActionFlags::BeginPass)
m_pDriver->GetCmdRenderState().BeginRenderPassAndApplyState(
@@ -2146,7 +2146,7 @@ struct TestsFailedCallback : public VulkanPixelHistoryCallback
}
~TestsFailedCallback() {}
- void PreDraw(uint32_t eid, VkCommandBuffer cmd)
+ void PreDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
if(!m_Events.contains(eid))
return;
@@ -2176,20 +2176,20 @@ struct TestsFailedCallback : public VulkanPixelHistoryCallback
false);
}
- bool PostDraw(uint32_t eid, VkCommandBuffer cmd) { return false; }
+ bool PostDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
void AliasEvent(uint32_t primary, uint32_t alias)
{
// TODO: handle aliased events.
}
- void PostRedraw(uint32_t eid, VkCommandBuffer cmd)
+ void PostRedraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
// nothing to do
}
- void PreDispatch(uint32_t eid, VkCommandBuffer cmd) {}
- bool PostDispatch(uint32_t eid, VkCommandBuffer cmd) { return false; }
- void PostRedispatch(uint32_t eid, VkCommandBuffer cmd) {}
+ void PreDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
+ bool PostDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
+ void PostRedispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
void PreMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
bool PostMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
void PostRemisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
@@ -2701,7 +2701,7 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback
VkPipeline postModPipe;
};
- void PreDraw(uint32_t eid, VkCommandBuffer cmd)
+ void PreDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
if(m_EventFragments.find(eid) == m_EventFragments.end())
return;
@@ -3027,8 +3027,8 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback
m_pDriver->GetCmdRenderState().BeginRenderPassAndApplyState(
m_pDriver, cmd, VulkanRenderState::BindGraphics, true);
}
- bool PostDraw(uint32_t eid, VkCommandBuffer cmd) { return false; }
- void PostRedraw(uint32_t eid, VkCommandBuffer cmd) {}
+ bool PostDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
+ void PostRedraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
// CreatePerFragmentPipelines for getting per fragment information.
Pipelines CreatePerFragmentPipelines(ResourceId pipe, VkRenderPass rp, VkRenderPass origRpWithDepth,
uint32_t eid, uint32_t fragmentIndex,
@@ -3230,9 +3230,9 @@ struct VulkanPixelHistoryPerFragmentCallback : VulkanPixelHistoryCallback
return pipes;
}
- void PreDispatch(uint32_t eid, VkCommandBuffer cmd) {}
- bool PostDispatch(uint32_t eid, VkCommandBuffer cmd) { return false; }
- void PostRedispatch(uint32_t eid, VkCommandBuffer cmd) {}
+ void PreDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
+ bool PostDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
+ void PostRedispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
void PreMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
bool PostMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
void PostRemisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
@@ -3291,7 +3291,7 @@ struct VulkanPixelHistoryDiscardedFragmentsCallback : VulkanPixelHistoryCallback
m_pDriver->vkDestroyPipeline(m_pDriver->GetDev(), pipe, NULL);
}
- void PreDraw(uint32_t eid, VkCommandBuffer cmd)
+ void PreDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
if(m_Events.find(eid) == m_Events.end())
return;
@@ -3372,11 +3372,11 @@ struct VulkanPixelHistoryDiscardedFragmentsCallback : VulkanPixelHistoryCallback
return m_OcclusionResults[it->second] == 0;
}
- bool PostDraw(uint32_t eid, VkCommandBuffer cmd) { return false; }
- void PostRedraw(uint32_t eid, VkCommandBuffer cmd) {}
- void PreDispatch(uint32_t eid, VkCommandBuffer cmd) {}
- bool PostDispatch(uint32_t eid, VkCommandBuffer cmd) { return false; }
- void PostRedispatch(uint32_t eid, VkCommandBuffer cmd) {}
+ bool PostDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
+ void PostRedraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
+ void PreDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
+ bool PostDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
+ void PostRedispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
void PreMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
bool PostMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
void PostRemisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
diff --git a/renderdoc/driver/vulkan/vk_postvs.cpp b/renderdoc/driver/vulkan/vk_postvs.cpp
index 01305d77d..62a72b787 100644
--- a/renderdoc/driver/vulkan/vk_postvs.cpp
+++ b/renderdoc/driver/vulkan/vk_postvs.cpp
@@ -3591,18 +3591,18 @@ struct VulkanInitPostVSCallback : public VulkanActionCallback
m_pDriver->SetActionCB(this);
}
~VulkanInitPostVSCallback() { m_pDriver->SetActionCB(NULL); }
- void PreDraw(uint32_t eid, VkCommandBuffer cmd)
+ void PreDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd)
{
if(m_Events.contains(eid))
m_pDriver->GetReplay()->InitPostVSBuffers(eid, m_pDriver->GetCmdRenderState());
}
- bool PostDraw(uint32_t eid, VkCommandBuffer cmd) { return false; }
- void PostRedraw(uint32_t eid, VkCommandBuffer cmd) {}
+ bool PostDraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
+ void PostRedraw(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
// Dispatches don't rasterize, so do nothing
- void PreDispatch(uint32_t eid, VkCommandBuffer cmd) {}
- bool PostDispatch(uint32_t eid, VkCommandBuffer cmd) { return false; }
- void PostRedispatch(uint32_t eid, VkCommandBuffer cmd) {}
+ void PreDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
+ bool PostDispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
+ void PostRedispatch(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
// Ditto copy/etc
void PreMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) {}
bool PostMisc(uint32_t eid, ActionFlags flags, VkCommandBuffer cmd) { return false; }
diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp
index d49176a94..a7d42871d 100644
--- a/renderdoc/driver/vulkan/vk_replay.cpp
+++ b/renderdoc/driver/vulkan/vk_replay.cpp
@@ -1217,12 +1217,22 @@ void VulkanReplay::SavePipelineState(uint32_t eventId)
// Shader Stages
VKPipe::Shader *stages[] = {
- &ret.vertexShader, &ret.tessControlShader, &ret.tessEvalShader,
- &ret.geometryShader, &ret.fragmentShader,
+ &ret.vertexShader,
+ &ret.tessControlShader,
+ &ret.tessEvalShader,
+ &ret.geometryShader,
+ &ret.fragmentShader,
+ // compute
+ NULL,
+ &ret.taskShader,
+ &ret.meshShader,
};
for(size_t i = 0; i < ARRAY_COUNT(stages); i++)
{
+ if(stages[i] == NULL)
+ continue;
+
stages[i]->resourceId = rm->GetUnreplacedOriginalID(p.shaders[i].module);
stages[i]->entryPoint = p.shaders[i].entryPoint;
@@ -1591,8 +1601,8 @@ void VulkanReplay::SavePipelineState(uint32_t eventId)
ret.vertexInput.vertexBuffers.clear();
VKPipe::Shader *stages[] = {
- &ret.vertexShader, &ret.tessControlShader, &ret.tessEvalShader,
- &ret.geometryShader, &ret.fragmentShader,
+ &ret.vertexShader, &ret.tessControlShader, &ret.tessEvalShader, &ret.geometryShader,
+ &ret.fragmentShader, &ret.taskShader, &ret.meshShader,
};
for(size_t i = 0; i < ARRAY_COUNT(stages); i++)
diff --git a/renderdoc/driver/vulkan/vk_serialise.cpp b/renderdoc/driver/vulkan/vk_serialise.cpp
index 4f225b5b8..716df5bc2 100644
--- a/renderdoc/driver/vulkan/vk_serialise.cpp
+++ b/renderdoc/driver/vulkan/vk_serialise.cpp
@@ -773,6 +773,12 @@ SERIALISE_VK_HANDLES();
VkPhysicalDeviceMemoryPriorityFeaturesEXT) \
PNEXT_STRUCT(VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT, VkMemoryPriorityAllocateInfoEXT) \
\
+ /* VK_EXT_mesh_shader */ \
+ PNEXT_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT, \
+ VkPhysicalDeviceMeshShaderFeaturesEXT) \
+ PNEXT_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_EXT, \
+ VkPhysicalDeviceMeshShaderPropertiesEXT) \
+ \
/* VK_EXT_mutable_descriptor_type */ \
PNEXT_STRUCT(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT, \
VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT) \
@@ -1507,10 +1513,6 @@ SERIALISE_VK_HANDLES();
/* VK_EXT_legacy_dithering */ \
PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_LEGACY_DITHERING_FEATURES_EXT) \
\
- /* VK_EXT_mesh_shader */ \
- PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT) \
- PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_EXT) \
- \
/* VK_EXT_metal_objects */ \
PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_EXPORT_METAL_OBJECT_CREATE_INFO_EXT) \
PNEXT_UNSUPPORTED(VK_STRUCTURE_TYPE_EXPORT_METAL_OBJECTS_INFO_EXT) \
@@ -8273,6 +8275,14 @@ void DoSerialise(SerialiserType &ser, VkDrawIndexedIndirectCommand &el)
SERIALISE_MEMBER(firstInstance);
}
+template
+void DoSerialise(SerialiserType &ser, VkDrawMeshTasksIndirectCommandEXT &el)
+{
+ SERIALISE_MEMBER(groupCountX);
+ SERIALISE_MEMBER(groupCountY);
+ SERIALISE_MEMBER(groupCountZ);
+}
+
template
void DoSerialise(SerialiserType &ser, VkDeviceQueueInfo2 &el)
{
@@ -9911,6 +9921,69 @@ void Deserialise(const VkPhysicalDevicePageableDeviceLocalMemoryFeaturesEXT &el)
DeserialiseNext(el.pNext);
}
+template
+void DoSerialise(SerialiserType &ser, VkPhysicalDeviceMeshShaderFeaturesEXT &el)
+{
+ RDCASSERT(ser.IsReading() ||
+ el.sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT);
+ SerialiseNext(ser, el.sType, el.pNext);
+
+ SERIALISE_MEMBER(taskShader);
+ SERIALISE_MEMBER(meshShader);
+ SERIALISE_MEMBER(multiviewMeshShader);
+ SERIALISE_MEMBER(primitiveFragmentShadingRateMeshShader);
+ SERIALISE_MEMBER(meshShaderQueries);
+}
+
+template <>
+void Deserialise(const VkPhysicalDeviceMeshShaderFeaturesEXT &el)
+{
+ DeserialiseNext(el.pNext);
+}
+
+template
+void DoSerialise(SerialiserType &ser, VkPhysicalDeviceMeshShaderPropertiesEXT &el)
+{
+ RDCASSERT(ser.IsReading() ||
+ el.sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_PROPERTIES_EXT);
+ SerialiseNext(ser, el.sType, el.pNext);
+
+ SERIALISE_MEMBER(maxTaskWorkGroupTotalCount);
+ SERIALISE_MEMBER(maxTaskWorkGroupCount);
+ SERIALISE_MEMBER(maxTaskWorkGroupInvocations);
+ SERIALISE_MEMBER(maxTaskWorkGroupSize);
+ SERIALISE_MEMBER(maxTaskPayloadSize);
+ SERIALISE_MEMBER(maxTaskSharedMemorySize);
+ SERIALISE_MEMBER(maxTaskPayloadAndSharedMemorySize);
+ SERIALISE_MEMBER(maxMeshWorkGroupTotalCount);
+ SERIALISE_MEMBER(maxMeshWorkGroupCount);
+ SERIALISE_MEMBER(maxMeshWorkGroupInvocations);
+ SERIALISE_MEMBER(maxMeshWorkGroupSize);
+ SERIALISE_MEMBER(maxMeshSharedMemorySize);
+ SERIALISE_MEMBER(maxMeshPayloadAndSharedMemorySize);
+ SERIALISE_MEMBER(maxMeshOutputMemorySize);
+ SERIALISE_MEMBER(maxMeshPayloadAndOutputMemorySize);
+ SERIALISE_MEMBER(maxMeshOutputComponents);
+ SERIALISE_MEMBER(maxMeshOutputVertices);
+ SERIALISE_MEMBER(maxMeshOutputPrimitives);
+ SERIALISE_MEMBER(maxMeshOutputLayers);
+ SERIALISE_MEMBER(maxMeshMultiviewViewCount);
+ SERIALISE_MEMBER(meshOutputPerVertexGranularity);
+ SERIALISE_MEMBER(meshOutputPerPrimitiveGranularity);
+ SERIALISE_MEMBER(maxPreferredTaskWorkGroupInvocations);
+ SERIALISE_MEMBER(maxPreferredMeshWorkGroupInvocations);
+ SERIALISE_MEMBER(prefersLocalInvocationVertexOutput);
+ SERIALISE_MEMBER(prefersLocalInvocationPrimitiveOutput);
+ SERIALISE_MEMBER(prefersCompactVertexOutput);
+ SERIALISE_MEMBER(prefersCompactPrimitiveOutput);
+}
+
+template <>
+void Deserialise(const VkPhysicalDeviceMeshShaderPropertiesEXT &el)
+{
+ DeserialiseNext(el.pNext);
+}
+
template
void DoSerialise(SerialiserType &ser, VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT &el)
{
@@ -11587,6 +11660,8 @@ INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceMaintenance4Properties);
INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceMemoryBudgetPropertiesEXT);
INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceMemoryPriorityFeaturesEXT);
INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceMemoryProperties2);
+INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceMeshShaderFeaturesEXT);
+INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceMeshShaderPropertiesEXT);
INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceMultisampledRenderToSingleSampledFeaturesEXT);
INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceMultiviewFeatures);
INSTANTIATE_SERIALISE_TYPE(VkPhysicalDeviceMultiviewProperties);
@@ -11803,6 +11878,7 @@ INSTANTIATE_SERIALISE_TYPE(VkDisplayPlanePropertiesKHR);
INSTANTIATE_SERIALISE_TYPE(VkDisplayPropertiesKHR);
INSTANTIATE_SERIALISE_TYPE(VkDrawIndexedIndirectCommand);
INSTANTIATE_SERIALISE_TYPE(VkDrawIndirectCommand);
+INSTANTIATE_SERIALISE_TYPE(VkDrawMeshTasksIndirectCommandEXT);
INSTANTIATE_SERIALISE_TYPE(VkExtent2D);
INSTANTIATE_SERIALISE_TYPE(VkExtent3D);
INSTANTIATE_SERIALISE_TYPE(VkExternalMemoryProperties);
diff --git a/renderdoc/driver/vulkan/vk_shader_feedback.cpp b/renderdoc/driver/vulkan/vk_shader_feedback.cpp
index 02a96a438..504795b61 100644
--- a/renderdoc/driver/vulkan/vk_shader_feedback.cpp
+++ b/renderdoc/driver/vulkan/vk_shader_feedback.cpp
@@ -623,7 +623,7 @@ void AnnotateShader(const ShaderReflection &refl, const SPIRVPatchData &patchDat
if(Vulkan_PrintfFetch())
{
editor.AddVariable(rdcspv::OpVariable(
- editor.DeclareType(rdcspv::Pointer(uvec3Type, rdcspv::StorageClass::Private)),
+ editor.DeclareType(rdcspv::Pointer(uvec4Type, rdcspv::StorageClass::Private)),
printfLocationVar, rdcspv::StorageClass::Private));
if(editor.EntryPointAllGlobals())
@@ -720,6 +720,68 @@ void AnnotateShader(const ShaderReflection &refl, const SPIRVPatchData &patchDat
// the location for compute is easy, it's just the global invocation
location = fetchOrAddGlobalInput("rdoc_invocation", ShaderBuiltin::DispatchThreadIndex,
rdcspv::BuiltIn::GlobalInvocationId, uvec3Type, true);
+
+ location = locationGather.add(
+ rdcspv::OpVectorShuffle(uvec4Type, editor.MakeId(), location, location, {0, 1, 2, 0}));
+ }
+ else if(stage == ShaderStage::Task)
+ {
+ // the location for task shaders is the same
+ location = fetchOrAddGlobalInput("rdoc_invocation", ShaderBuiltin::DispatchThreadIndex,
+ rdcspv::BuiltIn::GlobalInvocationId, uvec3Type, true);
+
+ location = locationGather.add(
+ rdcspv::OpVectorShuffle(uvec4Type, editor.MakeId(), location, location, {0, 1, 2, 0}));
+ }
+ else if(stage == ShaderStage::Mesh)
+ {
+ // the location for mesh shaders is packed a smidge tighter.
+ // we need three 3D locators:
+ // (optional) task group index
+ // mesh group index
+ // local thread index
+ //
+ // the local index has a compile-time known stride so we can use the linear index, which we
+ // can give 16 bits to be very generous (10 bits is a more realistic upper bound)
+ //
+ // similarly the task group index has a known stride so we can use a linear index for it as
+ // well. Giving it 32 bits covers any reasonable use (~26 bits is the max reported at the time
+ // of writing)
+ //
+ // annoyingly this leaves us 48 bits per task group index dimension. That is enough for a
+ // linear ID easily but it does not have a easily known stride (for a task shader it depends
+ // on the OpEmitMeshTasksEXT dimensions). It's not enough for the worst case in each dimension
+ // which some drivers report as [4194304,65535,65535] which requires 22,16,16 bits. Those
+ // drivers don't allow a shader to dispatch that many in all dimensions as the product is
+ // still constrained.
+ //
+ // So instead we've just used 4 uints for the location just for the mesh shader. We still have
+ // to compress things a little so we put the mesh thread in the upper 16-bits with mesh group
+ // z
+ rdcspv::Id meshThread =
+ fetchOrAddGlobalInput("rdoc_meshThread", ShaderBuiltin::GroupFlatIndex,
+ rdcspv::BuiltIn::LocalInvocationIndex, uint32Type, true);
+ rdcspv::Id meshGroup = fetchOrAddGlobalInput("rdoc_meshGroup", ShaderBuiltin::GroupIndex,
+ rdcspv::BuiltIn::WorkgroupId, uvec3Type, true);
+
+ // TODO read task ID from payload
+ rdcspv::Id taskId = zero;
+
+ rdcspv::Id meshThreadShifted = locationGather.add(rdcspv::OpShiftLeftLogical(
+ uint32Type, editor.MakeId(), meshThread, editor.AddConstantImmediate(16U)));
+ rdcspv::Id meshGroupX =
+ locationGather.add(rdcspv::OpCompositeExtract(uint32Type, editor.MakeId(), meshGroup, {0}));
+ rdcspv::Id meshGroupY =
+ locationGather.add(rdcspv::OpCompositeExtract(uint32Type, editor.MakeId(), meshGroup, {1}));
+ rdcspv::Id meshGroupZ =
+ locationGather.add(rdcspv::OpCompositeExtract(uint32Type, editor.MakeId(), meshGroup, {2}));
+ meshGroupZ = locationGather.add(rdcspv::OpBitwiseAnd(
+ uint32Type, editor.MakeId(), meshGroupZ, editor.AddConstantImmediate(0xffff)));
+ meshGroupZ = locationGather.add(
+ rdcspv::OpBitwiseOr(uint32Type, editor.MakeId(), meshGroupZ, meshThreadShifted));
+
+ location = locationGather.add(rdcspv::OpCompositeConstruct(
+ uvec4Type, editor.MakeId(), {meshGroupX, meshGroupY, meshGroupZ, taskId}));
}
else if(stage == ShaderStage::Vertex || stage == ShaderStage::Pixel)
{
@@ -745,7 +807,7 @@ void AnnotateShader(const ShaderReflection &refl, const SPIRVPatchData &patchDat
rdcspv::BuiltIn::InstanceIndex, uint32Type, true);
location = locationGather.add(
- rdcspv::OpCompositeConstruct(uvec3Type, editor.MakeId(), {vtx, inst, view}));
+ rdcspv::OpCompositeConstruct(uvec4Type, editor.MakeId(), {vtx, inst, view, zero}));
}
else if(stage == ShaderStage::Pixel)
{
@@ -812,7 +874,7 @@ void AnnotateShader(const ShaderReflection &refl, const SPIRVPatchData &patchDat
}
location = locationGather.add(
- rdcspv::OpCompositeConstruct(uvec3Type, editor.MakeId(), {coord, view, prim}));
+ rdcspv::OpCompositeConstruct(uvec4Type, editor.MakeId(), {coord, view, prim, zero}));
}
}
else if(stage == ShaderStage::Geometry)
@@ -835,13 +897,13 @@ void AnnotateShader(const ShaderReflection &refl, const SPIRVPatchData &patchDat
}
location = locationGather.add(
- rdcspv::OpCompositeConstruct(uvec3Type, editor.MakeId(), {prim, view, zero}));
+ rdcspv::OpCompositeConstruct(uvec4Type, editor.MakeId(), {prim, view, zero, zero}));
}
else
{
RDCWARN("No identifier stored for %s stage", ToStr(stage).c_str());
location = locationGather.add(
- rdcspv::OpCompositeConstruct(uvec3Type, editor.MakeId(), {zero, zero, zero}));
+ rdcspv::OpCompositeConstruct(uvec4Type, editor.MakeId(), {zero, zero, zero, zero}));
}
locationGather.add(rdcspv::OpStore(printfLocationVar, location));
@@ -1226,7 +1288,7 @@ void AnnotateShader(const ShaderReflection &refl, const SPIRVPatchData &patchDat
// load the location out of the global where we put it
rdcspv::Id location =
- editor.AddOperation(it, rdcspv::OpLoad(uvec3Type, editor.MakeId(), printfLocationVar));
+ editor.AddOperation(it, rdcspv::OpLoad(uvec4Type, editor.MakeId(), printfLocationVar));
it++;
// extract each component and add it as a new word after the header
@@ -1242,6 +1304,10 @@ void AnnotateShader(const ShaderReflection &refl, const SPIRVPatchData &patchDat
3, editor.AddOperation(
it, rdcspv::OpCompositeExtract(uint32Type, editor.MakeId(), location, {2})));
it++;
+ packetWords.insert(
+ 4, editor.AddOperation(
+ it, rdcspv::OpCompositeExtract(uint32Type, editor.MakeId(), location, {3})));
+ it++;
rdcspv::Id counterptr;
@@ -1491,7 +1557,8 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId)
const ActionDescription *action = m_pDriver->GetAction(eventId);
- if(action == NULL || !(action->flags & (ActionFlags::Dispatch | ActionFlags::Drawcall)))
+ if(action == NULL ||
+ !(action->flags & (ActionFlags::Dispatch | ActionFlags::MeshDispatch | ActionFlags::Drawcall)))
{
// deliberately show no bindings as used for non-draws
result.valid = true;
@@ -1810,22 +1877,22 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId)
}
else
{
- bool hasGeom = false;
+ bool hasGeomOrMesh = false;
for(uint32_t i = 0; i < graphicsInfo.stageCount; i++)
{
VkPipelineShaderStageCreateInfo &stage =
(VkPipelineShaderStageCreateInfo &)graphicsInfo.pStages[i];
- if((stage.stage & VK_SHADER_STAGE_GEOMETRY_BIT) != 0)
+ if((stage.stage & (VK_SHADER_STAGE_GEOMETRY_BIT | VK_SHADER_STAGE_MESH_BIT_EXT)) != 0)
{
- hasGeom = true;
+ hasGeomOrMesh = true;
break;
}
}
bool usePrimitiveID =
- !hasGeom && m_pDriver->GetDeviceEnabledFeatures().geometryShader != VK_FALSE;
+ !hasGeomOrMesh && m_pDriver->GetDeviceEnabledFeatures().geometryShader != VK_FALSE;
bool usesMultiview = state.GetRenderPass() != ResourceId()
? creationInfo.m_RenderPass[state.GetRenderPass()]
@@ -2055,7 +2122,7 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId)
uint32_t *location = printfBuf;
- printfBuf += 3;
+ printfBuf += 4;
const PrintfData &fmt = it->second;
@@ -2097,6 +2164,15 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId)
msg.location.compute.thread[x] = location[x] % threadDimX;
}
}
+ else if(stage == ShaderStage::Task)
+ {
+ for(int x = 0; x < 3; x++)
+ {
+ uint32_t threadDimX = sh.refl->dispatchThreadsDimension[x];
+ msg.location.mesh.taskGroup[x] = location[x] / threadDimX;
+ msg.location.mesh.thread[x] = location[x] % threadDimX;
+ }
+ }
else if(stage == ShaderStage::Vertex)
{
msg.location.vertex.vertexIndex = location[0];
@@ -2114,6 +2190,40 @@ bool VulkanReplay::FetchShaderFeedback(uint32_t eventId)
msg.location.geometry.primitive = location[0];
msg.location.geometry.view = location[1];
}
+ else if(stage == ShaderStage::Mesh)
+ {
+ for(int x = 0; x < 3; x++)
+ msg.location.mesh.meshGroup[x] = location[x];
+
+ uint32_t meshThread = msg.location.mesh.meshGroup[2] >> 16U;
+ msg.location.mesh.meshGroup[2] &= 0xffffu;
+
+ msg.location.mesh.thread[0] = meshThread % sh.refl->dispatchThreadsDimension[0];
+ msg.location.mesh.thread[1] = (meshThread / sh.refl->dispatchThreadsDimension[0]) %
+ sh.refl->dispatchThreadsDimension[1];
+ msg.location.mesh.thread[2] = meshThread / (sh.refl->dispatchThreadsDimension[0] *
+ sh.refl->dispatchThreadsDimension[1]);
+
+ const VulkanCreationInfo::Pipeline::Shader &tasksh =
+ pipeInfo.shaders[(uint32_t)ShaderStage::Task];
+
+ if(tasksh.module == ResourceId())
+ {
+ msg.location.mesh.taskGroup = {ShaderMeshMessageLocation::NotUsed,
+ ShaderMeshMessageLocation::NotUsed,
+ ShaderMeshMessageLocation::NotUsed};
+ }
+ else
+ {
+ uint32_t taskGroup = location[3];
+
+ msg.location.mesh.taskGroup[0] = taskGroup % tasksh.refl->dispatchThreadsDimension[0];
+ msg.location.mesh.taskGroup[1] = (taskGroup / tasksh.refl->dispatchThreadsDimension[0]) %
+ tasksh.refl->dispatchThreadsDimension[1];
+ msg.location.mesh.taskGroup[2] = taskGroup / (tasksh.refl->dispatchThreadsDimension[0] *
+ tasksh.refl->dispatchThreadsDimension[1]);
+ }
+ }
else
{
msg.location.pixel.x = location[0] >> 16U;
diff --git a/renderdoc/driver/vulkan/vk_shaderdebug.cpp b/renderdoc/driver/vulkan/vk_shaderdebug.cpp
index 3aa73ceab..ad520e8d0 100644
--- a/renderdoc/driver/vulkan/vk_shaderdebug.cpp
+++ b/renderdoc/driver/vulkan/vk_shaderdebug.cpp
@@ -4110,7 +4110,7 @@ ShaderDebugTrace *VulkanReplay::DebugPixel(uint32_t eventId, uint32_t x, uint32_
const ActionDescription *action = m_pDriver->GetAction(eventId);
- if(!(action->flags & ActionFlags::Drawcall))
+ if(!(action->flags & (ActionFlags::MeshDispatch | ActionFlags::Drawcall)))
{
RDCLOG("No drawcall selected");
return new ShaderDebugTrace();
diff --git a/renderdoc/driver/vulkan/vk_stringise.cpp b/renderdoc/driver/vulkan/vk_stringise.cpp
index 036884a03..44b3baa69 100644
--- a/renderdoc/driver/vulkan/vk_stringise.cpp
+++ b/renderdoc/driver/vulkan/vk_stringise.cpp
@@ -28,7 +28,7 @@
template <>
rdcstr DoStringise(const VulkanChunk &el)
{
- RDCCOMPILE_ASSERT((uint32_t)VulkanChunk::Max == 1198, "Chunks changed without updating names");
+ RDCCOMPILE_ASSERT((uint32_t)VulkanChunk::Max == 1201, "Chunks changed without updating names");
BEGIN_ENUM_STRINGISE(VulkanChunk)
{
@@ -230,6 +230,9 @@ rdcstr DoStringise(const VulkanChunk &el)
STRINGISE_ENUM_CLASS(vkCmdSetSampleLocationsEnableEXT)
STRINGISE_ENUM_CLASS(vkCmdSetSampleMaskEXT)
STRINGISE_ENUM_CLASS(vkCmdSetTessellationDomainOriginEXT)
+ STRINGISE_ENUM_CLASS(vkCmdDrawMeshTasksEXT)
+ STRINGISE_ENUM_CLASS(vkCmdDrawMeshTasksIndirectEXT)
+ STRINGISE_ENUM_CLASS(vkCmdDrawMeshTasksIndirectCountEXT)
STRINGISE_ENUM_CLASS_NAMED(Max, "Max Chunk");
}
END_ENUM_STRINGISE()
diff --git a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp
index 122bbb1f8..6f6e99f7b 100644
--- a/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp
+++ b/renderdoc/driver/vulkan/wrappers/vk_device_funcs.cpp
@@ -3177,6 +3177,26 @@ bool WrappedVulkan::Serialise_vkCreateDevice(SerialiserType &ser, VkPhysicalDevi
(ext->extendedDynamicState3DepthClipNegativeOneToOne != VK_FALSE);
}
END_PHYS_EXT_CHECK();
+
+ BEGIN_PHYS_EXT_CHECK(VkPhysicalDeviceMeshShaderFeaturesEXT,
+ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT);
+ {
+ CHECK_PHYS_EXT_FEATURE(taskShader);
+ CHECK_PHYS_EXT_FEATURE(meshShader);
+ CHECK_PHYS_EXT_FEATURE(multiviewMeshShader);
+ CHECK_PHYS_EXT_FEATURE(primitiveFragmentShadingRateMeshShader);
+ CHECK_PHYS_EXT_FEATURE(meshShaderQueries);
+
+ m_MeshShaders = ext->meshShader != VK_FALSE;
+ m_TaskShaders = ext->taskShader != VK_FALSE;
+ m_MeshQueries = avail.meshShaderQueries != VK_FALSE;
+
+ if(avail.meshShaderQueries)
+ ext->meshShaderQueries = true;
+ else
+ RDCWARN("meshShaderQueries = false, mesh shader performance counters unavailable");
+ }
+ END_PHYS_EXT_CHECK();
}
if(availFeatures.depthClamp)
diff --git a/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp
index 5282adfec..b2ea1a15b 100644
--- a/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp
+++ b/renderdoc/driver/vulkan/wrappers/vk_draw_funcs.cpp
@@ -54,6 +54,10 @@ VkIndirectPatchData WrappedVulkan::FetchIndirectData(VkIndirectPatchType type,
dataSize = sizeof(VkDrawIndexedIndirectCommand) + (count - 1) * stride;
break;
case VkIndirectPatchType::DrawIndirectByteCount: dataSize = 4; break;
+ case VkIndirectPatchType::MeshIndirect:
+ case VkIndirectPatchType::MeshIndirectCount:
+ dataSize = sizeof(VkDrawMeshTasksIndirectCommandEXT) + (count - 1) * stride;
+ break;
}
bufInfo.size = AlignUp16(dataSize);
@@ -187,12 +191,12 @@ bool WrappedVulkan::Serialise_vkCmdDraw(SerialiserType &ser, VkCommandBuffer com
ObjDisp(commandBuffer)
->CmdDraw(Unwrap(commandBuffer), vertexCount, instanceCount, firstVertex, firstInstance);
- if(eventId && m_ActionCallback->PostDraw(eventId, commandBuffer))
+ if(eventId && m_ActionCallback->PostDraw(eventId, ActionFlags::Drawcall, commandBuffer))
{
ObjDisp(commandBuffer)
->CmdDraw(Unwrap(commandBuffer), vertexCount, instanceCount, firstVertex,
firstInstance);
- m_ActionCallback->PostRedraw(eventId, commandBuffer);
+ m_ActionCallback->PostRedraw(eventId, ActionFlags::Drawcall, commandBuffer);
}
}
}
@@ -277,12 +281,12 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexed(SerialiserType &ser, VkCommandBuf
->CmdDrawIndexed(Unwrap(commandBuffer), indexCount, instanceCount, firstIndex,
vertexOffset, firstInstance);
- if(eventId && m_ActionCallback->PostDraw(eventId, commandBuffer))
+ if(eventId && m_ActionCallback->PostDraw(eventId, ActionFlags::Drawcall, commandBuffer))
{
ObjDisp(commandBuffer)
->CmdDrawIndexed(Unwrap(commandBuffer), indexCount, instanceCount, firstIndex,
vertexOffset, firstInstance);
- m_ActionCallback->PostRedraw(eventId, commandBuffer);
+ m_ActionCallback->PostRedraw(eventId, ActionFlags::Drawcall, commandBuffer);
}
}
}
@@ -377,11 +381,11 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirect(SerialiserType &ser, VkCommandBu
ObjDisp(commandBuffer)
->CmdDrawIndirect(Unwrap(commandBuffer), Unwrap(buffer), offset, count, stride);
- if(eventId && m_ActionCallback->PostDraw(eventId, commandBuffer))
+ if(eventId && m_ActionCallback->PostDraw(eventId, ActionFlags::Drawcall, commandBuffer))
{
ObjDisp(commandBuffer)
->CmdDrawIndirect(Unwrap(commandBuffer), Unwrap(buffer), offset, count, stride);
- m_ActionCallback->PostRedraw(eventId, commandBuffer);
+ m_ActionCallback->PostRedraw(eventId, ActionFlags::Drawcall, commandBuffer);
}
}
}
@@ -455,12 +459,13 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirect(SerialiserType &ser, VkCommandBu
->CmdDrawIndirect(Unwrap(commandBuffer), Unwrap(m_IndirectBuffer.buf), 0, i + 1,
stride);
- if(eventId && m_ActionCallback->PostDraw(eventId, commandBuffer))
+ if(eventId &&
+ m_ActionCallback->PostDraw(eventId, ActionFlags::Drawcall, commandBuffer))
{
ObjDisp(commandBuffer)
->CmdDrawIndirect(Unwrap(commandBuffer), Unwrap(m_IndirectBuffer.buf), 0,
i + 1, stride);
- m_ActionCallback->PostRedraw(eventId, commandBuffer);
+ m_ActionCallback->PostRedraw(eventId, ActionFlags::Drawcall, commandBuffer);
}
// now that we're done, nop out this draw so that the next time around we only draw
@@ -785,12 +790,12 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexedIndirect(SerialiserType &ser,
ObjDisp(commandBuffer)
->CmdDrawIndexedIndirect(Unwrap(commandBuffer), Unwrap(buffer), offset, count, stride);
- if(eventId && m_ActionCallback->PostDraw(eventId, commandBuffer))
+ if(eventId && m_ActionCallback->PostDraw(eventId, ActionFlags::Drawcall, commandBuffer))
{
ObjDisp(commandBuffer)
->CmdDrawIndexedIndirect(Unwrap(commandBuffer), Unwrap(buffer), offset, count,
stride);
- m_ActionCallback->PostRedraw(eventId, commandBuffer);
+ m_ActionCallback->PostRedraw(eventId, ActionFlags::Drawcall, commandBuffer);
}
}
}
@@ -834,12 +839,13 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexedIndirect(SerialiserType &ser,
->CmdDrawIndexedIndirect(Unwrap(commandBuffer), Unwrap(buffer), offset, 1,
stride);
- if(eventId && m_ActionCallback->PostDraw(eventId, commandBuffer))
+ if(eventId &&
+ m_ActionCallback->PostDraw(eventId, ActionFlags::Drawcall, commandBuffer))
{
ObjDisp(commandBuffer)
->CmdDrawIndexedIndirect(Unwrap(commandBuffer), Unwrap(buffer), offset, 1,
stride);
- m_ActionCallback->PostRedraw(eventId, commandBuffer);
+ m_ActionCallback->PostRedraw(eventId, ActionFlags::Drawcall, commandBuffer);
}
offset += stride;
@@ -1150,10 +1156,10 @@ bool WrappedVulkan::Serialise_vkCmdDispatch(SerialiserType &ser, VkCommandBuffer
ObjDisp(commandBuffer)->CmdDispatch(Unwrap(commandBuffer), x, y, z);
- if(eventId && m_ActionCallback->PostDispatch(eventId, commandBuffer))
+ if(eventId && m_ActionCallback->PostDispatch(eventId, ActionFlags::Dispatch, commandBuffer))
{
ObjDisp(commandBuffer)->CmdDispatch(Unwrap(commandBuffer), x, y, z);
- m_ActionCallback->PostRedispatch(eventId, commandBuffer);
+ m_ActionCallback->PostRedispatch(eventId, ActionFlags::Dispatch, commandBuffer);
}
}
}
@@ -1226,10 +1232,10 @@ bool WrappedVulkan::Serialise_vkCmdDispatchIndirect(SerialiserType &ser,
ObjDisp(commandBuffer)->CmdDispatchIndirect(Unwrap(commandBuffer), Unwrap(buffer), offset);
- if(eventId && m_ActionCallback->PostDispatch(eventId, commandBuffer))
+ if(eventId && m_ActionCallback->PostDispatch(eventId, ActionFlags::Dispatch, commandBuffer))
{
ObjDisp(commandBuffer)->CmdDispatchIndirect(Unwrap(commandBuffer), Unwrap(buffer), offset);
- m_ActionCallback->PostRedispatch(eventId, commandBuffer);
+ m_ActionCallback->PostRedispatch(eventId, ActionFlags::Dispatch, commandBuffer);
}
}
}
@@ -2668,12 +2674,12 @@ bool WrappedVulkan::Serialise_vkCmdDispatchBase(SerialiserType &ser, VkCommandBu
->CmdDispatchBase(Unwrap(commandBuffer), baseGroupX, baseGroupY, baseGroupZ,
groupCountX, groupCountY, groupCountZ);
- if(eventId && m_ActionCallback->PostDispatch(eventId, commandBuffer))
+ if(eventId && m_ActionCallback->PostDispatch(eventId, ActionFlags::Dispatch, commandBuffer))
{
ObjDisp(commandBuffer)
->CmdDispatchBase(Unwrap(commandBuffer), baseGroupX, baseGroupY, baseGroupZ,
groupCountX, groupCountY, groupCountZ);
- m_ActionCallback->PostRedispatch(eventId, commandBuffer);
+ m_ActionCallback->PostRedispatch(eventId, ActionFlags::Dispatch, commandBuffer);
}
}
}
@@ -2838,12 +2844,12 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirectCount(SerialiserType &ser,
->CmdDrawIndirect(Unwrap(commandBuffer), Unwrap(m_IndirectBuffer.buf), 0, i + 1,
stride);
- if(eventId && m_ActionCallback->PostDraw(eventId, commandBuffer))
+ if(eventId && m_ActionCallback->PostDraw(eventId, ActionFlags::Drawcall, commandBuffer))
{
ObjDisp(commandBuffer)
->CmdDrawIndirect(Unwrap(commandBuffer), Unwrap(m_IndirectBuffer.buf), 0, i + 1,
stride);
- m_ActionCallback->PostRedraw(eventId, commandBuffer);
+ m_ActionCallback->PostRedraw(eventId, ActionFlags::Drawcall, commandBuffer);
}
// now that we're done, nop out this draw so that the next time around we only draw
@@ -3185,12 +3191,12 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndexedIndirectCount(
->CmdDrawIndexedIndirect(Unwrap(commandBuffer), Unwrap(m_IndirectBuffer.buf), 0,
i + 1, stride);
- if(eventId && m_ActionCallback->PostDraw(eventId, commandBuffer))
+ if(eventId && m_ActionCallback->PostDraw(eventId, ActionFlags::Drawcall, commandBuffer))
{
ObjDisp(commandBuffer)
->CmdDrawIndexedIndirect(Unwrap(commandBuffer), Unwrap(m_IndirectBuffer.buf), 0,
i + 1, stride);
- m_ActionCallback->PostRedraw(eventId, commandBuffer);
+ m_ActionCallback->PostRedraw(eventId, ActionFlags::Drawcall, commandBuffer);
}
// now that we're done, nop out this draw so that the next time around we only draw
@@ -3463,13 +3469,13 @@ bool WrappedVulkan::Serialise_vkCmdDrawIndirectByteCountEXT(
Unwrap(counterBuffer), counterBufferOffset, counterOffset,
vertexStride);
- if(eventId && m_ActionCallback->PostDraw(eventId, commandBuffer))
+ if(eventId && m_ActionCallback->PostDraw(eventId, ActionFlags::Drawcall, commandBuffer))
{
ObjDisp(commandBuffer)
->CmdDrawIndirectByteCountEXT(Unwrap(commandBuffer), instanceCount, firstInstance,
Unwrap(counterBuffer), counterBufferOffset,
counterOffset, vertexStride);
- m_ActionCallback->PostRedraw(eventId, commandBuffer);
+ m_ActionCallback->PostRedraw(eventId, ActionFlags::Drawcall, commandBuffer);
}
}
}
@@ -4334,6 +4340,878 @@ void WrappedVulkan::vkCmdResolveImage2(VkCommandBuffer commandBuffer,
}
}
+template
+bool WrappedVulkan::Serialise_vkCmdDrawMeshTasksEXT(SerialiserType &ser,
+ VkCommandBuffer commandBuffer,
+ uint32_t groupCountX, uint32_t groupCountY,
+ uint32_t groupCountZ)
+{
+ SERIALISE_ELEMENT(commandBuffer);
+ SERIALISE_ELEMENT(groupCountX).Important();
+ SERIALISE_ELEMENT(groupCountY).Important();
+ SERIALISE_ELEMENT(groupCountZ).Important();
+
+ Serialise_DebugMessages(ser);
+
+ SERIALISE_CHECK_READ_ERRORS();
+
+ if(IsReplayingAndReading())
+ {
+ m_LastCmdBufferID = GetResourceManager()->GetOriginalID(GetResID(commandBuffer));
+
+ if(IsActiveReplaying(m_State))
+ {
+ if(InRerecordRange(m_LastCmdBufferID))
+ {
+ commandBuffer = RerecordCmdBuf(m_LastCmdBufferID);
+
+ uint32_t eventId = HandlePreCallback(commandBuffer, ActionFlags::MeshDispatch);
+
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksEXT(Unwrap(commandBuffer), groupCountX, groupCountY, groupCountZ);
+
+ if(eventId && m_ActionCallback->PostDraw(eventId, ActionFlags::MeshDispatch, commandBuffer))
+ {
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksEXT(Unwrap(commandBuffer), groupCountX, groupCountY, groupCountZ);
+ m_ActionCallback->PostRedraw(eventId, ActionFlags::MeshDispatch, commandBuffer);
+ }
+ }
+ }
+ else
+ {
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksEXT(Unwrap(commandBuffer), groupCountX, groupCountY, groupCountZ);
+
+ {
+ AddEvent();
+
+ ActionDescription action;
+ action.dispatchDimension[0] = groupCountX;
+ action.dispatchDimension[1] = groupCountY;
+ action.dispatchDimension[2] = groupCountZ;
+
+ action.flags |= ActionFlags::MeshDispatch;
+
+ AddAction(action);
+ }
+ }
+ }
+
+ return true;
+}
+
+void WrappedVulkan::vkCmdDrawMeshTasksEXT(VkCommandBuffer commandBuffer, uint32_t groupCountX,
+ uint32_t groupCountY, uint32_t groupCountZ)
+{
+ SCOPED_DBG_SINK();
+
+ SERIALISE_TIME_CALL(
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksEXT(Unwrap(commandBuffer), groupCountX, groupCountY, groupCountZ));
+
+ if(IsCaptureMode(m_State))
+ {
+ VkResourceRecord *record = GetRecord(commandBuffer);
+
+ CACHE_THREAD_SERIALISER();
+
+ ser.SetActionChunk();
+ SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdDrawMeshTasksEXT);
+ Serialise_vkCmdDrawMeshTasksEXT(ser, commandBuffer, groupCountX, groupCountY, groupCountZ);
+
+ record->AddChunk(scope.Get(&record->cmdInfo->alloc));
+ }
+}
+
+template
+bool WrappedVulkan::Serialise_vkCmdDrawMeshTasksIndirectEXT(SerialiserType &ser,
+ VkCommandBuffer commandBuffer,
+ VkBuffer buffer, VkDeviceSize offset,
+ uint32_t drawCount, uint32_t stride)
+{
+ SERIALISE_ELEMENT(commandBuffer);
+ SERIALISE_ELEMENT(buffer).Important();
+ SERIALISE_ELEMENT(offset);
+ SERIALISE_ELEMENT(drawCount).Important();
+ SERIALISE_ELEMENT(stride);
+
+ Serialise_DebugMessages(ser);
+
+ SERIALISE_CHECK_READ_ERRORS();
+
+ bool multidraw = drawCount > 1;
+
+ if(IsReplayingAndReading())
+ {
+ m_LastCmdBufferID = GetResourceManager()->GetOriginalID(GetResID(commandBuffer));
+
+ // do execution (possibly partial)
+ if(IsActiveReplaying(m_State))
+ {
+ if(!multidraw)
+ {
+ // for single draws, it's pretty simple
+
+ // account for the fake indirect subcommand before checking if we're in re-record range
+ if(drawCount > 0)
+ m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID++;
+
+ if(InRerecordRange(m_LastCmdBufferID))
+ {
+ commandBuffer = RerecordCmdBuf(m_LastCmdBufferID);
+
+ uint32_t eventId = HandlePreCallback(commandBuffer);
+
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), Unwrap(buffer), offset,
+ drawCount, stride);
+
+ if(eventId && m_ActionCallback->PostDraw(eventId, ActionFlags::MeshDispatch, commandBuffer))
+ {
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), Unwrap(buffer), offset,
+ drawCount, stride);
+ m_ActionCallback->PostRedraw(eventId, ActionFlags::MeshDispatch, commandBuffer);
+ }
+ }
+ }
+ else
+ {
+ if(InRerecordRange(m_LastCmdBufferID))
+ {
+ commandBuffer = RerecordCmdBuf(m_LastCmdBufferID);
+
+ uint32_t curEID = m_RootEventID;
+
+ if(m_FirstEventID <= 1)
+ {
+ curEID = m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID;
+
+ if(m_Partial[Primary].partialParent == m_LastCmdBufferID)
+ curEID += m_Partial[Primary].baseEvent;
+ else if(m_Partial[Secondary].partialParent == m_LastCmdBufferID)
+ curEID += m_Partial[Secondary].baseEvent;
+ }
+
+ ActionUse use(m_CurChunkOffset, 0);
+ auto it = std::lower_bound(m_ActionUses.begin(), m_ActionUses.end(), use);
+
+ if(it == m_ActionUses.end())
+ {
+ RDCERR("Unexpected action not found in uses vector, offset %llu", m_CurChunkOffset);
+ }
+ else
+ {
+ uint32_t baseEventID = it->eventId;
+
+ // when we have a callback, submit every action individually to the callback
+ if(m_ActionCallback)
+ {
+ VkMarkerRegion::Begin(
+ StringFormat::Fmt("Mesh Drawcall callback replay (drawCount=%u)", drawCount),
+ commandBuffer);
+
+ // first copy off the buffer segment to our indirect action buffer
+ VkBufferMemoryBarrier bufBarrier = {
+ VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+ NULL,
+ VK_ACCESS_INDIRECT_COMMAND_READ_BIT,
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ VK_QUEUE_FAMILY_IGNORED,
+ VK_QUEUE_FAMILY_IGNORED,
+ Unwrap(buffer),
+ offset,
+ (drawCount > 0 ? stride * (drawCount - 1) : 0) +
+ sizeof(VkDrawMeshTasksIndirectCommandEXT),
+ };
+
+ DoPipelineBarrier(commandBuffer, 1, &bufBarrier);
+ VkBufferCopy region = {offset, 0, bufBarrier.size};
+ ObjDisp(commandBuffer)
+ ->CmdCopyBuffer(Unwrap(commandBuffer), Unwrap(buffer),
+ Unwrap(m_IndirectBuffer.buf), 1, ®ion);
+
+ // wait for the copy to finish
+ bufBarrier.buffer = Unwrap(m_IndirectBuffer.buf);
+ bufBarrier.offset = 0;
+ DoPipelineBarrier(commandBuffer, 1, &bufBarrier);
+
+ bufBarrier.size = sizeof(VkDrawMeshTasksIndirectCommandEXT);
+
+ for(uint32_t i = 0; i < drawCount; i++)
+ {
+ uint32_t eventId = HandlePreCallback(commandBuffer, ActionFlags::MeshDispatch, i + 1);
+
+ // action up to and including i. The previous draws will be nop'd out
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer),
+ Unwrap(m_IndirectBuffer.buf), 0, i + 1, stride);
+
+ if(eventId &&
+ m_ActionCallback->PostDraw(eventId, ActionFlags::MeshDispatch, commandBuffer))
+ {
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer),
+ Unwrap(m_IndirectBuffer.buf), 0, i + 1, stride);
+ m_ActionCallback->PostRedraw(eventId, ActionFlags::MeshDispatch, commandBuffer);
+ }
+
+ // now that we're done, nop out this draw so that the next time around we only draw
+ // the next draw.
+ bufBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
+ bufBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ DoPipelineBarrier(commandBuffer, 1, &bufBarrier);
+ ObjDisp(commandBuffer)
+ ->CmdFillBuffer(Unwrap(commandBuffer), bufBarrier.buffer, bufBarrier.offset,
+ bufBarrier.size, 0);
+ bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ bufBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
+ DoPipelineBarrier(commandBuffer, 1, &bufBarrier);
+
+ bufBarrier.offset += stride;
+ }
+
+ VkMarkerRegion::End(commandBuffer);
+ }
+ // To add the multidraw, we made an event N that is the 'parent' marker, then
+ // N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected
+ // then we'll replay up to N but not N+1, so just do nothing - we DON'T want to draw
+ // the first sub-draw in that range.
+ else if(m_LastEventID > baseEventID)
+ {
+ uint32_t drawidx = 0;
+
+ if(m_FirstEventID <= 1)
+ {
+ // if we're replaying part-way into a multidraw, we can replay the first part
+ // 'easily'
+ // by just reducing the Count parameter to however many we want to replay. This only
+ // works if we're replaying from the first multidraw to the nth (n less than Count)
+ drawCount = RDCMIN(drawCount, m_LastEventID - baseEventID);
+ }
+ else
+ {
+ // otherwise we do the 'hard' case, draw only one multidraw
+ // note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or
+ // a single draw.
+ //
+ // We also need to draw the same number of draws so that DrawIndex is faithful. In
+ // order to preserve the draw index we write a custom indirect buffer that has zeros
+ // for the parameters of all previous draws.
+ drawidx = (curEID - baseEventID - 1);
+
+ offset += stride * drawidx;
+
+ // ensure the custom buffer is large enough
+ VkDeviceSize bufLength = sizeof(VkDrawMeshTasksIndirectCommandEXT) * (drawidx + 1);
+
+ RDCASSERT(bufLength <= m_IndirectBufferSize, bufLength, m_IndirectBufferSize);
+
+ VkBufferMemoryBarrier bufBarrier = {
+ VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+ NULL,
+ VK_ACCESS_INDIRECT_COMMAND_READ_BIT,
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ VK_QUEUE_FAMILY_IGNORED,
+ VK_QUEUE_FAMILY_IGNORED,
+ Unwrap(m_IndirectBuffer.buf),
+ 0,
+ m_IndirectBufferSize,
+ };
+
+ VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ NULL,
+ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
+
+ ObjDisp(m_IndirectCommandBuffer)
+ ->BeginCommandBuffer(Unwrap(m_IndirectCommandBuffer), &beginInfo);
+
+ // wait for any previous indirect draws to complete before filling/transferring
+ DoPipelineBarrier(m_IndirectCommandBuffer, 1, &bufBarrier);
+
+ // initialise to 0 so all other draws don't draw anything
+ ObjDisp(m_IndirectCommandBuffer)
+ ->CmdFillBuffer(Unwrap(m_IndirectCommandBuffer), Unwrap(m_IndirectBuffer.buf),
+ 0, m_IndirectBufferSize, 0);
+
+ // wait for fill to complete before copy
+ bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ bufBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+
+ DoPipelineBarrier(m_IndirectCommandBuffer, 1, &bufBarrier);
+
+ // copy over the actual parameter set into the right place
+ VkBufferCopy region = {offset, bufLength - sizeof(VkDrawMeshTasksIndirectCommandEXT),
+ sizeof(VkDrawMeshTasksIndirectCommandEXT)};
+ ObjDisp(m_IndirectCommandBuffer)
+ ->CmdCopyBuffer(Unwrap(m_IndirectCommandBuffer), Unwrap(buffer),
+ Unwrap(m_IndirectBuffer.buf), 1, ®ion);
+
+ // finally wait for copy to complete before drawing from it
+ bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ bufBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
+
+ DoPipelineBarrier(m_IndirectCommandBuffer, 1, &bufBarrier);
+
+ ObjDisp(m_IndirectCommandBuffer)->EndCommandBuffer(Unwrap(m_IndirectCommandBuffer));
+
+ // draw from our custom buffer
+ m_IndirectDraw = true;
+ buffer = m_IndirectBuffer.buf;
+ offset = 0;
+ drawCount = drawidx + 1;
+ stride = sizeof(VkDrawMeshTasksIndirectCommandEXT);
+ }
+
+ {
+ uint32_t eventId =
+ HandlePreCallback(commandBuffer, ActionFlags::MeshDispatch, drawidx + 1);
+
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), Unwrap(buffer), offset,
+ drawCount, stride);
+
+ if(eventId &&
+ m_ActionCallback->PostDraw(eventId, ActionFlags::MeshDispatch, commandBuffer))
+ {
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), Unwrap(buffer), offset,
+ drawCount, stride);
+ m_ActionCallback->PostRedraw(eventId, ActionFlags::MeshDispatch, commandBuffer);
+ }
+ }
+ }
+ }
+ }
+
+ // multidraws skip the event ID past the whole thing
+ m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID += drawCount + 1;
+ }
+ }
+ else
+ {
+ VkIndirectPatchData indirectPatch = FetchIndirectData(
+ VkIndirectPatchType::MeshIndirect, commandBuffer, buffer, offset, drawCount, stride);
+
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), Unwrap(buffer), offset, drawCount,
+ stride);
+
+ // add on the size we'll need for an indirect buffer in the worst case.
+ // Note that we'll only ever be partially replaying one draw at a time, so we only need the
+ // worst case.
+ m_IndirectBufferSize = RDCMAX(m_IndirectBufferSize,
+ sizeof(VkDrawMeshTasksIndirectCommandEXT) + drawCount * stride);
+
+ rdcstr name = "vkCmdDrawMeshTasksIndirectEXT";
+
+ SDChunk *baseChunk = m_StructuredFile->chunks.back();
+
+ // for 'single' draws, don't do complex multi-draw just inline it
+ if(drawCount == 1)
+ {
+ ActionDescription action;
+
+ AddEvent();
+
+ // add a fake chunk for this individual indirect draw
+ SDChunk *fakeChunk = new SDChunk("Indirect sub-command"_lit);
+ fakeChunk->metadata = baseChunk->metadata;
+ fakeChunk->metadata.chunkID = (uint32_t)VulkanChunk::vkCmdIndirectSubCommand;
+
+ {
+ StructuredSerialiser structuriser(fakeChunk, ser.GetChunkLookup());
+
+ structuriser.Serialise("drawIndex"_lit, 0U);
+ ResourceId bufid = GetResourceManager()->GetOriginalID(GetResID(buffer));
+ structuriser.Serialise("buffer"_lit, bufid);
+ structuriser.Serialise("offset"_lit, offset);
+ structuriser.Serialise("stride"_lit, stride);
+ structuriser.Serialise("command"_lit, VkDrawMeshTasksIndirectCommandEXT()).Important();
+ }
+
+ m_StructuredFile->chunks.insert(m_StructuredFile->chunks.size() - 1, fakeChunk);
+
+ m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID++;
+
+ AddEvent();
+
+ action.customName = name;
+ action.flags = ActionFlags::MeshDispatch | ActionFlags::Indirect;
+
+ AddAction(action);
+
+ VulkanActionTreeNode &actionNode = GetActionStack().back()->children.back();
+
+ actionNode.indirectPatch = indirectPatch;
+
+ actionNode.resourceUsage.push_back(make_rdcpair(
+ GetResID(buffer), EventUsage(actionNode.action.eventId, ResourceUsage::Indirect)));
+
+ return true;
+ }
+
+ ActionDescription action;
+ action.customName = name;
+ action.flags = ActionFlags::MultiAction | ActionFlags::PushMarker;
+
+ if(drawCount == 0)
+ {
+ action.flags = ActionFlags::MeshDispatch | ActionFlags::Indirect;
+ action.customName += "(0)";
+ }
+
+ AddEvent();
+ AddAction(action);
+
+ VulkanActionTreeNode &actionNode = GetActionStack().back()->children.back();
+
+ actionNode.indirectPatch = indirectPatch;
+
+ actionNode.resourceUsage.push_back(make_rdcpair(
+ GetResID(buffer), EventUsage(actionNode.action.eventId, ResourceUsage::Indirect)));
+
+ if(drawCount > 0)
+ m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID++;
+
+ for(uint32_t i = 0; i < drawCount; i++)
+ {
+ ActionDescription multi;
+
+ multi.customName = name;
+
+ multi.flags |= ActionFlags::MeshDispatch | ActionFlags::Indirect;
+
+ // add a fake chunk for this individual indirect action
+ SDChunk *fakeChunk = new SDChunk("Indirect sub-command"_lit);
+ fakeChunk->metadata = baseChunk->metadata;
+ fakeChunk->metadata.chunkID = (uint32_t)VulkanChunk::vkCmdIndirectSubCommand;
+
+ {
+ StructuredSerialiser structuriser(fakeChunk, ser.GetChunkLookup());
+
+ structuriser.Serialise("drawIndex"_lit, 0U);
+ ResourceId bufid = GetResourceManager()->GetOriginalID(GetResID(buffer));
+ structuriser.Serialise("buffer"_lit, bufid);
+ structuriser.Serialise("offset"_lit, offset);
+ structuriser.Serialise("stride"_lit, stride);
+ structuriser.Serialise("command"_lit, VkDrawMeshTasksIndirectCommandEXT()).Important();
+ }
+
+ m_StructuredFile->chunks.push_back(fakeChunk);
+
+ AddEvent();
+ AddAction(multi);
+
+ m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID++;
+ }
+
+ if(drawCount > 0)
+ {
+ AddEvent();
+ action.customName = name + " end";
+ action.flags = ActionFlags::PopMarker;
+ AddAction(action);
+ }
+ }
+ }
+
+ return true;
+}
+
+void WrappedVulkan::vkCmdDrawMeshTasksIndirectEXT(VkCommandBuffer commandBuffer, VkBuffer buffer,
+ VkDeviceSize offset, uint32_t drawCount,
+ uint32_t stride)
+{
+ SCOPED_DBG_SINK();
+
+ SERIALISE_TIME_CALL(ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), Unwrap(buffer),
+ offset, drawCount, stride));
+
+ if(IsCaptureMode(m_State))
+ {
+ VkResourceRecord *record = GetRecord(commandBuffer);
+
+ CACHE_THREAD_SERIALISER();
+
+ ser.SetActionChunk();
+ SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdDrawMeshTasksIndirectEXT);
+ Serialise_vkCmdDrawMeshTasksIndirectEXT(ser, commandBuffer, buffer, offset, drawCount, stride);
+
+ record->AddChunk(scope.Get(&record->cmdInfo->alloc));
+
+ record->MarkBufferFrameReferenced(
+ GetRecord(buffer), offset,
+ stride * (drawCount - 1) + sizeof(VkDrawMeshTasksIndirectCommandEXT), eFrameRef_Read);
+ }
+}
+
+template
+bool WrappedVulkan::Serialise_vkCmdDrawMeshTasksIndirectCountEXT(
+ SerialiserType &ser, VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
+ VkBuffer countBuffer, VkDeviceSize countBufferOffset, uint32_t maxDrawCount, uint32_t stride)
+{
+ SERIALISE_ELEMENT(commandBuffer);
+ SERIALISE_ELEMENT(buffer).Important();
+ SERIALISE_ELEMENT(offset);
+ SERIALISE_ELEMENT(countBuffer).Important();
+ SERIALISE_ELEMENT(countBufferOffset);
+ SERIALISE_ELEMENT(maxDrawCount).Important();
+ SERIALISE_ELEMENT(stride);
+
+ Serialise_DebugMessages(ser);
+
+ SERIALISE_CHECK_READ_ERRORS();
+
+ if(IsReplayingAndReading())
+ {
+ m_LastCmdBufferID = GetResourceManager()->GetOriginalID(GetResID(commandBuffer));
+
+ // do execution (possibly partial)
+ if(IsActiveReplaying(m_State))
+ {
+ // this count is wrong if we're not re-recording and fetching the actual count below, but it's
+ // impossible without having a particular submission in mind because without a specific
+ // instance we can't know what the actual count was (it could vary between submissions).
+ // Fortunately when we're not in the re-recording command buffer the EID tracking isn't
+ // needed.
+ uint32_t count = maxDrawCount;
+
+ if(InRerecordRange(m_LastCmdBufferID))
+ {
+ commandBuffer = RerecordCmdBuf(m_LastCmdBufferID);
+
+ uint32_t curEID = m_RootEventID;
+
+ if(m_FirstEventID <= 1)
+ {
+ curEID = m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID;
+
+ if(m_Partial[Primary].partialParent == m_LastCmdBufferID)
+ curEID += m_Partial[Primary].baseEvent;
+ else if(m_Partial[Secondary].partialParent == m_LastCmdBufferID)
+ curEID += m_Partial[Secondary].baseEvent;
+ }
+
+ ActionUse use(m_CurChunkOffset, 0);
+ auto it = std::lower_bound(m_ActionUses.begin(), m_ActionUses.end(), use);
+
+ if(it == m_ActionUses.end() || GetAction(it->eventId) == NULL)
+ {
+ RDCERR("Unexpected action not found in uses vector, offset %llu", m_CurChunkOffset);
+ }
+ else
+ {
+ uint32_t baseEventID = it->eventId;
+
+ // get the number of draws by looking at how many children the parent action has.
+ const rdcarray &children = GetAction(it->eventId)->children;
+ count = (uint32_t)children.size();
+
+ // don't count the popmarker child
+ if(!children.empty() && children.back().flags & ActionFlags::PopMarker)
+ count--;
+
+ // when we have a callback, submit every action individually to the callback
+ if(m_ActionCallback)
+ {
+ VkMarkerRegion::Begin(
+ StringFormat::Fmt("Mesh Dispatch callback replay (drawCount=%u)", count),
+ commandBuffer);
+
+ // first copy off the buffer segment to our indirect draw buffer
+ VkBufferMemoryBarrier bufBarrier = {
+ VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+ NULL,
+ VK_ACCESS_INDIRECT_COMMAND_READ_BIT,
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ VK_QUEUE_FAMILY_IGNORED,
+ VK_QUEUE_FAMILY_IGNORED,
+ Unwrap(buffer),
+ offset,
+ (count > 0 ? stride * (count - 1) : 0) + sizeof(VkDrawMeshTasksIndirectCommandEXT),
+ };
+
+ DoPipelineBarrier(commandBuffer, 1, &bufBarrier);
+ VkBufferCopy region = {offset, 0, bufBarrier.size};
+ ObjDisp(commandBuffer)
+ ->CmdCopyBuffer(Unwrap(commandBuffer), Unwrap(buffer), Unwrap(m_IndirectBuffer.buf),
+ 1, ®ion);
+
+ // wait for the copy to finish
+ bufBarrier.buffer = Unwrap(m_IndirectBuffer.buf);
+ bufBarrier.offset = 0;
+ DoPipelineBarrier(commandBuffer, 1, &bufBarrier);
+
+ bufBarrier.size = sizeof(VkDrawMeshTasksIndirectCommandEXT);
+
+ for(uint32_t i = 0; i < count; i++)
+ {
+ uint32_t eventId = HandlePreCallback(commandBuffer, ActionFlags::MeshDispatch, i + 1);
+
+ // action up to and including i. The previous draws will be nop'd out
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), Unwrap(m_IndirectBuffer.buf),
+ 0, i + 1, stride);
+
+ if(eventId &&
+ m_ActionCallback->PostDraw(eventId, ActionFlags::MeshDispatch, commandBuffer))
+ {
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer),
+ Unwrap(m_IndirectBuffer.buf), 0, i + 1, stride);
+ m_ActionCallback->PostRedraw(eventId, ActionFlags::MeshDispatch, commandBuffer);
+ }
+
+ // now that we're done, nop out this draw so that the next time around we only draw
+ // the next draw.
+ bufBarrier.srcAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
+ bufBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ DoPipelineBarrier(commandBuffer, 1, &bufBarrier);
+ ObjDisp(commandBuffer)
+ ->CmdFillBuffer(Unwrap(commandBuffer), bufBarrier.buffer, bufBarrier.offset,
+ bufBarrier.size, 0);
+ bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ bufBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
+ DoPipelineBarrier(commandBuffer, 1, &bufBarrier);
+
+ bufBarrier.offset += stride;
+ }
+
+ VkMarkerRegion::End(commandBuffer);
+ }
+ // To add the multidraw, we made an event N that is the 'parent' marker, then
+ // N+1, N+2, N+3, ... for each of the sub-draws. If the first sub-draw is selected
+ // then we'll replay up to N but not N+1, so just do nothing - we DON'T want to draw
+ // the first sub-draw in that range.
+ else if(m_LastEventID > baseEventID)
+ {
+ uint32_t drawidx = 0;
+
+ if(m_FirstEventID <= 1)
+ {
+ // if we're replaying part-way into a multidraw, we can replay the first part
+ // 'easily'
+ // by just reducing the Count parameter to however many we want to replay. This only
+ // works if we're replaying from the first multidraw to the nth (n less than Count)
+ count = RDCMIN(count, m_LastEventID - baseEventID);
+ }
+ else
+ {
+ // otherwise we do the 'hard' case, draw only one multidraw
+ // note we'll never be asked to do e.g. 3rd-7th of a multidraw. Only ever 0th-nth or
+ // a single draw.
+ //
+ // We also need to draw the same number of draws so that DrawIndex is faithful. In
+ // order to preserve the draw index we write a custom indirect buffer that has zeros
+ // for the parameters of all previous draws.
+ drawidx = (curEID - baseEventID - 1);
+
+ offset += stride * drawidx;
+
+ // ensure the custom buffer is large enough
+ VkDeviceSize bufLength = sizeof(VkDrawMeshTasksIndirectCommandEXT) * (drawidx + 1);
+
+ RDCASSERT(bufLength <= m_IndirectBufferSize, bufLength, m_IndirectBufferSize);
+
+ VkBufferMemoryBarrier bufBarrier = {
+ VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
+ NULL,
+ VK_ACCESS_INDIRECT_COMMAND_READ_BIT,
+ VK_ACCESS_TRANSFER_WRITE_BIT,
+ VK_QUEUE_FAMILY_IGNORED,
+ VK_QUEUE_FAMILY_IGNORED,
+ Unwrap(m_IndirectBuffer.buf),
+ 0,
+ m_IndirectBufferSize,
+ };
+
+ VkCommandBufferBeginInfo beginInfo = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, NULL,
+ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT};
+
+ ObjDisp(m_IndirectCommandBuffer)
+ ->BeginCommandBuffer(Unwrap(m_IndirectCommandBuffer), &beginInfo);
+
+ // wait for any previous indirect draws to complete before filling/transferring
+ DoPipelineBarrier(m_IndirectCommandBuffer, 1, &bufBarrier);
+
+ // initialise to 0 so all other draws don't draw anything
+ ObjDisp(m_IndirectCommandBuffer)
+ ->CmdFillBuffer(Unwrap(m_IndirectCommandBuffer), Unwrap(m_IndirectBuffer.buf), 0,
+ m_IndirectBufferSize, 0);
+
+ // wait for fill to complete before copy
+ bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ bufBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+
+ DoPipelineBarrier(m_IndirectCommandBuffer, 1, &bufBarrier);
+
+ // copy over the actual parameter set into the right place
+ VkBufferCopy region = {offset, bufLength - sizeof(VkDrawMeshTasksIndirectCommandEXT),
+ sizeof(VkDrawMeshTasksIndirectCommandEXT)};
+ ObjDisp(m_IndirectCommandBuffer)
+ ->CmdCopyBuffer(Unwrap(m_IndirectCommandBuffer), Unwrap(buffer),
+ Unwrap(m_IndirectBuffer.buf), 1, ®ion);
+
+ // finally wait for copy to complete before drawing from it
+ bufBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ bufBarrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT;
+
+ DoPipelineBarrier(m_IndirectCommandBuffer, 1, &bufBarrier);
+
+ ObjDisp(m_IndirectCommandBuffer)->EndCommandBuffer(Unwrap(m_IndirectCommandBuffer));
+
+ // draw from our custom buffer
+ m_IndirectDraw = true;
+ buffer = m_IndirectBuffer.buf;
+ offset = 0;
+ count = drawidx + 1;
+ stride = sizeof(VkDrawMeshTasksIndirectCommandEXT);
+ }
+
+ {
+ uint32_t eventId =
+ HandlePreCallback(commandBuffer, ActionFlags::MeshDispatch, drawidx + 1);
+
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), Unwrap(buffer), offset,
+ count, stride);
+
+ if(eventId &&
+ m_ActionCallback->PostDraw(eventId, ActionFlags::MeshDispatch, commandBuffer))
+ {
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksIndirectEXT(Unwrap(commandBuffer), Unwrap(buffer), offset,
+ count, stride);
+ m_ActionCallback->PostRedraw(eventId, ActionFlags::MeshDispatch, commandBuffer);
+ }
+ }
+ }
+ }
+ }
+
+ // multidraws skip the event ID past the whole thing
+ m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID += count + 1;
+ }
+ else
+ {
+ VkIndirectPatchData indirectPatch =
+ FetchIndirectData(VkIndirectPatchType::MeshIndirectCount, commandBuffer, buffer, offset,
+ maxDrawCount, stride, countBuffer, countBufferOffset);
+
+ ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksIndirectCountEXT(Unwrap(commandBuffer), Unwrap(buffer), offset,
+ Unwrap(countBuffer), countBufferOffset, maxDrawCount,
+ stride);
+
+ // add on the size we'll need for an indirect buffer in the worst case.
+ // Note that we'll only ever be partially replaying one draw at a time, so we only need the
+ // worst case.
+ m_IndirectBufferSize =
+ RDCMAX(m_IndirectBufferSize, sizeof(VkDrawMeshTasksIndirectCommandEXT) +
+ (maxDrawCount > 0 ? maxDrawCount - 1 : 0) * stride);
+
+ rdcstr name = "vkCmdDrawMeshTasksIndirectCountEXT";
+
+ SDChunk *baseChunk = m_StructuredFile->chunks.back();
+
+ ActionDescription action;
+ action.customName = name;
+ action.flags = ActionFlags::MultiAction | ActionFlags::PushMarker;
+
+ if(maxDrawCount == 0)
+ action.customName = name + "(0)";
+
+ AddEvent();
+ AddAction(action);
+
+ VulkanActionTreeNode &actionNode = GetActionStack().back()->children.back();
+
+ actionNode.indirectPatch = indirectPatch;
+
+ actionNode.resourceUsage.push_back(make_rdcpair(
+ GetResID(buffer), EventUsage(actionNode.action.eventId, ResourceUsage::Indirect)));
+
+ m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID++;
+
+ // only allocate up to one indirect sub-command to avoid pessimistic allocation if
+ // maxDrawCount is very high but the actual action count is low.
+ for(uint32_t i = 0; i < RDCMIN(1U, maxDrawCount); i++)
+ {
+ ActionDescription multi;
+
+ multi.customName = name;
+
+ multi.flags |= ActionFlags::MeshDispatch | ActionFlags::Indirect;
+
+ // add a fake chunk for this individual indirect action
+ SDChunk *fakeChunk = new SDChunk("Indirect sub-command"_lit);
+ fakeChunk->metadata = baseChunk->metadata;
+ fakeChunk->metadata.chunkID = (uint32_t)VulkanChunk::vkCmdIndirectSubCommand;
+
+ {
+ StructuredSerialiser structuriser(fakeChunk, ser.GetChunkLookup());
+
+ structuriser.Serialise("drawIndex"_lit, 0U);
+ ResourceId bufid = GetResourceManager()->GetOriginalID(GetResID(buffer));
+ structuriser.Serialise("buffer"_lit, bufid);
+ structuriser.Serialise("offset"_lit, offset);
+ structuriser.Serialise("stride"_lit, stride);
+ structuriser.Serialise("command"_lit, VkDrawMeshTasksIndirectCommandEXT()).Important();
+ }
+
+ m_StructuredFile->chunks.push_back(fakeChunk);
+
+ AddEvent();
+ AddAction(multi);
+
+ m_BakedCmdBufferInfo[m_LastCmdBufferID].curEventID++;
+ }
+
+ AddEvent();
+ action.customName = name + " end";
+ action.flags = ActionFlags::PopMarker;
+ AddAction(action);
+ }
+ }
+
+ return true;
+}
+
+void WrappedVulkan::vkCmdDrawMeshTasksIndirectCountEXT(VkCommandBuffer commandBuffer, VkBuffer buffer,
+ VkDeviceSize offset, VkBuffer countBuffer,
+ VkDeviceSize countBufferOffset,
+ uint32_t maxDrawCount, uint32_t stride)
+{
+ SCOPED_DBG_SINK();
+
+ SERIALISE_TIME_CALL(ObjDisp(commandBuffer)
+ ->CmdDrawMeshTasksIndirectCountEXT(
+ Unwrap(commandBuffer), Unwrap(buffer), offset, Unwrap(countBuffer),
+ countBufferOffset, maxDrawCount, stride));
+
+ if(IsCaptureMode(m_State))
+ {
+ VkResourceRecord *record = GetRecord(commandBuffer);
+
+ CACHE_THREAD_SERIALISER();
+
+ ser.SetActionChunk();
+ SCOPED_SERIALISE_CHUNK(VulkanChunk::vkCmdDrawMeshTasksIndirectCountEXT);
+ Serialise_vkCmdDrawMeshTasksIndirectCountEXT(ser, commandBuffer, buffer, offset, countBuffer,
+ countBufferOffset, maxDrawCount, stride);
+
+ record->AddChunk(scope.Get(&record->cmdInfo->alloc));
+
+ record->MarkBufferFrameReferenced(
+ GetRecord(buffer), offset,
+ stride * (maxDrawCount - 1) + sizeof(VkDrawMeshTasksIndirectCommandEXT), eFrameRef_Read);
+ record->MarkBufferFrameReferenced(GetRecord(countBuffer), countBufferOffset, 4, eFrameRef_Read);
+ }
+}
+
INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdDraw, VkCommandBuffer commandBuffer, uint32_t vertexCount,
uint32_t instanceCount, uint32_t firstVertex, uint32_t firstInstance);
@@ -4434,3 +5312,12 @@ INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdBlitImage2, VkCommandBuffer commandBu
const VkBlitImageInfo2 *pBlitImageInfo);
INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdResolveImage2, VkCommandBuffer commandBuffer,
const VkResolveImageInfo2 *pResolveImageInfo);
+INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdDrawMeshTasksEXT, VkCommandBuffer commandBuffer,
+ uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ);
+INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdDrawMeshTasksIndirectEXT, VkCommandBuffer commandBuffer,
+ VkBuffer buffer, VkDeviceSize offset, uint32_t drawCount,
+ uint32_t stride);
+INSTANTIATE_FUNCTION_SERIALISED(void, vkCmdDrawMeshTasksIndirectCountEXT,
+ VkCommandBuffer commandBuffer, VkBuffer buffer, VkDeviceSize offset,
+ VkBuffer countBuffer, VkDeviceSize countBufferOffset,
+ uint32_t maxDrawCount, uint32_t stride);
diff --git a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp
index 352e21989..f01408784 100644
--- a/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp
+++ b/renderdoc/driver/vulkan/wrappers/vk_queue_funcs.cpp
@@ -503,7 +503,20 @@ bool WrappedVulkan::PatchIndirectDraw(size_t drawIndex, uint32_t paramStride,
action.drawIndex = (uint32_t)drawIndex;
- if(type == VkIndirectPatchType::DrawIndirect || type == VkIndirectPatchType::DrawIndirectCount)
+ if(type == VkIndirectPatchType::MeshIndirectCount)
+ {
+ if(argptr && argptr + sizeof(VkDrawMeshTasksIndirectCommandEXT) <= argend)
+ {
+ VkDrawMeshTasksIndirectCommandEXT *arg = (VkDrawMeshTasksIndirectCommandEXT *)argptr;
+
+ action.dispatchDimension[0] = arg->groupCountX;
+ action.dispatchDimension[1] = arg->groupCountY;
+ action.dispatchDimension[2] = arg->groupCountZ;
+
+ valid = true;
+ }
+ }
+ else if(type == VkIndirectPatchType::DrawIndirect || type == VkIndirectPatchType::DrawIndirectCount)
{
if(argptr && argptr + sizeof(VkDrawIndirectCommand) <= argend)
{
@@ -623,14 +636,36 @@ void WrappedVulkan::InsertActionsAndRefreshIDs(BakedCmdBufferInfo &cmdBufInfo)
n.action.dispatchDimension[1] = args->y;
n.action.dispatchDimension[2] = args->z;
}
+ else if(n.indirectPatch.type == VkIndirectPatchType::MeshIndirect)
+ {
+ VkDrawMeshTasksIndirectCommandEXT unknown = {0};
+ bytebuf argbuf;
+ GetDebugManager()->GetBufferData(GetResID(n.indirectPatch.buf), 0, 0, argbuf);
+ VkDrawMeshTasksIndirectCommandEXT *args = (VkDrawMeshTasksIndirectCommandEXT *)&argbuf[0];
+
+ if(argbuf.size() < sizeof(VkDrawMeshTasksIndirectCommandEXT))
+ {
+ RDCERR("Couldn't fetch arguments buffer for vkCmdDrawMeshTasksIndirectEXT");
+ args = &unknown;
+ }
+
+ n.action.customName =
+ StringFormat::Fmt("vkCmdDrawMeshTasksIndirectEXT(<%u, %u, %u>)", args->groupCountX,
+ args->groupCountY, args->groupCountZ);
+ n.action.dispatchDimension[0] = args->groupCountX;
+ n.action.dispatchDimension[1] = args->groupCountY;
+ n.action.dispatchDimension[2] = args->groupCountZ;
+ }
else if(n.indirectPatch.type == VkIndirectPatchType::DrawIndirectByteCount ||
n.indirectPatch.type == VkIndirectPatchType::DrawIndirect ||
n.indirectPatch.type == VkIndirectPatchType::DrawIndexedIndirect ||
n.indirectPatch.type == VkIndirectPatchType::DrawIndirectCount ||
- n.indirectPatch.type == VkIndirectPatchType::DrawIndexedIndirectCount)
+ n.indirectPatch.type == VkIndirectPatchType::DrawIndexedIndirectCount ||
+ n.indirectPatch.type == VkIndirectPatchType::MeshIndirectCount)
{
bool hasCount = (n.indirectPatch.type == VkIndirectPatchType::DrawIndirectCount ||
- n.indirectPatch.type == VkIndirectPatchType::DrawIndexedIndirectCount);
+ n.indirectPatch.type == VkIndirectPatchType::DrawIndexedIndirectCount ||
+ n.indirectPatch.type == VkIndirectPatchType::MeshIndirectCount);
bytebuf argbuf;
GetDebugManager()->GetBufferData(GetResID(n.indirectPatch.buf), 0, 0, argbuf);
@@ -809,11 +844,23 @@ void WrappedVulkan::InsertActionsAndRefreshIDs(BakedCmdBufferInfo &cmdBufInfo)
name = GetStructuredFile()->chunks[n2.action.events.back().chunkIndex]->name;
- if(valid)
- n2.action.customName = StringFormat::Fmt("%s[%zu](<%u, %u>)", name.c_str(), j,
- n2.action.numIndices, n2.action.numInstances);
+ if(n.indirectPatch.type == VkIndirectPatchType::MeshIndirectCount)
+ {
+ if(valid)
+ n2.action.customName = StringFormat::Fmt(
+ "%s[%zu](<%u, %u, %u>)", name.c_str(), j, n2.action.dispatchDimension[0],
+ n2.action.dispatchDimension[1], n2.action.dispatchDimension[2]);
+ else
+ n2.action.customName = StringFormat::Fmt("%s[%zu](, ?>)", name.c_str(), j);
+ }
else
- n2.action.customName = StringFormat::Fmt("%s[%zu](, ?>)", name.c_str(), j);
+ {
+ if(valid)
+ n2.action.customName = StringFormat::Fmt("%s[%zu](<%u, %u>)", name.c_str(), j,
+ n2.action.numIndices, n2.action.numInstances);
+ else
+ n2.action.customName = StringFormat::Fmt("%s[%zu](, ?>)", name.c_str(), j);
+ }
if(ptr)
ptr += n.indirectPatch.stride;