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

◆ op_connected_components()

void MayaFlux::Yantra::VisionGpuExecutor::op_connected_components ( VisionGpuContexts &  contexts,
const Kinesis::Vision::ConnectedComponentsParams &  p 
)
private

Label components in the current image on the GPU.

Produces component counts and bounds for structured output. When FindContours follows immediately, the labels stay on the GPU for that step and component bounds are not read back to the host.

Definition at line 10 of file VisionComponentsDispatch.cpp.

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) {
81 ctx.stage_image_at(1, seed_input, GpuBufferBinding::ElementType::IMAGE_STORAGE);
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) {
90 ctx.stage_image_at(1, seed_input, GpuBufferBinding::ElementType::IMAGE_STORAGE);
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) {
106 ctx.stage_image_at(1, seed_input, GpuBufferBinding::ElementType::IMAGE_STORAGE);
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;
115 cc_ctx.mode = ExecutionMode::DEPENDENCY;
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
140 Kinesis::Vision::ComponentResult cc_result;
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}
vk::Fence fence
uint32_t h
Definition InkPress.cpp:29
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_components
Maximum number of connected components that can be labeled in a single pass.
constexpr std::array< uint32_t, 3 > k_wg2d
Standard 2D workgroup used by all pixel-to-pixel vision shaders.
@ DEPENDENCY
Part of dependency graph.

References MayaFlux::Kinesis::Vision::VisionPass< Handle >::ahead(), MayaFlux::Yantra::DependencyParams::async, MayaFlux::Yantra::VisionInternal::CCCompressPC::block_width, MayaFlux::Kinesis::Vision::ComponentResult::boxes, MayaFlux::Kinesis::Vision::VisionResult::buffers, MayaFlux::Kinesis::Vision::VisionResult::component_boxes, MayaFlux::Kinesis::Vision::ImageOutputs::component_colors, MayaFlux::Yantra::VisionGpuContexts::component_contours, MayaFlux::Portal::Graphics::COMPUTE, MayaFlux::Kinesis::Vision::ComponentResult::count, MayaFlux::Kinesis::Vision::VisionPass< Handle >::current, MayaFlux::Yantra::DEPENDENCY, MayaFlux::Kinesis::Vision::ConnectedComponentsParams::export_boxes, MayaFlux::Kinesis::Vision::ConnectedComponentsParams::export_label_buffer, MayaFlux::Kinesis::Vision::ConnectedComponentsParams::export_labels, fence, MayaFlux::Portal::Graphics::get_shader_foundry(), MayaFlux::Kinesis::Vision::VisionResult::h, MayaFlux::Kinesis::Vision::VisionPass< Handle >::h, h, MayaFlux::Portal::Graphics::GpuBufferBinding::IMAGE_STORAGE, MayaFlux::Kinesis::Vision::VisionResult::images, MayaFlux::Portal::Graphics::INDIRECT, MayaFlux::Portal::Graphics::INVALID_FENCE, MayaFlux::Yantra::VisionInternal::k_max_components, MayaFlux::Yantra::VisionInternal::k_wg2d, MayaFlux::Kinesis::Vision::BufferOutputs::labels, MayaFlux::Yantra::VisionInternal::CCResetPC::lut_size, MayaFlux::Yantra::ExecutionContext::mode, MayaFlux::Kinesis::Vision::VisionStep::op, MayaFlux::Yantra::ExecutionContext::parameters, MayaFlux::Yantra::VisionGpuContexts::pass, MayaFlux::Kinesis::Vision::VisionPass< Handle >::result, MayaFlux::Yantra::GpuDispatchCore::set_push_constants(), MayaFlux::Yantra::GpuDispatchCore::shared_buffer_hazard(), MayaFlux::Yantra::GpuDispatchCore::stage_image_at(), MayaFlux::Yantra::DependencyParams::stages, MayaFlux::Kinesis::Vision::VisionResult::structured, MayaFlux::Portal::Graphics::GpuBufferBinding::UINT32, MayaFlux::Kinesis::Vision::VisionResult::w, MayaFlux::Kinesis::Vision::VisionPass< Handle >::w, MayaFlux::Yantra::VisionInternal::CCBlockInitPC::width, MayaFlux::Yantra::VisionInternal::CCMergePC::width, MayaFlux::Yantra::VisionInternal::CCFinalLabelPC::width, and MayaFlux::Kinesis::Vision::ConnectedComponentsParams::with_colors.

+ Here is the call graph for this function: