MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
StagingUtils.hpp
Go to the documentation of this file.
1#pragma once
2
7
8namespace MayaFlux::Buffers {
9
10class VKBuffer;
11class AudioBuffer;
12class ComputeMeshBuffer;
13
14inline constexpr float k_buffer_growth_factor = 1.5F;
15
16/**
17 * @brief Opaque handle to an in-flight transfer.
18 */
19using TransferHandle = std::shared_ptr<void>;
20
21/**
22 * @brief Upload data to a host-visible buffer
23 * @param target Target VKBuffer to upload data into
24 * @param data DataVariant containing the data to upload
25 *
26 * This function handles uploading data from a Kakshya::DataVariant into a
27 * host-visible VKBuffer. It maps the buffer memory, copies the data, and
28 * marks the buffer as dirty for synchronization.
29 *
30 * @param dst_offset Byte offset into @p target to write at. The default 0
31 * reproduces the whole-buffer overwrite exactly; a nonzero value
32 * writes a sub-range and leaves the rest of the buffer untouched.
33 */
34MAYAFLUX_API void upload_host_visible(const std::shared_ptr<VKBuffer>& target, const Kakshya::DataVariant& data, size_t dst_offset = 0);
35
36/**
37 * @brief Upload data to a device-local buffer using a staging buffer
38 * @param target Target VKBuffer to upload data into
39 * @param staging_buffer Host-visible staging VKBuffer used for the upload
40 * @param data DataVariant containing the data to upload
41 *
42 * This function handles uploading data from a Kakshya::DataVariant into a
43 * device-local VKBuffer by utilizing a staging buffer. It copies the data
44 * into the staging buffer, flushes it, and then issues a command to copy
45 * from the staging buffer to the target device-local buffer.
46 *
47 * @param dst_offset Byte offset into @p target the staging copy lands at.
48 * The default 0 reproduces the whole-buffer overwrite exactly; a
49 * nonzero value writes a sub-range and leaves the rest untouched.
50 * The staging buffer itself only needs to hold @p data.
51 */
52MAYAFLUX_API void upload_device_local(const std::shared_ptr<VKBuffer>& target, const std::shared_ptr<VKBuffer>& staging_buffer, const Kakshya::DataVariant& data, size_t dst_offset = 0);
53
54/**
55 * @brief Upload host memory into a raw back_buffers slot.
56 * @param slot Destination slot, typically from VKBuffer::get_back_buffer().
57 * @param data Source pointer, at least @p size bytes.
58 * @param size Byte count to copy.
59 * @param staging Persistent staging buffer for device-local slots. Ignored,
60 * and may be null, when slot.mapped_ptr is already valid.
61 *
62 * Writes through slot.mapped_ptr directly when present (host-visible slot,
63 * no transfer). Otherwise copies into @p staging and records a fenced
64 * device-to-device copy into slot.buffer, mirroring download_back_buffer
65 * with the direction reversed.
66 */
67MAYAFLUX_API void upload_back_buffer(
69 const void* data,
70 size_t size,
71 std::shared_ptr<VKBuffer>& staging);
72
73/**
74 * @brief Upload to a back_buffers slot without waiting for completion.
75 * @param slot Destination slot, typically from VKBuffer::get_back_buffer().
76 * @param data Source pointer, at least @p size bytes. Copied into staging
77 * before returning, so the caller may free it immediately.
78 * @param size Byte count to copy.
79 * @param staging Persistent staging buffer, grown if too small.
80 * @return Handle to pass to resolve_transfer, or null for host-visible
81 * slots where the write was a direct memcpy.
82 *
83 * The staging buffer is in use until the returned handle is resolved. Any
84 * further transfer through the same staging buffer must resolve this one
85 * first, or the copy source is overwritten mid-flight.
86 */
89 const void* data,
90 size_t size,
91 std::shared_ptr<VKBuffer>& staging);
92
93/**
94 * @brief Wait for a transfer and release its resources.
95 * @param handle Handle from upload_back_buffer_async. Nulled on return.
96 *
97 * No-op on a null handle. Blocks the calling thread until the fence
98 * signals, which is immediate when a frame or more has elapsed since
99 * submission.
100 */
101MAYAFLUX_API void resolve_transfer(TransferHandle& handle);
102
103/**
104 * @brief Download data from a host-visible buffer
105 * @param source Source VKBuffer to download data from
106 * @param target Target VKBuffer to store the downloaded data
107 *
108 * This function handles downloading data from a host-visible VKBuffer.
109 * It maps the buffer memory, copies the data into a CPU-accessible format,
110 * and updates the associated target buffer.
111 */
112MAYAFLUX_API void download_host_visible(const std::shared_ptr<VKBuffer>& source, const std::shared_ptr<VKBuffer>& target);
113
114/**
115 * @brief Download data from a device-local buffer using a staging buffer
116 * @param source Source VKBuffer to download data from
117 * @param target Target VKBuffer to store the downloaded data
118 * @param staging_buffer Host-visible staging VKBuffer used for the download
119 *
120 * This function handles downloading data from a device-local VKBuffer by
121 * utilizing a staging buffer. It issues a command to copy the data from
122 * the device-local buffer to the staging buffer, invalidates the staging
123 * buffer memory, and then copies the data into a CPU-accessible format
124 * to update the target buffer.
125 */
126MAYAFLUX_API void download_device_local(const std::shared_ptr<VKBuffer>& source, const std::shared_ptr<VKBuffer>& target, const std::shared_ptr<VKBuffer>& staging_buffer);
127
128/**
129 * @brief Download a raw back_buffers slot to host memory.
130 * @param slot Source slot, typically from VKBuffer::get_back_buffer().
131 * @param size Byte count to copy.
132 * @param data Destination pointer, at least @p size bytes.
133 * @param staging Persistent staging buffer for device-local slots. Ignored,
134 * and may be null, when slot.mapped_ptr is already valid.
135 *
136 * Reads slot.mapped_ptr directly when present (host-visible slot, no
137 * transfer). Otherwise records a fenced copy into @p staging exactly as
138 * download_from_gpu_async does, using slot.buffer as the copy source.
139 */
140MAYAFLUX_API void download_back_buffer(
142 void* data,
143 size_t size,
144 std::shared_ptr<VKBuffer>& staging);
145
146/**
147 * @brief Record a slot-to-staging copy without waiting for completion.
148 * @param slot Source slot, typically from VKBuffer::get_back_buffer().
149 * @param size Byte count to copy.
150 * @param staging Staging buffer, already large enough for
151 * @p staging_offset + @p size. Not grown here: reallocating
152 * mid-batch would orphan copies already recorded against the old
153 * allocation. Size it with create_staging_buffer before the batch.
154 * @param staging_offset Byte offset within @p staging the copy lands at,
155 * so several reads share one staging buffer and their transfers
156 * overlap instead of serialising one round trip each.
157 * @return Handle to pass to resolve_back_buffer_read, or null for
158 * host-visible slots where no transfer was recorded.
159 *
160 * Mirrors upload_back_buffer_async with the direction reversed, and with
161 * one asymmetry: an upload's memcpy precedes the fence, a download's
162 * follows it, so the host copy lives in resolve_back_buffer_read rather
163 * than here. download_back_buffer is the two called back to back.
164 *
165 * Note the naming clash with download_from_gpu_async, which is a
166 * different family: that one operates on a whole VKBuffer, waits
167 * internally, and means only that it avoids queue.waitIdle.
168 */
171 size_t size,
172 std::shared_ptr<VKBuffer>& staging,
173 size_t staging_offset = 0);
174
175/**
176 * @brief Wait for a recorded download, then copy it out of staging.
177 * @param handle Handle from download_back_buffer_async. Nulled on return.
178 * A null handle means the slot was host-visible; @p slot is read
179 * directly in that case.
180 * @param slot Source slot, needed only for the host-visible path.
181 * @param data Destination pointer, at least @p size bytes.
182 * @param size Byte count.
183 * @param staging The same staging buffer the copy was recorded against.
184 * @param staging_offset The same offset it was recorded at.
185 *
186 * Issue every download_back_buffer_async in a batch before resolving any
187 * of them; resolving as you go reintroduces the serialisation the split
188 * exists to avoid.
189 */
190MAYAFLUX_API void resolve_back_buffer_read(
191 TransferHandle& handle,
193 void* data,
194 size_t size,
195 std::shared_ptr<VKBuffer>& staging,
196 size_t staging_offset = 0);
197
198/**
199 * @brief Upload raw data to GPU buffer (auto-detects host-visible vs device-local)
200 * @param data Source data pointer
201 * @param size Size in bytes
202 * @param target Target GPU buffer
203 * @param staging Optional staging buffer (created if needed for device-local)
204 *
205 * Convenience wrapper over StagingUtils that:
206 * - Converts raw pointer → DataVariant
207 * - Auto-detects if buffer is host-visible or device-local
208 * - Handles staging buffer creation if needed
209 *
210 * @param dst_offset Byte offset into @p target to write at. The default 0
211 * overwrites from the start; a nonzero value uploads a sub-range,
212 * leaving the rest of @p target as it was.
213 */
214MAYAFLUX_API void upload_to_gpu(
215 const void* data,
216 size_t size,
217 const std::shared_ptr<VKBuffer>& target,
218 const std::shared_ptr<VKBuffer>& staging = nullptr,
219 size_t dst_offset = 0);
220
221/**
222 * @brief Zero a registered GPU buffer in place (auto-detects host-visible vs device-local)
223 * @param target Buffer to clear
224 * @return True if the buffer was cleared
225 *
226 * Mapped buffers are cleared on the host. Unmapped buffers are filled on the
227 * GPU with no staging allocation, and the call blocks until the fill is done.
228 * Handles and device addresses are unchanged. Logs an error and returns false
229 * if the buffer is null or unregistered, lacks transfer destination usage, or
230 * the BufferService is unavailable. Never throws.
231 */
232MAYAFLUX_API bool clear_gpu_buffer(const std::shared_ptr<VKBuffer>& target);
233
234/**
235 * @brief Upload typed data to GPU buffer
236 * @tparam T Data type (float, double, int, etc.)
237 * @param data Source data span
238 * @param target Target GPU buffer
239 * @param staging Optional staging buffer
240 */
241template <typename T>
243 std::span<const T> data,
244 const std::shared_ptr<VKBuffer>& target,
245 const std::shared_ptr<VKBuffer>& staging = nullptr)
246{
247 upload_to_gpu(data.data(), data.size_bytes(), target, staging);
248}
249
250/**
251 * @brief Upload vector to GPU buffer
252 * @tparam T Data type
253 * @param data Source data vector
254 * @param target Target GPU buffer
255 * @param staging Optional staging buffer
256 */
257template <typename T>
259 const std::vector<T>& data,
260 const std::shared_ptr<VKBuffer>& target,
261 const std::shared_ptr<VKBuffer>& staging = nullptr)
262{
263 upload_to_gpu(std::span<const T>(data), target, staging);
264}
265
266/**
267 * @brief Upload @p size bytes to @p target, growing both buffers first if needed.
268 * @param data Source pointer.
269 * @param size Byte count to upload.
270 * @param target Destination GPU buffer.
271 * @param staging Paired staging buffer, or nullptr for host-visible targets.
272 * @param growth_factor Forwarded to ensure_gpu_capacity.
273 *
274 * Combines ensure_gpu_capacity() and upload_to_gpu() into a single call.
275 * The upload is clamped to the post-resize capacity, which is always >= @p size
276 * after ensure_gpu_capacity returns.
277 */
278MAYAFLUX_API void upload_resizing(
279 const void* data,
280 size_t size,
281 const std::shared_ptr<VKBuffer>& target,
282 const std::shared_ptr<VKBuffer>& staging,
283 float growth_factor = 1.5F);
284
285/**
286 * @brief Download from GPU buffer to raw data (auto-detects host-visible vs device-local)
287 * @param source Source GPU buffer
288 * @param data Destination data pointer
289 * @param size Size in bytes
290 * @param staging Optional staging buffer (created if needed for device-local)
291 *
292 * Convenience wrapper over StagingUtils that:
293 * - Auto-detects if buffer is host-visible or device-local
294 * - Handles staging buffer creation if needed
295 * - Copies data to destination pointer
296 */
297MAYAFLUX_API void download_from_gpu(
298 const std::shared_ptr<VKBuffer>& source,
299 void* data,
300 size_t size,
301 const std::shared_ptr<VKBuffer>& staging = nullptr);
302
303/**
304 * @brief Download from GPU buffer to typed span
305 * @tparam T Data type
306 * @param source Source GPU buffer
307 * @param data Destination data span
308 * @param staging Optional staging buffer
309 */
310template <typename T>
312 const std::shared_ptr<VKBuffer>& source,
313 std::span<T> data,
314 const std::shared_ptr<VKBuffer>& staging = nullptr)
315{
316 download_from_gpu(source, data.data(), data.size_bytes(), staging);
317}
318
319/**
320 * @brief Download from GPU buffer to vector
321 * @tparam T Data type
322 * @param source Source GPU buffer
323 * @param data Destination data vector (resized if needed)
324 * @param staging Optional staging buffer
325 */
326template <typename T>
328 const std::shared_ptr<VKBuffer>& source,
329 std::vector<T>& data,
330 const std::shared_ptr<VKBuffer>& staging = nullptr)
331{
332 size_t element_count = source->get_size_bytes() / sizeof(T);
333 data.resize(element_count);
334 download_from_gpu(source, std::span<T>(data), staging);
335}
336
337/**
338 * @brief Download from a device-local GPU buffer without stalling the graphics queue.
339 *
340 * Records a buffer copy into a fenced command buffer, waits on the fence from
341 * the calling thread, then memcpys from the mapped staging buffer into @p data.
342 * Unlike download_from_gpu, this does not call queue.waitIdle, making it safe
343 * to call from any thread that is not the graphics thread itself.
344 *
345 * @p staging is allocated and cached by the caller to avoid per-call Vulkan
346 * object churn. Pass the same staging buffer each frame; it is resized if
347 * @p size exceeds its current capacity.
348 *
349 * @param source Device-local source buffer.
350 * @param data Destination host pointer, at least @p size bytes.
351 * @param size Byte count to copy.
352 * @param staging Persistent host-visible staging buffer. Created via
353 * create_staging_buffer(). Resized in-place if too small.
354 */
355MAYAFLUX_API void download_from_gpu_async(
356 const std::shared_ptr<VKBuffer>& source,
357 void* data,
358 size_t size,
359 std::shared_ptr<VKBuffer>& staging);
360
361/**
362 * @brief Resolve the GPU-resident image from any GpuImageSource buffer.
363 *
364 * Selects get_texture() or get_gpu_texture() at compile time based on which
365 * the concrete buffer type exposes. Returns nullptr if the buffer has not yet
366 * produced a GPU texture.
367 *
368 * @tparam T A type satisfying GpuImageSource.
369 * @param buffer The pixel-bearing buffer to query.
370 * @return GPU-resident VKImage, or nullptr.
371 */
372template <GpuImageSource T>
373[[nodiscard]] std::shared_ptr<Core::VKImage> resolve_gpu_image(const T& buffer)
374{
375 if constexpr (requires(const T& b) {
376 { b.get_texture() } -> std::convertible_to<std::shared_ptr<Core::VKImage>>;
377 }) {
378 return buffer.get_texture();
379 } else {
380 return buffer.get_gpu_texture();
381 }
382}
383
384/**
385 * @brief Download a VKImage to CPU and return a normalised float span.
386 *
387 * Downloads @p image into @p raw_staging via TextureLoom::download_data,
388 * then normalises into @p work via Kakshya::as_normalised_float.
389 *
390 * @p raw_staging is resized to match the image byte footprint when the image
391 * dimensions or format change. @p work is the caller-owned scratch buffer
392 * reused across calls to avoid per-frame allocation.
393 *
394 * Returns an empty span if @p image is null, not initialised, or its format
395 * has no ImageFormat mapping.
396 *
397 * @param image GPU-resident source image.
398 * @param raw_staging Persistent byte buffer for the GPU download. Reuse across calls.
399 * @param work Persistent float buffer for normalisation output. Reuse across calls.
400 * @param gpu_staging Optional persistent staging buffer for device-local images. Reuse across calls.
401 * @return Normalised float span pointing into @p work (or directly into the
402 * variant storage for float-format images).
403 */
404[[nodiscard]] MAYAFLUX_API std::span<const float> download_and_normalise(
405 const std::shared_ptr<Core::VKImage>& image,
406 std::vector<uint8_t>& raw_staging,
407 std::vector<float>& work,
408 const std::shared_ptr<VKBuffer>& gpu_staging);
409
410/**
411 * @brief Create staging buffer for transfers
412 * @param size Size in bytes
413 * @return Host-visible staging buffer ready for transfers
414 */
415MAYAFLUX_API std::shared_ptr<VKBuffer> create_staging_buffer(size_t size);
416
417/**
418 * @brief Allocate a persistent host-visible staging buffer sized for
419 * repeated streaming uploads to an image of @p size bytes.
420 * Call once; pass the result to upload_image_streaming() every frame.
421 * @param size Byte footprint of the target image (use VKImage::get_size_bytes()).
422 * @return Initialised host-visible VKBuffer, or nullptr on failure.
423 */
424[[nodiscard]] MAYAFLUX_API std::shared_ptr<VKBuffer> create_image_staging_buffer(size_t size);
425
426/**
427 * @brief Check if buffer is device-local (staging needed)
428 * @param buffer Buffer to check
429 * @return True if buffer is device-local
430 */
431MAYAFLUX_API bool is_device_local(const std::shared_ptr<VKBuffer>& buffer);
432
433/**
434 * @brief Whether a buffer's memory can be the source of a transfer.
435 * @param buffer Buffer to query; null yields false.
436 * @return True when its usage flags include transfer-source.
437 */
438[[nodiscard]] MAYAFLUX_API bool is_transfer_source(const std::shared_ptr<VKBuffer>& buffer);
439
440/**
441 * @brief View a buffer's primary GPU storage without reading it back.
442 * @param buffer Buffer to view; null yields an empty handle.
443 * @return Non-owning handle carrying the Vulkan buffer, its host mapping when
444 * the memory is host visible, and the buffer's size in bytes. The
445 * caller must keep @p buffer alive while using it.
446 */
448 const std::shared_ptr<VKBuffer>& buffer);
449
450/**
451 * @brief View a raw back_buffers slot without reading it back.
452 * @param slot Slot, typically from VKBuffer::get_buffer_resources().back_buffers
453 * or a named state accessor such as NetworkGeometryBuffer::read_state_slot().
454 * @param size_bytes Byte size of the slot's contents. A slot does not carry its
455 * size, so the owning buffer's own accessor supplies it.
456 * @return Non-owning handle carrying the slot's Vulkan buffer and host mapping,
457 * or an empty handle when the slot's buffer is null.
458 */
460 const VKBufferResources::GenerationSlot& slot,
461 size_t size_bytes);
462
463/**
464 * @brief Express a buffer handle as the slot the back_buffers transfer helpers take.
465 * @param handle Handle to convert.
466 * @return Slot carrying the handle's Vulkan buffer and host mapping. Its memory
467 * handle is left null, which the transfer helpers do not use.
468 */
469[[nodiscard]] MAYAFLUX_API VKBufferResources::GenerationSlot generation_slot(
471
472/**
473 * @brief Resolve the vertex extent of a buffer's primary storage for reading.
474 * @param source Initialized buffer carrying a vertex layout.
475 * @return The buffer's layout when it has a nonzero stride and count and
476 * vertex_count * stride_bytes fits inside the allocation; nullopt otherwise.
477 *
478 * vertex_count is whatever the layout declares. On buffers whose geometry is
479 * generated on the GPU it may describe capacity rather than live vertices.
480 */
481[[nodiscard]] MAYAFLUX_API std::optional<Kakshya::VertexLayout> resolve_vertex_layout(
482 const std::shared_ptr<VKBuffer>& source);
483
484/**
485 * @brief Wrap non-indexed triangle vertices as MeshData.
486 * @param vertex_bytes Interleaved Kakshya::MeshVertex records; the vertex
487 * count must be a nonzero multiple of 3.
488 * @return MeshData with a sequential index array (0..N-1) and the canonical
489 * mesh layout; nullopt, with an error logged, if MeshInsertion rejects it.
490 */
491[[nodiscard]] MAYAFLUX_API std::optional<Kakshya::MeshData> triangle_soup_mesh(
492 std::span<const uint8_t> vertex_bytes);
493
494/**
495 * @brief Download a ComputeMeshBuffer's current live geometry to CPU.
496 *
497 * One-shot and blocking, via download_from_gpu_async: call it off the
498 * graphics thread. Downloads exactly ComputeMeshBuffer::get_live_vertex_count() vertices
499 * rather than the worst-case allocation.
500 *
501 * @param buffer Source buffer. setup_processors() must have run.
502 * @return The geometry as non-indexed triangles, or nullopt if the buffer is
503 * null, has no readable counter, or the live vertex count is zero.
504 */
505[[nodiscard]] MAYAFLUX_API std::optional<Kakshya::MeshData> download_compute_mesh(
506 const std::shared_ptr<ComputeMeshBuffer>& buffer);
507
508/**
509 * @brief Grow a GPU buffer (and its paired staging buffer) to fit @p required bytes.
510 * @param target Device-local or host-visible GPU buffer to resize.
511 * @param staging Paired staging buffer, or nullptr if the target is host-visible.
512 * @param required Bytes needed for the next upload.
513 * @param growth_factor Multiplier applied when a resize is triggered (default 1.5).
514 *
515 * No-op when @p target already has sufficient capacity. When a resize is necessary
516 * both buffers are grown to `required * growth_factor` so subsequent small
517 * increments do not trigger further allocations. Existing GPU data is not preserved
518 * (preserve_data = false) because the caller is about to overwrite it.
519 */
520MAYAFLUX_API void ensure_gpu_capacity(
521 const std::shared_ptr<VKBuffer>& target,
522 const std::shared_ptr<VKBuffer>& staging,
523 size_t required,
524 float growth_factor = k_buffer_growth_factor);
525
526/**
527 * @brief Upload several byte ranges of one source to the same offsets in @p target.
528 * @param base Start of the source; range [s, e) is read from base + s.
529 * @param intervals Half-open byte ranges. Overlapping and adjacent ranges are merged first.
530 * @param target Destination buffer, already large enough for the highest end.
531 * @param staging Staging buffer for device-local targets, grown when too small.
532 * May be null for host-visible targets.
533 *
534 * Host-visible targets are written straight from @p base with one flush per
535 * merged range. Device-local targets pack the ranges into @p staging and
536 * record every copy in one blocking submit, so N ranges cost one submit, not N.
537 * Source and destination share offsets, which is what lets a CPU mirror of the
538 * target be the source.
539 */
540MAYAFLUX_API void upload_intervals(
541 const void* base,
542 std::span<const std::pair<size_t, size_t>> intervals,
543 const std::shared_ptr<VKBuffer>& target,
544 std::shared_ptr<VKBuffer>& staging);
545
546/**
547 * @brief Upload data from DataAccess view to GPU buffer (precision-preserving)
548 * @tparam T View type (double, glm::dvec2, glm::dvec3, glm::vec3, float, etc.)
549 * @param accessor DataAccess instance providing the view
550 * @param target Target GPU buffer
551 * @param staging Optional staging buffer (auto-created if needed)
552 *
553 * Zero-copy when types match, automatic conversion cache when they don't.
554 * For AUDIO modalities, defaults to DOUBLE precision to preserve accuracy.
555 */
556template <typename T>
558 const Kakshya::DataAccess& accessor,
559 const std::shared_ptr<VKBuffer>& target,
560 const std::shared_ptr<VKBuffer>& staging = nullptr)
561{
562 auto view = accessor.view<T>();
563
564 const void* data_ptr = view.data();
565 size_t data_bytes = view.size() * sizeof(T);
566
567 if constexpr (std::is_same_v<T, double>) {
568 const auto modality = target->get_modality();
569 if (modality != Kakshya::DataModality::AUDIO_1D
572 "Uploading double precision to buffer with modality {}. Consider using AUDIO_1D or AUDIO_MULTICHANNEL.",
574 }
575 }
576
577 upload_to_gpu(data_ptr, data_bytes, target, staging);
578}
579
580/**
581 * @brief Upload structured data with GLM double-precision types
582 * @tparam T GLM type (glm::dvec2, glm::dvec3, glm::dvec4 for double precision)
583 * @param accessor DataAccess with structured dimensions
584 * @param target Target GPU buffer
585 * @param staging Optional staging buffer
586 *
587 * Use this for high-precision structured data like audio samples interpreted
588 * as multi-dimensional vectors. Supports both single and double precision GLM types.
589 */
590template <typename T>
591 requires GlmType<T>
593 const Kakshya::DataAccess& accessor,
594 const std::shared_ptr<VKBuffer>& target,
595 const std::shared_ptr<VKBuffer>& staging = nullptr)
596{
597 if (!accessor.is_structured()) {
598 error<std::invalid_argument>(
601 std::source_location::current(),
602 "Cannot upload structured view from non-structured data");
603 }
604
605 auto structured_view = accessor.view<T>();
606 upload_to_gpu(structured_view.data(), structured_view.size_bytes(), target, staging);
607}
608
609/**
610 * @brief Download GPU buffer to DataAccess-compatible format (precision-preserving)
611 */
612template <typename T>
614 const std::shared_ptr<VKBuffer>& source,
615 Kakshya::DataVariant& target_variant,
616 const std::vector<Kakshya::DataDimension>& dimensions,
617 Kakshya::DataModality modality,
618 const std::shared_ptr<VKBuffer>& staging = nullptr)
619{
620 size_t element_count = source->get_size_bytes() / sizeof(T);
621
622 std::vector<T> temp_buffer(element_count);
623 download_from_gpu(source, temp_buffer, staging);
624
625 target_variant = std::move(temp_buffer);
626
627 return Kakshya::DataAccess(target_variant, dimensions, modality);
628}
629
630/**
631 * @brief Upload AudioBuffer to GPU (always double precision)
632 * @param audio_buffer Source audio buffer (double[])
633 * @param gpu_buffer Target GPU buffer (must support R64Sfloat format)
634 * @param staging Optional staging buffer (auto-created if needed)
635 *
636 * AudioBuffer is always double precision. This function ensures the GPU buffer
637 * is configured for double precision and performs a direct upload with no conversion.
638 *
639 * @throws std::runtime_error if gpu_buffer doesn't support double precision
640 */
641MAYAFLUX_API void upload_audio_to_gpu(
642 const std::shared_ptr<AudioBuffer>& audio_buffer,
643 const std::shared_ptr<VKBuffer>& gpu_buffer,
644 const std::shared_ptr<VKBuffer>& staging = nullptr);
645
646/**
647 * @brief Download GPU buffer to AudioBuffer (expects double precision)
648 * @param gpu_buffer Source GPU buffer (should contain double precision data)
649 * @param audio_buffer Target audio buffer (always double[])
650 * @param staging Optional staging buffer (auto-created if needed)
651 *
652 * Downloads GPU data and copies to AudioBuffer. If the GPU buffer contains
653 * float data instead of double, DataAccess will handle the upconversion
654 * (though this is not recommended for audio precision).
655 */
656MAYAFLUX_API void download_audio_from_gpu(
657 const std::shared_ptr<VKBuffer>& gpu_buffer,
658 const std::shared_ptr<AudioBuffer>& audio_buffer,
659 const std::shared_ptr<VKBuffer>& staging = nullptr);
660
661} // namespace MayaFlux::Buffers
#define MF_WARN(comp, ctx,...)
IO::ImageData image
Definition Decoder.cpp:47
bool size
std::shared_ptr< NetworkGeometryBuffer > buffer
size_t b
auto view() const
Get explicit typed view of data.
Type-erased accessor for NDData with semantic view construction.
void resolve_transfer(TransferHandle &handle)
Wait for a transfer and release its resources.
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...
std::shared_ptr< void > TransferHandle
Opaque handle to an in-flight transfer.
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.
Portal::Graphics::GpuBufferHandle gpu_buffer_handle(const std::shared_ptr< VKBuffer > &buffer)
View a buffer's primary GPU storage without reading it back.
constexpr float k_buffer_growth_factor
TransferHandle upload_back_buffer_async(const VKBufferResources::GenerationSlot &slot, const void *data, size_t size, std::shared_ptr< VKBuffer > &staging)
Upload to a back_buffers slot without waiting for completion.
void upload_intervals(const void *base, std::span< const std::pair< size_t, size_t > > intervals, const std::shared_ptr< VKBuffer > &target, std::shared_ptr< VKBuffer > &staging)
Upload several byte ranges of one source to the same offsets in target.
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)
std::optional< Kakshya::MeshData > triangle_soup_mesh(std::span< const uint8_t > vertex_bytes)
Wrap non-indexed triangle vertices as MeshData.
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.
VKBufferResources::GenerationSlot generation_slot(const Portal::Graphics::GpuBufferHandle &handle)
Express a buffer handle as the slot the back_buffers transfer helpers take.
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)
std::optional< Kakshya::MeshData > download_compute_mesh(const std::shared_ptr< ComputeMeshBuffer > &buffer)
Download a ComputeMeshBuffer's current live geometry to CPU.
bool clear_gpu_buffer(const std::shared_ptr< VKBuffer > &target)
Zero a registered GPU buffer in place (auto-detects host-visible vs device-local)
void upload_to_gpu(const void *data, size_t size, const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging, size_t dst_offset)
Upload raw data to GPU buffer (auto-detects host-visible vs device-local)
std::optional< Kakshya::VertexLayout > resolve_vertex_layout(const std::shared_ptr< VKBuffer > &source)
Resolve the vertex extent of a buffer's primary storage for reading.
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 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 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_back_buffer(const VKBufferResources::GenerationSlot &slot, void *data, size_t size, std::shared_ptr< VKBuffer > &staging)
Download a raw back_buffers slot to host memory.
void resolve_back_buffer_read(TransferHandle &handle, const VKBufferResources::GenerationSlot &slot, void *data, size_t size, std::shared_ptr< VKBuffer > &staging, size_t staging_offset)
Wait for a recorded download, then copy it out of staging.
bool is_transfer_source(const std::shared_ptr< VKBuffer > &buffer)
Whether a buffer's memory can be the source of a transfer.
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_host_visible(const std::shared_ptr< VKBuffer > &target, const Kakshya::DataVariant &data, size_t dst_offset)
Upload data to a host-visible buffer.
void upload_device_local(const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging_buffer, const Kakshya::DataVariant &data, size_t dst_offset)
Upload data to a device-local buffer using a staging buffer.
void upload_back_buffer(const VKBufferResources::GenerationSlot &slot, const void *data, size_t size, std::shared_ptr< VKBuffer > &staging)
Upload host memory into a raw back_buffers slot.
TransferHandle download_back_buffer_async(const VKBufferResources::GenerationSlot &slot, size_t size, std::shared_ptr< VKBuffer > &staging, size_t staging_offset)
Record a slot-to-staging copy without waiting for completion.
@ 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:70
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:132
@ AUDIO_MULTICHANNEL
Multi-channel audio.
std::string_view modality_to_string(DataModality modality)
Convert DataModality enum to string representation.
Definition NDData.cpp:102
Non-owning view of a GPU storage buffer produced by a compute path.