MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VKBuffer.cpp
Go to the documentation of this file.
1#include "VKBuffer.hpp"
2
5
9
11
12namespace MayaFlux::Buffers {
13
15 size_t size_bytes,
16 Usage usage,
17 Kakshya::DataModality modality)
18 : m_size_bytes(size_bytes)
19 , m_usage(usage)
20 , m_modality(modality)
21 , m_processing_chain(std::make_shared<Buffers::BufferProcessingChain>())
22 , m_processing_token(ProcessingToken::GRAPHICS_BACKEND)
23{
25
27 "VKBuffer created (uninitialized): {} bytes, modality: {}",
28 size_bytes, Kakshya::modality_to_string(modality));
29}
30
31VKBuffer::~VKBuffer() = default;
32
34{
35 if (!is_initialized()) {
37 "Cannot clear uninitialized VKBuffer");
38 return;
39 }
40
42 std::memset(m_resources.mapped_ptr, 0, m_size_bytes);
43 } else {
45 "clear() on device-local buffer requires ClearBufferProcessor");
46 }
47}
48
50{
51 if (!is_initialized()) {
53 "Cannot get device address of uninitialized VKBuffer");
54 return 0;
55 }
56
59 "get_device_address() called on buffer without BDA usage flags");
60 return 0;
61 }
62
63 auto* buffer_service = Registry::BackendRegistry::instance()
65
66 if (!buffer_service) {
67 error<std::runtime_error>(
70 std::source_location::current(),
71 "Cannot query buffer device address: BufferService not available");
72 }
73
74 auto shared_this = std::const_pointer_cast<VKBuffer>(
75 std::static_pointer_cast<const VKBuffer>(shared_from_this()));
76
77 return buffer_service->get_buffer_device_address(
78 std::static_pointer_cast<void>(shared_this));
79}
80
81void VKBuffer::set_data(const std::vector<Kakshya::DataVariant>& data)
82{
83 if (!is_initialized()) {
85 "Cannot set_data on uninitialized VKBuffer. Register with BufferManager first.");
86 return;
87 }
88
89 if (data.empty()) {
90 clear();
91 return;
92 }
93
95 Kakshya::DataAccess accessor(
96 const_cast<Kakshya::DataVariant&>(data[0]),
97 {},
99
100 auto [ptr, bytes, format_hint] = accessor.gpu_buffer();
101
102 if (bytes > m_size_bytes) {
103 error<std::runtime_error>(
106 std::source_location::current(),
107 "Data size {} exceeds buffer capacity {}",
108 bytes, m_size_bytes);
109 }
110
111 std::memcpy(m_resources.mapped_ptr, ptr, bytes);
113
115 } else {
117 "set_data() on device-local buffer requires BufferUploadProcessor in chain");
118 }
119}
120
121std::vector<Kakshya::DataVariant> VKBuffer::get_data()
122{
123 if (!is_initialized()) {
125 "Cannot get_data from uninitialized VKBuffer");
126 return {};
127 }
128
131
132 std::vector<uint8_t> raw_bytes(m_size_bytes);
133 std::memcpy(raw_bytes.data(), m_resources.mapped_ptr, m_size_bytes);
134 return { raw_bytes };
135 }
136
138 "get_data() on device-local buffer requires BufferDownloadProcessor");
139 return {};
140}
141
142void VKBuffer::resize(size_t new_size, bool preserve_data)
143{
144 if (new_size == m_size_bytes)
145 return;
146
147 if (!is_initialized()) {
148 m_size_bytes = new_size;
150
152 "Cannot resize uninitialized VKBuffer");
153 return;
154 }
155
156 auto buffer_service = Registry::BackendRegistry::instance()
158
159 if (!buffer_service) {
160 error<std::runtime_error>(
163 std::source_location::current(),
164 "Cannot resize buffer: BufferService not available");
165 }
166
167 std::vector<uint8_t> old_data;
168 if (preserve_data && is_host_visible() && m_resources.mapped_ptr) {
169 size_t copy_size = std::min(m_size_bytes, new_size);
170 old_data.resize(copy_size);
171 std::memcpy(old_data.data(), m_resources.mapped_ptr, copy_size);
172
174 "Preserved {} bytes of old buffer data", copy_size);
175 }
176
177 buffer_service->destroy_buffer(shared_from_this());
178
179 m_resources.buffer = vk::Buffer {};
180 m_resources.memory = vk::DeviceMemory {};
181 m_resources.mapped_ptr = nullptr;
182
183 m_size_bytes = new_size;
185
186 buffer_service->initialize_buffer(shared_from_this());
187
188 if (!is_initialized()) {
189 error<std::runtime_error>(
192 std::source_location::current(),
193 "Failed to recreate buffer after resize");
194 }
195
196 if (preserve_data && !old_data.empty() && is_host_visible() && m_resources.mapped_ptr) {
197 std::memcpy(m_resources.mapped_ptr, old_data.data(), old_data.size());
198 mark_dirty_range(0, old_data.size());
199
201 "Restored {} bytes to resized buffer", old_data.size());
202 }
203
204 // clear_pipeline_commands();
205
207 "VKBuffer resize complete: {} bytes", m_size_bytes);
208}
209
210vk::Format VKBuffer::get_format() const
211{
212 using namespace Kakshya;
213
214 switch (m_modality) {
215 case DataModality::VERTEX_POSITIONS_3D:
216 case DataModality::VERTEX_NORMALS_3D:
217 case DataModality::VERTEX_TANGENTS_3D:
218 case DataModality::VERTEX_COLORS_RGB:
219 return vk::Format::eR32G32B32Sfloat;
220
221 case DataModality::TEXTURE_COORDS_2D:
222 return vk::Format::eR32G32Sfloat;
223
224 case DataModality::VERTEX_COLORS_RGBA:
225 return vk::Format::eR32G32B32A32Sfloat;
226
227 case DataModality::AUDIO_1D:
228 case DataModality::AUDIO_MULTICHANNEL:
229 return vk::Format::eR64Sfloat;
230
231 case DataModality::IMAGE_2D:
232 case DataModality::IMAGE_COLOR:
233 case DataModality::TEXTURE_2D:
234 return vk::Format::eR8G8B8A8Unorm;
235
236 case DataModality::SPECTRAL_2D:
237 return vk::Format::eR32G32Sfloat;
238
239 default:
240 return vk::Format::eUndefined;
241 }
242}
243
249
250vk::BufferUsageFlags VKBuffer::get_usage_flags() const
251{
252 vk::BufferUsageFlags flags = vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst;
253
254 switch (m_usage) {
255 case Usage::STAGING:
256 break;
257 case Usage::DEVICE:
258 case Usage::COMPUTE:
259 flags |= vk::BufferUsageFlagBits::eStorageBuffer;
260 break;
261 case Usage::VERTEX:
262 flags |= vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eStorageBuffer;
263 break;
264 case Usage::INDEX:
265 flags |= vk::BufferUsageFlagBits::eIndexBuffer;
266 break;
267 case Usage::UNIFORM:
268 flags |= vk::BufferUsageFlagBits::eUniformBuffer;
269 break;
271 flags |= vk::BufferUsageFlagBits::eUniformBuffer
272 | vk::BufferUsageFlagBits::eShaderDeviceAddress;
273 break;
275 flags |= vk::BufferUsageFlagBits::eStorageBuffer
276 | vk::BufferUsageFlagBits::eShaderDeviceAddress;
277 break;
278 }
279
280 return flags;
281}
282
283vk::MemoryPropertyFlags VKBuffer::get_memory_properties() const
284{
285 if (is_host_visible()) {
286 return vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent;
287 }
288 return vk::MemoryPropertyFlagBits::eDeviceLocal;
289}
290
292{
294 m_default_processor->process(shared_from_this());
295 }
296}
297
298void VKBuffer::set_default_processor(const std::shared_ptr<BufferProcessor>& processor)
299{
301 m_default_processor->on_detach(shared_from_this());
302 }
303 if (processor) {
304 processor->on_attach(shared_from_this());
305 }
306 m_default_processor = processor;
307}
308
309std::shared_ptr<Buffers::BufferProcessor> VKBuffer::get_default_processor() const
310{
311 return m_default_processor;
312}
313
314std::shared_ptr<Buffers::BufferProcessingChain> VKBuffer::get_processing_chain()
315{
316 return m_processing_chain;
317}
318
319void VKBuffer::set_processing_chain(const std::shared_ptr<BufferProcessingChain>& chain, bool force)
320{
321 if (m_processing_chain && !force) {
322 m_processing_chain->merge_chain(chain);
323 return;
324 }
325 m_processing_chain = chain;
326}
327
329{
330 using namespace Kakshya;
331
332 m_dimensions.clear();
333
334 switch (m_modality) {
335 case DataModality::VERTEX_POSITIONS_3D: {
336 uint64_t count = byte_count / sizeof(glm::vec3);
337 m_dimensions.push_back(DataDimension::vertex_positions(count));
338 break;
339 }
340
341 case DataModality::VERTEX_NORMALS_3D: {
342 uint64_t count = byte_count / sizeof(glm::vec3);
343 m_dimensions.push_back(DataDimension::vertex_normals(count));
344 break;
345 }
346
347 case DataModality::TEXTURE_COORDS_2D: {
348 uint64_t count = byte_count / sizeof(glm::vec2);
349 m_dimensions.push_back(DataDimension::texture_coords(count));
350 break;
351 }
352
353 case DataModality::VERTEX_COLORS_RGB: {
354 uint64_t count = byte_count / sizeof(glm::vec3);
355 m_dimensions.push_back(DataDimension::vertex_colors(count, false));
356 break;
357 }
358
359 case DataModality::VERTEX_COLORS_RGBA: {
360 uint64_t count = byte_count / sizeof(glm::vec4);
361 m_dimensions.push_back(DataDimension::vertex_colors(count, true));
362 break;
363 }
364
365 case DataModality::AUDIO_1D: {
366 uint64_t samples = byte_count / sizeof(double);
367 m_dimensions.push_back(DataDimension::time(samples));
368 break;
369 }
370
371 default:
372 m_dimensions.emplace_back("data", byte_count, 1, DataDimension::Role::CUSTOM);
373 break;
374 }
375}
376
377void VKBuffer::mark_dirty_range(size_t offset, size_t size)
378{
379 if (is_host_visible()) {
380 m_dirty_ranges.emplace_back(offset, size);
381 }
382}
383
384void VKBuffer::mark_invalid_range(size_t offset, size_t size)
385{
386 if (is_host_visible()) {
387 m_invalid_ranges.emplace_back(offset, size);
388 }
389}
390
391std::vector<std::pair<size_t, size_t>> VKBuffer::get_and_clear_dirty_ranges()
392{
393 return std::exchange(m_dirty_ranges, {});
394}
395
396std::vector<std::pair<size_t, size_t>> VKBuffer::get_and_clear_invalid_ranges()
397{
398 return std::exchange(m_invalid_ranges, {});
399}
400
402{
403 auto computed_layout = layout;
404 computed_layout.compute_stride();
405 m_vertex_layout = computed_layout;
406}
407
408std::shared_ptr<Buffers::VKBuffer> VKBuffer::clone_to(Usage usage)
409{
410 auto buffer = std::make_shared<VKBuffer>(m_size_bytes, usage, m_modality);
411
412 if (auto layout = get_vertex_layout(); layout.has_value()) {
413 buffer->set_vertex_layout(layout.value());
414 }
415
416 buffer->set_processing_chain(get_processing_chain());
417 buffer->set_default_processor(get_default_processor());
418
419 if (is_host_visible()) {
420 if (buffer->is_host_visible()) {
421 auto src_ptr = static_cast<uint8_t*>(m_resources.mapped_ptr);
422 buffer->set_data({ std::vector<uint8_t>(src_ptr, src_ptr + m_size_bytes) });
423 } else {
425 std::dynamic_pointer_cast<VKBuffer>(shared_from_this()),
426 buffer,
427 nullptr);
428 }
429 } else {
430 if (buffer->is_host_visible()) {
432 buffer,
433 std::dynamic_pointer_cast<VKBuffer>(shared_from_this()),
434 get_data()[0]);
435 } else {
437 "Cloning device-local VKBuffer to another device-local VKBuffer requires external data transfer");
438 }
439 }
440
441 return buffer;
442}
443
444std::shared_ptr<Buffers::Buffer> VKBuffer::clone_to(uint8_t dest_desc)
445{
446 auto usage = static_cast<Usage>(dest_desc);
447 return std::dynamic_pointer_cast<Buffers::Buffer>(clone_to(usage));
448}
449
455
461
462void VKBufferProcessor::ensure_initialized(const std::shared_ptr<VKBuffer>& buffer)
463{
464 if (!m_buffer_service) {
467 }
468 if (!m_buffer_service) {
469 error<std::runtime_error>(
472 std::source_location::current(),
473 "VKBufferProcessor requires a valid BufferService");
474 }
475 if (!buffer->is_initialized()) {
477 }
478}
479
480} // namespace MayaFlux::Buffers::Vulkan
#define MF_INFO(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
Eigen::Index count
Range size
Advanced pipeline manager for multi-stage buffer transformations with backend optimization.
Registry::Service::ComputeService * m_compute_service
Definition VKBuffer.hpp:652
Registry::Service::BufferService * m_buffer_service
Definition VKBuffer.hpp:651
void ensure_initialized(const std::shared_ptr< VKBuffer > &buffer)
Definition VKBuffer.cpp:462
std::vector< std::pair< size_t, size_t > > m_dirty_ranges
Definition VKBuffer.hpp:635
void infer_dimensions_from_data(size_t byte_count)
Infer Kakshya::DataDimension entries from a given byte count.
Definition VKBuffer.cpp:328
void set_vertex_layout(const Kakshya::VertexLayout &layout)
Set vertex layout for this buffer.
Definition VKBuffer.cpp:401
std::vector< std::pair< size_t, size_t > > get_and_clear_invalid_ranges()
Retrieve and clear all invalid ranges.
Definition VKBuffer.cpp:396
void resize(size_t new_size, bool preserve_data=false)
Resize buffer and recreate GPU resources if needed.
Definition VKBuffer.cpp:142
void process_default() override
Run the buffer's default processor (if set and enabled)
Definition VKBuffer.cpp:291
std::vector< Kakshya::DataVariant > get_data()
Read buffer contents as Kakshya DataVariant.
Definition VKBuffer.cpp:121
std::shared_ptr< Buffers::BufferProcessor > get_default_processor() const override
Get the currently attached default processor.
Definition VKBuffer.cpp:309
@ STORAGE_BDA
Storage buffer with device address query support.
@ UNIFORM
Uniform buffer (host-visible)
@ COMPUTE
Storage buffer for compute shaders.
@ STAGING
Host-visible staging buffer (CPU-writable)
@ UNIFORM_BDA
Uniform buffer with device address query support.
@ DEVICE
Device-local GPU-only buffer.
std::optional< Kakshya::VertexLayout > m_vertex_layout
Definition VKBuffer.hpp:618
std::vector< Kakshya::DataDimension > m_dimensions
Definition VKBuffer.hpp:616
std::shared_ptr< Buffers::BufferProcessingChain > m_processing_chain
Definition VKBuffer.hpp:627
std::vector< std::pair< size_t, size_t > > get_and_clear_dirty_ranges()
Retrieve and clear all dirty ranges.
Definition VKBuffer.cpp:391
std::shared_ptr< Buffers::BufferProcessingChain > get_processing_chain() override
Access the buffer's processing chain.
Definition VKBuffer.cpp:314
void set_default_processor(const std::shared_ptr< BufferProcessor > &processor) override
Set the buffer's default processor.
Definition VKBuffer.cpp:298
std::vector< std::pair< size_t, size_t > > m_invalid_ranges
Definition VKBuffer.hpp:636
void clear() override
Clear buffer contents.
Definition VKBuffer.cpp:33
std::optional< Kakshya::VertexLayout > get_vertex_layout() const
Get vertex layout if set.
Definition VKBuffer.hpp:491
void set_processing_chain(const std::shared_ptr< BufferProcessingChain > &chain, bool force=false) override
Replace the buffer's processing chain.
Definition VKBuffer.cpp:319
VKBufferResources m_resources
Definition VKBuffer.hpp:607
std::shared_ptr< Buffer > clone_to(uint8_t dest_desc) override
Creates a clone of this buffer for a specific channel or usage enum.
Definition VKBuffer.cpp:444
std::shared_ptr< Buffers::BufferProcessor > m_default_processor
Definition VKBuffer.hpp:626
void mark_dirty_range(size_t offset, size_t size)
Get device memory handle.
Definition VKBuffer.cpp:377
bool is_initialized() const
Check whether Vulkan handles are present (buffer registered)
Definition VKBuffer.hpp:287
vk::BufferUsageFlags get_usage_flags() const
Get appropriate VkBufferUsageFlags for creation based on Usage.
Definition VKBuffer.cpp:250
vk::MemoryPropertyFlags get_memory_properties() const
Get appropriate VkMemoryPropertyFlags for allocation based on Usage.
Definition VKBuffer.cpp:283
Kakshya::DataModality m_modality
Definition VKBuffer.hpp:615
void set_modality(Kakshya::DataModality modality)
Update the semantic modality and re-infer dimensions.
Definition VKBuffer.cpp:244
void set_data(const std::vector< Kakshya::DataVariant > &data)
Write data into the buffer.
Definition VKBuffer.cpp:81
uint64_t get_device_address() const
Get the Vulkan device address for this buffer (if applicable)
Definition VKBuffer.cpp:49
vk::Format get_format() const
Convert modality to a recommended VkFormat.
Definition VKBuffer.cpp:210
bool is_host_visible() const
Whether this VKBuffer should be host-visible.
Definition VKBuffer.hpp:374
~VKBuffer() override
Virtual destructor.
void mark_invalid_range(size_t offset, size_t size)
Mark a range as invalid (needs download)
Definition VKBuffer.cpp:384
auto gpu_buffer() const
Get raw buffer info for GPU upload.
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_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.
ProcessingToken
Bitfield enum defining processing characteristics and backend requirements for buffer operations.
@ GRAPHICS_BACKEND
Standard graphics processing backend configuration.
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.
@ BufferManagement
Buffer Management (Buffers::BufferManager, creating buffers)
@ 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
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:81
std::string_view modality_to_string(DataModality modality)
Convert DataModality enum to string representation.
Definition NDData.cpp:83
void compute_stride()
Helper: compute stride from attributes if not explicitly set.
Complete description of vertex data layout in a buffer.
std::function< void(const std::shared_ptr< void > &)> initialize_buffer
Initialize a buffer object.
Backend buffer management service interface.
Backend compute shader and pipeline service interface.