MayaFlux 0.2.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VKBuffer.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "Buffer.hpp"
4
7
9
11struct BufferService;
12struct ComputeService;
13}
14
15namespace MayaFlux::Core {
16class Window;
17}
18
19namespace MayaFlux::Buffers {
20
22 vk::Buffer buffer;
23 vk::DeviceMemory memory;
25};
26
27using RenderPipelineID = uint64_t;
28using CommandBufferID = uint64_t;
29
30/**
31 * @class VKBuffer
32 * @brief Vulkan-backed buffer wrapper used in processing chains
33 *
34 * VKBuffer is a lightweight, high-level representation of a GPU buffer used by
35 * the MayaFlux processing pipeline. It carries semantic metadata (Kakshya
36 * modalities and dimensions), integrates with the Buffer processing chain and
37 * BufferManager, and exposes Vulkan handles once the backend registers the
38 * buffer. Prior to registration the object contains no GPU resources and can
39 * be manipulated cheaply (like AudioBuffer).
40 *
41 * Responsibilities:
42 * - Store buffer size, usage intent and semantic modality
43 * - Provide inferred data dimensions for processors and pipeline inspection
44 * - Hold Vulkan handles (VkBuffer, VkDeviceMemory) assigned by graphics backend
45 * - Provide convenience helpers for common Vulkan creation flags and memory props
46 * - Integrate with BufferProcessor / BufferProcessingChain for default processing
47 *
48 * Note: Actual allocation, mapping and command-based transfers are performed by
49 * the graphics backend / BufferManager. VKBuffer only stores handles and
50 * metadata and provides helpers for processors to operate on it.
51 */
52class MAYAFLUX_API VKBuffer : public Buffer {
53public:
54 enum class Usage : uint8_t {
55 STAGING, ///< Host-visible staging buffer (CPU-writable)
56 DEVICE, ///< Device-local GPU-only buffer
57 COMPUTE, ///< Storage buffer for compute shaders
58 VERTEX, ///< Vertex buffer
59 INDEX, ///< Index buffer
60 UNIFORM ///< Uniform buffer (host-visible when requested)
61 };
62
63 /**
64 * @brief Context shared with BufferProcessors during pipeline execution
65 *
66 * Processors can use this struct to share data (e.g., push constant staging)
67 * and metadata during processing of this buffer in a chain.
68 */
70 std::vector<uint8_t> push_constant_staging;
71
72 std::vector<Portal::Graphics::DescriptorBindingInfo> descriptor_buffer_bindings;
73
74 std::unordered_map<std::string, std::any> metadata;
75 };
76
77 /**
78 * @brief Construct an unregistered VKBuffer
79 *
80 * Creates a VKBuffer object with the requested capacity, usage intent and
81 * semantic modality. No Vulkan resources are created by this constructor —
82 * registration with the BufferManager / backend is required to allocate
83 * VkBuffer and VkDeviceMemory.
84 *
85 * @param size_bytes Buffer capacity in bytes.
86 * @param usage Intended usage pattern (affects Vulkan flags and memory).
87 * @param modality Semantic interpretation of the buffer contents.
88 */
90 size_t size_bytes,
91 Usage usage,
92 Kakshya::DataModality modality);
93
94 VKBuffer() = default;
95
96 /**
97 * @brief Virtual destructor
98 *
99 * VKBuffer does not own Vulkan resources directly; cleanup is handled by
100 * the backend during unregistration. The destructor ensures derived class
101 * cleanup and safe destruction semantics.
102 */
103 ~VKBuffer() override;
104
105 /**
106 * @brief Clear buffer contents
107 *
108 * If the buffer is host-visible and mapped the memory is zeroed. For
109 * device-local buffers the backend must provide a ClearBufferProcessor
110 * that performs the appropriate GPU-side clear.
111 */
112 void clear() override;
113
114 /**
115 * @brief Read buffer contents as Kakshya DataVariant
116 *
117 * For host-visible buffers this returns a single DataVariant containing the
118 * raw bytes. For device-local buffers this warns and returns empty — a
119 * BufferDownloadProcessor should be used to read GPU-only memory.
120 *
121 * @return Vector of DataVariant objects representing buffer contents.
122 */
123 std::vector<Kakshya::DataVariant> get_data();
124
125 /**
126 * @brief Write data into the buffer
127 *
128 * If the buffer is host-visible and mapped the provided data is copied into
129 * the mapped memory. For device-local buffers, a BufferUploadProcessor must
130 * be present in the processing chain to perform the staging/upload.
131 *
132 * @param data Vector of Kakshya::DataVariant containing the payload to copy.
133 */
134 void set_data(const std::vector<Kakshya::DataVariant>& data);
135
136 /**
137 * @brief Resize buffer and recreate GPU resources if needed
138 * @param new_size New size in bytes
139 * @param preserve_data If true, copy existing data to new buffer
140 *
141 * If buffer is already initialized (has GPU resources), this will:
142 * 1. Create new GPU buffer with new size
143 * 2. Optionally copy old data
144 * 3. Destroy old GPU buffer
145 * 4. Update buffer resources
146 *
147 * If buffer is not initialized, just updates logical size.
148 */
149 void resize(size_t new_size, bool preserve_data = false);
150
151 /**
152 * @brief Get current logical size in bytes
153 * @return Buffer size in bytes.
154 */
155 size_t get_size() const { return m_size_bytes; }
156
157 /**
158 * @brief Run the buffer's default processor (if set and enabled)
159 *
160 * Invokes the attached default BufferProcessor. Processors should be
161 * prepared to handle mapped/unmapped memory according to buffer usage.
162 */
163 void process_default() override;
164
165 /**
166 * @brief Set the buffer's default processor
167 *
168 * Attaches a processor that will be invoked by process_default(). The
169 * previous default processor (if any) is detached first.
170 *
171 * @param processor Shared pointer to a BufferProcessor or nullptr to clear.
172 */
173 void set_default_processor(const std::shared_ptr<BufferProcessor>& processor) override;
174
175 /**
176 * @brief Get the currently attached default processor
177 * @return Shared pointer to the default BufferProcessor or nullptr.
178 */
179 std::shared_ptr<Buffers::BufferProcessor> get_default_processor() const override;
180
181 /**
182 * @brief Access the buffer's processing chain
183 * @return Shared pointer to the BufferProcessingChain used for this buffer.
184 */
185 std::shared_ptr<Buffers::BufferProcessingChain> get_processing_chain() override;
186
187 /**
188 * @brief Replace the buffer's processing chain
189 * @param chain New processing chain to assign.
190 * @param force If true, replaces existing chain even if one is set.
191 */
192 void set_processing_chain(const std::shared_ptr<BufferProcessingChain>& chain, bool force = false) override;
193
194 bool has_data_for_cycle() const override { return m_has_data; }
195 bool needs_removal() const override { return m_needs_removal; }
196 void mark_for_processing(bool has_data) override { m_has_data = has_data; }
197 void mark_for_removal() override { m_needs_removal = true; }
198 void enforce_default_processing(bool should_process) override { m_process_default = should_process; }
199 bool needs_default_processing() override { return m_process_default; }
200
201 /**
202 * @brief Try to acquire processing lock for this buffer
203 * @return True if lock acquired, false if already processing.
204 *
205 * Uses an atomic flag to guard concurrent processing attempts.
206 */
207 inline bool try_acquire_processing() override
208 {
209 bool expected = false;
210 return m_is_processing.compare_exchange_strong(expected, true,
211 std::memory_order_acquire, std::memory_order_relaxed);
212 }
213
214 /**
215 * @brief Release previously acquired processing lock
216 */
217 inline void release_processing() override
218 {
219 m_is_processing.store(false, std::memory_order_release);
220 }
221
222 /**
223 * @brief Query whether the buffer is currently being processed
224 * @return True if a processing operation holds the lock.
225 */
226 inline bool is_processing() const override
227 {
228 return m_is_processing.load(std::memory_order_acquire);
229 }
230
231 /** Get VkBuffer handle (VK_NULL_HANDLE if not registered) */
232 vk::Buffer& get_buffer() { return m_resources.buffer; }
233
234 /* Get logical buffer size as VkDeviceSize */
235 vk::DeviceSize get_size_bytes() const { return m_size_bytes; }
236
237 /**
238 * @brief Convert modality to a recommended VkFormat
239 * @return VkFormat corresponding to the buffer's modality, or VK_FORMAT_UNDEFINED.
240 */
241 vk::Format get_format() const;
242
243 /** Check whether Vulkan handles are present (buffer registered) */
244 bool is_initialized() const { return m_resources.buffer != VK_NULL_HANDLE; }
245
246 /**
247 * @brief Setup processors with a processing token
248 * @param token ProcessingToken to assign.
249 *
250 * For VKBuffer this is a no-op as processors get the token.
251 * This is meant for derived classes that need to setup default processors
252 */
254
255 /** Get the buffer's semantic modality */
256 Kakshya::DataModality get_modality() const { return m_modality; }
257
258 /** Get the inferred data dimensions for the buffer contents */
259 const std::vector<Kakshya::DataDimension>& get_dimensions() const { return m_dimensions; }
260
261 /**
262 * @brief Update the semantic modality and re-infer dimensions
263 * @param modality New Kakshya::DataModality to apply.
264 */
265 void set_modality(Kakshya::DataModality modality);
266
267 /** Retrieve the declared usage intent */
268 Usage get_usage() const { return m_usage; }
269
270 /** Set VkBuffer handle after backend allocation */
271 void set_buffer(vk::Buffer buffer) { m_resources.buffer = buffer; }
272
273 /** Set device memory handle after backend allocation */
274 void set_memory(vk::DeviceMemory memory) { m_resources.memory = memory; }
275
276 /** Set mapped host pointer (for host-visible allocations) */
277 void set_mapped_ptr(void* ptr) { m_resources.mapped_ptr = ptr; }
278
279 /** Set all buffer resources at once */
280 inline void set_buffer_resources(const VKBufferResources& resources)
281 {
282 m_resources = resources;
283 }
284
285 /** Get all buffer resources at once */
286 inline const VKBufferResources& get_buffer_resources() { return m_resources; }
287
288 /**
289 * @brief Whether this VKBuffer should be host-visible
290 * @return True for staging or uniform buffers, false for device-local types.
291 */
292 bool is_host_visible() const
293 {
294 return m_usage == Usage::STAGING || m_usage == Usage::UNIFORM;
295 }
296
297 /**
298 * @brief Get appropriate VkBufferUsageFlags for creation based on Usage
299 * @return VkBufferUsageFlags to be used when creating VkBuffer.
300 */
301 vk::BufferUsageFlags get_usage_flags() const;
302
303 /**
304 * @brief Get appropriate VkMemoryPropertyFlags for allocation based on Usage
305 * @return VkMemoryPropertyFlags to request during memory allocation.
306 */
307 vk::MemoryPropertyFlags get_memory_properties() const;
308
309 /** Get mapped host pointer (nullptr if not host-visible or unmapped) */
310 void* get_mapped_ptr() const { return m_resources.mapped_ptr; }
311
312 /** Get device memory handle */
313 void mark_dirty_range(size_t offset, size_t size);
314
315 /** Mark a range as invalid (needs download) */
316 void mark_invalid_range(size_t offset, size_t size);
317
318 /** Retrieve and clear all dirty ranges */
319 std::vector<std::pair<size_t, size_t>> get_and_clear_dirty_ranges();
320
321 /** Retrieve and clear all invalid ranges */
322 std::vector<std::pair<size_t, size_t>> get_and_clear_invalid_ranges();
323
324 /**
325 * @brief Associate this buffer with a window for rendering
326 * @param window Target window for rendering this buffer's content
327 *
328 * When this buffer is processed, its content will be rendered to the associated window.
329 * Currently supports one window per buffer (will be extended to multiple windows).
330 */
331 void set_pipeline_window(RenderPipelineID id, const std::shared_ptr<Core::Window>& window)
332 {
333 m_window_pipelines[id] = window;
334 }
335
336 /**
337 * @brief Get the window associated with this buffer
338 * @return Target window, or nullptr if not set
339 */
340 std::shared_ptr<Core::Window> get_pipeline_window(RenderPipelineID id) const
341 {
342 auto it = m_window_pipelines.find(id);
343 if (it != m_window_pipelines.end()) {
344 return it->second;
345 }
346 return nullptr;
347 }
348
349 /**
350 * @brief Check if this buffer has a rendering pipeline configured
351 */
353 {
354 return !m_window_pipelines.empty();
355 }
356
357 /**
358 * @brief Get all render pipelines associated with this buffer
359 * @return Map of RenderPipelineID to associated windows
360 */
361 std::unordered_map<RenderPipelineID, std::shared_ptr<Core::Window>> get_render_pipelines() const
362 {
363 return m_window_pipelines;
364 }
365
366 /**
367 * @brief Store recorded command buffer for a pipeline
368 */
370 CommandBufferID cmd_id)
371 {
372 m_pipeline_commands[pipeline_id] = cmd_id;
373 }
374
375 /**
376 * @brief Get recorded command buffer for a pipeline
377 */
379 {
380 auto it = m_pipeline_commands.find(pipeline_id);
381 return it != m_pipeline_commands.end() ? it->second : 0;
382 }
383
384 /**
385 * @brief Clear all recorded commands (called after presentation)
386 */
388 {
389 m_pipeline_commands.clear();
390 }
391
392 /**
393 * @brief Set vertex layout for this buffer
394 *
395 * Required before using buffer with graphics rendering.
396 * Describes how to interpret buffer data as vertices.
397 *
398 * @param layout VertexLayout describing vertex structure
399 */
400 void set_vertex_layout(const Kakshya::VertexLayout& layout);
401
402 /**
403 * @brief Get vertex layout if set
404 * @return Optional containing layout, or empty if not set
405 */
406 std::optional<Kakshya::VertexLayout> get_vertex_layout() const { return m_vertex_layout; }
407
408 /**
409 * @brief Check if this buffer has vertex layout configured
410 */
411 bool has_vertex_layout() const { return m_vertex_layout.has_value(); }
412
413 /**
414 * @brief Clear vertex layout
415 */
417 {
418 m_vertex_layout.reset();
419 }
420
421 std::shared_ptr<Buffer> clone_to(uint8_t dest_desc) override;
422
423 /**
424 * @brief Create a clone of this buffer with the same data and properties
425 * @param usage Usage enum for the cloned buffer
426 * @return Shared pointer to the cloned VKBuffer
427 *
428 * The cloned buffer will have the same size, modality, dimensions,
429 * processing chain and default processor as the original. Changes to
430 * one buffer after cloning do not affect the other.
431 */
432 std::shared_ptr<VKBuffer> clone_to(Usage usage);
433
434 /** Set whether this buffer is for internal engine usage */
435 void force_internal_usage(bool internal) override { m_internal_usage = internal; }
436
437 /** Check whether this buffer is for internal engine usage */
438 bool is_internal_only() const override { return m_internal_usage; }
439
440 /** Access the pipeline context for custom metadata (non-const) */
441 PipelineContext& get_pipeline_context() { return m_pipeline_context; }
442
443 /** Access the pipeline context for custom metadata (const) */
444 const PipelineContext& get_pipeline_context() const { return m_pipeline_context; }
445
446private:
448
449 // Buffer parameters
450 size_t m_size_bytes {};
451 Usage m_usage {};
452
453 // Semantic metadata
455 std::vector<Kakshya::DataDimension> m_dimensions;
456
457 std::optional<Kakshya::VertexLayout> m_vertex_layout;
458
459 // Buffer interface state
460 bool m_has_data { true };
461 bool m_needs_removal {};
462 bool m_process_default { true };
463 bool m_internal_usage {};
464 std::atomic<bool> m_is_processing;
465 std::shared_ptr<Buffers::BufferProcessor> m_default_processor;
466 std::shared_ptr<Buffers::BufferProcessingChain> m_processing_chain;
469
470 std::unordered_map<RenderPipelineID, std::shared_ptr<Core::Window>> m_window_pipelines;
471 std::unordered_map<RenderPipelineID, CommandBufferID> m_pipeline_commands;
472
473 std::vector<std::pair<size_t, size_t>> m_dirty_ranges;
474 std::vector<std::pair<size_t, size_t>> m_invalid_ranges;
475
476 /**
477 * @brief Infer Kakshya::DataDimension entries from a given byte count
478 *
479 * Uses the current modality and provided byte count to populate
480 * m_dimensions so processors and UI code can reason about the buffer's layout.
481 *
482 * @param byte_count Number of bytes of data to infer dimensions from.
483 */
484 void infer_dimensions_from_data(size_t byte_count);
485};
486
495
496} // namespace MayaFlux::Buffers
static MayaFlux::Nodes::ProcessingToken token
Definition Timers.cpp:8
Central computational transformation interface for continuous buffer processing.
Backend-agnostic interface for sequential data storage and transformation.
Definition Buffer.hpp:37
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
std::unordered_map< RenderPipelineID, std::shared_ptr< Core::Window > > get_render_pipelines() const
Get all render pipelines associated with this buffer.
Definition VKBuffer.hpp:361
void * get_mapped_ptr() const
Get mapped host pointer (nullptr if not host-visible or unmapped)
Definition VKBuffer.hpp:310
CommandBufferID get_pipeline_command(RenderPipelineID pipeline_id) const
Get recorded command buffer for a pipeline.
Definition VKBuffer.hpp:378
ProcessingToken m_processing_token
Definition VKBuffer.hpp:467
void set_pipeline_command(RenderPipelineID pipeline_id, CommandBufferID cmd_id)
Store recorded command buffer for a pipeline.
Definition VKBuffer.hpp:369
PipelineContext m_pipeline_context
Definition VKBuffer.hpp:468
std::unordered_map< RenderPipelineID, std::shared_ptr< Core::Window > > m_window_pipelines
Definition VKBuffer.hpp:470
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
bool is_processing() const override
Query whether the buffer is currently being processed.
Definition VKBuffer.hpp:226
bool has_render_pipeline() const
Check if this buffer has a rendering pipeline configured.
Definition VKBuffer.hpp:352
bool needs_removal() const override
Checks if the buffer should be removed from processing chains.
Definition VKBuffer.hpp:195
bool has_data_for_cycle() const override
Checks if the buffer has data for the current processing cycle.
Definition VKBuffer.hpp:194
void clear_pipeline_commands()
Clear all recorded commands (called after presentation)
Definition VKBuffer.hpp:387
Usage get_usage() const
Retrieve the declared usage intent.
Definition VKBuffer.hpp:268
Kakshya::DataModality get_modality() const
Get the buffer's semantic modality.
Definition VKBuffer.hpp:256
const PipelineContext & get_pipeline_context() const
Access the pipeline context for custom metadata (const)
Definition VKBuffer.hpp:444
std::vector< std::pair< size_t, size_t > > m_invalid_ranges
Definition VKBuffer.hpp:474
void set_mapped_ptr(void *ptr)
Set mapped host pointer (for host-visible allocations)
Definition VKBuffer.hpp:277
vk::DeviceSize get_size_bytes() const
Definition VKBuffer.hpp:235
std::optional< Kakshya::VertexLayout > get_vertex_layout() const
Get vertex layout if set.
Definition VKBuffer.hpp:406
VKBufferResources m_resources
Definition VKBuffer.hpp:447
PipelineContext & get_pipeline_context()
Access the pipeline context for custom metadata (non-const)
Definition VKBuffer.hpp:441
void clear_vertex_layout()
Clear vertex layout.
Definition VKBuffer.hpp:416
void set_memory(vk::DeviceMemory memory)
Set device memory handle after backend allocation.
Definition VKBuffer.hpp:274
vk::Buffer & get_buffer()
Get VkBuffer handle (VK_NULL_HANDLE if not registered)
Definition VKBuffer.hpp:232
bool needs_default_processing() override
Checks if the buffer should undergo default processing.
Definition VKBuffer.hpp:199
std::shared_ptr< Buffers::BufferProcessor > m_default_processor
Definition VKBuffer.hpp:465
virtual void setup_processors(ProcessingToken token)
Setup processors with a processing token.
Definition VKBuffer.hpp:253
void set_pipeline_window(RenderPipelineID id, const std::shared_ptr< Core::Window > &window)
Associate this buffer with a window for rendering.
Definition VKBuffer.hpp:331
bool is_initialized() const
Check whether Vulkan handles are present (buffer registered)
Definition VKBuffer.hpp:244
void enforce_default_processing(bool should_process) override
Controls whether the buffer should use default processing.
Definition VKBuffer.hpp:198
bool is_internal_only() const override
Check whether this buffer is for internal engine usage.
Definition VKBuffer.hpp:438
void set_buffer(vk::Buffer buffer)
Set VkBuffer handle after backend allocation.
Definition VKBuffer.hpp:271
bool has_vertex_layout() const
Check if this buffer has vertex layout configured.
Definition VKBuffer.hpp:411
const std::vector< Kakshya::DataDimension > & get_dimensions() const
Get the inferred data dimensions for the buffer contents.
Definition VKBuffer.hpp:259
std::atomic< bool > m_is_processing
Definition VKBuffer.hpp:464
const VKBufferResources & get_buffer_resources()
Get all buffer resources at once.
Definition VKBuffer.hpp:286
Kakshya::DataModality m_modality
Definition VKBuffer.hpp:454
void release_processing() override
Release previously acquired processing lock.
Definition VKBuffer.hpp:217
void set_buffer_resources(const VKBufferResources &resources)
Set all buffer resources at once.
Definition VKBuffer.hpp:280
void force_internal_usage(bool internal) override
Set whether this buffer is for internal engine usage.
Definition VKBuffer.hpp:435
size_t get_size() const
Get current logical size in bytes.
Definition VKBuffer.hpp:155
void mark_for_removal() override
Marks the buffer for removal from processing chains.
Definition VKBuffer.hpp:197
std::unordered_map< RenderPipelineID, CommandBufferID > m_pipeline_commands
Definition VKBuffer.hpp:471
bool is_host_visible() const
Whether this VKBuffer should be host-visible.
Definition VKBuffer.hpp:292
void mark_for_processing(bool has_data) override
Marks the buffer's data availability for the current processing cycle.
Definition VKBuffer.hpp:196
std::shared_ptr< Core::Window > get_pipeline_window(RenderPipelineID id) const
Get the window associated with this buffer.
Definition VKBuffer.hpp:340
bool try_acquire_processing() override
Try to acquire processing lock for this buffer.
Definition VKBuffer.hpp:207
Vulkan-backed buffer wrapper used in processing chains.
Definition VKBuffer.hpp:52
ProcessingToken
Bitfield enum defining processing characteristics and backend requirements for buffer operations.
uint64_t RenderPipelineID
Definition VKBuffer.hpp:27
uint64_t CommandBufferID
Definition VKBuffer.hpp:28
DataModality
Data modality types for cross-modal analysis.
Definition NDData.hpp:78
std::vector< Portal::Graphics::DescriptorBindingInfo > descriptor_buffer_bindings
Definition VKBuffer.hpp:72
std::vector< uint8_t > push_constant_staging
Definition VKBuffer.hpp:70
std::unordered_map< std::string, std::any > metadata
Definition VKBuffer.hpp:74
Context shared with BufferProcessors during pipeline execution.
Definition VKBuffer.hpp:69
Complete description of vertex data layout in a buffer.
Backend buffer management service interface.
Backend compute shader and pipeline service interface.