MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches

◆ op_find_contours()

bool MayaFlux::Yantra::VisionGpuExecutor::op_find_contours ( VisionGpuContexts &  contexts,
const Kinesis::Vision::FindContoursParams &  p 
)
private

Trace contours from the immediately preceding component labels.

Publishes either structured contours or a rendered contour image, according to the step parameters.

Returns
False if ConnectedComponents is not the preceding step.

Definition at line 176 of file VisionComponentsDispatch.cpp.

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
216 auto max_points = p.max_points_per_contour > 0 ? std::min<uint32_t>(p.max_points_per_contour, k_max_points_per_contour) : k_max_points_per_contour;
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}
#define MF_ERROR(comp, ctx,...)
vk::Fence fence
uint32_t h
Definition InkPress.cpp:29
vk::PhysicalDeviceType type
Definition VKDevice.cpp:126
@ ComputeMatrix
Compute operations (Yantra - algorithms, matrices, DSP)
@ Yantra
DSP algorithms, computational units, matrix operations, Grammar.
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
@ 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.
ElementType
Element type the shader expects in this binding.
enum MayaFlux::Portal::Graphics::GpuBufferBinding::ElementType FLOAT32

References MayaFlux::Kinesis::Vision::VisionPass< Handle >::behind(), MayaFlux::Yantra::VisionInternal::CC_UNCLAIMED_HOST, MayaFlux::Yantra::VisionGpuContexts::component_contours, MayaFlux::Portal::Graphics::COMPUTE, MayaFlux::Journal::ComputeMatrix, fence, MayaFlux::Portal::Graphics::GpuBufferBinding::FLOAT32, MayaFlux::Portal::Graphics::get_shader_foundry(), MayaFlux::Kinesis::Vision::VisionPass< Handle >::h, h, MayaFlux::Portal::Graphics::GpuBufferBinding::IMAGE_SAMPLED, MayaFlux::Portal::Graphics::GpuBufferBinding::INPUT_OUTPUT, MayaFlux::Yantra::VisionInternal::k_max_components, MayaFlux::Yantra::VisionInternal::k_max_holes_per_label, MayaFlux::Yantra::VisionInternal::k_max_points_per_contour, MayaFlux::Yantra::VisionInternal::k_max_trace_slots, MayaFlux::Yantra::VisionInternal::k_wg2d, MayaFlux::Kinesis::Vision::FindContoursParams::max_points_per_contour, MF_ERROR, MayaFlux::Kinesis::Vision::FindContoursParams::min_area, MayaFlux::Yantra::VisionGpuContexts::pass, MayaFlux::Yantra::VisionGpuContexts::source, MayaFlux::Portal::Graphics::GpuBufferBinding::UINT32, MayaFlux::Kinesis::Vision::VisionPass< Handle >::w, MayaFlux::Yantra::VisionInternal::ContourMarchPC::width, and MayaFlux::Journal::Yantra.

+ Here is the call graph for this function: