graphics: draw single indexed indirect draws from GPU-read arguments

This commit is contained in:
nmzik
2026-10-22 13:36:24 +02:00
parent b54057942d
commit c513a4a13a
7 changed files with 185 additions and 98 deletions
@@ -52,6 +52,7 @@ vk::MemoryBarrier MakeShaderWriteDependency() {
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite |
vk::AccessFlagBits::eVertexAttributeRead |
vk::AccessFlagBits::eIndexRead | vk::AccessFlagBits::eUniformRead |
vk::AccessFlagBits::eIndirectCommandRead |
vk::AccessFlagBits::eTransferRead | vk::AccessFlagBits::eTransferWrite |
vk::AccessFlagBits::eColorAttachmentRead |
vk::AccessFlagBits::eColorAttachmentWrite;
+4 -1
View File
@@ -57,6 +57,8 @@ struct DrawIndexArgs {
uint32_t first_instance = 0;
DrawOffsetSource offset_source = DrawOffsetSource::DrawState;
uint32_t render_target_slice_offset = 0;
// Nonzero: the GPU reads this VkDrawIndexedIndirectCommand; index_count is INDEX_BUFFER_SIZE.
uint64_t indirect_args = 0;
};
struct DrawAutoArgs {
@@ -163,7 +165,8 @@ public:
std::span<PreparedBindings* const> bindings);
private:
void DrawIndex(uint64_t submit_id, CommandBuffer& buffer, const DrawIndexArgs& args);
// False, with nothing drawn, when the shaders cannot take indirect_args from the GPU.
bool DrawIndex(uint64_t submit_id, CommandBuffer& buffer, const DrawIndexArgs& args);
void DrawAuto(uint64_t submit_id, CommandBuffer& buffer, const DrawAutoArgs& args);
struct GraphicsBindings {
+26 -7
View File
@@ -635,6 +635,7 @@ struct DrawEmitInfo {
int32_t vertex_offset = 0;
uint32_t first_vertex = 0;
uint32_t first_instance = 0;
uint64_t indirect_args = 0;
};
struct DrawIndexBufferSource {
@@ -1189,6 +1190,12 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
vertex_bindings = AcquireVertexBuffers(buffer, state.vertex_info[0]);
index_binding = PrepareIndexBuffer(buffer, index_source);
}
std::pair<Buffer*, uint64_t> indirect {};
if (emit.indirect_args != 0) {
indirect = m_context.GetBufferCache().ObtainBuffer(
emit.indirect_args, sizeof(vk::DrawIndexedIndirectCommand), false);
EXIT_IF((indirect.second & 3u) != 0);
}
if (draw.IsIndexed()) {
LogDrawPhase(draw.Name(), "CreatePipeline");
}
@@ -1275,6 +1282,8 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
}
if (mesh_active) {
vk_buffer.drawMeshTasksEXT(mesh_groups, draw.instance_count, 1);
} else if (indirect.first != nullptr) {
vk_buffer.drawIndexedIndirect(indirect.first->Handle(), indirect.second, 1, 0);
} else {
EmitDrawPrimitives(ucfg, vk_buffer, draw, emit);
}
@@ -1294,7 +1303,7 @@ void RenderExecutor::ExecutePreparedDraw(uint64_t submit_id, CommandBuffer& buff
}
}
void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
bool RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
const DrawIndexArgs& args) {
KYTY_PROFILER_FUNCTION();
@@ -1309,19 +1318,19 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
reinterpret_cast<uint64_t>(args.index_addr));
Common::LockGuard lock(m_context.GetMutex());
if (args.index_count == 0 || args.instance_count == 0) {
return;
if (args.index_count == 0 || (args.instance_count == 0 && args.indirect_args == 0)) {
return true;
}
if (ConsumeMetadataColorOperation(buffer, args.render_target_slice_offset) ||
DepthStencilCopy(buffer) ||
ResolveColorTargets(buffer, args.render_target_slice_offset)) {
ResetBindings();
return;
return true;
}
if (!DrawHasValidVertexShader(sh_ctx)) {
return;
return true;
}
if (graphics_debug_dump_enabled()) {
@@ -1347,7 +1356,7 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
vk::PrimitiveTopology topology = vk::PrimitiveTopology::ePointList;
if (!GetDrawTopology(ucfg, topology)) {
return;
return true;
}
DrawIndexBufferSource index_source {};
@@ -1386,11 +1395,19 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
DrawRenderState state {};
if (!PrepareDrawRenderState(buffer, draw, args.render_target_slice_offset, state)) {
ResetBindings();
return;
return true;
}
LogDrawStateIfNeeded(buffer, draw, state, args.index_type_and_size,
args.index_addr);
// Only embedded fetches apply Vulkan's offsets; mesh and quad-list draws need CPU counts.
if (args.indirect_args != 0 &&
(!state.vertex_info[0].fetch_embedded ||
state.vertex_info[0].stage.program->stage == ShaderType::Mesh ||
ucfg.GetPrimType() == Prospero::PrimitiveType::kQuadListLegacy)) {
ResetBindings();
return false;
}
const bool indirect = args.offset_source == DrawOffsetSource::IndirectArgs;
const auto [vertex_offset, instance_offset] =
@@ -1402,10 +1419,12 @@ void RenderExecutor::DrawIndex(uint64_t submit_id, CommandBuffer& buffer,
const bool native_indirect = indirect && !state.vertex_info[0].fetch_embedded;
emit.vertex_offset = native_indirect ? 0 : vertex_offset + args.base_vertex;
emit.first_instance = native_indirect ? 0 : instance_offset;
emit.indirect_args = args.indirect_args;
ExecutePreparedDraw(submit_id, buffer, draw, state, topology, emit, index_source,
primitive_restart);
ResetBindings();
return true;
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity)