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->execute_immediate([&](void* ptr) {
113 vk::BufferCopy copy_region;
114 copy_region.srcOffset = 0;
115 copy_region.dstOffset = 0;
116 copy_region.size = bytes;
117
118 vk::CommandBuffer cmd(static_cast<VkCommandBuffer>(ptr));
119
120 cmd.copyBuffer(
121 staging_buffer->get_buffer(),
122 target->get_buffer(),
123 1, &copy_region);
124 });
125}
126
127void download_host_visible(const std::shared_ptr<VKBuffer>& source, const std::shared_ptr<VKBuffer>& target)
128{
129 auto& source_resources = source->get_buffer_resources();
130 void* mapped = source_resources.mapped_ptr;
131 if (!mapped) {
132 error<std::runtime_error>(
135 std::source_location::current(),
136 "Host-visible buffer has no mapped pointer");
137 }
138
139 source->mark_invalid_range(0, source->get_size_bytes());
140
141 auto buffer_service = Registry::BackendRegistry::instance()
143
144 if (!buffer_service) {
145 error<std::runtime_error>(
148 std::source_location::current(),
149 "upload_host_visible requires a valid buffer service");
150 }
151
152 auto invalid_ranges = source->get_and_clear_invalid_ranges();
153 for (auto& [offset, size] : invalid_ranges) {
154 buffer_service->invalidate_range(
155 source_resources.memory,
156 offset,
157 size);
158 }
159
160 std::vector<uint8_t> raw_bytes(source->get_size_bytes());
161 std::memcpy(raw_bytes.data(), mapped, source->get_size_bytes());
162
163 std::dynamic_pointer_cast<VKBuffer>(target)->set_data({ raw_bytes });
164}
165
166void download_device_local(const std::shared_ptr<VKBuffer>& source, const std::shared_ptr<VKBuffer>& target, const std::shared_ptr<VKBuffer>& staging_buffer)
167{
168 auto buffer_service = Registry::BackendRegistry::instance()
170
171 if (!buffer_service) {
172 error<std::runtime_error>(
175 std::source_location::current(),
176 "download_device_local requires a valid buffer service");
177 }
178
179 vk::BufferCopy copy_region;
180 copy_region.srcOffset = 0;
181 copy_region.dstOffset = 0;
182 copy_region.size = source->get_size_bytes();
183
184 buffer_service->execute_immediate([&](void* ptr) {
185 vk::CommandBuffer cmd(static_cast<VkCommandBuffer>(ptr));
186
187 cmd.copyBuffer(
188 source->get_buffer(),
189 staging_buffer->get_buffer(),
190 1, &copy_region);
191 });
192
193 staging_buffer->mark_invalid_range(0, source->get_size_bytes());
194
195 auto& staging_resources = staging_buffer->get_buffer_resources();
196 auto invalid_ranges = staging_buffer->get_and_clear_invalid_ranges();
197 for (auto& [offset, size] : invalid_ranges) {
198 buffer_service->invalidate_range(
199 staging_resources.memory,
200 offset,
201 size);
202 }
203
204 void* staging_mapped = staging_resources.mapped_ptr;
205 if (!staging_mapped) {
206 error<std::runtime_error>(
209 std::source_location::current(),
210 "Staging buffer has no mapped pointer");
211 }
212
213 std::vector<uint8_t> raw_bytes(source->get_size_bytes());
214 std::memcpy(raw_bytes.data(), staging_mapped, source->get_size_bytes());
215
216 std::dynamic_pointer_cast<VKBuffer>(target)->set_data({ raw_bytes });
217}
218
219bool is_device_local(const std::shared_ptr<VKBuffer>& buffer)
220{
221 return buffer && !buffer->is_host_visible();
222}
223
224std::shared_ptr<VKBuffer> create_staging_buffer(size_t size)
225{
226 auto buffer = std::make_shared<VKBuffer>(
227 size,
230
231 auto buffer_service = Registry::BackendRegistry::instance()
233 if (!buffer_service) {
234 error<std::runtime_error>(
237 std::source_location::current(),
238 "create_staging_buffer requires a valid buffer service");
239 }
240
241 buffer_service->initialize_buffer(buffer);
242
243 return buffer;
244}
245
246std::shared_ptr<VKBuffer> create_image_staging_buffer(size_t size)
247{
248 auto buf = std::make_shared<VKBuffer>(
249 size,
252
253 auto buffer_service = Registry::BackendRegistry::instance()
255
256 if (!buffer_service) {
257 error<std::runtime_error>(
260 std::source_location::current(),
261 "create_image_staging_buffer requires a valid buffer service");
262 }
263
264 buffer_service->initialize_buffer(buf);
265
267 "create_image_staging_buffer: allocated {} bytes", size);
268
269 return buf;
270}
271
273 const std::shared_ptr<VKBuffer>& target,
274 const std::shared_ptr<VKBuffer>& staging,
275 size_t required,
276 float growth_factor)
277{
278 if (required <= target->get_size_bytes()) {
279 return;
280 }
281
282 const auto new_size = static_cast<size_t>(static_cast<float>(required) * growth_factor);
283
284 target->resize(new_size, false);
285
286 if (staging) {
287 staging->resize(new_size, false);
288 }
289}
290
292 const void* data,
293 size_t size,
294 const std::shared_ptr<VKBuffer>& target,
295 const std::shared_ptr<VKBuffer>& staging)
296{
297 if (!target) {
298 error<std::invalid_argument>(
301 std::source_location::current(),
302 "upload_to_gpu: target buffer is null");
303 }
304
305 if (size == 0) {
306 return;
307 }
308
309 std::vector<uint8_t> raw_bytes(size);
310 std::memcpy(raw_bytes.data(), data, size);
311 Kakshya::DataVariant data_variant(raw_bytes);
312
313 if (target->is_host_visible()) {
314 upload_host_visible(target, data_variant);
315 } else {
316 std::shared_ptr<VKBuffer> staging_buf = staging;
317
318 if (!staging_buf) {
319 staging_buf = create_staging_buffer(size);
320 }
321
322 upload_device_local(target, staging_buf, data_variant);
323 }
324}
325
327 const void* data,
328 size_t size,
329 const std::shared_ptr<VKBuffer>& target,
330 const std::shared_ptr<VKBuffer>& staging,
331 float growth_factor)
332{
333 ensure_gpu_capacity(target, staging, size, growth_factor);
334 upload_to_gpu(data, size, target, staging);
335}
336
338 const std::shared_ptr<VKBuffer>& source,
339 void* data,
340 size_t size,
341 const std::shared_ptr<VKBuffer>& staging)
342{
343 if (!source) {
344 error<std::invalid_argument>(
347 std::source_location::current(),
348 "download_from_gpu: source buffer is null");
349 }
350
351 if (size == 0) {
352 return;
353 }
354
355 auto temp_target = std::make_shared<VKBuffer>(
357
358 auto buffer_service = Registry::BackendRegistry::instance()
360
361 if (!buffer_service) {
362 error<std::runtime_error>(
365 std::source_location::current(),
366 "download_from_gpu requires a valid buffer service");
367 }
368
369 buffer_service->initialize_buffer(temp_target);
370
371 if (source->is_host_visible()) {
372 download_host_visible(source, temp_target);
373 } else {
374 std::shared_ptr<VKBuffer> staging_buf = staging;
375
376 if (!staging_buf) {
377 staging_buf = create_staging_buffer(size);
378 }
379
380 download_device_local(source, temp_target, staging_buf);
381 }
382
383 auto temp_data = temp_target->get_data();
384
385 if (temp_data.empty()) {
386 error<std::runtime_error>(
389 std::source_location::current(),
390 "download_from_gpu: failed to retrieve data from temporary buffer");
391 }
392
393 if (temp_data.size() > 1) {
395 "download_from_gpu: unexpected multiple data variants in temporary buffer. Only the first will be used.");
396 }
397
398 Kakshya::DataAccess accessor(
399 const_cast<Kakshya::DataVariant&>(temp_data[0]),
400 {},
401 source->get_modality());
402
403 auto [ptr, bytes, format_hint] = accessor.gpu_buffer();
404
405 std::memcpy(data, ptr, std::min(size, bytes));
406}
407
409 const std::shared_ptr<AudioBuffer>& audio_buffer,
410 const std::shared_ptr<VKBuffer>& gpu_buffer,
411 const std::shared_ptr<VKBuffer>& staging)
412{
413 if (gpu_buffer->get_format() != vk::Format::eR64Sfloat && gpu_buffer->get_format() != vk::Format::eUndefined) {
415 "GPU buffer format is {} but audio requires R64Sfloat for double precision. "
416 "Create VKBuffer with DataModality::AUDIO_1D or AUDIO_MULTICHANNEL.",
417 vk::to_string(gpu_buffer->get_format()));
418 error<std::runtime_error>(
421 std::source_location::current(),
422 "GPU buffer format mismatch for audio upload");
423 }
424
425 auto& audio_data = audio_buffer->get_data();
426
427 if (audio_data.empty()) {
429 "AudioBuffer contains no data to upload");
430 return;
431 }
432
433 const void* data_ptr = audio_data.data();
434 size_t data_bytes = audio_data.size() * sizeof(double);
435
436 upload_to_gpu(data_ptr, data_bytes, gpu_buffer, staging);
437
439 "Uploaded {} bytes of double-precision audio to GPU", data_bytes);
440}
441
443 const std::shared_ptr<VKBuffer>& gpu_buffer,
444 const std::shared_ptr<AudioBuffer>& audio_buffer,
445 const std::shared_ptr<VKBuffer>& staging)
446{
447 Kakshya::DataVariant downloaded_data;
448
449 auto dimensions = std::vector<Kakshya::DataDimension> {
451 gpu_buffer->get_size_bytes() / sizeof(double),
452 "samples")
453 };
454 Kakshya::DataAccess accessor = download_to_view<double>(
455 gpu_buffer, downloaded_data, dimensions,
457
458 auto double_view = accessor.view<double>();
459
460 audio_buffer->get_data() = std::vector<double>(double_view.begin(), double_view.end());
461
463 "Downloaded {} samples of double-precision audio from GPU", double_view.size());
464}
465
466} // namespace MayaFlux::Buffers
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
vk::CommandBuffer cmd
Range size
@ STAGING
Host-visible staging buffer (CPU-writable)
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.
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
@ UNKNOWN
Unknown or undefined modality.
@ IMAGE_COLOR
2D RGB/RGBA image
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.