11#include <spdlog/spdlog.h>
13#include "HalideRuntime.h"
28 spdlog::error(
"[WorkingImageGPU_Halide::bindView]: Failed to init working buffer.");
31 m_original_halide_buffer = Halide::Buffer<float>::make_interleaved(
37 m_original_halide_buffer.set_name(
"original_cpu_buffer");
39 m_original_halide_buffer.set_host_dirty();
43 if (!vram_result.has_value()) {
44 spdlog::error(
"[WorkingImageGPU_Halide::bindView]: Failed to transfer to VRAM.");
47 spdlog::debug(
"[WorkingImageGPU_Halide::bindView]: Bound and initialized GPU Halide buffer ({}x{}).",
52std::expected<void, ErrorHandling::CoreError>
55 if (m_pipelines_initialized)
return {};
62 Halide::Func reset_func(
"gpu_reset");
64 reset_func(x, y, c) = m_original_halide_buffer(x, y, c);
65 reset_func.output_buffer().dim(0).set_stride(4);
66 reset_func.output_buffer().dim(2).set_stride(1);
67 reset_func.compile_jit(target);
68 m_reset_pipeline = Halide::Pipeline(reset_func);
70 if (!m_downsample_built) {
71 buildDownsamplePipeline();
74 m_pipelines_initialized =
true;
75 spdlog::info(
"[WorkingImageGPU_Halide::transferToVRAM]: Pipelines compiled successfully.");
77 }
catch (
const std::exception& e) {
78 spdlog::critical(
"[WorkingImageGPU_Halide::transferToVRAM]: {}", e.what());
85 if (!m_pipelines_initialized)
return false;
88 if (ret != 0)
return false;
94void WorkingImageGPU_Halide::initDataForHalide()
108 if (!m_pipelines_initialized)
return;
123 Halide::Expr xx { Halide::abs(x) };
124 Halide::Expr xx2 { xx * xx };
125 Halide::Expr xx3 { xx2 * xx };
126 Halide::Expr a { -0.5f };
128 return Halide::select(xx < 1.0f,
129 (a + 2.0f) * xx3 - (a + 3.0f) * xx2 + 1.0f,
130 Halide::select(xx < 2.0f,
131 a * xx3 - 5.0f * a * xx2 + 8.0f * a * xx - 4.0f * a,
142 if (target_width == 0 || target_height == 0) {
151 if (!m_downsample_built) {
152 buildDownsamplePipeline();
155 spdlog::debug(
"[WorkingImageGPU_Halide::downsample]: Starting downsample from {}x{} to {}x{}.",
161 Halide::Buffer<float> dst_buffer { Halide::Buffer<float>::make_interleaved(
163 static_cast<int>(target_width),
164 static_cast<int>(target_height),
173 m_downsample_pipeline.realize(dst_buffer, target);
174 dst_buffer.copy_to_host();
176 spdlog::debug(
"[WorkingImageGPU_Halide::downsample]: Successful downsample.");
178 return std::make_unique<Common::ImageRegion>(
179 std::move(result_data),
180 static_cast<int>(target_width),
181 static_cast<int>(target_height),
185 }
catch (
const std::bad_alloc&) {
187 }
catch (
const std::exception& e) {
188 spdlog::critical(
"[WorkingImageGPU_Halide::downsample]: {}", e.what());
193void WorkingImageGPU_Halide::buildDownsamplePipeline()
195 if (m_downsample_built)
return;
197 m_downsample_input.dim(0).set_stride(4);
198 m_downsample_input.dim(2).set_stride(1);
201 Halide::Func clamped{Halide::BoundaryConditions::repeat_edge(m_downsample_input)};
203 Halide::Expr inv_scale_x{1.0f / m_downsample_scale_x};
204 Halide::Expr inv_scale_y{1.0f / m_downsample_scale_y};
206 Halide::Expr sourcex{(Halide::cast<float>(x) + 0.5f) * inv_scale_x - 0.5f};
207 Halide::Expr sourcey{(Halide::cast<float>(y) + 0.5f) * inv_scale_y - 0.5f};
209 Halide::Expr fx{Halide::floor(sourcex)};
210 Halide::Expr fy{Halide::floor(sourcey)};
212 Halide::Expr ix = Halide::cast<int>(fx);
213 Halide::Expr iy = Halide::cast<int>(fy);
215 Halide::Func resized_y{
"resized_y"};
221 resized_y(x, y, c) = wy0 * clamped(x, iy - 1, c) +
222 wy1 * clamped(x, iy , c) +
223 wy2 * clamped(x, iy + 1, c) +
224 wy3 * clamped(x, iy + 2, c);
226 Halide::Func resized_x{
"resized_x"};
232 resized_x(x, y, c) = wx0 * resized_y(ix - 1, y, c) +
233 wx1 * resized_y(ix , y, c) +
234 wx2 * resized_y(ix + 1, y, c) +
235 wx3 * resized_y(ix + 2, y, c);
237 Halide::Func final_output{
"final_output"};
238 final_output(x, y, c) = Halide::clamp(resized_x(x, y, c), 0.0f, 1.0f);
240 final_output.output_buffer().dim(0).set_stride(4);
241 final_output.output_buffer().dim(2).set_stride(1);
245 if (target.has_gpu_feature())
248 final_output.compute_root().gpu_tile(x, y, xi, yi, 16, 16);
251 m_downsample_pipeline = Halide::Pipeline(final_output);
252 final_output.compile_jit(target);
253 m_downsample_built =
true;
Application-wide configuration singleton.
static const Halide::Target & getHalideTarget()
Gets the configured Halide Target object.
virtual bool bindView(const ImageView &view)
Binds a view of the image data to this hardware worker.
ImageView m_view_data_image
The image view bound to this hardware worker.
bool isValid() const override
Checks if the GPU view AND the Halide buffer are valid.
std::expected< void, ErrorHandling::CoreError > transferToVRAM() override
Transfers the current image data to GPU VRAM With Halide.
Halide::Buffer< float > & getExecutionBuffer()
Provides access to the Halide buffer used for GPU execution.
void resetExecutionBuffer()
Resets the execution buffer on the GPU to prepare for a new Halide pipeline execution.
bool downloadDeviceToHost() override
A pure method that specific backends (Halide, Vulkan, etc.) must implement.
std::expected< std::unique_ptr< Common::ImageRegion >, ErrorHandling::CoreError > downsample(Common::ImageDim target_width, Common::ImageDim target_height) override
Downsamples the current image to the target dimensions using Halide on GPU.
bool bindView(const ImageView &view) override
Binds the view and initializes the Halide buffer on the device.
bool isValid() const override
Checks if the GPU buffer is valid and allocated.
std::expected< std::unique_ptr< Common::ImageRegion >, ErrorHandling::CoreError > getFullResImage() override
Exports current internal image data by downloading from the GPU device to Host memory.
void initializeHalide(std::span< float > data, Common::ImageDim width, Common::ImageDim height, Common::ImageChan channels)
Creates a Halide buffer view over existing pixel data.
Halide::Buffer< float > m_halide_buffer
Internal Halide buffer holding the image data. Managed directly by this object. Halide::Buffer handle...
bool isHalideBufferValid() const noexcept
Checks if the Halide buffer is defined.
std::size_t ImageDim
Type for image dimensions (width, height).
CoreError
Enumeration of all error codes for CaptureMoment Core.
@ InvalidImageRegion
The provided ImageRegion dimensions or data are invalid.
@ InvalidWorkingImage
The working image state is invalid or corrupted.
@ AllocationFailed
Memory allocation failed.
@ Unexpected
An unexpected error occurred.
static Halide::Expr kernel_cubic(Halide::Expr x)
Represents a zero-copy window over contiguous image memory.
Common::ImageChan channels
std::span< float > working_data
Mutable span for processing.
std::span< const float > original_data
Const span for non-destructive reset.
Concrete implementation of image working buffer on GPU using Halide for processing.