From fc415aee762d086523e9b5a986a110b35af95023 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 30 Sep 2026 03:44:03 -0400 Subject: [PATCH] [Fix] (Magma, P8-SV): the wire arm's 3D native mip blit uses one layer with the depth in its z offsets, the depth/stencil mip decline gets its WireDeclineTally row (MipmapDepthStencilAspect), the depth shader mip refuses 1D/1D-array/3D view types before the chain grows, and glDispatchComputeIndirect is issued as vkCmdDispatchIndirect from the wire store (P8-D's barrier rule, wdsp counter, two named declines) --- .../MG_Backend/DirectVulkan/DirectVulkan.cpp | 10 +-- .../DirectVulkan/Renderer/VulkanRenderer.cpp | 9 +++ .../DirectVulkan/Renderer/VulkanRenderer.h | 3 + .../DirectVulkan/Renderer/WireDeclines.def | 12 ++- .../DirectVulkan/Renderer/WireDepthMipmap.inc | 8 ++ .../DirectVulkan/Renderer/WireDraw.inc | 76 +++++++++++++++++++ .../DirectVulkan/Renderer/WireFramebuffer.inc | 55 ++++++++++---- MobileGL/MG_Test/Util/PipeStatsTest.cpp | 4 +- MobileGL/MG_Util/Metrics/PipeStats.cpp | 3 + MobileGL/MG_Util/Metrics/PipeStats.h | 4 + 10 files changed, 161 insertions(+), 23 deletions(-) diff --git a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp index 6ccace30..61c1724c 100644 --- a/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/DirectVulkan.cpp @@ -937,12 +937,12 @@ namespace MobileGL::MG_Backend::DirectVulkan { void DispatchComputeIndirect(GLintptr indirect) { #if MOBILEGL_BUILD_DISAGGREGATED + // P8-SV: THE GPU READS THE GROUP COUNTS on the wire arm too - VulkanRenderer's + // DispatchComputeIndirect takes its transport branch into DispatchWireComputeIndirect + // (WireDraw.inc), which issues vkCmdDispatchIndirect from the verb's store. The CPU read + // that was here (ReadWireBuffer, a whole-GPU wait once a shader had written the store) is gone. if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { - Uint32 groups[3]{}; - if (indirect >= 0 && pVulkanRenderer->GetWireBufferManager().ReadWireBuffer( - MG_Pipe::MGPipeApplier().VerbDispatchIndirectBuffer, - static_cast(indirect), sizeof(groups), groups)) - pVulkanRenderer->DispatchCompute(groups[0], groups[1], groups[2]); + pVulkanRenderer->DispatchComputeIndirect(indirect); return; } #endif diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp index 765059be..c92856ff 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.cpp @@ -7789,6 +7789,15 @@ void main() { void VulkanRenderer::DispatchComputeIndirect(GLintptr indirect) { SplitOversizedRecording(); +#if MOBILEGL_BUILD_DISAGGREGATED + // P8-SV: the wire arm's own indirect dispatch, DispatchCompute's shape one call over. + if (MG_Config::Transport != MG_Config::TransportMode::Monolith) { + RewindWireDescriptorSetsIfDue(); + m_textureManager->CollectGarbage(); + DispatchWireComputeIndirect(indirect); + return; + } +#endif m_textureManager->CollectGarbage(); auto& frame = m_frameContext.GetCurrent(); // See DispatchCompute: the dispatch accessor, not the draw one. diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h index 08fd2e85..a6d09d54 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/VulkanRenderer.h @@ -693,6 +693,9 @@ namespace MobileGL::MG_Backend::DirectVulkan { // a transport (still the `buffer-legacy-arm` role violation). Bool m_wireNativeIndirectDraw = false; void DispatchWireCompute(GLuint x, GLuint y, GLuint z); + // P8-SV: glDispatchComputeIndirect on the wire arm - vkCmdDispatchIndirect from the + // dispatch-indirect store, as the monolith arm issues it (WireDraw.inc). + void DispatchWireComputeIndirect(GLintptr offset); void RewindWireDescriptorSetsIfDue(); UniquePtr m_wireDrawPass; Vector m_wireDrawViews; diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDeclines.def b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDeclines.def index 0fd20f7a..ab39a53d 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDeclines.def +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDeclines.def @@ -58,7 +58,7 @@ MGL_WIRE_DECLINE(CurrentAttributePayload) MGL_WIRE_DECLINE(CurrentAttributeUpload) MGL_WIRE_DECLINE(BeginRenderPass) -// ---- DispatchWireCompute ---------------------------------------------------------------- +// ---- DispatchWireCompute (and DispatchWireComputeIndirect, P8-SV) ------------------------- MGL_WIRE_DECLINE(ComputeProgramLinkOrSpirv) MGL_WIRE_DECLINE(ComputeTextureResourcesUnprepared) MGL_WIRE_DECLINE(ComputePipelineUnavailable) @@ -116,3 +116,13 @@ MGL_WIRE_DECLINE(IndirectBufferUnbound) MGL_WIRE_DECLINE(IndirectCommandRange) MGL_WIRE_DECLINE(IndirectCountRange) MGL_WIRE_DECLINE(IndirectIndexStore) + +// ---- P8-SV ------------------------------------------------------------------------------- +// VulkanRenderer::GenerateWireMipmap: a combined depth/stencil (or stencil-only) chain, which no +// arm generates (CONTRACT-P7 ยง3.2). It logged but was never counted, so MGWIRE-DECLINES could +// not see it. Counted once per refused glGenerateMipmap, whose chain is left as it was. +MGL_WIRE_DECLINE(MipmapDepthStencilAspect) +// VulkanRenderer::DispatchWireComputeIndirect, in statement order: the dispatch-indirect store +// is missing, or the three group-count words lie outside it or are misaligned. +MGL_WIRE_DECLINE(DispatchIndirectBufferUnbound) +MGL_WIRE_DECLINE(DispatchIndirectRange) diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.inc b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.inc index 2d14a2ae..8f5cc86f 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.inc +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDepthMipmap.inc @@ -64,6 +64,14 @@ if (source.samples != VK_SAMPLE_COUNT_1_BIT || destination.samples != VK_SAMPLE_COUNT_1_BIT) return false; if (source.is3D || destination.is3D) return false; + // P8-SV: THE PASS MAKES 2D VIEWS (makeView below), which a 1D, 1D-array or 3D image does + // not admit. A caller that fills viewType from the image gets a refusal here, and the + // wire arm's GenerateWireMipmap asks the same question before it grows the chain. + for (const VkImageViewType viewType : {source.viewType, destination.viewType}) { + if (viewType == VK_IMAGE_VIEW_TYPE_1D || viewType == VK_IMAGE_VIEW_TYPE_1D_ARRAY || + viewType == VK_IMAGE_VIEW_TYPE_3D) + return false; + } if (source.format != destination.format) return false; // DEPTH ONLY. A combined depth/stencil image can be sampled for its depth and // rendered as a depth attachment, but this pass writes gl_FragDepth and NOTHING diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDraw.inc b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDraw.inc index de724113..e4e016aa 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDraw.inc +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireDraw.inc @@ -770,4 +770,80 @@ vkCmdBindPipeline(frame.commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); vkCmdDispatch(frame.commandBuffer, x, y, z); } + + // ---- P8-SV: THE WIRE ARM'S INDIRECT DISPATCH READS ITS GROUP COUNTS ON THE GPU ---------- + // + // The monolith arm's shape (VulkanRenderer.cpp's DispatchComputeIndirect) with the wire + // store's own VkBuffer in place of the frontend buffer's resident slice. Before this the wire + // arm read the three words through ReadWireBuffer and dispatched them from the CPU, and a store + // a shader had written made that read a whole-GPU wait (WaitForWireBufferHostAccess) per call. + // There is no shape the device cannot issue, so there is no CPU path left: a record whose + // store or words are unusable is declined by name. + // + // The barrier is P8-D's draw rule (DrawWireIndirectNative): a store marked shader-written gets + // one MEMORY_WRITE -> INDIRECT_COMMAND_READ barrier before its next indirect read, whether or + // not the application issued GL_COMMAND_BARRIER_BIT; DRAW_INDIRECT is the stage that consumes + // VkDispatchIndirectCommand too. It is TAKEN before the bindings below, because binding this + // dispatch's own storage buffers marks them written, and a store that is both this dispatch's + // parameters and one of its outputs must keep that mark for the next reader. + void VulkanRenderer::DispatchWireComputeIndirect(GLintptr offset) { + RetireWireDrawPass(); + const auto& state = MG_Pipe::MGPipeApplier(); + constexpr Uint64 kCommandSize = 3 * sizeof(Uint32); // GL 4.6 10.3.10 / VkDispatchIndirectCommand + BufferSlice parameters{}; + if (MG_Pipe::MGPipeHandleIsNull(state.VerbDispatchIndirectBuffer) || + !m_bufferManager.AcquireWireSlice(BufferKind::Indirect, state.VerbDispatchIndirectBuffer, parameters)) { + MGL_WIRE_DECLINE_AT(DispatchIndirectBufferUnbound, "an indirect dispatch names no live dispatch-indirect store"); + return; + } + // GL refuses an offset that is negative or not a multiple of four (INVALID_VALUE) and a + // command past the end of the buffer (INVALID_OPERATION); a peer's record that breaks + // either is declined, not issued. + if (offset < 0 || (static_cast(offset) & 3u) != 0 || static_cast(offset) > parameters.size || + parameters.size - static_cast(offset) < kCommandSize) { + MGL_WIRE_DECLINE_AT(DispatchIndirectRange, "the dispatch group counts lie outside their store or are misaligned"); + return; + } + auto& frame = m_frameContext.GetCurrent(); + if (m_bufferManager.TakeWireIndirectReadBarrier(state.VerbDispatchIndirectBuffer)) { + if (!frame.isCommandRecording) m_frameContext.BeginCommandRecording(); + if (VkRenderPassManager::GetActiveRenderPass() != nullptr) + VkRenderPassManager::EndRenderPass(frame.commandBuffer); + VkMemoryBarrier barrier{VK_STRUCTURE_TYPE_MEMORY_BARRIER}; + barrier.srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_INDIRECT_COMMAND_READ_BIT; + vkCmdPipelineBarrier(frame.commandBuffer, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT, 0, 1, &barrier, 0, nullptr, 0, nullptr); + if (MG_Util::PipeStats::Enabled()) + MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::WireIndirectBarriers, 1); + } + const auto program = WireProgramSource(state.DispatchProgram); + if (!program.GetLinkStatus() || !program.GetSpirvStatus()) { + MGL_WIRE_DECLINE_AT(ComputeProgramLinkOrSpirv, "the compute program is not linked or carries no SPIR-V"); + return; + } + const auto& programObj = m_programFactory->GetOrCreateProgram(program, {}); + if (!m_uniformManager->PrepareWireTextureResources(program, programObj)) { + MGL_WIRE_DECLINE_AT(ComputeTextureResourcesUnprepared, "a sampled/storage image could not be made ready for the dispatch"); + return; + } + const auto pipeline = GetOrCreateComputePipeline(programObj); + if (!pipeline) { + MGL_WIRE_DECLINE_AT(ComputePipelineUnavailable, "no compute pipeline could be created for this dispatch"); + return; + } + // PrepareWireTextureResources may have flushed and begun a new recording. + auto& dispatchFrame = m_frameContext.GetCurrent(); + if (!dispatchFrame.isCommandRecording) m_frameContext.BeginCommandRecording(); + if (!m_uniformManager->BindProgramUniformBuffers(dispatchFrame.commandBuffer, program, programObj, + m_frameContext.GetCurrentFrameIndex(), VK_PIPELINE_BIND_POINT_COMPUTE)) { + MGL_WIRE_DECLINE_AT(ComputeUniformBufferBinding, "the dispatch's uniform/storage bindings could not be bound"); + return; + } + vkCmdBindPipeline(dispatchFrame.commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline); + vkCmdDispatchIndirect(dispatchFrame.commandBuffer, parameters.buffer, + parameters.offset + static_cast(offset)); + if (MG_Util::PipeStats::Enabled()) + MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::WireIndirectNativeDispatches, 1); + } #endif diff --git a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.inc b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.inc index 9a772c02..3eb8b553 100644 --- a/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.inc +++ b/MobileGL/MG_Backend/DirectVulkan/Renderer/WireFramebuffer.inc @@ -1394,12 +1394,16 @@ // release build both arms ship as. Nothing in GL promises a stencil mip either - the // pass writes gl_FragDepth and would leave the stencil aspect of the level it produced // undefined, which is worse than leaving the whole level alone and saying so. + // + // P8-SV: a NAMED decline (WireDeclines.def MipmapDepthStencilAspect), so MGWIRE-DECLINES + // counts it like every other refusal on this arm; the line keeps its words. if (resource->aspect != VK_IMAGE_ASPECT_COLOR_BIT && resource->aspect != VK_IMAGE_ASPECT_DEPTH_BIT) { - MGLOG_E_ONCE("DirectVulkan::GenerateWireMipmap: aspect 0x%x (a combined depth/stencil or " - "stencil-only texture, format %d) has no mip generation on either arm; " - "declining and leaving levels %u..%u as they are", - static_cast(resource->aspect), static_cast(resource->format), - base + 1, base + count - 1); + MGL_WIRE_DECLINE_AT(MipmapDepthStencilAspect, + "DirectVulkan::GenerateWireMipmap: aspect 0x%x (a combined depth/stencil or " + "stencil-only texture, format %d) has no mip generation on either arm; " + "declining and leaving levels %u..%u as they are", + static_cast(resource->aspect), static_cast(resource->format), + base + 1, base + count - 1); return; } // P8-F: WHICH ARM TAKES THIS CHAIN IS DECIDED BEFORE THE CHAIN IS GROWN, for the @@ -1417,18 +1421,23 @@ VK_FILTER_LINEAR : VK_FILTER_NEAREST; const Bool layersInRange = baseLayer < resource->arrayLayers && layerCount <= resource->arrayLayers - baseLayer; - // The depth arm's shape: the layer window, and what WireDepthMipmap.inc's pass refuses - // for an image this function hands it - a format that does not both sample and attach - // as depth (its other refusals, 3D / multisample / a default image, are never built here). - const auto depthPassFeatures = - VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; - const Bool depthArmTakesShape = - layersInRange && (props.optimalTilingFeatures & depthPassFeatures) == depthPassFeatures; - // The colour arm's shape (the two shader shapes are explained at the colour arm below). - const Bool colour = resource->aspect == VK_IMAGE_ASPECT_COLOR_BIT; + // Both shader arms render a 2D view of one layer of one level, so a 1D, 1D-array or 3D + // image is a shape neither takes. const Bool planar = resource->viewType != VK_IMAGE_VIEW_TYPE_1D && resource->viewType != VK_IMAGE_VIEW_TYPE_1D_ARRAY && resource->viewType != VK_IMAGE_VIEW_TYPE_3D; + // The depth arm's shape: the layer window, and what WireDepthMipmap.inc's pass refuses + // for an image this function hands it (the view type is passed to it below) - a 1D, + // 1D-array or 3D view type, and a format that does not both sample and attach as depth. + // Its other refusals, multisample and a default image, are never built here. P8-SV: the + // view type half is new; before it a 1D depth chain reached the pass, which made a 2D + // view of the 1D image. + const auto depthPassFeatures = + VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT | VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT; + const Bool depthArmTakesShape = planar && + layersInRange && (props.optimalTilingFeatures & depthPassFeatures) == depthPassFeatures; + // The colour arm's shape (the two shader shapes are explained at the colour arm below). + const Bool colour = resource->aspect == VK_IMAGE_ASPECT_COLOR_BIT; // The shader arm samples through a sampler2D and writes a vec4, which is what // BlitWireColorImage's own float check enforces; naming it here rather than letting the // blit return false is what puts the reason in the decline line. @@ -1477,6 +1486,10 @@ source.image = resource->image; source.format = format; source.aspect = VK_IMAGE_ASPECT_DEPTH_BIT; + // P8-SV: the image's own view type, so the pass can refuse a shape it cannot + // render (it makes 2D views) instead of receiving the struct's 2D default. + source.viewType = resource->viewType; + source.is3D = resource->viewType == VK_IMAGE_VIEW_TYPE_3D; source.layout = resource->layout; source.trackedLayout = &resource->layout; source.levels = resource->mipLevels; @@ -1603,14 +1616,24 @@ if (!VkTextureManager::TransitionImageLayout(frame.commandBuffer,resource->image,resource->layout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,stage,VK_PIPELINE_STAGE_TRANSFER_BIT,access, VK_ACCESS_TRANSFER_WRITE_BIT,resource->aspect,0,resource->mipLevels)) MagmaWireFatal("mipmap-transition"); + // P8-SV: A 3D IMAGE HAS ONE ARRAY LAYER, AND ITS DEPTH IS THE Z EXTENT OF THE BLIT. + // ResolveWireTextureStorage answers `layerCount` in slices for Tex3D (Depth >> level, + // VkTextureManager.cpp) - the addressing attachments, descriptors and readback use - and + // passing that on as the blit's subresource layerCount names layers the image (created + // with arrayLayers = 1) does not have (VUID-vkCmdBlitImage-srcImage-00240, + // -srcSubresource-01707, -dstSubresource-01708). The monolith arm blits layer 0, count 1, + // with the z offsets below halving the depth level by level; so does this one now. + const Bool volume = resource->viewType == VK_IMAGE_VIEW_TYPE_3D; + const Uint32 blitBaseLayer = volume ? 0u : baseLayer; + const Uint32 blitLayerCount = volume ? 1u : layerCount; for (Uint32 level=base+1;levelimage,previous, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,VK_PIPELINE_STAGE_TRANSFER_BIT,VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,VK_ACCESS_TRANSFER_READ_BIT,resource->aspect,level-1,1); VkImageBlit blit{}; - blit.srcSubresource = {resource->aspect,level-1,baseLayer,layerCount}; - blit.dstSubresource = {resource->aspect,level,baseLayer,layerCount}; + blit.srcSubresource = {resource->aspect,level-1,blitBaseLayer,blitLayerCount}; + blit.dstSubresource = {resource->aspect,level,blitBaseLayer,blitLayerCount}; blit.srcOffsets[1] = {static_cast(std::max(resource->extent.width>>(level-1),1u)), static_cast(std::max(resource->extent.height>>(level-1),1u)), static_cast(std::max(resource->depth>>(level-1),1u))}; diff --git a/MobileGL/MG_Test/Util/PipeStatsTest.cpp b/MobileGL/MG_Test/Util/PipeStatsTest.cpp index 9485b07a..f48ccfcf 100644 --- a/MobileGL/MG_Test/Util/PipeStatsTest.cpp +++ b/MobileGL/MG_Test/Util/PipeStatsTest.cpp @@ -583,8 +583,10 @@ namespace { PS::AddCalls(PS::CallClass::WireIndirectNativeDraws, 3); PS::AddCalls(PS::CallClass::WireIndirectCpuExpansions, 2); PS::AddCalls(PS::CallClass::WireHostWaitMicros, 1500); + PS::AddCalls(PS::CallClass::WireIndirectNativeDispatches, 4); const String line = PS::FormatWindowLine(); - EXPECT_NE(line.find("windr[wind=3 wixp=2 wibar=0 whw=0 whwi=0 whwus=1500]"), String::npos) << line; + EXPECT_NE(line.find("windr[wind=3 wixp=2 wibar=0 whw=0 whwi=0 whwus=1500 wdsp=4]"), String::npos) << line; + EXPECT_STREQ(PS::NameOf(PS::CallClass::WireIndirectNativeDispatches), "wire-indirect-native-dispatches"); EXPECT_STREQ(PS::NameOf(PS::CallClass::WireIndirectNativeDraws), "wire-indirect-native-draws"); EXPECT_STREQ(PS::NameOf(PS::CallClass::WireIndirectCpuExpansions), "wire-indirect-cpu-expansions"); EXPECT_STREQ(PS::NameOf(PS::CallClass::WireIndirectBarriers), "wire-indirect-barriers"); diff --git a/MobileGL/MG_Util/Metrics/PipeStats.cpp b/MobileGL/MG_Util/Metrics/PipeStats.cpp index 629facd6..9e029406 100644 --- a/MobileGL/MG_Util/Metrics/PipeStats.cpp +++ b/MobileGL/MG_Util/Metrics/PipeStats.cpp @@ -240,6 +240,7 @@ namespace MobileGL::MG_Util::PipeStats { "residual-pulls", "server-verb-boundaries", "wire-records", "wire-indirect-native-draws", "wire-indirect-cpu-expansions", "wire-indirect-barriers", "wire-host-waits", "wire-host-waits-indirect", "wire-host-wait-us", + "wire-indirect-native-dispatches", #endif }; const char* const kGateNames[kGateCount] = { @@ -701,6 +702,8 @@ namespace MobileGL::MG_Util::PipeStats { line += " whw=" + std::to_string(calls[static_cast(CallClass::WireHostWaits)]); line += " whwi=" + std::to_string(calls[static_cast(CallClass::WireHostWaitsIndirect)]); line += " whwus=" + std::to_string(calls[static_cast(CallClass::WireHostWaitMicros)]); + // P8-SV: the indirect dispatches, on the same bracket (their barriers are in `wibar`). + line += " wdsp=" + std::to_string(calls[static_cast(CallClass::WireIndirectNativeDispatches)]); #endif line += "] gates["; for (Uint32 i = 0; i < kGateCount; ++i) { diff --git a/MobileGL/MG_Util/Metrics/PipeStats.h b/MobileGL/MG_Util/Metrics/PipeStats.h index b7d0683a..c0d3682d 100644 --- a/MobileGL/MG_Util/Metrics/PipeStats.h +++ b/MobileGL/MG_Util/Metrics/PipeStats.h @@ -283,6 +283,10 @@ namespace MobileGL::MG_Util::PipeStats { WireHostWaits, WireHostWaitsIndirect, WireHostWaitMicros, + // P8-SV: `wdsp` - glDispatchComputeIndirect calls issued as vkCmdDispatchIndirect from the + // wire store (VulkanRenderer::DispatchWireComputeIndirect). Their barriers count in `wibar` + // with the draws': one barrier per shader write, whichever indirect read consumes it. + WireIndirectNativeDispatches, #endif Count };