MayaFlux 0.3.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
49void VKBuffer::set_data(const std::vector<Kakshya::DataVariant>& data)
50{
51 if (!is_initialized()) {
53 "Cannot set_data on uninitialized VKBuffer. Register with BufferManager first.");
54 return;
55 }
56
57 if (data.empty()) {
58 clear();
59 return;
60 }
61
63 Kakshya::DataAccess accessor(
64 const_cast<Kakshya::DataVariant&>(data[0]),
65 {},
67
68 auto [ptr, bytes, format_hint] = accessor.gpu_buffer();
69
70 if (bytes > m_size_bytes) {
71 error<std::runtime_error>(
74 std::source_location::current(),
75 "Data size {} exceeds buffer capacity {}",
76 bytes, m_size_bytes);
77 }
78
79 std::memcpy(m_resources.mapped_ptr, ptr, bytes);
81
83 } else {
85 "set_data() on device-local buffer requires BufferUploadProcessor in chain");
86 }
87}
88
89std::vector<Kakshya::DataVariant> VKBuffer::get_data()
90{
91 if (!is_initialized()) {
93 "Cannot get_data from uninitialized VKBuffer");
94 return {};
95 }
96
99
100 std::vector<uint8_t> raw_bytes(m_size_bytes);
101 std::memcpy(raw_bytes.data(), m_resources.mapped_ptr, m_size_bytes);
102 return { raw_bytes };
103 }
104
106 "get_data() on device-local buffer requires BufferDownloadProcessor");
107 return {};
108}
109
110void VKBuffer::resize(size_t new_size, bool preserve_data)
111{
112 if (new_size == m_size_bytes)
113 return;
114
115 if (!is_initialized()) {
116 m_size_bytes = new_size;
118
120 "Cannot resize uninitialized VKBuffer");
121 return;
122 }
123
124 auto buffer_service = Registry::BackendRegistry::instance()
126
127 if (!buffer_service) {
128 error<std::runtime_error>(
131 std::source_location::current(),
132 "Cannot resize buffer: BufferService not available");
133 }
134
135 std::vector<uint8_t> old_data;
136 if (preserve_data && is_host_visible() && m_resources.mapped_ptr) {
137 size_t copy_size = std::min(m_size_bytes, new_size);
138 old_data.resize(copy_size);
139 std::memcpy(old_data.data(), m_resources.mapped_ptr, copy_size);
140
142 "Preserved {} bytes of old buffer data", copy_size);
143 }
144
145 buffer_service->destroy_buffer(shared_from_this());
146
147 m_resources.buffer = vk::Buffer {};
148 m_resources.memory = vk::DeviceMemory {};
149 m_resources.mapped_ptr = nullptr;
150
151 m_size_bytes = new_size;
153
154 buffer_service->initialize_buffer(shared_from_this());
155
156 if (!is_initialized()) {
157 error<std::runtime_error>(
160 std::source_location::current(),
161 "Failed to recreate buffer after resize");
162 }
163
164 if (preserve_data && !old_data.empty() && is_host_visible() && m_resources.mapped_ptr) {
165 std::memcpy(m_resources.mapped_ptr, old_data.data(), old_data.size());
166 mark_dirty_range(0, old_data.size());
167
169 "Restored {} bytes to resized buffer", old_data.size());
170 }
171
172 // clear_pipeline_commands();
173
175 "VKBuffer resize complete: {} bytes", m_size_bytes);
176}
177
178vk::Format VKBuffer::get_format() const
179{
180 using namespace Kakshya;
181
182 switch (m_modality) {
183 case DataModality::VERTEX_POSITIONS_3D:
184 case DataModality::VERTEX_NORMALS_3D:
185 case DataModality::VERTEX_TANGENTS_3D:
186 case DataModality::VERTEX_COLORS_RGB:
187 return vk::Format::eR32G32B32Sfloat;
188
189 case DataModality::TEXTURE_COORDS_2D:
190 return vk::Format::eR32G32Sfloat;
191
192 case DataModality::VERTEX_COLORS_RGBA:
193 return vk::Format::eR32G32B32A32Sfloat;
194
195 case DataModality::AUDIO_1D:
196 case DataModality::AUDIO_MULTICHANNEL:
197 return vk::Format::eR64Sfloat;
198
199 case DataModality::IMAGE_2D:
200 case DataModality::IMAGE_COLOR:
201 case DataModality::TEXTURE_2D:
202 return vk::Format::eR8G8B8A8Unorm;
203
204 case DataModality::SPECTRAL_2D:
205 return vk::Format::eR32G32Sfloat;
206
207 default:
208 return vk::Format::eUndefined;
209 }
210}
211
217
218vk::BufferUsageFlags VKBuffer::get_usage_flags() const
219{
220 vk::BufferUsageFlags flags = vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst;
221
222 switch (m_usage) {
223 case Usage::STAGING:
224 break;
225 case Usage::DEVICE:
226 case Usage::COMPUTE:
227 flags |= vk::BufferUsageFlagBits::eStorageBuffer;
228 break;
229 case Usage::VERTEX:
230 flags |= vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eStorageBuffer;
231 break;
232 case Usage::INDEX:
233 flags |= vk::BufferUsageFlagBits::eIndexBuffer;
234 break;
235 case Usage::UNIFORM:
236 flags |= vk::BufferUsageFlagBits::eUniformBuffer;
237 break;
238 }
239
240 return flags;
241}
242
243vk::MemoryPropertyFlags VKBuffer::get_memory_properties() const
244{
245 if (is_host_visible()) {
246 return vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent;
247 }
248 return vk::MemoryPropertyFlagBits::eDeviceLocal;
249}
250
252{
254 m_default_processor->process(shared_from_this());
255 }
256}
257
258void VKBuffer::set_default_processor(const std::shared_ptr<BufferProcessor>& processor)
259{
261 m_default_processor->on_detach(shared_from_this());
262 }
263 if (processor) {
264 processor->on_attach(shared_from_this());
265 }
266 m_default_processor = processor;
267}
268
269std::shared_ptr<Buffers::BufferProcessor> VKBuffer::get_default_processor() const
270{
271 return m_default_processor;
272}
273
274std::shared_ptr<Buffers::BufferProcessingChain> VKBuffer::get_processing_chain()
275{
276 return m_processing_chain;
277}
278
279void VKBuffer::set_processing_chain(const std::shared_ptr<BufferProcessingChain>& chain, bool force)
280{
281 if (m_processing_chain && !force) {
282 m_processing_chain->merge_chain(chain);
283 return;
284 }
285 m_processing_chain = chain;
286}
287
289{
290 using namespace Kakshya;
291
292 m_dimensions.clear();
293
294 switch (m_modality) {
295 case DataModality::VERTEX_POSITIONS_3D: {
296 uint64_t count = byte_count / sizeof(glm::vec3);
297 m_dimensions.push_back(DataDimension::vertex_positions(count));
298 break;
299 }
300
301 case DataModality::VERTEX_NORMALS_3D: {
302 uint64_t count = byte_count / sizeof(glm::vec3);
303 m_dimensions.push_back(DataDimension::vertex_normals(count));
304 break;
305 }
306
307 case DataModality::TEXTURE_COORDS_2D: {
308 uint64_t count = byte_count / sizeof(glm::vec2);
309 m_dimensions.push_back(DataDimension::texture_coords(count));
310 break;
311 }
312
313 case DataModality::VERTEX_COLORS_RGB: {
314 uint64_t count = byte_count / sizeof(glm::vec3);
315 m_dimensions.push_back(DataDimension::vertex_colors(count, false));
316 break;
317 }
318
319 case DataModality::VERTEX_COLORS_RGBA: {
320 uint64_t count = byte_count / sizeof(glm::vec4);
321 m_dimensions.push_back(DataDimension::vertex_colors(count, true));
322 break;
323 }
324
325 case DataModality::AUDIO_1D: {
326 uint64_t samples = byte_count / sizeof(double);
327 m_dimensions.push_back(DataDimension::time(samples));
328 break;
329 }
330
331 default:
332 m_dimensions.emplace_back("data", byte_count, 1, DataDimension::Role::CUSTOM);
333 break;
334 }
335}
336
337void VKBuffer::mark_dirty_range(size_t offset, size_t size)
338{
339 if (is_host_visible()) {
340 m_dirty_ranges.emplace_back(offset, size);
341 }
342}
343
344void VKBuffer::mark_invalid_range(size_t offset, size_t size)
345{
346 if (is_host_visible()) {
347 m_invalid_ranges.emplace_back(offset, size);
348 }
349}
350
351std::vector<std::pair<size_t, size_t>> VKBuffer::get_and_clear_dirty_ranges()
352{
353 return std::exchange(m_dirty_ranges, {});
354}
355
356std::vector<std::pair<size_t, size_t>> VKBuffer::get_and_clear_invalid_ranges()
357{
358 return std::exchange(m_invalid_ranges, {});
359}
360
362{
363 auto computed_layout = layout;
364 computed_layout.compute_stride();
365 m_vertex_layout = computed_layout;
366}
367
368std::shared_ptr<Buffers::VKBuffer> VKBuffer::clone_to(Usage usage)
369{
370 auto buffer = std::make_shared<VKBuffer>(m_size_bytes, usage, m_modality);
371
372 if (auto layout = get_vertex_layout(); layout.has_value()) {
373 buffer->set_vertex_layout(layout.value());
374 }
375
376 buffer->set_processing_chain(get_processing_chain());
377 buffer->set_default_processor(get_default_processor());
378
379 if (is_host_visible()) {
380 if (buffer->is_host_visible()) {
381 auto src_ptr = static_cast<uint8_t*>(m_resources.mapped_ptr);
382 buffer->set_data({ std::vector<uint8_t>(src_ptr, src_ptr + m_size_bytes) });
383 } else {
385 std::dynamic_pointer_cast<VKBuffer>(shared_from_this()),
386 buffer,
387 nullptr);
388 }
389 } else {
390 if (buffer->is_host_visible()) {
392 buffer,
393 std::dynamic_pointer_cast<VKBuffer>(shared_from_this()),
394 get_data()[0]);
395 } else {
397 "Cloning device-local VKBuffer to another device-local VKBuffer requires external data transfer");
398 }
399 }
400
401 return buffer;
402}
403
404std::shared_ptr<Buffers::Buffer> VKBuffer::clone_to(uint8_t dest_desc)
405{
406 auto usage = static_cast<Usage>(dest_desc);
407 return std::dynamic_pointer_cast<Buffers::Buffer>(clone_to(usage));
408}
409
415
421
422} // 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
Advanced pipeline manager for multi-stage buffer transformations with backend optimization.
Registry::Service::ComputeService * m_compute_service
Definition VKBuffer.hpp:556
Registry::Service::BufferService * m_buffer_service
Definition VKBuffer.hpp:555
std::vector< std::pair< size_t, size_t > > m_dirty_ranges
Definition VKBuffer.hpp:539
void infer_dimensions_from_data(size_t byte_count)
Infer Kakshya::DataDimension entries from a given byte count.
Definition VKBuffer.cpp:288
void set_vertex_layout(const Kakshya::VertexLayout &layout)
Set vertex layout for this buffer.
Definition VKBuffer.cpp:361
std::vector< std::pair< size_t, size_t > > get_and_clear_invalid_ranges()
Retrieve and clear all invalid ranges.
Definition VKBuffer.cpp:356
void resize(size_t new_size, bool preserve_data=false)
Resize buffer and recreate GPU resources if needed.
Definition VKBuffer.cpp:110
void process_default() override
Run the buffer's default processor (if set and enabled)
Definition VKBuffer.cpp:251
std::vector< Kakshya::DataVariant > get_data()
Read buffer contents as Kakshya DataVariant.
Definition VKBuffer.cpp:89
std::shared_ptr< Buffers::BufferProcessor > get_default_processor() const override
Get the currently attached default processor.
Definition VKBuffer.cpp:269
@ UNIFORM
Uniform buffer (host-visible when requested)
@ COMPUTE
Storage buffer for compute shaders.
@ STAGING
Host-visible staging buffer (CPU-writable)
@ DEVICE
Device-local GPU-only buffer.
std::optional< Kakshya::VertexLayout > m_vertex_layout
Definition VKBuffer.hpp:523
std::vector< Kakshya::DataDimension > m_dimensions
Definition VKBuffer.hpp:521
std::shared_ptr< Buffers::BufferProcessingChain > m_processing_chain
Definition VKBuffer.hpp:532
std::vector< std::pair< size_t, size_t > > get_and_clear_dirty_ranges()
Retrieve and clear all dirty ranges.
Definition VKBuffer.cpp:351
std::shared_ptr< Buffers::BufferProcessingChain > get_processing_chain() override
Access the buffer's processing chain.
Definition VKBuffer.cpp:274
void set_default_processor(const std::shared_ptr< BufferProcessor > &processor) override
Set the buffer's default processor.
Definition VKBuffer.cpp:258
std::vector< std::pair< size_t, size_t > > m_invalid_ranges
Definition VKBuffer.hpp:540
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:408
void set_processing_chain(const std::shared_ptr< BufferProcessingChain > &chain, bool force=false) override
Replace the buffer's processing chain.
Definition VKBuffer.cpp:279
VKBufferResources m_resources
Definition VKBuffer.hpp:512
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:404
std::shared_ptr< Buffers::BufferProcessor > m_default_processor
Definition VKBuffer.hpp:531
void mark_dirty_range(size_t offset, size_t size)
Get device memory handle.
Definition VKBuffer.cpp:337
bool is_initialized() const
Check whether Vulkan handles are present (buffer registered)
Definition VKBuffer.hpp:246
vk::BufferUsageFlags get_usage_flags() const
Get appropriate VkBufferUsageFlags for creation based on Usage.
Definition VKBuffer.cpp:218
vk::MemoryPropertyFlags get_memory_properties() const
Get appropriate VkMemoryPropertyFlags for allocation based on Usage.
Definition VKBuffer.cpp:243
Kakshya::DataModality m_modality
Definition VKBuffer.hpp:520
void set_modality(Kakshya::DataModality modality)
Update the semantic modality and re-infer dimensions.
Definition VKBuffer.cpp:212
void set_data(const std::vector< Kakshya::DataVariant > &data)
Write data into the buffer.
Definition VKBuffer.cpp:49
vk::Format get_format() const
Convert modality to a recommended VkFormat.
Definition VKBuffer.cpp:178
bool is_host_visible() const
Whether this VKBuffer should be host-visible.
Definition VKBuffer.hpp:294
~VKBuffer() override
Virtual destructor.
void mark_invalid_range(size_t offset, size_t size)
Mark a range as invalid (needs download)
Definition VKBuffer.cpp:344
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)
@ 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:73
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:78
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.
Backend buffer management service interface.
Backend compute shader and pipeline service interface.