mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[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)
This commit is contained in:
@@ -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<Uint64>(indirect), sizeof(groups), groups))
|
||||
pVulkanRenderer->DispatchCompute(groups[0], groups[1], groups[2]);
|
||||
pVulkanRenderer->DispatchComputeIndirect(indirect);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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<RenderPassEntry> m_wireDrawPass;
|
||||
Vector<VkImageView> m_wireDrawViews;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Uint64>(offset) & 3u) != 0 || static_cast<Uint64>(offset) > parameters.size ||
|
||||
parameters.size - static_cast<Uint64>(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<VkDeviceSize>(offset));
|
||||
if (MG_Util::PipeStats::Enabled())
|
||||
MG_Util::PipeStats::AddCalls(MG_Util::PipeStats::CallClass::WireIndirectNativeDispatches, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -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<Uint32>(resource->aspect), static_cast<Int>(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<Uint32>(resource->aspect), static_cast<Int>(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;level<base+count;++level) {
|
||||
VkImageLayout previous = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||||
VkTextureManager::TransitionImageLayout(frame.commandBuffer,resource->image,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<Int32>(std::max(resource->extent.width>>(level-1),1u)),
|
||||
static_cast<Int32>(std::max(resource->extent.height>>(level-1),1u)),
|
||||
static_cast<Int32>(std::max(resource->depth>>(level-1),1u))};
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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<Uint32>(CallClass::WireHostWaits)]);
|
||||
line += " whwi=" + std::to_string(calls[static_cast<Uint32>(CallClass::WireHostWaitsIndirect)]);
|
||||
line += " whwus=" + std::to_string(calls[static_cast<Uint32>(CallClass::WireHostWaitMicros)]);
|
||||
// P8-SV: the indirect dispatches, on the same bracket (their barriers are in `wibar`).
|
||||
line += " wdsp=" + std::to_string(calls[static_cast<Uint32>(CallClass::WireIndirectNativeDispatches)]);
|
||||
#endif
|
||||
line += "] gates[";
|
||||
for (Uint32 i = 0; i < kGateCount; ++i) {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user