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