MayaFlux 1.0.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
14
16
17namespace MayaFlux::Buffers {
18
20 size_t size_bytes,
22 Kakshya::DataModality modality)
23 : m_size_bytes(size_bytes)
24 , m_usage(usage)
25 , m_modality(modality)
26 , m_processing_chain(std::make_shared<Buffers::BufferProcessingChain>())
27 , m_processing_token(ProcessingToken::GRAPHICS_BACKEND)
28{
30
32 "VKBuffer created (uninitialized): {} bytes, modality: {}",
33 size_bytes, Kakshya::modality_to_string(modality));
34}
35
36VKBuffer::~VKBuffer() = default;
37
39{
40 if (!is_initialized()) {
42 "clear() on VKBuffer with no GPU resources: nothing to clear");
43 return;
44 }
45
46 clear_gpu_buffer(std::static_pointer_cast<VKBuffer>(shared_from_this()));
47}
48
50{
51 if (!is_initialized()) {
53 "Cannot get device address of uninitialized VKBuffer");
54 return 0;
55 }
56
57 if (m_usage != Usage::UNIFORM_BDA && m_usage != Usage::STORAGE_BDA) {
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 auto display_service = Registry::BackendRegistry::instance()
179
180 if (!display_service) {
181 error<std::runtime_error>(
184 std::source_location::current(),
185 "Cannot resize buffer: BufferService not available");
186 }
187
188 display_service->wait_idle();
189 buffer_service->destroy_buffer(shared_from_this());
190
191 m_resources.buffer = vk::Buffer {};
192 m_resources.memory = vk::DeviceMemory {};
193 m_resources.mapped_ptr = nullptr;
194
195 m_size_bytes = new_size;
197
198 buffer_service->initialize_buffer(shared_from_this());
199
200 if (!is_initialized()) {
201 error<std::runtime_error>(
204 std::source_location::current(),
205 "Failed to recreate buffer after resize");
206 }
207
208 if (preserve_data && !old_data.empty() && is_host_visible() && m_resources.mapped_ptr) {
209 std::memcpy(m_resources.mapped_ptr, old_data.data(), old_data.size());
210 mark_dirty_range(0, old_data.size());
211
213 "Restored {} bytes to resized buffer", old_data.size());
214 }
215
216 // clear_pipeline_commands();
217
219 "VKBuffer resize complete: {} bytes", m_size_bytes);
220}
221
227
228vk::BufferUsageFlags VKBuffer::get_usage_flags() const
229{
230 if (m_usage == Usage::INDIRECT) {
231 auto default_flag = vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst;
233 "Indirect buffer usage is not supported in VKBuffer. Force returning default {} flag.", vk::to_string(default_flag));
234 return default_flag;
235 }
236
238}
239
240vk::MemoryPropertyFlags VKBuffer::get_memory_properties() const
241{
242 if (is_host_visible()) {
243 return vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent;
244 }
245 return vk::MemoryPropertyFlagBits::eDeviceLocal;
246}
247
249{
251 m_default_processor->process(shared_from_this());
252 }
253}
254
255void VKBuffer::set_default_processor(const std::shared_ptr<BufferProcessor>& processor)
256{
258 m_default_processor->on_detach(shared_from_this());
259 }
260 if (processor) {
261 processor->on_attach(shared_from_this());
262 }
263 m_default_processor = processor;
264}
265
266std::shared_ptr<Buffers::BufferProcessor> VKBuffer::get_default_processor() const
267{
268 return m_default_processor;
269}
270
271std::shared_ptr<Buffers::BufferProcessingChain> VKBuffer::get_processing_chain()
272{
273 return m_processing_chain;
274}
275
276void VKBuffer::set_processing_chain(const std::shared_ptr<BufferProcessingChain>& chain, bool force)
277{
278 if (m_processing_chain && !force) {
279 m_processing_chain->merge_chain(chain);
280 return;
281 }
283}
284
286{
287 using namespace Kakshya;
288
289 m_dimensions.clear();
290
291 switch (m_modality) {
292 case DataModality::VERTICES_3D: {
293 const uint64_t count = byte_count / sizeof(Kakshya::Vertex);
294 m_dimensions.push_back(DataDimension::vertex_positions(count));
295 m_dimensions.push_back(DataDimension::vertex_colors(count));
296 m_dimensions.push_back(DataDimension::grouped("scalar", count, 1, DataDimension::Role::CUSTOM));
297 m_dimensions.push_back(DataDimension::texture_coords(count));
298 m_dimensions.push_back(DataDimension::vertex_normals(count));
299 m_dimensions.push_back(DataDimension::grouped("tangents", count, 3, DataDimension::Role::CUSTOM));
300 break;
301 }
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::apply_render_config(const RenderConfig& config, const ShaderConfig& shader_config)
345{
346 apply_render_config(m_render_processor, config, shader_config);
347}
348
349std::shared_ptr<RenderProcessor> VKBuffer::get_render_processor(RenderPipelineID id)
350{
351 auto default_render = std::dynamic_pointer_cast<RenderProcessor>(m_default_processor);
352 if (default_render && default_render->get_render_pipeline_id() == id)
353 return default_render;
354
356 return nullptr;
357
358 auto buffer = shared_from_this();
359 for (const auto& processor : m_processing_chain->get_processors(buffer)) {
360 auto render = std::dynamic_pointer_cast<RenderProcessor>(processor);
361 if (render && render->get_render_pipeline_id() == id)
362 return render;
363 }
364
365 for (const auto& processor : {
366 m_processing_chain->get_preprocessor(buffer),
367 m_processing_chain->get_postprocessor(buffer),
368 m_processing_chain->get_final_processor(buffer) }) {
369 auto render = std::dynamic_pointer_cast<RenderProcessor>(processor);
370 if (render && render->get_render_pipeline_id() == id)
371 return render;
372 }
373 return nullptr;
374}
375
376std::vector<std::shared_ptr<RenderProcessor>> VKBuffer::get_additional_render_processors()
377{
378 std::vector<std::shared_ptr<RenderProcessor>> render_processors;
379 auto primary = get_render_processor();
380
381 auto add_processor = [&render_processors, &primary](const std::shared_ptr<BufferProcessor>& processor) {
382 auto render = std::dynamic_pointer_cast<RenderProcessor>(processor);
383 if (!render || render == primary
384 || std::ranges::find(render_processors, render) != render_processors.end()) {
385 return;
386 }
387 render_processors.push_back(std::move(render));
388 };
389
391
392 if (m_processing_chain) {
393 auto buffer = shared_from_this();
394 for (const auto& processor : m_processing_chain->get_processors(buffer))
395 add_processor(processor);
396
397 add_processor(m_processing_chain->get_preprocessor(buffer));
398 add_processor(m_processing_chain->get_postprocessor(buffer));
399 add_processor(m_processing_chain->get_final_processor(buffer));
400 }
401
402 return render_processors;
403}
404
406 std::shared_ptr<RenderProcessor>& render_processor,
407 const RenderConfig& config,
408 const ShaderConfig& shader_config)
409{
410 if (!render_processor)
411 render_processor = std::make_shared<RenderProcessor>(shader_config);
412
413 render_processor->set_fragment_shader(config.fragment_shader);
414
415 render_processor->set_target_window(
416 config.target_window,
417 std::dynamic_pointer_cast<VKBuffer>(shared_from_this()));
418
419 render_processor->set_primitive_topology(config.topology);
420 render_processor->set_triangulate(config.triangulate);
421 render_processor->set_polygon_mode(config.polygon_mode);
422 render_processor->set_cull_mode(config.cull_mode);
423 render_processor->set_scissor(config.scissor);
424
425 if (!config.geometry_shader.empty())
426 render_processor->set_geometry_shader(config.geometry_shader);
427}
428
430{
431 if (is_host_visible()) {
432 m_dirty_ranges.emplace_back(offset, size);
433 }
434}
435
437{
438 if (is_host_visible()) {
439 m_invalid_ranges.emplace_back(offset, size);
440 }
441}
442
443std::vector<std::pair<size_t, size_t>> VKBuffer::get_and_clear_dirty_ranges()
444{
445 return std::exchange(m_dirty_ranges, {});
446}
447
448std::vector<std::pair<size_t, size_t>> VKBuffer::get_and_clear_invalid_ranges()
449{
450 return std::exchange(m_invalid_ranges, {});
451}
452
454{
455 auto computed_layout = layout;
456 computed_layout.compute_stride();
457 m_vertex_layout = computed_layout;
458}
459
460std::shared_ptr<Buffers::VKBuffer> VKBuffer::clone_to(Usage usage)
461{
462 auto buffer = std::make_shared<VKBuffer>(m_size_bytes, usage, m_modality);
463
464 if (auto layout = get_vertex_layout(); layout.has_value()) {
465 buffer->set_vertex_layout(layout.value());
466 }
467
468 buffer->set_processing_chain(get_processing_chain());
469 buffer->set_default_processor(get_default_processor());
470
471 if (is_host_visible()) {
472 if (buffer->is_host_visible()) {
473 auto src_ptr = static_cast<uint8_t*>(m_resources.mapped_ptr);
474 buffer->set_data({ std::vector<uint8_t>(src_ptr, src_ptr + m_size_bytes) });
475 } else {
477 std::dynamic_pointer_cast<VKBuffer>(shared_from_this()),
478 buffer,
479 nullptr);
480 }
481 } else {
482 if (buffer->is_host_visible()) {
484 buffer,
485 std::dynamic_pointer_cast<VKBuffer>(shared_from_this()),
486 get_data()[0]);
487 } else {
489 "Cloning device-local VKBuffer to another device-local VKBuffer requires external data transfer");
490 }
491 }
492
493 return buffer;
494}
495
496std::shared_ptr<Buffers::Buffer> VKBuffer::clone_to(uint8_t dest_desc)
497{
498 auto usage = static_cast<Usage>(dest_desc);
499 return std::dynamic_pointer_cast<Buffers::Buffer>(clone_to(usage));
500}
501
507
513
514void VKBufferProcessor::ensure_initialized(const std::shared_ptr<VKBuffer>& buffer)
515{
516 if (!m_buffer_service) {
519 }
520 if (!m_buffer_service) {
521 error<std::runtime_error>(
524 std::source_location::current(),
525 "VKBufferProcessor requires a valid BufferService");
526 }
527 if (!buffer->is_initialized()) {
529 }
530}
531
532} // namespace MayaFlux::Buffers::Vulkan
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_TRACE(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
uint64_t offset
Definition Composite.cpp:10
bool size
std::shared_ptr< NetworkGeometryBuffer > buffer
BufferProcessingChain * chain
uint16_t usage
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:703
Registry::Service::BufferService * m_buffer_service
Definition VKBuffer.hpp:702
void ensure_initialized(const std::shared_ptr< VKBuffer > &buffer)
Definition VKBuffer.cpp:514
std::vector< std::pair< size_t, size_t > > m_dirty_ranges
Definition VKBuffer.hpp:686
void infer_dimensions_from_data(size_t byte_count)
Infer Kakshya::DataDimension entries from a given byte count.
Definition VKBuffer.cpp:285
void set_vertex_layout(const Kakshya::VertexLayout &layout)
Set vertex layout for this buffer.
Definition VKBuffer.cpp:453
std::vector< std::pair< size_t, size_t > > get_and_clear_invalid_ranges()
Retrieve and clear all invalid ranges.
Definition VKBuffer.cpp:448
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:248
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:266
std::optional< Kakshya::VertexLayout > m_vertex_layout
Definition VKBuffer.hpp:668
std::vector< Kakshya::DataDimension > m_dimensions
Definition VKBuffer.hpp:666
std::shared_ptr< Buffers::BufferProcessingChain > m_processing_chain
Definition VKBuffer.hpp:677
std::vector< std::pair< size_t, size_t > > get_and_clear_dirty_ranges()
Retrieve and clear all dirty ranges.
Definition VKBuffer.cpp:443
std::shared_ptr< Buffers::BufferProcessingChain > get_processing_chain() override
Access the buffer's processing chain.
Definition VKBuffer.cpp:271
void set_default_processor(const std::shared_ptr< BufferProcessor > &processor) override
Set the buffer's default processor.
Definition VKBuffer.cpp:255
virtual std::shared_ptr< RenderProcessor > get_render_processor() const
Get a RenderProcessor suitable for rendering this buffer.
Definition VKBuffer.hpp:605
std::vector< std::pair< size_t, size_t > > m_invalid_ranges
Definition VKBuffer.hpp:687
void clear() override
Clear buffer contents.
Definition VKBuffer.cpp:38
std::optional< Kakshya::VertexLayout > get_vertex_layout() const
Get vertex layout if set.
Definition VKBuffer.hpp:513
void set_processing_chain(const std::shared_ptr< BufferProcessingChain > &chain, bool force=false) override
Replace the buffer's processing chain.
Definition VKBuffer.cpp:276
VKBufferResources m_resources
Definition VKBuffer.hpp:657
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:496
void apply_render_config(const RenderConfig &config, const ShaderConfig &shader_config)
Configure the internal m_render_processor from a RenderConfig.
Definition VKBuffer.cpp:344
std::shared_ptr< Buffers::BufferProcessor > m_default_processor
Definition VKBuffer.hpp:676
void mark_dirty_range(size_t offset, size_t size)
Get device memory handle.
Definition VKBuffer.cpp:429
bool is_initialized() const
Check whether Vulkan handles are present (buffer registered)
Definition VKBuffer.hpp:283
vk::BufferUsageFlags get_usage_flags() const
Get appropriate VkBufferUsageFlags for creation based on Usage.
Definition VKBuffer.cpp:228
std::shared_ptr< RenderProcessor > m_render_processor
Definition VKBuffer.hpp:654
vk::MemoryPropertyFlags get_memory_properties() const
Get appropriate VkMemoryPropertyFlags for allocation based on Usage.
Definition VKBuffer.cpp:240
std::vector< std::shared_ptr< RenderProcessor > > get_additional_render_processors()
Get additional live render processors attached through the chain.
Definition VKBuffer.cpp:376
Kakshya::DataModality m_modality
Definition VKBuffer.hpp:665
void set_modality(Kakshya::DataModality modality)
Update the semantic modality and re-infer dimensions.
Definition VKBuffer.cpp:222
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
bool is_host_visible() const
Whether this VKBuffer should be host-visible.
Definition VKBuffer.hpp:373
~VKBuffer() override
Virtual destructor.
void mark_invalid_range(size_t offset, size_t size)
Mark a range as invalid (needs download)
Definition VKBuffer.cpp:436
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.
ProcessingToken
Bitfield enum defining processing characteristics and backend requirements for buffer operations.
@ GRAPHICS_BACKEND
Standard graphics processing backend configuration.
uint64_t RenderPipelineID
Definition VKBuffer.hpp:51
bool clear_gpu_buffer(const std::shared_ptr< VKBuffer > &target)
Zero a registered GPU buffer in place (auto-detects host-visible vs device-local)
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.
void upload_device_local(const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging_buffer, const Kakshya::DataVariant &data, size_t dst_offset)
Upload data to 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:70
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:132
std::string_view modality_to_string(DataModality modality)
Convert DataModality enum to string representation.
Definition NDData.cpp:102
BufferUsageHint
Semantic usage hint for buffer allocation and memory properties.
vk::BufferUsageFlags to_buffer_usage_flags(BufferUsageHint hint)
Resolve the extra vk::BufferUsageFlags a BufferUsageHint requires, on top of whatever base usage the ...
void add_processor(const std::shared_ptr< Buffers::BufferProcessor > &processor, const std::shared_ptr< Buffers::Buffer > &buffer, Buffers::ProcessingToken token)
Adds a processor to a specific buffer.
Definition Graph.cpp:153
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
std::optional< Kinesis::Scissor > scissor
Clip this buffer's draws to an NDC region.
bool triangulate
Reduce supplied spans to triangles before drawing them.
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.
Backend display and presentation service interface.