13using Buffers::VKBuffer;
17 constexpr uint32_t WORKGROUP = 256;
18 constexpr uint32_t ABSENT = 0xFFFFFFFFU;
21 constexpr size_t RUN_WORDS = 3;
32 uint32_t
uv { ABSENT };
40 bool offsets_from(
const Kakshya::VertexLayout& layout, MillOffsets&
out)
42 if (layout.stride_bytes == 0 || layout.stride_bytes %
sizeof(uint32_t) != 0) {
51 out.stride_words = layout.stride_bytes /
static_cast<uint32_t
>(
sizeof(uint32_t));
84 static_assert(
sizeof(MillPC) == 14 *
sizeof(uint32_t),
85 "MillPC must be tightly packed 4-byte fields matching build_mill_spec's pc() list");
87 void add_helpers(ShaderSpec::Assemble& assemble)
89 assemble.function(
"uint",
"find_run",
"uint g, uint n",
92 " while (lo + 1u < hi) {\n"
93 " uint mid = (lo + hi) >> 1u;\n"
94 " if (prefix[mid] <= g) { lo = mid; } else { hi = mid; }\n"
98 assemble.function(
"vec3",
"read_pos",
"uint v, uint sw, uint po",
99 " uint b = v * sw + po;\n"
100 " return vec3(uintBitsToFloat(src[b]), uintBitsToFloat(src[b + 1u]), "
101 "uintBitsToFloat(src[b + 2u]));\n");
103 assemble.function(
"float",
"read_extent",
"uint v, uint sw, uint so, float fb",
104 " if (so == 0xffffffffu) { return fb; }\n"
105 " return uintBitsToFloat(src[v * sw + so]);\n");
107 assemble.function(
"void",
"copy_vertex",
"uint d, uint s, uint sw",
108 " uint db = d * sw;\n"
109 " uint sb = s * sw;\n"
110 " for (uint k = 0u; k < sw; ++k) { dst[db + k] = src[sb + k]; }\n");
112 assemble.function(
"void",
"write_pos",
"uint d, uint sw, uint po, vec3 p",
113 " uint b = d * sw + po;\n"
114 " dst[b] = floatBitsToUint(p.x);\n"
115 " dst[b + 1u] = floatBitsToUint(p.y);\n"
116 " dst[b + 2u] = floatBitsToUint(p.z);\n");
118 assemble.function(
"void",
"write_uv",
"uint d, uint sw, uint uo, vec2 t",
119 " if (uo == 0xffffffffu) { return; }\n"
120 " uint b = d * sw + uo;\n"
121 " dst[b] = floatBitsToUint(t.x);\n"
122 " dst[b + 1u] = floatBitsToUint(t.y);\n");
124 assemble.function(
"vec3",
"view_normal",
"vec3 p, vec3 eye",
125 " vec3 n = eye - p;\n"
126 " float l = length(n);\n"
127 " return l < 1e-8 ? vec3(0.0, 0.0, 1.0) : n / l;\n");
135 assemble.function(
"vec3",
"dir_in",
"uint s, uint lo, uint sw, uint po",
136 " vec3 p = read_pos(s, sw, po);\n"
138 " for (uint k = 0u; k < 4u; ++k) {\n"
139 " if (c <= lo) { break; }\n"
141 " vec3 q = read_pos(c, sw, po);\n"
142 " if (length(p - q) > 1e-6) { return normalize(p - q); }\n"
144 " return vec3(0.0);\n");
147 assemble.function(
"vec3",
"dir_out",
"uint s, uint hi, uint sw, uint po",
148 " vec3 p = read_pos(s, sw, po);\n"
150 " for (uint k = 0u; k < 4u; ++k) {\n"
152 " if (c >= hi) { break; }\n"
153 " vec3 q = read_pos(c, sw, po);\n"
154 " if (length(q - p) > 1e-6) { return normalize(q - p); }\n"
156 " return vec3(0.0);\n");
170 assemble.function(
"float",
"extent_at",
171 "uint s, uint lo, uint hi, uint sw, uint po, uint so, float fb",
172 " vec3 p = read_pos(s, sw, po);\n"
173 " float sum = read_extent(s, sw, so, fb);\n"
174 " float cnt = 1.0;\n"
176 " for (uint k = 0u; k < 4u; ++k) {\n"
177 " if (c <= lo) { break; }\n"
179 " sum += read_extent(c, sw, so, fb);\n"
181 " if (length(read_pos(c, sw, po) - p) > 1e-6) { break; }\n"
184 " for (uint k = 0u; k < 4u; ++k) {\n"
186 " if (c >= hi) { break; }\n"
187 " sum += read_extent(c, sw, so, fb);\n"
189 " if (length(read_pos(c, sw, po) - p) > 1e-6) { break; }\n"
191 " return sum / cnt;\n");
203 assemble.function(
"vec3",
"offset_from",
204 "vec3 p, vec3 din, vec3 dout, vec3 seg, vec3 eye, float half_w",
205 " vec3 vn = view_normal(p, eye);\n"
206 " vec3 ns = cross(seg, vn);\n"
207 " float nsl = length(ns);\n"
208 " if (nsl < 1e-6) { return vec3(0.0); }\n"
210 " vec3 t = din + dout;\n"
211 " float tl = length(t);\n"
212 " if (tl < 1e-6) { return ns * half_w; }\n"
213 " vec3 nm = cross(t / tl, vn);\n"
214 " float nml = length(nm);\n"
215 " if (nml < 1e-6) { return ns * half_w; }\n"
217 " float proj = dot(nm, ns);\n"
218 " if (abs(proj) < 0.25) { return ns * half_w; }\n"
219 " return nm * (half_w / proj);\n");
230 assemble.function(
"vec3",
"pair_dir_in",
"uint s, uint lo, uint sw, uint po",
231 " if (s < lo + 2u) { return vec3(0.0); }\n"
232 " vec3 p = read_pos(s, sw, po);\n"
233 " vec3 b = read_pos(s - 1u, sw, po);\n"
234 " if (length(b - p) > 1e-6) { return vec3(0.0); }\n"
235 " vec3 a = read_pos(s - 2u, sw, po);\n"
237 " float l = length(d);\n"
238 " return l < 1e-6 ? vec3(0.0) : d / l;\n");
241 assemble.function(
"vec3",
"pair_dir_out",
"uint s, uint hi, uint sw, uint po",
242 " if (s + 2u >= hi) { return vec3(0.0); }\n"
243 " vec3 p = read_pos(s, sw, po);\n"
244 " vec3 a = read_pos(s + 1u, sw, po);\n"
245 " if (length(a - p) > 1e-6) { return vec3(0.0); }\n"
246 " vec3 b = read_pos(s + 2u, sw, po);\n"
248 " float l = length(d);\n"
249 " return l < 1e-6 ? vec3(0.0) : d / l;\n");
252 assemble.function(
"float",
"pair_extent",
253 "uint s, uint o, uint lo, uint hi, uint sw, uint po, uint so, float fb",
254 " float e = read_extent(s, sw, so, fb);\n"
255 " if (o < lo || o >= hi) { return e; }\n"
256 " if (length(read_pos(o, sw, po) - read_pos(s, sw, po)) > 1e-6) { return e; }\n"
257 " return 0.5 * (e + read_extent(o, sw, so, fb));\n");
274 ShaderSpec build_mill_spec()
276 ShaderSpec::Assemble assemble;
296 .workgroup(WORKGROUP);
298 add_helpers(assemble);
301 body +=
" if (i >= total) { return; }\n";
302 body +=
" uint sw = stride_words;\n";
303 body +=
" uint po = position_offset;\n";
304 body +=
" vec3 eye = vec3(eye_x, eye_y, eye_z);\n";
305 body +=
" uint r = find_run(i, run_count);\n";
306 body +=
" uint rb = r * 3u;\n";
307 body +=
" uint topo = runs[rb];\n";
308 body +=
" uint voff = runs[rb + 1u];\n";
309 body +=
" uint local = i - prefix[r];\n";
311 body +=
" if (topo == 3u) {\n";
312 body +=
" copy_vertex(i, voff + local, sw);\n";
313 body +=
" return;\n";
316 body +=
" if (topo == 4u || topo == 5u) {\n";
317 body +=
" uint tri = local / 3u;\n";
318 body +=
" uint c = local - tri * 3u;\n";
319 body +=
" uint a; uint b1; uint b2;\n";
320 body +=
" if (topo == 5u) { a = 0u; b1 = tri + 1u; b2 = tri + 2u; }\n";
322 body +=
" a = tri; b1 = tri + 1u; b2 = tri + 2u;\n";
323 body +=
" if ((tri & 1u) == 1u) { uint t = a; a = b1; b1 = t; }\n";
325 body +=
" uint pick = c == 0u ? a : (c == 1u ? b1 : b2);\n";
326 body +=
" copy_vertex(i, voff + pick, sw);\n";
327 body +=
" return;\n";
330 body +=
" uint corner = local % 6u;\n";
331 body +=
" uint quad = local / 6u;\n";
332 body +=
" vec2 uv;\n";
334 body +=
" if (topo == 0u) {\n";
335 body +=
" uint s = voff + quad;\n";
336 body +=
" vec3 p = read_pos(s, sw, po);\n";
337 body +=
" float h = read_extent(s, sw, scalar_offset, fallback_extent)\n";
338 body +=
" * point_scale * 0.5;\n";
339 body +=
" vec3 rx; vec3 ry;\n";
340 body +=
" if (mode == 1u) {\n";
341 body +=
" rx = vec3(h, 0.0, 0.0);\n";
342 body +=
" ry = vec3(0.0, h, 0.0);\n";
343 body +=
" } else {\n";
344 body +=
" vec3 n = view_normal(p, eye);\n";
345 body +=
" vec3 up = abs(n.y) < 0.99 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);\n";
346 body +=
" rx = normalize(cross(up, n)) * h;\n";
347 body +=
" ry = normalize(cross(n, rx)) * h;\n";
349 body +=
" vec3 o;\n";
350 body +=
" if (corner == 0u) { o = -rx - ry; uv = vec2(10.0, 10.0); }\n";
351 body +=
" else if (corner == 1u) { o = rx - ry; uv = vec2(11.0, 10.0); }\n";
352 body +=
" else if (corner == 2u) { o = rx + ry; uv = vec2(11.0, 11.0); }\n";
353 body +=
" else if (corner == 3u) { o = -rx - ry; uv = vec2(10.0, 10.0); }\n";
354 body +=
" else if (corner == 4u) { o = rx + ry; uv = vec2(11.0, 11.0); }\n";
355 body +=
" else { o = -rx + ry; uv = vec2(10.0, 11.0); }\n";
356 body +=
" copy_vertex(i, s, sw);\n";
357 body +=
" write_pos(i, sw, po, p + o);\n";
358 body +=
" if (synth_uv == 1u) { write_uv(i, sw, uv_offset, uv); }\n";
359 body +=
" return;\n";
362 body +=
" uint s0 = topo == 1u ? voff + quad * 2u : voff + quad;\n";
363 body +=
" uint s1 = s0 + 1u;\n";
364 body +=
" vec3 p0 = read_pos(s0, sw, po);\n";
365 body +=
" vec3 p1 = read_pos(s1, sw, po);\n";
366 body +=
" vec3 d = p1 - p0;\n";
367 body +=
" if (length(d) < 1e-6) {\n";
368 body +=
" write_pos(i, sw, po, p0);\n";
369 body +=
" return;\n";
371 body +=
" float dl = length(d);\n";
372 body +=
" vec3 dn = d / dl;\n";
373 body +=
" float h0 = read_extent(s0, sw, scalar_offset, fallback_extent)\n";
374 body +=
" * width_scale * 0.5;\n";
375 body +=
" float h1 = read_extent(s1, sw, scalar_offset, fallback_extent)\n";
376 body +=
" * width_scale * 0.5;\n";
377 body +=
" vec3 e0; vec3 e1;\n";
378 body +=
" if (mode == 1u) {\n";
379 body +=
" vec3 sv = vec3(-dn.y, dn.x, 0.0);\n";
380 body +=
" e0 = sv * h0;\n";
381 body +=
" e1 = sv * h1;\n";
382 body +=
" } else {\n";
383 body +=
" uint lo = voff;\n";
384 body +=
" uint hi = voff + runs[rb + 2u];\n";
385 body +=
" vec3 din; vec3 dout;\n";
386 body +=
" if (topo == 2u) {\n";
387 body +=
" din = dir_in(s0, lo, sw, po);\n";
388 body +=
" dout = dir_out(s1, hi, sw, po);\n";
389 body +=
" h0 = extent_at(s0, lo, hi, sw, po, scalar_offset, fallback_extent)\n";
390 body +=
" * width_scale * 0.5;\n";
391 body +=
" h1 = extent_at(s1, lo, hi, sw, po, scalar_offset, fallback_extent)\n";
392 body +=
" * width_scale * 0.5;\n";
393 body +=
" } else {\n";
394 body +=
" din = pair_dir_in(s0, lo, sw, po);\n";
395 body +=
" dout = pair_dir_out(s1, hi, sw, po);\n";
396 body +=
" h0 = pair_extent(s0, s0 - 1u, lo, hi, sw, po, scalar_offset, fallback_extent)\n";
397 body +=
" * width_scale * 0.5;\n";
398 body +=
" h1 = pair_extent(s1, s1 + 1u, lo, hi, sw, po, scalar_offset, fallback_extent)\n";
399 body +=
" * width_scale * 0.5;\n";
401 body +=
" e0 = offset_from(p0, din, dn, dn, eye, h0);\n";
402 body +=
" e1 = offset_from(p1, dn, dout, dn, eye, h1);\n";
404 body +=
" vec3 pos; uint pick;\n";
405 body +=
" if (corner == 0u) { pos = p0 - e0; pick = s0; uv = vec2(0.0, 1.0); }\n";
406 body +=
" else if (corner == 1u) { pos = p0 + e0; pick = s0; uv = vec2(0.0, 0.0); }\n";
407 body +=
" else if (corner == 2u) { pos = p1 + e1; pick = s1; uv = vec2(1.0, 0.0); }\n";
408 body +=
" else if (corner == 3u) { pos = p0 - e0; pick = s0; uv = vec2(0.0, 1.0); }\n";
409 body +=
" else if (corner == 4u) { pos = p1 + e1; pick = s1; uv = vec2(1.0, 0.0); }\n";
410 body +=
" else { pos = p1 - e1; pick = s1; uv = vec2(1.0, 1.0); }\n";
411 body +=
" copy_vertex(i, pick, sw);\n";
412 body +=
" write_pos(i, sw, po, pos);\n";
413 body +=
" if (synth_uv == 1u) { write_uv(i, sw, uv_offset, uv); }\n";
415 assemble.kernel(KernelSource { .body = std::move(body) });
417 return assemble.build();
421 const MillSpec& spec,
422 const MillView& view,
423 const MillOffsets& off,
429 .run_count =
static_cast<uint32_t
>(
run_count),
431 .position_offset = off.position,
432 .scalar_offset = spec.use_vertex_extent ? off.scalar : ABSENT,
434 .
mode = spec.ribbon == MillSpec::Ribbon::WorldPlane ? 1U : 0
U,
435 .
synth_uv = spec.synthesize_uv ? 1U : 0
U,
446 void build_prefix(std::span<const DrawRun> runs, std::vector<uint32_t>& prefix)
448 prefix.assign(runs.size() + 1, 0U);
449 for (
size_t r = 0; r < runs.size(); ++r) {
450 prefix[r + 1] = prefix[r]
459 , m_output_ring(
std::
max(output_ring, 1U))
479 for (
const auto&
run : runs) {
491 const auto spec = build_mill_spec();
496 "PrimitiveMill: kernel expects {} push constant bytes but MillPC is {}",
505 "PrimitiveMill: kernel failed to compile");
513 "PrimitiveMill: pipeline creation failed");
520 "PrimitiveMill: descriptor allocation failed");
528 const std::shared_ptr<VKBuffer>& source,
537 "PrimitiveMill: BufferService unavailable");
542 const auto grown =
static_cast<uint32_t
>(
static_cast<float>(
total) * 1.5F);
544 auto milled_layout = layout;
553 slot = std::make_shared<VKBuffer>(
555 VKBuffer::Usage::VERTEX,
556 source->get_modality());
557 slot->set_vertex_layout(milled_layout);
558 svc->initialize_buffer(slot);
567 auto milled_layout = *slot->get_vertex_layout();
568 milled_layout.vertex_count =
total;
569 slot->set_vertex_layout(milled_layout);
573 const auto run_bytes = std::max<size_t>(
run_count * RUN_WORDS *
sizeof(uint32_t),
sizeof(uint32_t));
581 const auto prefix_bytes = std::max<size_t>(
m_prefix.size() *
sizeof(uint32_t),
sizeof(uint32_t));
600 const auto set =
m_sets.front();
602 const auto bind = [&](uint32_t binding,
const std::shared_ptr<VKBuffer>& buf) {
603 foundry.update_descriptor_buffer(
604 set, binding, vk::DescriptorType::eStorageBuffer,
605 buf->get_buffer(), 0, buf->get_size_bytes());
631 const std::shared_ptr<VKBuffer>& source,
632 std::span<const DrawRun> runs,
639 if (!source || runs.empty()) {
643 const auto layout = source->get_vertex_layout();
644 if (!layout.has_value()) {
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);
657 const uint32_t source_vertices = layout->vertex_count;
658 for (
const auto&
run : runs) {
659 if (
run.vertex_count == 0) {
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);
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");
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;
696 std::memcpy(prefix_ptr,
m_prefix.data(),
m_prefix.size() *
sizeof(uint32_t));
700 const auto pc = make_pc(
m_spec, view, off,
total, runs.size());
709 foundry.buffer_barrier(
712 vk::AccessFlagBits::eVertexAttributeRead,
713 vk::AccessFlagBits::eShaderWrite,
714 vk::PipelineStageFlagBits::eVertexInput,
715 vk::PipelineStageFlagBits::eComputeShader);
718 press.dispatch(cmd_id, (
total + WORKGROUP - 1) / WORKGROUP, 1, 1);
720 foundry.buffer_barrier(
723 vk::AccessFlagBits::eShaderWrite,
724 vk::AccessFlagBits::eVertexAttributeRead,
725 vk::PipelineStageFlagBits::eComputeShader,
726 vk::PipelineStageFlagBits::eVertexInput);
731 "PrimitiveMill: dispatch submission failed");
738 "PrimitiveMill: milled {} runs into {} vertices", runs.size(),
total);
786 [[nodiscard]] glm::vec3 read_pos(uint32_t v, uint32_t po)
const
790 std::memcpy(&p.x, w,
sizeof(
float));
791 std::memcpy(&p.y, w + 1,
sizeof(
float));
792 std::memcpy(&p.z, w + 2,
sizeof(
float));
796 [[nodiscard]]
float read_extent(uint32_t v, uint32_t so,
float fb)
const
802 std::memcpy(&f, src +
static_cast<size_t>(v) * stride_words + so,
sizeof(
float));
806 void copy_vertex(uint32_t d, uint32_t s)
const
809 dst +
static_cast<size_t>(d) * stride_words,
810 src +
static_cast<size_t>(s) * stride_words,
811 static_cast<size_t>(stride_words) *
sizeof(uint32_t));
814 void write_pos(uint32_t d, uint32_t po,
const glm::vec3& p)
const
816 uint32_t* w =
dst +
static_cast<size_t>(d) * stride_words + po;
817 std::memcpy(w, &p.x,
sizeof(
float));
818 std::memcpy(w + 1, &p.y,
sizeof(
float));
819 std::memcpy(w + 2, &p.z,
sizeof(
float));
822 void write_uv(uint32_t d, uint32_t uo,
const glm::vec2& t)
const
827 uint32_t* w =
dst +
static_cast<size_t>(d) * stride_words + uo;
828 std::memcpy(w, &t.x,
sizeof(
float));
829 std::memcpy(w + 1, &t.y,
sizeof(
float));
833 glm::vec3 host_view_normal(
const glm::vec3& p,
const glm::vec3& eye)
835 const glm::vec3 n = eye - p;
836 const float l = glm::length(n);
837 return l < 1e-8F ? glm::vec3(0.0F, 0.0F, 1.0F) : n / l;
840 glm::vec3 host_side_at(
842 const glm::vec3& seg,
843 const glm::vec3& eye,
848 const float dl = glm::length(dir);
850 return glm::vec3(0.0F);
854 glm::vec3 s = world_plane
855 ? glm::vec3(-dir.y, dir.x, 0.0F)
856 : glm::cross(dir, host_view_normal(p, eye));
858 const float sl = glm::length(s);
859 return (sl < 1e-6F ? glm::vec3(0.0F, 1.0F, 0.0F) : s / sl) * half_w;
865 std::span<const uint8_t>
src,
867 std::span<const DrawRun> runs,
870 std::vector<uint8_t>&
dst)
872 auto result = layout;
877 if (!offsets_from(layout, off)) {
881 std::vector<uint32_t> prefix;
882 build_prefix(runs, prefix);
883 const uint32_t
total = prefix.empty() ? 0U : prefix.back();
889 result.vertex_count =
total;
891 const HostRecords rec {
892 .src =
reinterpret_cast<const uint32_t*
>(
src.data()),
893 .
dst =
reinterpret_cast<uint32_t*
>(
dst.data()),
900 for (
size_t r = 0; r < runs.size(); ++r) {
901 const auto&
run = runs[r];
902 const uint32_t emitted = prefix[r + 1] - prefix[r];
903 const uint32_t base = prefix[r];
904 const uint32_t voff =
run.vertex_offset;
906 for (uint32_t local = 0; local < emitted; ++local) {
907 const uint32_t
out = base + local;
910 rec.copy_vertex(
out, voff + local);
916 const uint32_t tri = local / 3U;
917 const uint32_t c = local - tri * 3U;
919 uint32_t b1 = tri + 1U;
920 uint32_t b2 = tri + 2U;
923 if ((tri & 1U) == 1U) {
927 const uint32_t pick = c == 0U ?
a : (c == 1U ? b1 : b2);
928 rec.copy_vertex(
out, voff + pick);
932 const uint32_t corner = local % 6U;
933 const uint32_t quad = local / 6U;
936 const uint32_t s = voff + quad;
937 const glm::vec3 p = rec.read_pos(s, off.position);
944 rx = glm::vec3(
h, 0.0F, 0.0F);
945 ry = glm::vec3(0.0F,
h, 0.0F);
947 const glm::vec3 n = host_view_normal(p, view.
eye);
948 const glm::vec3 up = std::abs(n.y) < 0.99F
949 ? glm::vec3(0.0F, 1.0F, 0.0F)
950 : glm::vec3(1.0F, 0.0F, 0.0F);
951 rx = glm::normalize(glm::cross(up, n)) *
h;
952 ry = glm::normalize(glm::cross(n, rx)) *
h;
977 rec.copy_vertex(
out, s);
978 rec.write_pos(
out, off.position, p + o);
980 rec.write_uv(
out, off.uv,
uv);
988 const uint32_t s1 = s0 + 1U;
990 const glm::vec3 p0 = rec.read_pos(s0, off.position);
991 const glm::vec3 p1 = rec.read_pos(s1, off.position);
992 const glm::vec3 d = p1 - p0;
994 if (glm::length(d) < 1e-6F) {
995 rec.write_pos(
out, off.position, p0);
1004 const glm::vec3 e0 = host_side_at(
1005 p0, d, view.
eye, h0, world_plane);
1006 const glm::vec3 e1 = host_side_at(
1007 p1, d, view.
eye, h1, world_plane);
1015 uv = { 0.0F, 0.0F };
1021 uv = { 1.0F, 0.0F };
1026 uv = { 1.0F, 1.0F };
1030 uv = { 0.0F, 1.0F };
1034 rec.copy_vertex(
out, pick);
1035 rec.write_pos(
out, off.position, pos);
1037 rec.write_uv(
out, off.uv,
uv);
#define MF_ERROR(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_RT_TRACE(comp, ctx,...)
std::vector< float > * out
PrimitiveMill(MillSpec spec={}, uint32_t output_ring=2)
size_t m_bound_slot
Ring slot the descriptor set currently points at, for invalidation.
std::shared_ptr< Buffers::VKBuffer > output() const
The milled triangles: the ring slot the last mill() wrote.
std::vector< uint32_t > m_prefix
static uint32_t milled_vertex_count(std::span< const DrawRun > runs)
Vertices runs would mill to.
FenceID m_pending_fence
Outstanding dispatch, resolved at the start of the next mill().
size_t m_push_constant_size
std::weak_ptr< Buffers::VKBuffer > m_bound_source
Source the descriptor set currently points at, for invalidation.
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
void release()
Destroy the kernel, pipeline, descriptor sets and buffers.
std::shared_ptr< Buffers::VKBuffer > m_run_buf
bool m_descriptors_written
const MillSpec & spec() const
std::vector< DescriptorSetID > m_sets
uint32_t m_output_capacity
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
uint32_t mill(const std::shared_ptr< Buffers::VKBuffer > &source, std::span< const DrawRun > runs, const MillView &view)
Mill runs out of source into the owned buffer.
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.
vk::Queue get_graphics_queue() const
Get Vulkan graphics queue.
Interface * get_service()
Query for a backend service.
static BackendRegistry & instance()
Get the global registry instance.
@ Rendering
GPU rendering operations (graphics pipeline, frame rendering)
@ Portal
High-level user-facing API layer.
@ UNKNOWN
Unknown or undefined modality.
@ SCALAR_F32
Single-channel float data.
constexpr ShaderID INVALID_SHADER
constexpr FenceID INVALID_FENCE
Kakshya::VertexLayout mill_on_host(std::span< const uint8_t > src, const Kakshya::VertexLayout &layout, std::span< const DrawRun > runs, const MillSpec &spec, const MillView &view, std::vector< uint8_t > &dst)
Host equivalent of PrimitiveMill::mill, over raw bytes.
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
constexpr ComputePipelineID INVALID_COMPUTE_PIPELINE
constexpr uint32_t triangle_vertex_count(PrimitiveTopology topology, uint32_t n) noexcept
Vertices a span of n at topology yields once reduced to a TRIANGLE_LIST, with points becoming quads a...
MAYAFLUX_API ComputePress & get_compute_press()
uint32_t stride_bytes
Total bytes per vertex (stride in Vulkan terms) e.g., 3 floats (position) + 3 floats (normal) = 24 by...
uint32_t vertex_count
Total number of vertices in this buffer.
Complete description of vertex data layout in a buffer.
@ WorldPlane
Held in the XY plane, viewpoint ignored.
bool synthesize_uv
Write 0..1 across each ribbon and quad rather than copying source.
float fallback_extent
Extent used when the layout carries no scalar attribute.
float width_scale
World units of total ribbon width per unit of a vertex's scalar.
bool use_vertex_extent
Take ribbon width and point size from each vertex's own scalar (LineVertex::thickness,...
float point_scale
World units per unit of a vertex's own size, on point spans.
How a PrimitiveMill shapes spans.
Backend buffer management service interface.