MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VisionComponentsDispatch.cpp
Go to the documentation of this file.
1#include "DispatchLayouts.hpp"
2
3namespace MayaFlux::Yantra {
4
5using namespace VisionInternal;
6
7using namespace Portal::Graphics;
8using namespace Kinesis::Vision;
9
11 VisionGpuContexts& contexts,
13{
14 auto& cc_pipeline = contexts.component_contours;
15 auto w = contexts.pass.w;
16 auto h = contexts.pass.h;
18
19 const auto seed_input = contexts.pass.current;
20
21 const uint32_t block_width = (w + 1U) / 2U;
22 const uint32_t block_height = (h + 1U) / 2U;
23 cc_pipeline.ensure_shared_buffer(0, 2, static_cast<size_t>(block_width) * block_height, GpuBufferBinding::ElementType::UINT32);
24 cc_pipeline.ensure_shared_buffer(0, 3, 1, GpuBufferBinding::ElementType::UINT32);
25 cc_pipeline.ensure_shared_buffer(0, 4, static_cast<size_t>(block_width) * block_height, GpuBufferBinding::ElementType::UINT32);
26 cc_pipeline.ensure_shared_buffer(0, 5, 1, GpuBufferBinding::ElementType::UINT32);
27 cc_pipeline.ensure_shared_buffer(0, 6, static_cast<size_t>(w) * h, GpuBufferBinding::ElementType::UINT32,
29 cc_pipeline.ensure_shared_buffer(0, 7, static_cast<size_t>(k_max_components) * 2, GpuBufferBinding::ElementType::UINT32);
30 cc_pipeline.ensure_shared_buffer(0, 8, static_cast<size_t>(k_max_components) * 2, GpuBufferBinding::ElementType::UINT32);
31 cc_pipeline.ensure_shared_buffer(0, 9, k_max_components, GpuBufferBinding::ElementType::UINT32);
32 cc_pipeline.ensure_shared_buffer(0, 10, 6, GpuBufferBinding::ElementType::UINT32,
34
35 const CCBlockInitPC init_pc { .width = w, .height = h, .block_width = block_width, .block_height = block_height };
36 const CCMergePC merge_pc { .width = w, .height = h, .block_width = block_width, .block_height = block_height };
37 const auto* next = contexts.pass.ahead();
38 const bool contours_follow = next && next->op == VisionOp::FindContours;
39 const uint32_t export_labels = (p.export_labels || contours_follow) ? 1U : 0U;
40
41 const CCFinalLabelPC final_pc {
42 .width = w,
43 .height = h,
44 .block_width = block_width,
45 .block_height = block_height,
46 .max_components = k_max_components,
47 .export_labels = export_labels
48 };
49
50 const std::array<uint32_t, 3> block_groups {
51 (block_width + k_wg2d[0] - 1U) / k_wg2d[0],
52 (block_height + k_wg2d[1] - 1U) / k_wg2d[1],
53 1U
54 };
55
56 const uint32_t reset_elements = std::max(block_width * block_height, k_max_components);
57 const auto buffer_hazards = [](GpuDispatchCore& ctx, std::initializer_list<size_t> bindings) {
58 std::vector<HazardResource> hazards;
59 hazards.reserve(bindings.size());
60 for (const size_t binding : bindings) {
61 hazards.push_back(ctx.shared_buffer_hazard(
62 { .set = 0, .binding = static_cast<uint32_t>(binding), .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 }));
63 }
64 return hazards;
65 };
66
67 std::shared_ptr<Core::VKImage> cc_image;
68 std::vector<DependencyStage> cc_stages;
69 cc_stages.reserve(5);
70
71 cc_stages.push_back({
72 .config = { .shader_path = "cc_reset.comp.spv", .workgroup_size = { 256, 1, 1 }, .push_constant_size = sizeof(CCResetPC) },
73 .stage_fn = [=](GpuDispatchCore& ctx) { ctx.set_push_constants(CCResetPC { .lut_size = block_width * block_height, .max_components = k_max_components }); },
74 .hazard_fn = [=](GpuDispatchCore& ctx) { return buffer_hazards(ctx, { 4, 5, 7, 8, 9 }); },
75 .explicit_groups = std::array<uint32_t, 3> { (reset_elements + 255U) / 256U, 1U, 1U },
76 });
77
78 cc_stages.push_back({
79 .config = { .shader_path = "cc_block_init.comp.spv", .workgroup_size = k_wg2d, .push_constant_size = sizeof(CCBlockInitPC) },
80 .stage_fn = [seed_input, init_pc](GpuDispatchCore& ctx) {
82 ctx.set_push_constants(init_pc); },
83 .hazard_fn = [=](GpuDispatchCore& ctx) { return buffer_hazards(ctx, { 2 }); },
84 .explicit_groups = block_groups,
85 });
86
87 cc_stages.push_back({
88 .config = { .shader_path = "cc_merge.comp.spv", .workgroup_size = k_wg2d, .push_constant_size = sizeof(CCMergePC) },
89 .stage_fn = [seed_input, merge_pc](GpuDispatchCore& ctx) {
91 ctx.set_push_constants(merge_pc); },
92 .hazard_fn = [=](GpuDispatchCore& ctx) { return buffer_hazards(ctx, { 2 }); },
93 .explicit_groups = block_groups,
94 });
95
96 cc_stages.push_back({
97 .config = { .shader_path = "cc_compress.comp.spv", .workgroup_size = k_wg2d, .push_constant_size = sizeof(CCCompressPC) },
98 .stage_fn = [=](GpuDispatchCore& ctx) { ctx.set_push_constants(CCCompressPC { .block_width = block_width, .block_height = block_height }); },
99 .hazard_fn = [=](GpuDispatchCore& ctx) { return buffer_hazards(ctx, { 2, 4 }); },
100 .explicit_groups = block_groups,
101 });
102
103 cc_stages.push_back({
104 .config = { .shader_path = "cc_final_label.comp.spv", .workgroup_size = k_wg2d, .push_constant_size = sizeof(CCFinalLabelPC) },
105 .stage_fn = [seed_input, final_pc, w, h, &cc_pipeline, &cc_image](GpuDispatchCore& ctx) {
107 ctx.set_push_constants(final_pc);
108 cc_pipeline.prepare_output_image(w, h);
109 cc_image = cc_pipeline.get_output_image(0);
110 },
111 .explicit_groups = std::array<uint32_t, 3> { (w + k_wg2d[0] - 1U) / k_wg2d[0], (h + k_wg2d[1] - 1U) / k_wg2d[1], 1U },
112 });
113
114 ExecutionContext cc_ctx;
116 DependencyParams cc_params;
117 cc_params.stages = cc_stages;
118 cc_params.async = true;
119 cc_ctx.parameters = cc_params;
120 const auto cc_dispatch_result = cc_pipeline.execute(Datum<> {}, cc_ctx);
121 const auto fence = cc_dispatch_result.get_metadata<FenceID>("gpu_fence").value_or(INVALID_FENCE);
122 foundry.wait_for_fence(fence);
123 foundry.release_fence(fence);
124 cc_pipeline.clear_output_dimensions();
125
126 contexts.pass.result.images.component_colors = p.with_colors ? cc_image : nullptr;
127
128 if (p.export_label_buffer) {
129 contexts.pass.result.buffers.labels = std::make_shared<Portal::Graphics::GpuBufferHandle>(
130 cc_pipeline.shared_buffer_handle(0, 6));
131 }
132
133 if (contours_follow && !p.export_boxes)
134 return;
135
136 uint32_t compact_count = 0;
137 cc_pipeline.download_shared(0, 5, &compact_count, sizeof(uint32_t));
138 compact_count = std::min(compact_count, k_max_components);
139
141 cc_result.count = compact_count;
142 cc_result.boxes.reserve(compact_count);
143
144 if (compact_count > 0) {
145 std::vector<glm::uvec2> bmin(compact_count);
146 std::vector<glm::uvec2> bmax(compact_count);
147 std::vector<uint32_t> bcount(compact_count);
148 cc_pipeline.download_shared(0, 7, bmin.data(), bmin.size() * sizeof(glm::uvec2));
149 cc_pipeline.download_shared(0, 8, bmax.data(), bmax.size() * sizeof(glm::uvec2));
150 cc_pipeline.download_shared(0, 9, bcount.data(), bcount.size() * sizeof(uint32_t));
151
152 const float inv_w = 1.0F / static_cast<float>(w);
153 const float inv_h = 1.0F / static_cast<float>(h);
154
155 for (uint32_t i = 0; i < compact_count; ++i) {
156 if (bcount[i] == 0)
157 continue;
158 const float x = static_cast<float>(bmin[i].x) * inv_w;
159 const float y = static_cast<float>(bmin[i].y) * inv_h;
160 const float bw = static_cast<float>(bmax[i].x - bmin[i].x + 1) * inv_w;
161 const float bh = static_cast<float>(bmax[i].y - bmin[i].y + 1) * inv_h;
162 cc_result.boxes.push_back({ .x = x, .y = y, .w = bw, .h = bh, .confidence = 1.0F, .label_id = i + 1 });
163 }
164 }
165
166 contexts.pass.result.component_boxes = cc_result.boxes;
167
168 if (contours_follow)
169 return;
170
171 contexts.pass.result.structured = std::move(cc_result);
172 contexts.pass.result.w = 0;
173 contexts.pass.result.h = 0;
174}
175
177 VisionGpuContexts& contexts,
178 const FindContoursParams& p)
179{
180 const auto* prev = contexts.pass.behind();
181 if (!prev || prev->op != VisionOp::ConnectedComponents) {
183 "run_gpu: FindContours requires ConnectedComponents as the immediately preceding step");
184 return false;
185 }
186
187 auto& component_contours = contexts.component_contours;
188 auto w = contexts.pass.w;
189 auto h = contexts.pass.h;
190 auto& foundry = Portal::Graphics::get_shader_foundry();
191
192 component_contours.ensure_shared_buffer(1, 4, static_cast<size_t>(k_max_components) + 1U, GpuBufferBinding::ElementType::UINT32);
193 component_contours.ensure_shared_buffer(1, 5, static_cast<size_t>(k_max_components) * k_max_holes_per_label, GpuBufferBinding::ElementType::UINT32);
194 component_contours.ensure_shared_buffer(1, 6, static_cast<size_t>(k_max_trace_slots) * 2U, GpuBufferBinding::ElementType::UINT32,
196 component_contours.ensure_shared_buffer(1, 7, 1U, GpuBufferBinding::ElementType::UINT32);
197 component_contours.ensure_shared_buffer(1, 8, static_cast<size_t>(k_max_trace_slots) * k_max_points_per_contour * 2U, GpuBufferBinding::ElementType::FLOAT32);
198 component_contours.ensure_shared_buffer(1, 9, 1U, GpuBufferBinding::ElementType::UINT32);
199 component_contours.ensure_shared_buffer(1, 10, static_cast<size_t>(k_max_trace_slots) * 4U, GpuBufferBinding::ElementType::UINT32);
200 component_contours.ensure_shared_buffer(1, 11, static_cast<size_t>(k_max_trace_slots) * 2U, GpuBufferBinding::ElementType::FLOAT32);
201 component_contours.ensure_shared_buffer(2, 0, k_max_components, GpuBufferBinding::ElementType::FLOAT32);
202 component_contours.ensure_shared_buffer(2, 1, k_max_components, GpuBufferBinding::ElementType::FLOAT32);
203
204 {
205 std::vector<uint32_t> owner_reset(static_cast<size_t>(k_max_components) + 1U, CC_UNCLAIMED_HOST);
206 component_contours.upload_shared_raw(1, 4, reinterpret_cast<const uint8_t*>(owner_reset.data()), owner_reset.size() * sizeof(uint32_t));
207
208 std::vector<uint32_t> hole_owner_reset(static_cast<size_t>(k_max_components) * k_max_holes_per_label, CC_UNCLAIMED_HOST);
209 component_contours.upload_shared_raw(1, 5, reinterpret_cast<const uint8_t*>(hole_owner_reset.data()), hole_owner_reset.size() * sizeof(uint32_t));
210
211 const uint32_t zero = 0;
212 component_contours.upload_shared_raw(1, 7, reinterpret_cast<const uint8_t*>(&zero), sizeof(uint32_t));
213 component_contours.upload_shared_raw(1, 9, reinterpret_cast<const uint8_t*>(&zero), sizeof(uint32_t));
214 }
215
217 component_contours.swap_shader({ .shader_path = "contour_march.comp.spv", .workgroup_size = k_wg2d, .push_constant_size = sizeof(ContourMarchPC) });
218 component_contours.stage_image_at(1, contexts.source, GpuBufferBinding::ElementType::IMAGE_SAMPLED);
219 component_contours.prepare_output_image(w, h);
220 component_contours.set_push_constants(ContourMarchPC {
221 .width = w,
222 .height = h,
223 .max_components = k_max_components,
224 .max_points_per_contour = max_points,
225 .max_holes_per_label = k_max_holes_per_label,
226 .phase = 0U,
227 .min_area = p.min_area,
228 .compacted_count = 0U });
229
230 component_contours.set_output_dimensions(w, h);
231 {
232 const auto fence = component_contours.dispatch_async({});
233 foundry.wait_for_fence(fence);
234 foundry.release_fence(fence);
235 }
236
237 component_contours.set_push_constants(ContourMarchPC {
238 .width = w,
239 .height = h,
240 .max_components = k_max_components,
241 .max_points_per_contour = max_points,
242 .max_holes_per_label = k_max_holes_per_label,
243 .phase = 1U,
244 .min_area = p.min_area,
245 .compacted_count = 0U });
246 {
247 const auto fence = component_contours.dispatch_async({});
248 foundry.wait_for_fence(fence);
249 foundry.release_fence(fence);
250 }
251
252 component_contours.clear_output_dimensions();
253
254 const uint32_t total_owner_slots = k_max_components * (1U + k_max_holes_per_label);
255 const auto owner_hazards = [](GpuDispatchCore& ctx) {
256 return std::vector<HazardResource> {
257 ctx.shared_buffer_hazard({ .set = 1, .binding = 6, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 }),
258 ctx.shared_buffer_hazard({ .set = 1, .binding = 7, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 }),
259 };
260 };
261 const auto trace_pc = [&](uint32_t phase) {
262 return ContourMarchPC {
263 .width = w,
264 .height = h,
265 .max_components = k_max_components,
266 .max_points_per_contour = max_points,
267 .max_holes_per_label = k_max_holes_per_label,
268 .phase = phase,
269 .min_area = p.min_area,
270 .compacted_count = 0U
271 };
272 };
273 const GpuComputeConfig march_config { .shader_path = "contour_march.comp.spv", .workgroup_size = k_wg2d, .push_constant_size = sizeof(ContourMarchPC) };
274
275 std::vector<DependencyStage> trace_stages;
276 trace_stages.push_back({
277 .config = { .shader_path = "contour_compact.comp.spv", .workgroup_size = { 256, 1, 1 }, .push_constant_size = sizeof(ContourCompactPC) },
278 .stage_fn = [](GpuDispatchCore& ctx) {
279 ctx.set_push_constants(ContourCompactPC { .max_components = k_max_components, .max_holes_per_label = k_max_holes_per_label });
280 },
281 .hazard_fn = owner_hazards,
282 .explicit_groups = std::array<uint32_t, 3> { (total_owner_slots + 255U) / 256U, 1U, 1U },
283 });
284 trace_stages.push_back({
285 .config = march_config,
286 .stage_fn = [pc = trace_pc(3U)](GpuDispatchCore& ctx) { ctx.set_push_constants(pc); },
287 .explicit_groups = std::array<uint32_t, 3> { 1U, 1U, 1U },
288 });
289 const bool sort_requested = p.max_contours > 0U;
290 const auto contour_hazard = [](GpuDispatchCore& ctx, uint32_t set, uint32_t binding, GpuBufferBinding::ElementType type) {
291 return ctx.shared_buffer_hazard({ .set = set, .binding = binding, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = type });
292 };
293 const auto sort_hazards = [contour_hazard](GpuDispatchCore& ctx) {
294 return std::vector<HazardResource> {
295 contour_hazard(ctx, 2, 0, GpuBufferBinding::ElementType::FLOAT32),
296 contour_hazard(ctx, 2, 1, GpuBufferBinding::ElementType::FLOAT32),
297 };
298 };
299
300 DependencyStage trace_stage {
301 .config = march_config,
302 .stage_fn = [pc = trace_pc(2U)](GpuDispatchCore& ctx) { ctx.set_push_constants(pc); },
303 .explicit_groups = std::array<uint32_t, 3> { 1U, 1U, 1U },
304 .indirect_groups = IndirectGroupsSource { .set = 0, .binding = 10, .offset_bytes = 3U * sizeof(uint32_t) },
305 };
306 if (sort_requested || p.as_image) {
307 trace_stage.hazard_fn = [contour_hazard](GpuDispatchCore& ctx) {
308 return std::vector<HazardResource> {
309 contour_hazard(ctx, 1, 8, GpuBufferBinding::ElementType::FLOAT32),
310 contour_hazard(ctx, 1, 10, GpuBufferBinding::ElementType::UINT32),
311 contour_hazard(ctx, 2, 0, GpuBufferBinding::ElementType::FLOAT32),
312 contour_hazard(ctx, 2, 1, GpuBufferBinding::ElementType::FLOAT32),
313 };
314 };
315 }
316 trace_stages.push_back(std::move(trace_stage));
317
318 if (sort_requested) {
319 const uint32_t rounds = std::min(p.max_contours, k_max_components);
320 const GpuComputeConfig topk_config {
321 .shader_path = "contour_topk_select.comp.spv",
322 .workgroup_size = { 256, 1, 1 },
323 .push_constant_size = sizeof(ContourTopKPC),
324 };
325
326 for (uint32_t round = 0; round < rounds; ++round) {
327 const ContourTopKPC topk_pc { .round = round, .count = k_max_components };
328 trace_stages.push_back({
329 .config = topk_config,
330 .stage_fn = [topk_pc](GpuDispatchCore& ctx) { ctx.set_push_constants(topk_pc); },
331 .hazard_fn = sort_hazards,
332 .explicit_groups = std::array<uint32_t, 3> { 1U, 1U, 1U },
333 });
334 }
335 }
336
337 std::shared_ptr<Core::VKImage> contour_image;
338 if (p.as_image) {
339 const uint32_t render_slots = sort_requested ? std::min(p.max_contours, k_max_components) : k_max_trace_slots;
340 const ContourClearPC clear_pc { .width = w, .height = h };
341 const ContourRenderPC render_pc {
342 .width = w,
343 .height = h,
344 .max_components = k_max_components,
345 .max_points_per_contour = k_max_points_per_contour,
346 .max_contours = p.max_contours
347 };
348
349 trace_stages.push_back({
350 .config = { .shader_path = "contour_render_clear.comp.spv", .workgroup_size = k_wg2d, .push_constant_size = sizeof(ContourClearPC) },
351 .stage_fn = [&component_contours, &contour_image, w, h, clear_pc](GpuDispatchCore& ctx) {
352 component_contours.prepare_output_image(w, h);
353 contour_image = component_contours.get_output_image(0);
354 ctx.set_push_constants(clear_pc); },
355 .hazard_fn = [&contour_image](GpuDispatchCore&) { return std::vector<HazardResource> {
356 { .binding = { .set = 0, .binding = 0, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::IMAGE_STORAGE },
357 .image = contour_image->get_image() },
358 }; },
359 .explicit_groups = std::array<uint32_t, 3> { (w + k_wg2d[0] - 1U) / k_wg2d[0], (h + k_wg2d[1] - 1U) / k_wg2d[1], 1U },
360 });
361 trace_stages.push_back({
362 .config = { .shader_path = "contour_render.comp.spv", .workgroup_size = { 256, 1, 1 }, .push_constant_size = sizeof(ContourRenderPC) },
363 .stage_fn = [&contour_image, render_pc](GpuDispatchCore& ctx) {
364 ctx.stage_image_at(0, contour_image, GpuBufferBinding::ElementType::IMAGE_STORAGE);
365 ctx.set_push_constants(render_pc); },
366 .explicit_groups = std::array<uint32_t, 3> { (render_slots * k_max_points_per_contour + 255U) / 256U, 1U, 1U },
367 });
368 }
369
370 ExecutionContext trace_ctx;
371 trace_ctx.mode = ExecutionMode::DEPENDENCY;
372 DependencyParams trace_params;
373 trace_params.stages = trace_stages;
374 trace_ctx.parameters = trace_params;
375 component_contours.execute(Datum<> {}, trace_ctx);
376
377 if (p.as_image) {
378 contexts.pass.result.images.contour_image = contour_image;
379 contexts.pass.result.structured = std::monostate {};
380 contexts.pass.result.w = 0;
381 contexts.pass.result.h = 0;
382 return true;
383 }
384
385 uint32_t compacted_count = 0;
386 component_contours.download_shared(1, 7, &compacted_count, sizeof(uint32_t));
387
388 std::vector<glm::uvec4> meta(compacted_count);
389 std::vector<glm::vec2> area_perim(compacted_count);
390 if (compacted_count > 0) {
391 component_contours.download_shared(1, 10, meta.data(), compacted_count * sizeof(glm::uvec4));
392 component_contours.download_shared(1, 11, area_perim.data(), compacted_count * sizeof(glm::vec2));
393 }
394 uint32_t points_written = 0;
395 component_contours.download_shared(1, 9, &points_written, sizeof(uint32_t));
396 std::vector<glm::vec2> flat_points_full(points_written);
397
398 if (points_written > 0)
399 component_contours.download_shared(1, 8, flat_points_full.data(), static_cast<size_t>(points_written) * sizeof(glm::vec2));
400
401 if (p.export_contours_buffer) {
402 contexts.pass.result.buffers.contours = Kinesis::Vision::ContoursBufferView {
403 .count = compacted_count,
404 .points_written = points_written,
405 .meta = std::make_shared<Portal::Graphics::GpuBufferHandle>(component_contours.shared_buffer_handle(1, 10)),
406 .points = std::make_shared<Portal::Graphics::GpuBufferHandle>(component_contours.shared_buffer_handle(1, 8)),
407 .area_perim = std::make_shared<Portal::Graphics::GpuBufferHandle>(component_contours.shared_buffer_handle(1, 11)),
408 };
409 }
410
411 std::vector<uint32_t> order;
412 if (p.max_contours > 0U) {
413 const uint32_t take = std::min(p.max_contours, compacted_count);
414 std::vector<float> sorted_indices(take);
415 component_contours.download_shared(2, 1, sorted_indices.data(), take * sizeof(float));
416 order.reserve(take);
417 for (float f : sorted_indices)
418 order.push_back(static_cast<uint32_t>(f));
419 } else {
420 order.resize(compacted_count);
421 for (uint32_t i = 0; i < compacted_count; ++i)
422 order[i] = i;
423 }
424
425 std::vector<Kinesis::Vision::Contour> out_contours;
426 out_contours.reserve(order.size());
427
428 for (uint32_t idx : order) {
429 if (idx >= compacted_count)
430 continue;
431 const auto& m = meta[idx];
432 if (m.y < 3)
433 continue;
434 if (m.x > points_written || m.y > points_written - m.x)
435 continue;
436
437 std::vector<glm::vec2> pts(
438 flat_points_full.begin() + m.x,
439 flat_points_full.begin() + m.x + m.y);
440 const glm::vec2 ap = area_perim[idx];
441 out_contours.push_back({ .points = std::move(pts), .area = ap.x, .perimeter = ap.y, .parent_label = m.z, .label_id = m.w });
442 }
443
444 contexts.pass.result.structured = std::move(out_contours);
445 contexts.pass.result.w = 0;
446 contexts.pass.result.h = 0;
447 return true;
448}
449
450std::shared_ptr<Core::VKImage> VisionGpuExecutor::select_label(
451 VisionGpuContexts& contexts,
452 const std::shared_ptr<Core::VKImage>& source,
453 uint32_t target_label,
454 uint32_t w, uint32_t h)
455{
456 auto& component_contours = contexts.component_contours;
457 auto& foundry = Portal::Graphics::get_shader_foundry();
458
459 component_contours.swap_shader({
460 .shader_path = "vision_label_select.comp.spv",
461 .workgroup_size = k_wg2d,
462 .push_constant_size = sizeof(SelectLabelPC),
463 });
464 component_contours.stage_image_at(1, source, GpuBufferBinding::ElementType::IMAGE_SAMPLED);
465 component_contours.set_push_constants(SelectLabelPC {
466 .target_label = target_label, .width = w, .height = h });
467 component_contours.prepare_output_image(w, h);
468 component_contours.set_output_dimensions(w, h);
469
470 const auto fence = component_contours.dispatch_async({});
471 component_contours.clear_output_dimensions();
472 foundry.wait_for_fence(fence);
473 foundry.release_fence(fence);
474
475 return component_contours.get_output_image(0);
476}
477
478std::shared_ptr<Core::VKImage> VisionGpuExecutor::select_label(
479 const std::shared_ptr<Core::VKImage>& source,
480 uint32_t target_label,
481 uint32_t w, uint32_t h)
482{
483 if (!m_contexts)
484 m_contexts = std::make_unique<VisionGpuContexts>();
485
486 return select_label(*m_contexts, source, target_label, w, h);
487}
488
489std::vector<Kinesis::Vision::Contour> VisionGpuExecutor::read_exported_contours(
490 const Kinesis::Vision::VisionResult& result)
491{
492 const auto& view = result.buffers.contours;
493 if (!view.meta || !view.meta->mapped_ptr || !view.points || !view.points->mapped_ptr
494 || !view.area_perim || !view.area_perim->mapped_ptr) {
495 return {};
496 }
497
498 const auto* meta = static_cast<const glm::uvec4*>(view.meta->mapped_ptr);
499 const auto* points = static_cast<const glm::vec2*>(view.points->mapped_ptr);
500 const auto* area_perim = static_cast<const glm::vec2*>(view.area_perim->mapped_ptr);
501
502 const size_t meta_capacity = view.meta->size_bytes / sizeof(glm::uvec4);
503 const size_t area_capacity = view.area_perim->size_bytes / sizeof(glm::vec2);
504 const size_t points_capacity = view.points->size_bytes / sizeof(glm::vec2);
505 const auto count = static_cast<uint32_t>(
506 std::min<size_t>({ view.count, meta_capacity, area_capacity }));
507 const auto points_written = static_cast<uint32_t>(std::min<size_t>(view.points_written, points_capacity));
508
509 std::vector<Kinesis::Vision::Contour> out;
510 out.reserve(count);
511 for (uint32_t idx = 0; idx < count; ++idx) {
512 const auto& m = meta[idx];
513 if (m.y < 3U)
514 continue;
515 if (m.x > points_written || m.y > points_written - m.x)
516 continue;
517
518 std::vector<glm::vec2> pts(points + m.x, points + m.x + m.y);
519 const glm::vec2 ap = area_perim[idx];
520 out.push_back({ .points = std::move(pts), .area = ap.x, .perimeter = ap.y, .parent_label = m.z, .label_id = m.w });
521 }
522 return out;
523}
524
525} // namespace MayaFlux::Yantra
#define MF_ERROR(comp, ctx,...)
vk::Fence fence
std::vector< glm::vec2 > * points
uint32_t h
Definition InkPress.cpp:29
std::vector< float > * out
vk::PhysicalDeviceType type
Definition VKDevice.cpp:126
size_t count
void stage_image_at(size_t binding_index, std::shared_ptr< Core::VKImage > image, GpuBufferBinding::ElementType kind, vk::Sampler sampler=nullptr)
Register a VKImage at an explicit binding index.
Portal::Graphics::HazardResource shared_buffer_hazard(const GpuBufferBinding &spec) const
void set_push_constants(const void *data, size_t bytes)
Set push constant data from a raw byte pointer.
Non-template base that owns all type-independent GPU dispatch logic.
void op_connected_components(VisionGpuContexts &contexts, const Kinesis::Vision::ConnectedComponentsParams &p)
Label components in the current image on the GPU.
bool op_find_contours(VisionGpuContexts &contexts, const Kinesis::Vision::FindContoursParams &p)
Trace contours from the immediately preceding component labels.
@ ComputeMatrix
Compute operations (Yantra - algorithms, matrices, DSP)
@ Yantra
DSP algorithms, computational units, matrix operations, Grammar.
constexpr FenceID INVALID_FENCE
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
@ INDIRECT
Indirect draw/dispatch buffer (device-local)
@ COMPUTE
Storage buffer for compute shaders (device-local)
constexpr uint32_t k_max_holes_per_label
Maximum number of contours that can be stored in a single pass.
constexpr uint32_t k_max_components
Maximum number of connected components that can be labeled in a single pass.
constexpr uint32_t k_max_points_per_contour
Maximum number of points that can be stored in a single contour.
constexpr std::array< uint32_t, 3 > k_wg2d
Standard 2D workgroup used by all pixel-to-pixel vision shaders.
constexpr uint32_t k_max_trace_slots
Maximum number of trace slots that can be stored in a single pass (for contour tracing)
Portal::Graphics::GpuComputeConfig GpuComputeConfig
@ DEPENDENCY
Part of dependency graph.
std::shared_ptr< Portal::Graphics::GpuBufferHandle > labels
std::vector< BoundingBox > boxes
One BoundingBox per component, label 1..count.
uint32_t count
Number of components found.
Result of connected component labelling.
Connected-component label export and color output.
Definition VisionOp.hpp:302
std::shared_ptr< Portal::Graphics::GpuBufferHandle > meta
Contour filtering, trace limits, and output selection.
Definition VisionOp.hpp:326
std::shared_ptr< Core::VKImage > component_colors
ConnectedComponents::with_colors.
const VisionStep * behind(size_t offset=1) const noexcept
Op at offset steps back, or nullptr before the start.
const VisionStep * ahead(size_t offset=1) const noexcept
Op at offset steps ahead, or nullptr past the end.
std::vector< BoundingBox > component_boxes
One BoundingBox per component from a ConnectedComponents step in this sequence, label 1....
BufferOutputs buffers
GPU-resident export buffers, one named slot per exporting op.
ImageOutputs images
Image output of whichever image-producing ops ran in this sequence, one named slot per op.
Result of executing a VisionSequence on one frame.
ElementType
Element type the shader expects in this binding.
enum MayaFlux::Portal::Graphics::GpuBufferBinding::ElementType FLOAT32
Input/Output container for computation pipeline data flow with structure preservation.
Definition DataIO.hpp:26
bool async
Submit the sequence without waiting for it to complete.
std::vector< DependencyStage > stages
Parameters for ExecutionMode::DEPENDENCY.
ExecutionMode mode
Execution mode controlling scheduling behavior.
ExecutionParams parameters
Optional parameters specific to the execution mode.
Context information controlling how a compute operation executes.
TextureExecutionContext component_contours
Shared image and buffer pipeline for ConnectedComponents and FindContours.
Kinesis::Vision::GpuVisionPass pass
Walk state for the current run: sequence position, geometry, working image, and the result under cons...
std::shared_ptr< Core::VKImage > source
Input image the current walk started from.
Fixed set of TextureExecutionContexts covering every GPU-implemented VisionOp shape.