MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
DataWriteProcessor.hpp
Go to the documentation of this file.
1#pragma once
2
8
9namespace MayaFlux::Buffers {
10
11/**
12 * @class DataWriteProcessor
13 * @brief Modality-aware multi-slot write processor for plain VKBuffer.
14 *
15 * Accepts one or more DataVariant values per cycle. Slot 0 is the primary
16 * upload path and is routed by the attached buffer's DataModality:
17 *
18 * - VERTICES_3D and single-attribute vertex modalities (VERTEX_POSITIONS_3D,
19 * VERTEX_NORMALS_3D, VERTEX_TANGENTS_3D, VERTEX_COLORS_RGB,
20 * VERTEX_COLORS_RGBA, TEXTURE_COORDS_2D): all slots are passed as an
21 * ordered channel span to as_*_vertex_access(). Full 60-byte vertices
22 * are always assembled; partial channel sets are completed from
23 * VertexAccessConfig defaults.
24 *
25 * - Texture modalities (IMAGE_2D, IMAGE_COLOR, TEXTURE_2D):
26 * routed through Kakshya::as_texture_access(). GpuDataFormat from the
27 * result is available via last_texture_format() for pipeline configuration.
28 *
29 * - All other modalities: raw bytes via DataAccess::gpu_buffer().
30 * Zero-copy when the variant type matches the target layout.
31 *
32 * Slot 1..N are uploaded raw via DataAccess::gpu_buffer() regardless of
33 * modality. These are intended for secondary data (e.g. a texture payload
34 * alongside vertex data) where the caller has configured the corresponding
35 * descriptor bindings on the RenderProcessor.
36 *
37 * Modality is read from the attached VKBuffer in on_attach(). The processor
38 * warns if the supplied DataVariant is incompatible with that modality.
39 *
40 * Dirty gating: upload occurs only when set_data() has been called since
41 * the last cycle. No stale re-upload.
42 *
43 * Thread safety: set_data() and the graphics thread may run concurrently.
44 * Lock-free double-buffer swap via atomic_flag ensures the graphics thread
45 * never blocks on the supplier thread.
46 *
47 * Staging: a persistent host-visible staging buffer is allocated on
48 * on_attach() when the target is device-local, and reused every cycle.
49 *
50 * The write_* requests reach targets beyond the primary storage: another
51 * VKBuffer, a named field, a raw back_buffers slot, mesh geometry, and
52 * texture pixels. Each is a one-shot request applied at the start of the next
53 * graphics cycle, ahead of the set_* upload, in the order it was made. Each
54 * writes to the authority of its target, never to a GPU copy that its owner
55 * regenerates: meshes go to the owning MeshData or node, retained pixels to the
56 * TextureBuffer, and the rest to the GPU storage their owners read from. Attach
57 * the processor before the producers that consume the written data. Requests
58 * come from one supplier thread, as with set_data().
59 *
60 * Intended for use with plain VKBuffer where no specialised child class
61 * exists for the data being contributed. The caller is responsible for
62 * configuring a matching RenderProcessor via VKBuffer::setup_rendering().
63 */
64class MAYAFLUX_API DataWriteProcessor : public VKBufferProcessor {
65public:
67 ~DataWriteProcessor() override = default;
68
69 /**
70 * @brief Supply a single data value for the next cycle (slot 0).
71 *
72 * Thread-safe. The variant is moved into the pending slot and the dirty
73 * flag is set. Conversion to the upload representation is deferred to
74 * processing_function() on the graphics thread.
75 *
76 * @param variant DataVariant compatible with the attached buffer's modality.
77 */
78 void set_data(Kakshya::DataVariant variant);
79
80 /**
81 * @brief Supply multiple data values for the next cycle.
82 *
83 * Slot 0 is the primary upload path (modality-routed). Slots 1..N are
84 * uploaded raw and correspond to secondary resources (e.g. texture data
85 * alongside vertex data). The caller is responsible for ensuring the
86 * attached buffer and its RenderProcessor have been configured with
87 * matching descriptor bindings for any secondary slots.
88 *
89 * Thread-safe. The full vector is swapped atomically on the next
90 * processing_function() call.
91 *
92 * @param variants One or more DataVariant values. Must not be empty.
93 */
94 void set_data(std::vector<Kakshya::DataVariant> variants);
95
96 /**
97 * @brief Supply pre-packed interleaved vertex bytes for the next cycle.
98 *
99 * data must point to N contiguous 60-byte Vertex records.
100 * byte_count must be a multiple of 60. Routed through the
101 * VERTICES_3D path: no channel assembly, no conversion.
102 *
103 * Thread-safe. The bytes are copied into the pending slot and the
104 * dirty flag is set. Upload is deferred to processing_function()
105 * on the graphics thread.
106 *
107 * @param data Pointer to interleaved vertex data.
108 * @param byte_count Total size in bytes; must be a multiple of 60.
109 */
110 void set_vertices(const void* data, size_t byte_count);
111
112 /**
113 * @brief Supply typed vertex data for the next cycle.
114 *
115 * Reinterprets the span as raw bytes and delegates to set_vertices(void*, size_t).
116 * T must be exactly 60 bytes: one of Kakshya::Vertex, PointVertex, LineVertex,
117 * MeshVertex, or any user struct with the canonical layout.
118 *
119 * @tparam T 60-byte vertex type.
120 * @param vertices Source span.
121 */
122 template <typename T>
123 void set_vertices(std::span<const T> vertices)
124 {
125 static_assert(sizeof(T) == 60, "set_vertices: T must be a 60-byte vertex type");
126 set_vertices(vertices.data(), vertices.size_bytes());
127 }
128
129 /**
130 * @brief Returns true if a snapshot has been set and not yet consumed.
131 */
132 [[nodiscard]] bool has_pending() const noexcept;
133
134 /**
135 * @brief Supply a texture to bind on the next graphics tick.
136 *
137 * Stores the image and binding name behind an atomic dirty flag.
138 * On the next processing_function() call the image is bound to the
139 * buffer's RenderProcessor via bind_texture(). The descriptor slot
140 * must already exist in the pipeline (declared in the RenderConfig
141 * ShaderConfig bindings). Calling again replaces the pending binding
142 * before it is consumed.
143 *
144 * @param image GPU image. nullptr clears the binding.
145 * @param binding Descriptor name matching the fragment shader.
146 */
147 void set_texture(std::shared_ptr<Core::VKImage> image, std::string binding);
148
149 /**
150 * @brief Supply pixel data for the co-resident texture on the next cycle.
151 *
152 * Independent of the vertex data path. Requires setup_pixel_target() to
153 * have been called. On the next processing_function() call the variant
154 * is routed through as_texture_access() and uploaded to the GPU texture
155 * bound at the configured binding.
156 *
157 * Thread-safe. The variant is swapped atomically behind m_pixel_dirty.
158 *
159 * @param variant Pixel data compatible with the format passed to setup_pixel_target().
160 */
161 void set_pixel_data(Kakshya::DataVariant variant);
162
163 /**
164 * @brief Configure the pixel upload path for texture modalities.
165 *
166 * Must be called before the first set_data() when the attached buffer
167 * has a texture modality and is not a TextureBuffer. For TextureBuffer
168 * targets, call this directly
169 * TextureBuffer::set_pixel_data().
170 *
171 * @param width Texture width in texels.
172 * @param height Texture height in texels.
173 * @param format Pixel format.
174 * @param binding Descriptor name to bind the VKImage to on the RenderProcessor.
175 */
176 void setup_pixel_target(
177 uint32_t width,
178 uint32_t height,
179 Portal::Graphics::ImageFormat format,
180 std::string binding = "texSampler");
181
182 /**
183 * @brief Returns the GpuDataFormat resolved on the last texture upload.
184 *
185 * Valid only when the attached buffer has a texture modality and at least
186 * one successful upload has occurred. Returns GpuDataFormat::VEC4_F32
187 * as a default before first upload.
188 */
189 [[nodiscard]] Kakshya::GpuDataFormat last_texture_format() const noexcept;
190
191 /**
192 * @brief Write bytes into another VKBuffer, such as a plain descriptor-bound buffer.
193 *
194 * The target is not resized. A device-local target is written through a
195 * persistent staging buffer. A buffer whose descriptor binding has a node,
196 * audio, or host-buffer source is rewritten from that source each cycle, so
197 * write to the source instead.
198 *
199 * @param target Buffer to write; retained until the request runs.
200 * @param data Source data, converted to bytes when the request runs.
201 * @param dst_offset Byte offset into @p target. offset plus data size must fit its allocation.
202 */
203 void write_buffer(std::shared_ptr<VKBuffer> target, Kakshya::DataVariant data, size_t dst_offset = 0);
204
205 /**
206 * @brief Write a named field of the attached buffer.
207 *
208 * A VolumeGridBuffer field is written through seed_raw, and a
209 * NetworkGeometryBuffer state field into the slot its next stage reads.
210 * The data size must equal the field's byte size.
211 */
212 void write_field(std::string name, Kakshya::DataVariant data);
213
214 /**
215 * @brief Write a raw back_buffers slot of the attached buffer.
216 * @param index Slot index into its back_buffers.
217 * @param data Source data; a slot does not carry its size, so the data must
218 * not exceed what the buffer's owner allocated for it.
219 */
220 void write_back_buffer(size_t index, Kakshya::DataVariant data);
221
222 /**
223 * @brief Write mesh geometry to the attached buffer's authoritative copy.
224 *
225 * A MeshBuffer takes it as its MeshData, and a GeometryBuffer driven by a
226 * MeshWriterNode sets it on that node. A MeshNetworkBuffer needs @p slot to
227 * name the slot whose node is set. No transform is applied.
228 */
229 void write_mesh(Kakshya::MeshData data, std::string slot = {});
230
231 /**
232 * @brief Write the retained host pixels of the attached TextureBuffer.
233 *
234 * The image must match the texture's format and size. The buffer re-uploads
235 * to its GPU image on its own cycle.
236 */
237 void write_pixels(Kakshya::ImageData image);
238
239 /**
240 * @brief Write pixels straight into the attached TextureBuffer's GPU image.
241 *
242 * The image must match the GPU image's format and size, which must be
243 * writable (transfer-destination usage). Retained host pixels that are
244 * marked dirty re-upload over this write.
245 */
246 void write_texture_pixels(Kakshya::ImageData image);
247
248protected:
249 void on_attach(const std::shared_ptr<Buffer>& buffer) override;
250 void on_detach(const std::shared_ptr<Buffer>& buffer) override;
251 void processing_function(const std::shared_ptr<Buffer>& buffer) override;
252
253private:
255 std::shared_ptr<Core::VKImage> image;
256 std::string binding;
257 };
258
259 std::optional<PendingTexture> m_pending_texture;
260 std::atomic_flag m_texture_dirty;
261
264 std::atomic_flag m_pixel_dirty;
265 bool m_tex_binding_confirmed {};
266
267 uint32_t m_tex_width {};
268 uint32_t m_tex_height {};
269 Portal::Graphics::ImageFormat m_tex_format { Portal::Graphics::ImageFormat::RGBA32F };
270 std::string m_tex_binding;
271 std::shared_ptr<Core::VKImage> m_gpu_texture;
272 std::shared_ptr<VKBuffer> m_image_staging;
273
274 Kakshya::DataModality m_modality { Kakshya::DataModality::UNKNOWN };
275 Kakshya::GpuDataFormat m_last_texture_format { Kakshya::GpuDataFormat::VEC4_F32 };
276 Portal::Graphics::PrimitiveTopology m_topology { Portal::Graphics::PrimitiveTopology::POINT_LIST };
277
278 std::vector<Kakshya::DataVariant> m_data_pending;
279 std::vector<Kakshya::DataVariant> m_active;
280 std::atomic_flag m_data_dirty;
281
282 std::shared_ptr<VKBuffer> m_staging;
283
284 using WriteFn = std::function<void(DataWriteProcessor&, const std::shared_ptr<VKBuffer>&)>;
285 static constexpr size_t k_write_queue_capacity = 64;
286
287 std::unique_ptr<Memory::LockFreeQueue<WriteFn, k_write_queue_capacity>> m_writes;
288 std::shared_ptr<VKBuffer> m_write_staging;
289 std::shared_ptr<VKBuffer> m_write_image_staging;
290
291 void enqueue_write(WriteFn fn);
292
293 void upload_primary(const std::shared_ptr<VKBuffer>& vk, std::vector<Kakshya::DataVariant>& slots);
294 void upload_secondary(const std::shared_ptr<VKBuffer>& vk, Kakshya::DataVariant& slot);
295 void upload_vertex(const std::shared_ptr<VKBuffer>& vk, std::vector<Kakshya::DataVariant>& slots);
296 void upload_texture(const std::shared_ptr<VKBuffer>& vk, Kakshya::DataVariant& slot);
297 void upload_raw(const std::shared_ptr<VKBuffer>& vk, Kakshya::DataVariant& slot);
298
299 void ensure_capacity(const std::shared_ptr<VKBuffer>& vk, size_t required);
300
301 static bool is_vertex_modality(Kakshya::DataModality m) noexcept;
302 static bool is_texture_modality(Kakshya::DataModality m) noexcept;
303};
304
305} // namespace MayaFlux::Buffers
IO::ImageData image
Definition Decoder.cpp:47
bool format
std::shared_ptr< NetworkGeometryBuffer > buffer
std::string name
Definition VKDevice.cpp:123
uint32_t index
Definition VKDevice.cpp:122
uint32_t width
uint32_t height
std::vector< Kakshya::DataVariant > m_data_pending
std::shared_ptr< VKBuffer > m_write_staging
std::function< void(DataWriteProcessor &, const std::shared_ptr< VKBuffer > &)> WriteFn
std::shared_ptr< Core::VKImage > m_gpu_texture
std::optional< PendingTexture > m_pending_texture
void set_vertices(std::span< const T > vertices)
Supply typed vertex data for the next cycle.
std::shared_ptr< VKBuffer > m_write_image_staging
std::unique_ptr< Memory::LockFreeQueue< WriteFn, k_write_queue_capacity > > m_writes
std::vector< Kakshya::DataVariant > m_active
std::shared_ptr< VKBuffer > m_image_staging
Modality-aware multi-slot write processor for plain VKBuffer.
Vulkan-backed buffer wrapper used in processing chains.
Definition VKBuffer.hpp:76
ImageFormat
User-friendly image format enum.
Definition ImageData.hpp:12
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
GpuDataFormat
GPU data formats with explicit precision levels.
PrimitiveTopology
Vertex assembly primitive topology.
Raw image data loaded from file or read back from a GPU image.
Definition ImageData.hpp:55