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