MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
StagingUtils.hpp
Go to the documentation of this file.
1#pragma once
2
5
6namespace MayaFlux::Buffers {
7
8class VKBuffer;
9class AudioBuffer;
10
11inline constexpr float k_buffer_growth_factor = 1.5F;
12
13/**
14 * @brief Upload data to a host-visible buffer
15 * @param target Target VKBuffer to upload data into
16 * @param data DataVariant containing the data to upload
17 *
18 * This function handles uploading data from a Kakshya::DataVariant into a
19 * host-visible VKBuffer. It maps the buffer memory, copies the data, and
20 * marks the buffer as dirty for synchronization.
21 */
22MAYAFLUX_API void upload_host_visible(const std::shared_ptr<VKBuffer>& target, const Kakshya::DataVariant& data);
23
24/**
25 * @brief Upload data to a device-local buffer using a staging buffer
26 * @param target Target VKBuffer to upload data into
27 * @param staging_buffer Host-visible staging VKBuffer used for the upload
28 * @param data DataVariant containing the data to upload
29 *
30 * This function handles uploading data from a Kakshya::DataVariant into a
31 * device-local VKBuffer by utilizing a staging buffer. It copies the data
32 * into the staging buffer, flushes it, and then issues a command to copy
33 * from the staging buffer to the target device-local buffer.
34 */
35MAYAFLUX_API void upload_device_local(const std::shared_ptr<VKBuffer>& target, const std::shared_ptr<VKBuffer>& staging_buffer, const Kakshya::DataVariant& data);
36
37/**
38 * @brief Download data from a host-visible buffer
39 * @param source Source VKBuffer to download data from
40 * @param target Target VKBuffer to store the downloaded data
41 *
42 * This function handles downloading data from a host-visible VKBuffer.
43 * It maps the buffer memory, copies the data into a CPU-accessible format,
44 * and updates the associated target buffer.
45 */
46MAYAFLUX_API void download_host_visible(const std::shared_ptr<VKBuffer>& source, const std::shared_ptr<VKBuffer>& target);
47
48/**
49 * @brief Download data from a device-local buffer using a staging buffer
50 * @param source Source VKBuffer to download data from
51 * @param target Target VKBuffer to store the downloaded data
52 * @param staging_buffer Host-visible staging VKBuffer used for the download
53 *
54 * This function handles downloading data from a device-local VKBuffer by
55 * utilizing a staging buffer. It issues a command to copy the data from
56 * the device-local buffer to the staging buffer, invalidates the staging
57 * buffer memory, and then copies the data into a CPU-accessible format
58 * to update the target buffer.
59 */
60MAYAFLUX_API void download_device_local(const std::shared_ptr<VKBuffer>& source, const std::shared_ptr<VKBuffer>& target, const std::shared_ptr<VKBuffer>& staging_buffer);
61
62/**
63 * @brief Upload raw data to GPU buffer (auto-detects host-visible vs device-local)
64 * @param data Source data pointer
65 * @param size Size in bytes
66 * @param target Target GPU buffer
67 * @param staging Optional staging buffer (created if needed for device-local)
68 *
69 * Convenience wrapper over StagingUtils that:
70 * - Converts raw pointer → DataVariant
71 * - Auto-detects if buffer is host-visible or device-local
72 * - Handles staging buffer creation if needed
73 */
74MAYAFLUX_API void upload_to_gpu(
75 const void* data,
76 size_t size,
77 const std::shared_ptr<VKBuffer>& target,
78 const std::shared_ptr<VKBuffer>& staging = nullptr);
79
80/**
81 * @brief Upload typed data to GPU buffer
82 * @tparam T Data type (float, double, int, etc.)
83 * @param data Source data span
84 * @param target Target GPU buffer
85 * @param staging Optional staging buffer
86 */
87template <typename T>
89 std::span<const T> data,
90 const std::shared_ptr<VKBuffer>& target,
91 const std::shared_ptr<VKBuffer>& staging = nullptr)
92{
93 upload_to_gpu(data.data(), data.size_bytes(), target, staging);
94}
95
96/**
97 * @brief Upload vector to GPU buffer
98 * @tparam T Data type
99 * @param data Source data vector
100 * @param target Target GPU buffer
101 * @param staging Optional staging buffer
102 */
103template <typename T>
105 const std::vector<T>& data,
106 const std::shared_ptr<VKBuffer>& target,
107 const std::shared_ptr<VKBuffer>& staging = nullptr)
108{
109 upload_to_gpu(std::span<const T>(data), target, staging);
110}
111
112/**
113 * @brief Upload @p size bytes to @p target, growing both buffers first if needed.
114 * @param data Source pointer.
115 * @param size Byte count to upload.
116 * @param target Destination GPU buffer.
117 * @param staging Paired staging buffer, or nullptr for host-visible targets.
118 * @param growth_factor Forwarded to ensure_gpu_capacity.
119 *
120 * Combines ensure_gpu_capacity() and upload_to_gpu() into a single call.
121 * The upload is clamped to the post-resize capacity, which is always >= @p size
122 * after ensure_gpu_capacity returns.
123 */
124MAYAFLUX_API void upload_resizing(
125 const void* data,
126 size_t size,
127 const std::shared_ptr<VKBuffer>& target,
128 const std::shared_ptr<VKBuffer>& staging,
129 float growth_factor = 1.5F);
130
131/**
132 * @brief Download from GPU buffer to raw data (auto-detects host-visible vs device-local)
133 * @param source Source GPU buffer
134 * @param data Destination data pointer
135 * @param size Size in bytes
136 * @param staging Optional staging buffer (created if needed for device-local)
137 *
138 * Convenience wrapper over StagingUtils that:
139 * - Auto-detects if buffer is host-visible or device-local
140 * - Handles staging buffer creation if needed
141 * - Copies data to destination pointer
142 */
143MAYAFLUX_API void download_from_gpu(
144 const std::shared_ptr<VKBuffer>& source,
145 void* data,
146 size_t size,
147 const std::shared_ptr<VKBuffer>& staging = nullptr);
148
149/**
150 * @brief Download from GPU buffer to typed span
151 * @tparam T Data type
152 * @param source Source GPU buffer
153 * @param data Destination data span
154 * @param staging Optional staging buffer
155 */
156template <typename T>
158 const std::shared_ptr<VKBuffer>& source,
159 std::span<T> data,
160 const std::shared_ptr<VKBuffer>& staging = nullptr)
161{
162 download_from_gpu(source, data.data(), data.size_bytes(), staging);
163}
164
165/**
166 * @brief Download from GPU buffer to vector
167 * @tparam T Data type
168 * @param source Source GPU buffer
169 * @param data Destination data vector (resized if needed)
170 * @param staging Optional staging buffer
171 */
172template <typename T>
174 const std::shared_ptr<VKBuffer>& source,
175 std::vector<T>& data,
176 const std::shared_ptr<VKBuffer>& staging = nullptr)
177{
178 size_t element_count = source->get_size_bytes() / sizeof(T);
179 data.resize(element_count);
180 download_from_gpu(source, std::span<T>(data), staging);
181}
182
183/**
184 * @brief Download from a device-local GPU buffer without stalling the graphics queue.
185 *
186 * Records a buffer copy into a fenced command buffer, waits on the fence from
187 * the calling thread, then memcpys from the mapped staging buffer into @p data.
188 * Unlike download_from_gpu, this does not call queue.waitIdle, making it safe
189 * to call from any thread that is not the graphics thread itself.
190 *
191 * @p staging is allocated and cached by the caller to avoid per-call Vulkan
192 * object churn. Pass the same staging buffer each frame; it is resized if
193 * @p size exceeds its current capacity.
194 *
195 * @param source Device-local source buffer.
196 * @param data Destination host pointer, at least @p size bytes.
197 * @param size Byte count to copy.
198 * @param staging Persistent host-visible staging buffer. Created via
199 * create_staging_buffer(). Resized in-place if too small.
200 */
201MAYAFLUX_API void download_from_gpu_async(
202 const std::shared_ptr<VKBuffer>& source,
203 void* data,
204 size_t size,
205 std::shared_ptr<VKBuffer>& staging);
206
207/**
208 * @brief Resolve the GPU-resident image from any GpuImageSource buffer.
209 *
210 * Selects get_texture() or get_gpu_texture() at compile time based on which
211 * the concrete buffer type exposes. Returns nullptr if the buffer has not yet
212 * produced a GPU texture.
213 *
214 * @tparam T A type satisfying GpuImageSource.
215 * @param buffer The pixel-bearing buffer to query.
216 * @return GPU-resident VKImage, or nullptr.
217 */
218template <GpuImageSource T>
219[[nodiscard]] std::shared_ptr<Core::VKImage> resolve_gpu_image(const T& buffer)
220{
221 if constexpr (requires(const T& b) {
222 { b.get_texture() } -> std::convertible_to<std::shared_ptr<Core::VKImage>>;
223 }) {
224 return buffer.get_texture();
225 } else {
226 return buffer.get_gpu_texture();
227 }
228}
229
230/**
231 * @brief Download a VKImage to CPU and return a normalised float span.
232 *
233 * Downloads @p image into @p raw_staging via TextureLoom::download_data,
234 * then normalises into @p work via Kakshya::as_normalised_float.
235 *
236 * @p raw_staging is resized to match the image byte footprint when the image
237 * dimensions or format change. @p work is the caller-owned scratch buffer
238 * reused across calls to avoid per-frame allocation.
239 *
240 * Returns an empty span if @p image is null, not initialised, or its format
241 * has no ImageFormat mapping.
242 *
243 * @param image GPU-resident source image.
244 * @param raw_staging Persistent byte buffer for the GPU download. Reuse across calls.
245 * @param work Persistent float buffer for normalisation output. Reuse across calls.
246 * @param gpu_staging Optional persistent staging buffer for device-local images. Reuse across calls.
247 * @return Normalised float span pointing into @p work (or directly into the
248 * variant storage for float-format images).
249 */
250[[nodiscard]] MAYAFLUX_API std::span<const float> download_and_normalise(
251 const std::shared_ptr<Core::VKImage>& image,
252 std::vector<uint8_t>& raw_staging,
253 std::vector<float>& work,
254 const std::shared_ptr<VKBuffer>& gpu_staging);
255
256/**
257 * @brief Create staging buffer for transfers
258 * @param size Size in bytes
259 * @return Host-visible staging buffer ready for transfers
260 */
261MAYAFLUX_API std::shared_ptr<VKBuffer> create_staging_buffer(size_t size);
262
263/**
264 * @brief Allocate a persistent host-visible staging buffer sized for
265 * repeated streaming uploads to an image of @p size bytes.
266 * Call once; pass the result to upload_image_streaming() every frame.
267 * @param size Byte footprint of the target image (use VKImage::get_size_bytes()).
268 * @return Initialised host-visible VKBuffer, or nullptr on failure.
269 */
270[[nodiscard]] MAYAFLUX_API std::shared_ptr<VKBuffer> create_image_staging_buffer(size_t size);
271
272/**
273 * @brief Check if buffer is device-local (staging needed)
274 * @param buffer Buffer to check
275 * @return True if buffer is device-local
276 */
277MAYAFLUX_API bool is_device_local(const std::shared_ptr<VKBuffer>& buffer);
278
279/**
280 * @brief Grow a GPU buffer (and its paired staging buffer) to fit @p required bytes.
281 * @param target Device-local or host-visible GPU buffer to resize.
282 * @param staging Paired staging buffer, or nullptr if the target is host-visible.
283 * @param required Bytes needed for the next upload.
284 * @param growth_factor Multiplier applied when a resize is triggered (default 1.5).
285 *
286 * No-op when @p target already has sufficient capacity. When a resize is necessary
287 * both buffers are grown to `required * growth_factor` so subsequent small
288 * increments do not trigger further allocations. Existing GPU data is not preserved
289 * (preserve_data = false) because the caller is about to overwrite it.
290 */
291MAYAFLUX_API void ensure_gpu_capacity(
292 const std::shared_ptr<VKBuffer>& target,
293 const std::shared_ptr<VKBuffer>& staging,
294 size_t required,
295 float growth_factor = k_buffer_growth_factor);
296
297/**
298 * @brief Upload data from DataAccess view to GPU buffer (precision-preserving)
299 * @tparam T View type (double, glm::dvec2, glm::dvec3, glm::vec3, float, etc.)
300 * @param accessor DataAccess instance providing the view
301 * @param target Target GPU buffer
302 * @param staging Optional staging buffer (auto-created if needed)
303 *
304 * Zero-copy when types match, automatic conversion cache when they don't.
305 * For AUDIO modalities, defaults to DOUBLE precision to preserve accuracy.
306 */
307template <typename T>
309 const Kakshya::DataAccess& accessor,
310 const std::shared_ptr<VKBuffer>& target,
311 const std::shared_ptr<VKBuffer>& staging = nullptr)
312{
313 auto view = accessor.view<T>();
314
315 const void* data_ptr = view.data();
316 size_t data_bytes = view.size() * sizeof(T);
317
318 if constexpr (std::is_same_v<T, double>) {
319 const auto modality = target->get_modality();
320 if (modality != Kakshya::DataModality::AUDIO_1D
323 "Uploading double precision to buffer with modality {}. Consider using AUDIO_1D or AUDIO_MULTICHANNEL.",
325 }
326 }
327
328 upload_to_gpu(data_ptr, data_bytes, target, staging);
329}
330
331/**
332 * @brief Upload structured data with GLM double-precision types
333 * @tparam T GLM type (glm::dvec2, glm::dvec3, glm::dvec4 for double precision)
334 * @param accessor DataAccess with structured dimensions
335 * @param target Target GPU buffer
336 * @param staging Optional staging buffer
337 *
338 * Use this for high-precision structured data like audio samples interpreted
339 * as multi-dimensional vectors. Supports both single and double precision GLM types.
340 */
341template <typename T>
342 requires GlmType<T>
344 const Kakshya::DataAccess& accessor,
345 const std::shared_ptr<VKBuffer>& target,
346 const std::shared_ptr<VKBuffer>& staging = nullptr)
347{
348 if (!accessor.is_structured()) {
349 error<std::invalid_argument>(
352 std::source_location::current(),
353 "Cannot upload structured view from non-structured data");
354 }
355
356 auto structured_view = accessor.view<T>();
357 upload_to_gpu(structured_view.data(), structured_view.size_bytes(), target, staging);
358}
359
360/**
361 * @brief Download GPU buffer to DataAccess-compatible format (precision-preserving)
362 */
363template <typename T>
365 const std::shared_ptr<VKBuffer>& source,
366 Kakshya::DataVariant& target_variant,
367 const std::vector<Kakshya::DataDimension>& dimensions,
368 Kakshya::DataModality modality,
369 const std::shared_ptr<VKBuffer>& staging = nullptr)
370{
371 size_t element_count = source->get_size_bytes() / sizeof(T);
372
373 std::vector<T> temp_buffer(element_count);
374 download_from_gpu(source, temp_buffer, staging);
375
376 target_variant = std::move(temp_buffer);
377
378 return Kakshya::DataAccess(target_variant, dimensions, modality);
379}
380
381/**
382 * @brief Upload AudioBuffer to GPU (always double precision)
383 * @param audio_buffer Source audio buffer (double[])
384 * @param gpu_buffer Target GPU buffer (must support R64Sfloat format)
385 * @param staging Optional staging buffer (auto-created if needed)
386 *
387 * AudioBuffer is always double precision. This function ensures the GPU buffer
388 * is configured for double precision and performs a direct upload with no conversion.
389 *
390 * @throws std::runtime_error if gpu_buffer doesn't support double precision
391 */
393 const std::shared_ptr<AudioBuffer>& audio_buffer,
394 const std::shared_ptr<VKBuffer>& gpu_buffer,
395 const std::shared_ptr<VKBuffer>& staging = nullptr);
396
397/**
398 * @brief Download GPU buffer to AudioBuffer (expects double precision)
399 * @param gpu_buffer Source GPU buffer (should contain double precision data)
400 * @param audio_buffer Target audio buffer (always double[])
401 * @param staging Optional staging buffer (auto-created if needed)
402 *
403 * Downloads GPU data and copies to AudioBuffer. If the GPU buffer contains
404 * float data instead of double, DataAccess will handle the upconversion
405 * (though this is not recommended for audio precision).
406 */
408 const std::shared_ptr<VKBuffer>& gpu_buffer,
409 const std::shared_ptr<AudioBuffer>& audio_buffer,
410 const std::shared_ptr<VKBuffer>& staging = nullptr);
411
412} // namespace MayaFlux::Buffers
#define MF_WARN(comp, ctx,...)
IO::ImageData image
Definition Decoder.cpp:64
size_t b
auto view() const
Get explicit typed view of data.
Type-erased accessor for NDData with semantic view construction.
void upload_host_visible(const std::shared_ptr< VKBuffer > &target, const Kakshya::DataVariant &data)
Upload data to a host-visible buffer.
std::shared_ptr< VKBuffer > create_image_staging_buffer(size_t size)
Allocate a persistent host-visible staging buffer sized for repeated streaming uploads to an image of...
void upload_audio_to_gpu(const std::shared_ptr< AudioBuffer > &audio_buffer, const std::shared_ptr< VKBuffer > &gpu_buffer, const std::shared_ptr< VKBuffer > &staging)
Upload AudioBuffer to GPU (always double precision)
void upload_structured_view(const Kakshya::DataAccess &accessor, const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging=nullptr)
Upload structured data with GLM double-precision types.
void upload_resizing(const void *data, size_t size, const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging, float growth_factor)
Upload size bytes to target, growing both buffers first if needed.
std::shared_ptr< Core::VKImage > resolve_gpu_image(const T &buffer)
Resolve the GPU-resident image from any GpuImageSource buffer.
void upload_device_local(const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging_buffer, const Kakshya::DataVariant &data)
Upload data to a device-local buffer using a staging buffer.
constexpr float k_buffer_growth_factor
std::span< const float > download_and_normalise(const std::shared_ptr< Core::VKImage > &image, std::vector< uint8_t > &raw_staging, std::vector< float > &work, const std::shared_ptr< VKBuffer > &gpu_staging)
Download a VKImage to CPU and return a normalised float span.
void download_from_gpu_async(const std::shared_ptr< VKBuffer > &source, void *data, size_t size, std::shared_ptr< VKBuffer > &staging)
Download from a device-local GPU buffer without stalling the graphics queue.
std::shared_ptr< VKBuffer > create_staging_buffer(size_t size)
Create staging buffer for transfers.
bool is_device_local(const std::shared_ptr< VKBuffer > &buffer)
Check if buffer is device-local (staging needed)
void download_audio_from_gpu(const std::shared_ptr< VKBuffer > &gpu_buffer, const std::shared_ptr< AudioBuffer > &audio_buffer, const std::shared_ptr< VKBuffer > &staging)
Download GPU buffer to AudioBuffer (expects double precision)
void upload_from_view(const Kakshya::DataAccess &accessor, const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging=nullptr)
Upload data from DataAccess view to GPU buffer (precision-preserving)
void download_from_gpu(const std::shared_ptr< VKBuffer > &source, void *data, size_t size, const std::shared_ptr< VKBuffer > &staging)
Download from GPU buffer to raw data (auto-detects host-visible vs device-local)
void download_host_visible(const std::shared_ptr< VKBuffer > &source, const std::shared_ptr< VKBuffer > &target)
Download data from a host-visible buffer.
void ensure_gpu_capacity(const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging, size_t required, float growth_factor)
Grow a GPU buffer (and its paired staging buffer) to fit required bytes.
void download_device_local(const std::shared_ptr< VKBuffer > &source, const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging_buffer)
Download data from a device-local buffer using a staging buffer.
Kakshya::DataAccess download_to_view(const std::shared_ptr< VKBuffer > &source, Kakshya::DataVariant &target_variant, const std::vector< Kakshya::DataDimension > &dimensions, Kakshya::DataModality modality, const std::shared_ptr< VKBuffer > &staging=nullptr)
Download GPU buffer to DataAccess-compatible format (precision-preserving)
void upload_to_gpu(const void *data, size_t size, const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging)
Upload raw data to GPU buffer (auto-detects host-visible vs device-local)
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.
std::variant< std::vector< double >, std::vector< float >, std::vector< uint8_t >, std::vector< uint16_t >, std::vector< uint32_t >, std::vector< std::complex< float > >, std::vector< std::complex< double > >, std::vector< glm::vec2 >, std::vector< glm::vec3 >, std::vector< glm::vec4 >, std::vector< glm::mat4 > > DataVariant
Multi-type data storage for different precision needs.
Definition NDData.hpp:102
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:164
@ AUDIO_MULTICHANNEL
Multi-channel audio.
std::string_view modality_to_string(DataModality modality)
Convert DataModality enum to string representation.
Definition NDData.cpp:111