Does not wait on its own dispatch. It resolves the previous call's submission first, then submits this one and returns. output() is immediately valid to record a draw against; it is not valid to read from the host until a later mill() or release() has resolved the fence.
Dispatched on the graphics queue, not a compute one, which is what makes the recorded barriers mean anything: submission order spans vkQueueSubmit calls to one queue, so the trailing barrier orders this write before the draw submitted afterwards and the leading one orders the previous frame's vertex fetch before this write. Neither reaches across queues, and the destination is SharingMode::eExclusive, so a dedicated compute queue would need a semaphore and a queue family ownership transfer instead.
Waiting on the previous dispatch is not optional: the run and prefix tables are host visible and rewritten here, so the prior dispatch must have finished reading them.
The destination grows to fit and never shrinks.
634{
636
638
639 if (!source || runs.empty()) {
640 return 0;
641 }
642
643 const auto layout =
source->get_vertex_layout();
644 if (!layout.has_value()) {
645 return 0;
646 }
647
648 MillOffsets off;
649 if (!offsets_from(*layout, off)) {
651 "PrimitiveMill: layout is not millable, stride {} needs word alignment "
652 "and an addressable position attribute",
653 layout->stride_bytes);
654 return 0;
655 }
656
657 const uint32_t source_vertices = layout->vertex_count;
658 for (
const auto&
run : runs) {
659 if (
run.vertex_count == 0) {
660 continue;
661 }
662 if (
run.vertex_offset > source_vertices
663 ||
run.vertex_count > source_vertices -
run.vertex_offset) {
665 "PrimitiveMill: run [{}, {}) exceeds the source's {} vertices",
666 run.vertex_offset,
run.vertex_offset +
run.vertex_count, source_vertices);
667 return 0;
668 }
669 }
670
674 return 0;
675 }
676
678 return 0;
679 }
680
682
683 auto* run_ptr =
static_cast<uint32_t*
>(
m_run_buf->get_mapped_ptr());
684 auto* prefix_ptr =
static_cast<uint32_t*
>(
m_prefix_buf->get_mapped_ptr());
685 if (!run_ptr || !prefix_ptr) {
687 "PrimitiveMill: run or prefix buffer is not host mapped");
688 return 0;
689 }
690
691 for (size_t r = 0; r < runs.size(); ++r) {
692 run_ptr[r * RUN_WORDS + 0] = static_cast<uint32_t>(runs[r].topology);
693 run_ptr[r * RUN_WORDS + 1] = runs[r].vertex_offset;
694 run_ptr[r * RUN_WORDS + 2] = runs[r].vertex_count;
695 }
696 std::memcpy(prefix_ptr,
m_prefix.data(),
m_prefix.size() *
sizeof(uint32_t));
697
699
700 const auto pc = make_pc(
m_spec, view, off,
total, runs.size());
701
704
706
708
709 foundry.buffer_barrier(
710 cmd_id,
711 destination,
712 vk::AccessFlagBits::eVertexAttributeRead,
713 vk::AccessFlagBits::eShaderWrite,
714 vk::PipelineStageFlagBits::eVertexInput,
715 vk::PipelineStageFlagBits::eComputeShader);
716
718 press.dispatch(cmd_id, (
total + WORKGROUP - 1) / WORKGROUP, 1, 1);
719
720 foundry.buffer_barrier(
721 cmd_id,
722 destination,
723 vk::AccessFlagBits::eShaderWrite,
724 vk::AccessFlagBits::eVertexAttributeRead,
725 vk::PipelineStageFlagBits::eComputeShader,
726 vk::PipelineStageFlagBits::eVertexInput);
727
731 "PrimitiveMill: dispatch submission failed");
732 return 0;
733 }
734
736
738 "PrimitiveMill: milled {} runs into {} vertices", runs.size(),
total);
739
741}
#define MF_RT_ERROR(comp, ctx,...)
#define MF_RT_TRACE(comp, ctx,...)
std::vector< uint32_t > m_prefix
FenceID m_pending_fence
Outstanding dispatch, resolved at the start of the next mill().
bool ensure_buffers(const std::shared_ptr< Buffers::VKBuffer > &source, const Kakshya::VertexLayout &layout, uint32_t total, size_t run_count)
Grows the destination ring, run and prefix buffers to fit.
std::shared_ptr< Buffers::VKBuffer > m_prefix_buf
std::shared_ptr< Buffers::VKBuffer > m_run_buf
std::vector< DescriptorSetID > m_sets
bool ensure_kernel()
Compiles the kernel and allocates its descriptor sets, once.
std::vector< std::shared_ptr< Buffers::VKBuffer > > m_outputs
Milled buffers rotated between dispatches, all at m_output_capacity.
ComputePipelineID m_pipeline
void resolve_pending()
Wait on and reclaim the previous dispatch, if one is outstanding.
void write_descriptors(const std::shared_ptr< Buffers::VKBuffer > &source)
Points the descriptor set at the current buffer set, on any change of source or ring slot.
@ Rendering
GPU rendering operations (graphics pipeline, frame rendering)
@ Portal
High-level user-facing API layer.
constexpr FenceID INVALID_FENCE
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
MAYAFLUX_API ComputePress & get_compute_press()
std::shared_ptr< Buffers::TextBuffer > press(std::string_view text, const PressParams ¶ms)
Composite a UTF-8 string into a new TextBuffer.