99 Portal::Graphics::ImageFormat::RGBA32F,
100 TextureExecutionContext::OutputMode::SCALAR,
102 std::vector<GpuBufferBinding> {
103 { .set = 0, .binding = 1, .direction = GpuBufferBinding::Direction::OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
104 { .set = 0, .binding = 2, .direction = GpuBufferBinding::Direction::OUTPUT, .element_type = GpuBufferBinding::ElementType::FLOAT32 },
105 { .set = 0, .binding = 3, .direction = GpuBufferBinding::Direction::OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
106 { .set = 0, .binding = 4, .direction = GpuBufferBinding::Direction::OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
107 { .set = 1, .binding = 0, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::FLOAT32 },
108 { .set = 1, .binding = 1, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::FLOAT32 },
110 GpuBufferBinding::ElementType::IMAGE_STORAGE,
115 Portal::Graphics::ImageFormat::RGBA32F,
116 TextureExecutionContext::OutputMode::IMAGE,
118 std::vector<GpuBufferBinding> {
119 { .set = 0, .binding = 2, .direction = GpuBufferBinding::Direction::OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
120 { .set = 0, .binding = 4, .direction = GpuBufferBinding::Direction::OUTPUT, .element_type = GpuBufferBinding::ElementType::IMAGE_STORAGE },
122 GpuBufferBinding::ElementType::IMAGE_STORAGE,
124 , component_contours {
126 Portal::Graphics::ImageFormat::RGBA32F,
127 TextureExecutionContext::OutputMode::IMAGE,
129 std::vector<GpuBufferBinding> {
130 { .set = 0, .binding = 2, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
131 { .set = 0, .binding = 3, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
132 { .set = 0, .binding = 4, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
133 { .set = 0, .binding = 5, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
134 { .set = 0, .binding = 6, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
135 { .set = 0, .binding = 7, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
136 { .set = 0, .binding = 8, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
137 { .set = 0, .binding = 9, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
138 { .set = 0, .binding = 10, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
139 { .set = 1, .binding = 0, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
140 { .set = 1, .binding = 1, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
141 { .set = 1, .binding = 2, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
142 { .set = 1, .binding = 3, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
143 { .set = 1, .binding = 4, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
144 { .set = 1, .binding = 5, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
145 { .set = 1, .binding = 6, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
146 { .set = 1, .binding = 7, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
147 { .set = 1, .binding = 8, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::FLOAT32 },
148 { .set = 1, .binding = 9, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
149 { .set = 1, .binding = 10, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
150 { .set = 1, .binding = 11, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::FLOAT32 },
151 { .set = 2, .binding = 0, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::FLOAT32 },
152 { .set = 2, .binding = 1, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::FLOAT32 },
154 GpuBufferBinding::ElementType::IMAGE_STORAGE,
159 Portal::Graphics::ImageFormat::RGBA32F,
160 TextureExecutionContext::OutputMode::IMAGE,
162 std::vector<GpuBufferBinding> {},
163 GpuBufferBinding::ElementType::IMAGE_SAMPLED,
168 std::vector<GpuBufferBinding> {
169 { .set = 0, .binding = 0, .direction = GpuBufferBinding::Direction::INPUT, .element_type = GpuBufferBinding::ElementType::IMAGE_STORAGE },
170 { .set = 0, .binding = 1, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
171 { .set = 0, .binding = 2, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::FLOAT32 },
172 { .set = 0, .binding = 3, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::IMAGE_STORAGE },
173 { .set = 0, .binding = 4, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::IMAGE_STORAGE },
174 { .set = 1, .binding = 0, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
175 { .set = 1, .binding = 1, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::FLOAT32 },
176 { .set = 1, .binding = 2, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::FLOAT32 },
177 { .set = 1, .binding = 3, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
178 { .set = 1, .binding = 4, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::FLOAT32 },
179 { .set = 1, .binding = 5, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
180 { .set = 1, .binding = 6, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
181 { .set = 1, .binding = 7, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
182 { .set = 1, .binding = 8, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::FLOAT32 },
183 { .set = 1, .binding = 9, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::FLOAT32 },
184 { .set = 1, .binding = 10, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::UINT32 },
185 { .set = 0, .binding = 5, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::IMAGE_STORAGE },
186 { .set = 0, .binding = 6, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::IMAGE_STORAGE },
187 { .set = 0, .binding = 7, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::IMAGE_STORAGE },
188 { .set = 0, .binding = 8, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::IMAGE_STORAGE },
189 { .set = 0, .binding = 9, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::IMAGE_STORAGE },
190 { .set = 0, .binding = 10, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::IMAGE_STORAGE },
191 { .set = 0, .binding = 11, .direction = GpuBufferBinding::Direction::INPUT_OUTPUT, .element_type = GpuBufferBinding::ElementType::IMAGE_STORAGE },
193 TextureExecutionContext::OutputMode::SCALAR,
196 pixel.set_avoid_output_alias(
true);
197 structured.set_output_size(1,
sizeof(uint32_t));
198 structured.set_output_size(2,
static_cast<size_t>(4096) * 4 *
sizeof(
float));
199 structured.set_output_size(3,
static_cast<size_t>(256) *
sizeof(uint32_t));
200 structured.set_output_size(4,
sizeof(uint32_t));
453 const std::shared_ptr<Core::VKImage>&
image,
454 uint32_t w, uint32_t
h)
456 auto& pixel_ctx = contexts.
pixel;
458 auto& label_ctx = contexts.
labels;
462 std::unordered_map<size_t, CompletedOp> completed_ops;
468 "run_gpu: polling a suspension with a different sequence; "
469 "the walk continues on the sequence the run started from");
474 pending.
status = VisionStatus::SUSPENDED;
489 reap_fences(contexts);
495 const auto seed = op_ingest(contexts,
image, w,
h);
501 const auto& step = sequence.steps[contexts.
pass.
index];
503 if (step.op == VisionOp::Snapshot) {
504 op_snapshot(contexts);
508 const uint32_t w = contexts.
pass.
w;
509 const uint32_t
h = contexts.
pass.
h;
511 const auto cfg = config(step.op, step.params);
515 "run_gpu: no GPU implementation for VisionOp {}",
516 static_cast<int>(step.op));
522 pixel_ctx.swap_shader(cfg);
531 pixel_ctx.prepare_output_image(w,
h);
535 pixel_ctx.set_output_dimensions(w,
h);
538 case VisionOp::Downsample2x: {
539 const uint32_t new_w = std::max(1U, w / 2);
540 const uint32_t new_h = std::max(1U,
h / 2);
542 pixel_ctx.prepare_output_image(new_w, new_h);
543 pixel_ctx.set_output_dimensions(new_w, new_h);
545 const auto f = pixel_ctx.dispatch_async({});
546 foundry.wait_for_fence(f);
547 foundry.release_fence(f);
549 pixel_ctx.clear_output_dimensions();
551 auto downsampled = pixel_ctx.get_output_image(0);
563 case VisionOp::Threshold: {
564 const auto& p = std::get<ThresholdParams>(step.params);
565 if (p.channels == ChannelMask::NONE) {
569 .
lo0 = p.bands[0].lo, .lo1 = p.bands[1].lo, .lo2 = p.bands[2].lo,
570 .hi0 = p.bands[0].hi, .hi1 = p.bands[1].hi, .hi2 = p.bands[2].hi,
571 .channels =
static_cast<uint32_t
>(p.channels) });
575 case VisionOp::NormalizeRange: {
576 const auto& p = std::get<NormalizeRangeParams>(step.params);
577 const float scale = (p.hi > p.lo) ? 1.0F / (p.hi - p.lo) : 1.0F;
578 const float off = (p.hi > p.lo) ? -p.lo / (p.hi - p.lo) : 0.0F;
579 pixel_ctx.set_push_constants(
NormalizePC { .
scale = scale, .offset = off });
582 case VisionOp::RgbaToGray:
584 .
wr = 0.299F, .wg = 0.587F, .wb = 0.114F, .wa = 0.0F });
586 case VisionOp::GaussianBlur: {
587 const auto& p = std::get<GaussianBlurParams>(step.params);
588 const auto radius =
static_cast<uint32_t
>(std::ceil(p.sigma * 3.0F));
589 const auto& weights = gaussian_kernel_2d(
radius, p.sigma);
590 pixel_ctx.set_binding_data(2, std::span<const float>(weights));
594 case VisionOp::Erode:
595 case VisionOp::Dilate:
596 case VisionOp::MorphGradient:
597 pixel_ctx.set_push_constants(
MorphPC {
598 .
radius = std::get<MorphParams>(step.params).radius });
600 case VisionOp::ThresholdAdaptive: {
601 const auto& p = std::get<ThresholdAdaptiveParams>(step.params);
602 pixel_ctx.set_push_constants(
ThresholdAdaptivePC { .
block_size = p.block_size, .offset = p.offset, .channels =
static_cast<uint32_t
>(p.channels) });
605 case VisionOp::ThresholdOtsu: {
606 const auto* otsu = std::get_if<OtsuParams>(&step.params);
610 case VisionOp::Confine: {
615 case VisionOp::Close: {
619 case VisionOp::Canny: {
620 auto done = op_canny(contexts, step.params, std::get<CannyParams>(step.params));
624 case VisionOp::HarrisResponse: {
628 case VisionOp::ExtractPeaks: {
629 op_extract_peaks(contexts, std::get<ExtractPeaksParams>(step.params));
632 case VisionOp::ConnectedComponents: {
636 op_connected_components(contexts, std::get<Kinesis::Vision::ConnectedComponentsParams>(step.params));
639 case VisionOp::FindContours: {
640 if (!op_find_contours(contexts,
641 std::get<Kinesis::Vision::FindContoursParams>(step.params)))
645 case VisionOp::TrackKeypoints: {
646 if (op_track_keypoints(contexts, step)) {
648 pending.
status = VisionStatus::SUSPENDED;
654 case VisionOp::OpticalFlowDense: {
655 if (op_dense_flow(contexts, step)) {
657 pending.
status = VisionStatus::SUSPENDED;
668 const auto fence = pixel_ctx.dispatch_async({});
671 pixel_ctx.clear_output_dimensions();
672 contexts.
pass.
current = pixel_ctx.get_output_image(0);
675 const Kinesis::Vision::GpuVisionPass::Completed
done {
677 .input = dispatch_input
685 pending.
status = VisionStatus::SUSPENDED;
690 foundry.wait_for_fence(
fence);
691 foundry.release_fence(
fence);
693 pixel_ctx.clear_output_dimensions();
694 contexts.
pass.
current = pixel_ctx.get_output_image(0);
699 case VisionOp::RgbaToGray:
702 case VisionOp::RgbaToHsv:
705 case VisionOp::GrayToRgba:
708 case VisionOp::Threshold:
711 case VisionOp::ThresholdAdaptive:
714 case VisionOp::NormalizeInplace:
717 case VisionOp::NormalizeRange:
720 case VisionOp::GaussianBlur:
723 case VisionOp::FilterSeparable:
726 case VisionOp::Sobel:
729 case VisionOp::Scharr:
732 case VisionOp::Erode:
735 case VisionOp::Dilate:
738 case VisionOp::MorphGradient:
745 after_step(contexts, contexts.
pass.
index);