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
7
9
13
15
16namespace MayaFlux::Buffers {
17
19 size_t size_bytes,
20 Usage usage,
21 Kakshya::DataModality modality)
22 : m_size_bytes(size_bytes)
23 , m_usage(usage)
24 , m_modality(modality)
25 , m_processing_chain(std::make_shared<Buffers::BufferProcessingChain>())
26 , m_processing_token(ProcessingToken::GRAPHICS_BACKEND)
27{
29
31 "VKBuffer created (uninitialized): {} bytes, modality: {}",
32 size_bytes, Kakshya::modality_to_string(modality));
33}
34
35VKBuffer::~VKBuffer() = default;
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 } else {
49 "clear() on device-local buffer requires ClearBufferProcessor");
50 }
51}
52
54{
55 if (!is_initialized()) {
57 "Cannot get device address of uninitialized VKBuffer");
58 return 0;
59 }
60
63 "get_device_address() called on buffer without BDA usage flags");
64 return 0;
65 }
66
67 auto* buffer_service = Registry::BackendRegistry::instance()
69
70 if (!buffer_service) {
71 error<std::runtime_error>(
74 std::source_location::current(),
75 "Cannot query buffer device address: BufferService not available");
76 }
77
78 auto shared_this = std::const_pointer_cast<VKBuffer>(
79 std::static_pointer_cast<const VKBuffer>(shared_from_this()));
80
81 return buffer_service->get_buffer_device_address(
82 std::static_pointer_cast<void>(shared_this));
83}
84
85void VKBuffer::set_data(const std::vector<Kakshya::DataVariant>& data)
86{
87 if (!is_initialized()) {
89 "Cannot set_data on uninitialized VKBuffer. Register with BufferManager first.");
90 return;
91 }
92
93 if (data.empty()) {
94 clear();
95 return;
96 }
97
99 Kakshya::DataAccess accessor(
100 const_cast<Kakshya::DataVariant&>(data[0]),
101 {},
102 m_modality);
103
104 auto [ptr, bytes, format_hint] = accessor.gpu_buffer();
105
106 if (bytes > m_size_bytes) {
107 error<std::runtime_error>(
110 std::source_location::current(),
111 "Data size {} exceeds buffer capacity {}",
112 bytes, m_size_bytes);
113 }
114
115 std::memcpy(m_resources.mapped_ptr, ptr, bytes);
117
119 } else {
121 "set_data() on device-local buffer requires BufferUploadProcessor in chain");
122 }
123}
124
125std::vector<Kakshya::DataVariant> VKBuffer::get_data()
126{
127 if (!is_initialized()) {
129 "Cannot get_data from uninitialized VKBuffer");
130 return {};
131 }
132
135
136 std::vector<uint8_t> raw_bytes(m_size_bytes);
137 std::memcpy(raw_bytes.data(), m_resources.mapped_ptr, m_size_bytes);
138 return { raw_bytes };
139 }
140
142 "get_data() on device-local buffer requires BufferDownloadProcessor");
143 return {};
144}
145
146void VKBuffer::resize(size_t new_size, bool preserve_data)
147{
148 if (new_size == m_size_bytes)
149 return;
150
151 if (!is_initialized()) {
152 m_size_bytes = new_size;
154
156 "Cannot resize uninitialized VKBuffer");
157 return;
158 }
159
160 auto buffer_service = Registry::BackendRegistry::instance()
162
163 if (!buffer_service) {
164 error<std::runtime_error>(
167 std::source_location::current(),
168 "Cannot resize buffer: BufferService not available");
169 }
170
171 std::vector<uint8_t> old_data;
172 if (preserve_data && is_host_visible() && m_resources.mapped_ptr) {
173 size_t copy_size = std::min(m_size_bytes, new_size);
174 old_data.resize(copy_size);
175 std::memcpy(old_data.data(), m_resources.mapped_ptr, copy_size);
176
178 "Preserved {} bytes of old buffer data", copy_size);
179 }
180
181 buffer_service->destroy_buffer(shared_from_this());
182
183 m_resources.buffer = vk::Buffer {};
184 m_resources.memory = vk::DeviceMemory {};
185 m_resources.mapped_ptr = nullptr;
186
187 m_size_bytes = new_size;
189
190 buffer_service->initialize_buffer(shared_from_this());
191
192 if (!is_initialized()) {
193 error<std::runtime_error>(
196 std::source_location::current(),
197 "Failed to recreate buffer after resize");
198 }
199
200 if (preserve_data && !old_data.empty() && is_host_visible() && m_resources.mapped_ptr) {
201 std::memcpy(m_resources.mapped_ptr, old_data.data(), old_data.size());
202 mark_dirty_range(0, old_data.size());
203
205 "Restored {} bytes to resized buffer", old_data.size());
206 }
207
208 // clear_pipeline_commands();
209
211 "VKBuffer resize complete: {} bytes", m_size_bytes);
212}
213
219
220vk::BufferUsageFlags VKBuffer::get_usage_flags() const
221{
222 vk::BufferUsageFlags flags = vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst;
223
224 switch (m_usage) {
225 case Usage::STAGING:
226 break;
227 case Usage::DEVICE:
228 case Usage::COMPUTE:
229 flags |= vk::BufferUsageFlagBits::eStorageBuffer;
230 break;
231 case Usage::VERTEX:
232 flags |= vk::BufferUsageFlagBits::eVertexBuffer | vk::BufferUsageFlagBits::eStorageBuffer;
233 break;
234 case Usage::INDEX:
235 flags |= vk::BufferUsageFlagBits::eIndexBuffer;
236 break;
237 case Usage::UNIFORM:
238 flags |= vk::BufferUsageFlagBits::eUniformBuffer;
239 break;
241 flags |= vk::BufferUsageFlagBits::eUniformBuffer
242 | vk::BufferUsageFlagBits::eShaderDeviceAddress;
243 break;
245 flags |= vk::BufferUsageFlagBits::eStorageBuffer
246 | vk::BufferUsageFlagBits::eShaderDeviceAddress;
247 break;
249 flags |= vk::BufferUsageFlagBits::eStorageBuffer;
250 }
251
252 return flags;
253}
254
255vk::MemoryPropertyFlags VKBuffer::get_memory_properties() const
256{
257 if (is_host_visible()) {
258 return vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent;
259 }
260 return vk::MemoryPropertyFlagBits::eDeviceLocal;
261}
262
264{
266 m_default_processor->process(shared_from_this());
267 }
268}
269
270void VKBuffer::set_default_processor(const std::shared_ptr<BufferProcessor>& processor)
271{
273 m_default_processor->on_detach(shared_from_this());
274 }
275 if (processor) {
276 processor->on_attach(shared_from_this());
277 }
278 m_default_processor = processor;
279}
280
281std::shared_ptr<Buffers::BufferProcessor> VKBuffer::get_default_processor() const
282{
283 return m_default_processor;
284}
285
286std::shared_ptr<Buffers::BufferProcessingChain> VKBuffer::get_processing_chain()
287{
288 return m_processing_chain;
289}
290
291void VKBuffer::set_processing_chain(const std::shared_ptr<BufferProcessingChain>& chain, bool force)
292{
293 if (m_processing_chain && !force) {
294 m_processing_chain->merge_chain(chain);
295 return;
296 }
297 m_processing_chain = chain;
298}
299
301{
302 using namespace Kakshya;
303
304 m_dimensions.clear();
305
306 switch (m_modality) {
307 case DataModality::VERTICES_3D: {
308 const uint64_t count = byte_count / sizeof(Kakshya::Vertex);
309 m_dimensions.push_back(DataDimension::vertex_positions(count));
310 m_dimensions.push_back(DataDimension::vertex_colors(count));
311 m_dimensions.push_back(DataDimension::grouped("scalar", count, 1, DataDimension::Role::CUSTOM));
312 m_dimensions.push_back(DataDimension::texture_coords(count));
313 m_dimensions.push_back(DataDimension::vertex_normals(count));
314 m_dimensions.push_back(DataDimension::grouped("tangents", count, 3, DataDimension::Role::CUSTOM));
315 break;
316 }
317 case DataModality::VERTEX_POSITIONS_3D: {
318 uint64_t count = byte_count / sizeof(glm::vec3);
319 m_dimensions.push_back(DataDimension::vertex_positions(count));
320 break;
321 }
322
323 case DataModality::VERTEX_NORMALS_3D: {
324 uint64_t count = byte_count / sizeof(glm::vec3);
325 m_dimensions.push_back(DataDimension::vertex_normals(count));
326 break;
327 }
328
329 case DataModality::TEXTURE_COORDS_2D: {
330 uint64_t count = byte_count / sizeof(glm::vec2);
331 m_dimensions.push_back(DataDimension::texture_coords(count));
332 break;
333 }
334
335 case DataModality::VERTEX_COLORS_RGB: {
336 uint64_t count = byte_count / sizeof(glm::vec3);
337 m_dimensions.push_back(DataDimension::vertex_colors(count, false));
338 break;
339 }
340
341 case DataModality::VERTEX_COLORS_RGBA: {
342 uint64_t count = byte_count / sizeof(glm::vec4);
343 m_dimensions.push_back(DataDimension::vertex_colors(count, true));
344 break;
345 }
346
347 case DataModality::AUDIO_1D: {
348 uint64_t samples = byte_count / sizeof(double);
349 m_dimensions.push_back(DataDimension::time(samples));
350 break;
351 }
352
353 default:
354 m_dimensions.emplace_back("data", byte_count, 1, DataDimension::Role::CUSTOM);
355 break;
356 }
357}
358
359void VKBuffer::apply_render_config(const RenderConfig& config, const ShaderConfig& shader_config)
360{
361 apply_render_config(m_render_processor, config, shader_config);
362}
363
365 std::shared_ptr<RenderProcessor>& render_processor,
366 const RenderConfig& config,
367 const ShaderConfig& shader_config)
368{
369 if (!render_processor)
370 render_processor = std::make_shared<RenderProcessor>(shader_config);
371
372 render_processor->set_fragment_shader(config.fragment_shader);
373
374 render_processor->set_target_window(
375 config.target_window,
376 std::dynamic_pointer_cast<VKBuffer>(shared_from_this()));
377
378 render_processor->set_primitive_topology(config.topology);
379 render_processor->set_polygon_mode(config.polygon_mode);
380 render_processor->set_cull_mode(config.cull_mode);
381
382 if (!config.geometry_shader.empty())
383 render_processor->set_geometry_shader(config.geometry_shader);
384}
385
386void VKBuffer::mark_dirty_range(size_t offset, size_t size)
387{
388 if (is_host_visible()) {
389 m_dirty_ranges.emplace_back(offset, size);
390 }
391}
392
393void VKBuffer::mark_invalid_range(size_t offset, size_t size)
394{
395 if (is_host_visible()) {
396 m_invalid_ranges.emplace_back(offset, size);
397 }
398}
399
400std::vector<std::pair<size_t, size_t>> VKBuffer::get_and_clear_dirty_ranges()
401{
402 return std::exchange(m_dirty_ranges, {});
403}
404
405std::vector<std::pair<size_t, size_t>> VKBuffer::get_and_clear_invalid_ranges()
406{
407 return std::exchange(m_invalid_ranges, {});
408}
409
411{
412 auto computed_layout = layout;
413 computed_layout.compute_stride();
414 m_vertex_layout = computed_layout;
415}
416
417std::shared_ptr<Buffers::VKBuffer> VKBuffer::clone_to(Usage usage)
418{
419 auto buffer = std::make_shared<VKBuffer>(m_size_bytes, usage, m_modality);
420
421 if (auto layout = get_vertex_layout(); layout.has_value()) {
422 buffer->set_vertex_layout(layout.value());
423 }
424
425 buffer->set_processing_chain(get_processing_chain());
426 buffer->set_default_processor(get_default_processor());
427
428 if (is_host_visible()) {
429 if (buffer->is_host_visible()) {
430 auto src_ptr = static_cast<uint8_t*>(m_resources.mapped_ptr);
431 buffer->set_data({ std::vector<uint8_t>(src_ptr, src_ptr + m_size_bytes) });
432 } else {
434 std::dynamic_pointer_cast<VKBuffer>(shared_from_this()),
435 buffer,
436 nullptr);
437 }
438 } else {
439 if (buffer->is_host_visible()) {
441 buffer,
442 std::dynamic_pointer_cast<VKBuffer>(shared_from_this()),
443 get_data()[0]);
444 } else {
446 "Cloning device-local VKBuffer to another device-local VKBuffer requires external data transfer");
447 }
448 }
449
450 return buffer;
451}
452
453std::shared_ptr<Buffers::Buffer> VKBuffer::clone_to(uint8_t dest_desc)
454{
455 auto usage = static_cast<Usage>(dest_desc);
456 return std::dynamic_pointer_cast<Buffers::Buffer>(clone_to(usage));
457}
458
464
470
471void VKBufferProcessor::ensure_initialized(const std::shared_ptr<VKBuffer>& buffer)
472{
473 if (!m_buffer_service) {
476 }
477 if (!m_buffer_service) {
478 error<std::runtime_error>(
481 std::source_location::current(),
482 "VKBufferProcessor requires a valid BufferService");
483 }
484 if (!buffer->is_initialized()) {
486 }
487}
488
489} // 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,...)
size_t count
const uint8_t * ptr
Advanced pipeline manager for multi-stage buffer transformations with backend optimization.
Registry::Service::ComputeService * m_compute_service
Definition VKBuffer.hpp:666
Registry::Service::BufferService * m_buffer_service
Definition VKBuffer.hpp:665
void ensure_initialized(const std::shared_ptr< VKBuffer > &buffer)
Definition VKBuffer.cpp:471
std::vector< std::pair< size_t, size_t > > m_dirty_ranges
Definition VKBuffer.hpp:649
void infer_dimensions_from_data(size_t byte_count)
Infer Kakshya::DataDimension entries from a given byte count.
Definition VKBuffer.cpp:300
void set_vertex_layout(const Kakshya::VertexLayout &layout)
Set vertex layout for this buffer.
Definition VKBuffer.cpp:410
std::vector< std::pair< size_t, size_t > > get_and_clear_invalid_ranges()
Retrieve and clear all invalid ranges.
Definition VKBuffer.cpp:405
void resize(size_t new_size, bool preserve_data=false)
Resize buffer and recreate GPU resources if needed.
Definition VKBuffer.cpp:146
void process_default() override
Run the buffer's default processor (if set and enabled)
Definition VKBuffer.cpp:263
std::vector< Kakshya::DataVariant > get_data()
Read buffer contents as Kakshya DataVariant.
Definition VKBuffer.cpp:125
std::shared_ptr< Buffers::BufferProcessor > get_default_processor() const override
Get the currently attached default processor.
Definition VKBuffer.cpp:281
@ STORAGE_BDA
Storage buffer with device address query support.
@ HOST_STORAGE
Host-visible storage buffer (eStorageBuffer + eHostVisible|eHostCoherent)
@ UNIFORM
Uniform buffer (host-visible)
@ COMPUTE
Storage buffer for compute shaders (device-local)
@ STAGING
Host-visible staging buffer (CPU-writable, eTransferSrc|Dst)
@ 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:632
std::vector< Kakshya::DataDimension > m_dimensions
Definition VKBuffer.hpp:630
std::shared_ptr< Buffers::BufferProcessingChain > m_processing_chain
Definition VKBuffer.hpp:641
std::vector< std::pair< size_t, size_t > > get_and_clear_dirty_ranges()
Retrieve and clear all dirty ranges.
Definition VKBuffer.cpp:400
std::shared_ptr< Buffers::BufferProcessingChain > get_processing_chain() override
Access the buffer's processing chain.
Definition VKBuffer.cpp:286
void set_default_processor(const std::shared_ptr< BufferProcessor > &processor) override
Set the buffer's default processor.
Definition VKBuffer.cpp:270
std::vector< std::pair< size_t, size_t > > m_invalid_ranges
Definition VKBuffer.hpp:650
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:487
void set_processing_chain(const std::shared_ptr< BufferProcessingChain > &chain, bool force=false) override
Replace the buffer's processing chain.
Definition VKBuffer.cpp:291
VKBufferResources m_resources
Definition VKBuffer.hpp:621
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:453
void apply_render_config(const RenderConfig &config, const ShaderConfig &shader_config)
Configure the internal m_render_processor from a RenderConfig.
Definition VKBuffer.cpp:359
std::shared_ptr< Buffers::BufferProcessor > m_default_processor
Definition VKBuffer.hpp:640
void mark_dirty_range(size_t offset, size_t size)
Get device memory handle.
Definition VKBuffer.cpp:386
bool is_initialized() const
Check whether Vulkan handles are present (buffer registered)
Definition VKBuffer.hpp:282
vk::BufferUsageFlags get_usage_flags() const
Get appropriate VkBufferUsageFlags for creation based on Usage.
Definition VKBuffer.cpp:220
std::shared_ptr< RenderProcessor > m_render_processor
Definition VKBuffer.hpp:618
vk::MemoryPropertyFlags get_memory_properties() const
Get appropriate VkMemoryPropertyFlags for allocation based on Usage.
Definition VKBuffer.cpp:255
Kakshya::DataModality m_modality
Definition VKBuffer.hpp:629
void set_modality(Kakshya::DataModality modality)
Update the semantic modality and re-infer dimensions.
Definition VKBuffer.cpp:214
void set_data(const std::vector< Kakshya::DataVariant > &data)
Write data into the buffer.
Definition VKBuffer.cpp:85
uint64_t get_device_address() const
Get the Vulkan device address for this buffer (if applicable)
Definition VKBuffer.cpp:53
bool is_host_visible() const
Whether this VKBuffer should be host-visible.
Definition VKBuffer.hpp:369
~VKBuffer() override
Virtual destructor.
void mark_invalid_range(size_t offset, size_t size)
Mark a range as invalid (needs download)
Definition VKBuffer.cpp:393
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.
Type-neutral vertex carrying the universal 60-byte attribute layout.
std::shared_ptr< Core::Window > target_window
Unified rendering configuration for graphics buffers.
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.