From 5ad3e67a2dc72d67acde231ad03b7a7c1957dda5 Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 25 Aug 2023 16:35:27 +0100 Subject: [PATCH] Implement baseline mesh shader on vulkan * This supports capture and replay of mesh draws, shader editing with printf support, overlays, and pixel shader debugging. * Not supported yet include the mesh viewer and shader debugging. --- qrenderdoc/Code/Resources.h | 1 + qrenderdoc/Resources/draw_vertex.png | Bin 0 -> 510 bytes qrenderdoc/Resources/draw_vertex@2x.png | Bin 0 -> 797 bytes qrenderdoc/Resources/resources.qrc | 2 + .../VulkanPipelineStateViewer.cpp | 899 ++++++++++------- .../PipelineState/VulkanPipelineStateViewer.h | 6 + .../VulkanPipelineStateViewer.ui | 658 ++++++++++++ qrenderdoc/qrenderdoc_local.vcxproj | 2 + qrenderdoc/qrenderdoc_local.vcxproj.filters | 6 + renderdoc/api/replay/pipestate.inl | 24 +- renderdoc/driver/ihv/nv/nv_vk_counters.cpp | 22 +- renderdoc/driver/vulkan/vk_common.cpp | 4 + renderdoc/driver/vulkan/vk_common.h | 8 + renderdoc/driver/vulkan/vk_core.cpp | 96 +- renderdoc/driver/vulkan/vk_core.h | 36 +- renderdoc/driver/vulkan/vk_counters.cpp | 285 ++++-- renderdoc/driver/vulkan/vk_debug.cpp | 2 +- renderdoc/driver/vulkan/vk_hookset_defs.h | 16 +- renderdoc/driver/vulkan/vk_next_chains.cpp | 6 +- renderdoc/driver/vulkan/vk_overlay.cpp | 16 +- renderdoc/driver/vulkan/vk_pixelhistory.cpp | 64 +- renderdoc/driver/vulkan/vk_postvs.cpp | 12 +- renderdoc/driver/vulkan/vk_replay.cpp | 18 +- renderdoc/driver/vulkan/vk_serialise.cpp | 84 +- .../driver/vulkan/vk_shader_feedback.cpp | 134 ++- renderdoc/driver/vulkan/vk_shaderdebug.cpp | 2 +- renderdoc/driver/vulkan/vk_stringise.cpp | 5 +- .../vulkan/wrappers/vk_device_funcs.cpp | 20 + .../driver/vulkan/wrappers/vk_draw_funcs.cpp | 935 +++++++++++++++++- .../driver/vulkan/wrappers/vk_queue_funcs.cpp | 61 +- 30 files changed, 2855 insertions(+), 569 deletions(-) create mode 100644 qrenderdoc/Resources/draw_vertex.png create mode 100644 qrenderdoc/Resources/draw_vertex@2x.png 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 0000000000000000000000000000000000000000..bf4d3a37ff1a39b1a0f5cd1091980a10dd9b7ae2 GIT binary patch literal 510 zcmVxKxl^7l#K|Ys- zp*#prhhnP2m^6Kqff4Np>T5F4mVMN*hcjr;1<45P9t#z|h@J`C*a>^6xU&?!IRD_4)0=vt9AcK=FUjPy?0UES3%Hm$0z0iyCW5F)%a! zAvKIU?kIWsyPCd3s@_6T)I!9@&y?f^M})qB#O*KgkckS5V`syG7V0FK93&i7rv`|9uC&u@;BXgS4>V1%V+Xk2~= z;%6jUjx|kKHLlyl%*rl8C`~ZJLIH@`f%p&95q~HsU15%pHz}Uo$tf&lhtCm=4733t zM+gD2j7iDdK29MCoQ|NC0~nz3E(pYOKu7e$9Kix~L=Tc9XxSwD3r$Op&h#V~et-Ak z5GY~(0Vd3ft>@thlbM0G;6JqZ{`KtGrW0a@&YG{kFvv@b3u^vXb2WQ%VC71BG)hrI z;^&8#M~+{7#PIjq=NF)a&&WVO080~~EYA%sH=fed0k9SrCp4G*f@b*v?iY;q4M)5K b00ImErg?e)PvZIa00000NkvXXu0mjfavyX5 literal 0 HcmV?d00001 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;