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