MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
StagingUtils.cpp
Go to the documentation of this file.
1#include "StagingUtils.hpp"
2
6
7namespace MayaFlux::Buffers {
8
9void upload_host_visible(const std::shared_ptr<VKBuffer>& target, const Kakshya::DataVariant& data)
10{
11 Kakshya::DataAccess accessor(
12 const_cast<Kakshya::DataVariant&>(data),
13 {},
14 target->get_modality());
15
16 auto [ptr, bytes, format_hint] = accessor.gpu_buffer();
17
18 if (bytes > target->get_size_bytes()) {
19 error<std::runtime_error>(
22 std::source_location::current(),
23 "Upload data size {} exceeds buffer capacity {}",
24 bytes, target->get_size_bytes());
25 }
26
27 auto& target_resources = target->get_buffer_resources();
28 void* mapped = target_resources.mapped_ptr;
29 if (!mapped) {
30 error<std::runtime_error>(
33 std::source_location::current(),
34 "Host-visible buffer has no mapped pointer");
35 }
36
37 std::memcpy(mapped, ptr, bytes);
38
39 target->mark_dirty_range(0, bytes);
40
41 auto buffer_service = Registry::BackendRegistry::instance()
43
44 if (!buffer_service) {
45 error<std::runtime_error>(
48 std::source_location::current(),
49 "upload_host_visible requires a valid buffer service");
50 }
51
52 auto dirty_ranges = target->get_and_clear_dirty_ranges();
53 for (auto& [offset, size] : dirty_ranges) {
54 buffer_service->flush_range(
55 target_resources.memory,
56 offset,
57 size);
58 }
59}
60
61void upload_device_local(const std::shared_ptr<VKBuffer>& target, const std::shared_ptr<VKBuffer>& staging_buffer, const Kakshya::DataVariant& data)
62{
63 Kakshya::DataAccess accessor(
64 const_cast<Kakshya::DataVariant&>(data),
65 {},
66 target->get_modality());
67
68 auto [ptr, bytes, format_hint] = accessor.gpu_buffer();
69
70 if (bytes > target->get_size_bytes()) {
71 error<std::runtime_error>(
74 std::source_location::current(),
75 "Upload data size {} exceeds buffer capacity {}",
76 bytes, target->get_size_bytes());
77 }
78
79 auto& staging_resources = staging_buffer->get_buffer_resources();
80
81 void* staging_mapped = staging_resources.mapped_ptr;
82 if (!staging_mapped) {
83 error<std::runtime_error>(
86 std::source_location::current(),
87 "Staging buffer has no mapped pointer");
88 }
89
90 std::memcpy(staging_mapped, ptr, bytes);
91 staging_buffer->mark_dirty_range(0, bytes);
92
93 auto buffer_service = Registry::BackendRegistry::instance()
95
96 if (!buffer_service) {
97 error<std::runtime_error>(
100 std::source_location::current(),
101 "upload_host_visible requires a valid buffer service");
102 }
103
104 auto dirty_ranges = staging_buffer->get_and_clear_dirty_ranges();
105 for (auto& [offset, size] : dirty_ranges) {
106 buffer_service->flush_range(
107 staging_resources.memory,
108 offset,
109 size);
110 }
111
112 buffer_service->copy_buffer(
113 static_cast<void*>(staging_buffer->get_buffer()),
114 static_cast<void*>(target->get_buffer()),
115 bytes, 0, 0);
116}
117
118void download_host_visible(const std::shared_ptr<VKBuffer>& source, const std::shared_ptr<VKBuffer>& target)
119{
120 auto& source_resources = source->get_buffer_resources();
121 void* mapped = source_resources.mapped_ptr;
122 if (!mapped) {
123 error<std::runtime_error>(
126 std::source_location::current(),
127 "Host-visible buffer has no mapped pointer");
128 }
129
130 source->mark_invalid_range(0, source->get_size_bytes());
131
132 auto buffer_service = Registry::BackendRegistry::instance()
134
135 if (!buffer_service) {
136 error<std::runtime_error>(
139 std::source_location::current(),
140 "upload_host_visible requires a valid buffer service");
141 }
142
143 auto invalid_ranges = source->get_and_clear_invalid_ranges();
144 for (auto& [offset, size] : invalid_ranges) {
145 buffer_service->invalidate_range(
146 source_resources.memory,
147 offset,
148 size);
149 }
150
151 std::vector<uint8_t> raw_bytes(source->get_size_bytes());
152 std::memcpy(raw_bytes.data(), mapped, source->get_size_bytes());
153
154 std::dynamic_pointer_cast<VKBuffer>(target)->set_data({ raw_bytes });
155}
156
157void download_device_local(const std::shared_ptr<VKBuffer>& source, const std::shared_ptr<VKBuffer>& target, const std::shared_ptr<VKBuffer>& staging_buffer)
158{
159 auto buffer_service = Registry::BackendRegistry::instance()
161
162 if (!buffer_service) {
163 error<std::runtime_error>(
166 std::source_location::current(),
167 "download_device_local requires a valid buffer service");
168 }
169
170 buffer_service->copy_buffer(
171 static_cast<void*>(source->get_buffer()),
172 static_cast<void*>(staging_buffer->get_buffer()),
173 source->get_size_bytes(), 0, 0);
174
175 staging_buffer->mark_invalid_range(0, source->get_size_bytes());
176
177 auto& staging_resources = staging_buffer->get_buffer_resources();
178 auto invalid_ranges = staging_buffer->get_and_clear_invalid_ranges();
179 for (auto& [offset, size] : invalid_ranges) {
180 buffer_service->invalidate_range(
181 staging_resources.memory,
182 offset,
183 size);
184 }
185
186 void* staging_mapped = staging_resources.mapped_ptr;
187 if (!staging_mapped) {
188 error<std::runtime_error>(
191 std::source_location::current(),
192 "Staging buffer has no mapped pointer");
193 }
194
195 std::vector<uint8_t> raw_bytes(source->get_size_bytes());
196 std::memcpy(raw_bytes.data(), staging_mapped, source->get_size_bytes());
197
198 std::dynamic_pointer_cast<VKBuffer>(target)->set_data({ raw_bytes });
199}
200
201bool is_device_local(const std::shared_ptr<VKBuffer>& buffer)
202{
203 return buffer && !buffer->is_host_visible();
204}
205
206std::shared_ptr<VKBuffer> create_staging_buffer(size_t size)
207{
208 auto buffer = std::make_shared<VKBuffer>(
209 size,
212
213 auto buffer_service = Registry::BackendRegistry::instance()
215 if (!buffer_service) {
216 error<std::runtime_error>(
219 std::source_location::current(),
220 "create_staging_buffer requires a valid buffer service");
221 }
222
223 buffer_service->initialize_buffer(buffer);
224
225 return buffer;
226}
227
228std::shared_ptr<VKBuffer> create_image_staging_buffer(size_t size)
229{
230 auto buf = std::make_shared<VKBuffer>(
231 size,
234
235 auto buffer_service = Registry::BackendRegistry::instance()
237
238 if (!buffer_service) {
239 error<std::runtime_error>(
242 std::source_location::current(),
243 "create_image_staging_buffer requires a valid buffer service");
244 }
245
246 buffer_service->initialize_buffer(buf);
247
249 "create_image_staging_buffer: allocated {} bytes", size);
250
251 return buf;
252}
253
255 const std::shared_ptr<VKBuffer>& target,
256 const std::shared_ptr<VKBuffer>& staging,
257 size_t required,
258 float growth_factor)
259{
260 if (required <= target->get_size_bytes()) {
261 return;
262 }
263
264 const auto new_size = static_cast<size_t>(static_cast<float>(required) * growth_factor);
265
266 target->resize(new_size, false);
267
268 if (staging) {
269 staging->resize(new_size, false);
270 }
271}
272
274 const void* data,
275 size_t size,
276 const std::shared_ptr<VKBuffer>& target,
277 const std::shared_ptr<VKBuffer>& staging)
278{
279 if (!target) {
280 error<std::invalid_argument>(
283 std::source_location::current(),
284 "upload_to_gpu: target buffer is null");
285 }
286
287 if (size == 0) {
288 return;
289 }
290
291 std::vector<uint8_t> raw_bytes(size);
292 std::memcpy(raw_bytes.data(), data, size);
293 Kakshya::DataVariant data_variant(raw_bytes);
294
295 if (target->is_host_visible()) {
296 upload_host_visible(target, data_variant);
297 } else {
298 std::shared_ptr<VKBuffer> staging_buf = staging;
299
300 if (!staging_buf) {
301 staging_buf = create_staging_buffer(size);
302 }
303
304 upload_device_local(target, staging_buf, data_variant);
305 }
306}
307
309 const void* data,
310 size_t size,
311 const std::shared_ptr<VKBuffer>& target,
312 const std::shared_ptr<VKBuffer>& staging,
313 float growth_factor)
314{
315 ensure_gpu_capacity(target, staging, size, growth_factor);
316 upload_to_gpu(data, size, target, staging);
317}
318
320 const std::shared_ptr<VKBuffer>& source,
321 void* data,
322 size_t size,
323 const std::shared_ptr<VKBuffer>& staging)
324{
325 if (!source) {
326 error<std::invalid_argument>(
329 std::source_location::current(),
330 "download_from_gpu: source buffer is null");
331 }
332
333 if (size == 0) {
334 return;
335 }
336
337 auto temp_target = std::make_shared<VKBuffer>(
339
340 auto buffer_service = Registry::BackendRegistry::instance()
342
343 if (!buffer_service) {
344 error<std::runtime_error>(
347 std::source_location::current(),
348 "download_from_gpu requires a valid buffer service");
349 }
350
351 buffer_service->initialize_buffer(temp_target);
352
353 if (source->is_host_visible()) {
354 download_host_visible(source, temp_target);
355 } else {
356 std::shared_ptr<VKBuffer> staging_buf = staging;
357
358 if (!staging_buf) {
359 staging_buf = create_staging_buffer(size);
360 }
361
362 download_device_local(source, temp_target, staging_buf);
363 }
364
365 auto temp_data = temp_target->get_data();
366
367 if (temp_data.empty()) {
368 error<std::runtime_error>(
371 std::source_location::current(),
372 "download_from_gpu: failed to retrieve data from temporary buffer");
373 }
374
375 if (temp_data.size() > 1) {
377 "download_from_gpu: unexpected multiple data variants in temporary buffer. Only the first will be used.");
378 }
379
380 Kakshya::DataAccess accessor(
381 const_cast<Kakshya::DataVariant&>(temp_data[0]),
382 {},
383 source->get_modality());
384
385 auto [ptr, bytes, format_hint] = accessor.gpu_buffer();
386
387 std::memcpy(data, ptr, std::min(size, bytes));
388}
389
391 const std::shared_ptr<VKBuffer>& source,
392 void* data,
393 size_t size,
394 std::shared_ptr<VKBuffer>& staging)
395{
396 if (!source || !data || size == 0)
397 return;
398
399 auto buffer_service = Registry::BackendRegistry::instance()
401
402 if (!buffer_service
403 || !buffer_service->execute_fenced
404 || !buffer_service->wait_fenced
405 || !buffer_service->release_fenced
406 || !buffer_service->invalidate_range) {
407 error<std::runtime_error>(
410 std::source_location::current(),
411 "download_from_gpu_async: BufferService unavailable");
412 }
413
414 if (!staging || staging->get_size_bytes() < size)
415 staging = create_staging_buffer(size);
416
417 auto handle = buffer_service->copy_buffer_fenced(
418 static_cast<void*>(source->get_buffer()),
419 static_cast<void*>(staging->get_buffer()),
420 size, 0, 0);
421
422 if (!handle) {
423 error<std::runtime_error>(
426 std::source_location::current(),
427 "download_from_gpu_async: execute_fenced returned null");
428 }
429
430 buffer_service->wait_fenced(handle);
431
432 auto& resources = staging->get_buffer_resources();
433 buffer_service->invalidate_range(resources.memory, 0, size);
434
435 void* ptr = staging->get_mapped_ptr();
436 if (!ptr) {
437 buffer_service->release_fenced(handle);
438 error<std::runtime_error>(
441 std::source_location::current(),
442 "download_from_gpu_async: staging buffer has no mapped pointer");
443 }
444
445 std::memcpy(data, ptr, size);
446 buffer_service->release_fenced(handle);
447}
448
450 const std::shared_ptr<AudioBuffer>& audio_buffer,
451 const std::shared_ptr<VKBuffer>& gpu_buffer,
452 const std::shared_ptr<VKBuffer>& staging)
453{
454 const auto modality = gpu_buffer->get_modality();
455 if (modality != Kakshya::DataModality::AUDIO_1D
457 && modality != Kakshya::DataModality::UNKNOWN) {
458
459 error<std::runtime_error>(
462 std::source_location::current(),
463 "GPU buffer modality is {} but audio requires AUDIO_1D or AUDIO_MULTICHANNEL. "
464 "Create VKBuffer with DataModality::AUDIO_1D or AUDIO_MULTICHANNEL.",
466 }
467
468 auto& audio_data = audio_buffer->get_data();
469
470 if (audio_data.empty()) {
472 "AudioBuffer contains no data to upload");
473 return;
474 }
475
476 const void* data_ptr = audio_data.data();
477 size_t data_bytes = audio_data.size() * sizeof(double);
478
479 upload_to_gpu(data_ptr, data_bytes, gpu_buffer, staging);
480
482 "Uploaded {} bytes of double-precision audio to GPU", data_bytes);
483}
484
486 const std::shared_ptr<VKBuffer>& gpu_buffer,
487 const std::shared_ptr<AudioBuffer>& audio_buffer,
488 const std::shared_ptr<VKBuffer>& staging)
489{
490 Kakshya::DataVariant downloaded_data;
491
492 auto dimensions = std::vector<Kakshya::DataDimension> {
494 gpu_buffer->get_size_bytes() / sizeof(double),
495 "samples")
496 };
497 Kakshya::DataAccess accessor = download_to_view<double>(
498 gpu_buffer, downloaded_data, dimensions,
500
501 auto double_view = accessor.view<double>();
502
503 audio_buffer->get_data() = std::vector<double>(double_view.begin(), double_view.end());
504
506 "Downloaded {} samples of double-precision audio from GPU", double_view.size());
507}
508
509} // namespace MayaFlux::Buffers
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
const uint8_t * ptr
@ STAGING
Host-visible staging buffer (CPU-writable, eTransferSrc|Dst)
auto gpu_buffer() const
Get raw buffer info for GPU upload.
auto view() const
Get explicit typed view of data.
Type-erased accessor for NDData with semantic view construction.
Interface * get_service()
Query for a backend service.
static BackendRegistry & instance()
Get the global registry instance.
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_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.
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.
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 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.
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:76
@ AUDIO_MULTICHANNEL
Multi-channel audio.
@ UNKNOWN
Unknown or undefined modality.
@ IMAGE_COLOR
2D RGB/RGBA image
std::string_view modality_to_string(DataModality modality)
Convert DataModality enum to string representation.
Definition NDData.cpp:83
static DataDimension time(uint64_t samples, std::string name="time")
Convenience constructor for a temporal (time) dimension.
Definition NDData.cpp:17
Backend buffer management service interface.