MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
DataWriteProcessor.hpp
Go to the documentation of this file.
1#pragma once
2
5
6namespace MayaFlux::Buffers {
7
8/**
9 * @class DataWriteProcessor
10 * @brief Modality-aware multi-slot write processor for plain VKBuffer.
11 *
12 * Accepts one or more DataVariant values per cycle. Slot 0 is the primary
13 * upload path and is routed by the attached buffer's DataModality:
14 *
15 * - VERTICES_3D and single-attribute vertex modalities (VERTEX_POSITIONS_3D,
16 * VERTEX_NORMALS_3D, VERTEX_TANGENTS_3D, VERTEX_COLORS_RGB,
17 * VERTEX_COLORS_RGBA, TEXTURE_COORDS_2D): all slots are passed as an
18 * ordered channel span to as_*_vertex_access(). Full 60-byte vertices
19 * are always assembled; partial channel sets are completed from
20 * VertexAccessConfig defaults.
21 *
22 * - Texture modalities (IMAGE_2D, IMAGE_COLOR, TEXTURE_2D):
23 * routed through Kakshya::as_texture_access(). GpuDataFormat from the
24 * result is available via last_texture_format() for pipeline configuration.
25 *
26 * - All other modalities: raw bytes via DataAccess::gpu_buffer().
27 * Zero-copy when the variant type matches the target layout.
28 *
29 * Slot 1..N are uploaded raw via DataAccess::gpu_buffer() regardless of
30 * modality. These are intended for secondary data (e.g. a texture payload
31 * alongside vertex data) where the caller has configured the corresponding
32 * descriptor bindings on the RenderProcessor.
33 *
34 * Modality is read from the attached VKBuffer in on_attach(). The processor
35 * warns if the supplied DataVariant is incompatible with that modality.
36 *
37 * Dirty gating: upload occurs only when set_data() has been called since
38 * the last cycle. No stale re-upload.
39 *
40 * Thread safety: set_data() and the graphics thread may run concurrently.
41 * Lock-free double-buffer swap via atomic_flag ensures the graphics thread
42 * never blocks on the supplier thread.
43 *
44 * Staging: a persistent host-visible staging buffer is allocated on
45 * on_attach() when the target is device-local, and reused every cycle.
46 *
47 * Intended for use with plain VKBuffer where no specialised child class
48 * exists for the data being contributed. The caller is responsible for
49 * configuring a matching RenderProcessor via VKBuffer::setup_rendering().
50 */
51class MAYAFLUX_API DataWriteProcessor : public VKBufferProcessor {
52public:
54 ~DataWriteProcessor() override = default;
55
56 /**
57 * @brief Supply a single data value for the next cycle (slot 0).
58 *
59 * Thread-safe. The variant is moved into the pending slot and the dirty
60 * flag is set. Conversion to the upload representation is deferred to
61 * processing_function() on the graphics thread.
62 *
63 * @param variant DataVariant compatible with the attached buffer's modality.
64 */
65 void set_data(Kakshya::DataVariant variant);
66
67 /**
68 * @brief Supply multiple data values for the next cycle.
69 *
70 * Slot 0 is the primary upload path (modality-routed). Slots 1..N are
71 * uploaded raw and correspond to secondary resources (e.g. texture data
72 * alongside vertex data). The caller is responsible for ensuring the
73 * attached buffer and its RenderProcessor have been configured with
74 * matching descriptor bindings for any secondary slots.
75 *
76 * Thread-safe. The full vector is swapped atomically on the next
77 * processing_function() call.
78 *
79 * @param variants One or more DataVariant values. Must not be empty.
80 */
81 void set_data(std::vector<Kakshya::DataVariant> variants);
82
83 /**
84 * @brief Supply pre-packed interleaved vertex bytes for the next cycle.
85 *
86 * data must point to N contiguous 60-byte Vertex records.
87 * byte_count must be a multiple of 60. Routed through the
88 * VERTICES_3D path: no channel assembly, no conversion.
89 *
90 * Thread-safe. The bytes are copied into the pending slot and the
91 * dirty flag is set. Upload is deferred to processing_function()
92 * on the graphics thread.
93 *
94 * @param data Pointer to interleaved vertex data.
95 * @param byte_count Total size in bytes; must be a multiple of 60.
96 */
97 void set_vertices(const void* data, size_t byte_count);
98
99 /**
100 * @brief Supply typed vertex data for the next cycle.
101 *
102 * Reinterprets the span as raw bytes and delegates to set_vertices(void*, size_t).
103 * T must be exactly 60 bytes: one of Kakshya::Vertex, PointVertex, LineVertex,
104 * MeshVertex, or any user struct with the canonical layout.
105 *
106 * @tparam T 60-byte vertex type.
107 * @param vertices Source span.
108 */
109 template <typename T>
110 void set_vertices(std::span<const T> vertices)
111 {
112 static_assert(sizeof(T) == 60, "set_vertices: T must be a 60-byte vertex type");
113 set_vertices(vertices.data(), vertices.size_bytes());
114 }
115
116 /**
117 * @brief Returns true if a snapshot has been set and not yet consumed.
118 */
119 [[nodiscard]] bool has_pending() const noexcept;
120
121 /**
122 * @brief Supply a texture to bind on the next graphics tick.
123 *
124 * Stores the image and binding name behind an atomic dirty flag.
125 * On the next processing_function() call the image is bound to the
126 * buffer's RenderProcessor via bind_texture(). The descriptor slot
127 * must already exist in the pipeline (declared in the RenderConfig
128 * ShaderConfig bindings). Calling again replaces the pending binding
129 * before it is consumed.
130 *
131 * @param image GPU image. nullptr clears the binding.
132 * @param binding Descriptor name matching the fragment shader.
133 */
134 void set_texture(std::shared_ptr<Core::VKImage> image, std::string binding);
135
136 /**
137 * @brief Supply pixel data for the co-resident texture on the next cycle.
138 *
139 * Independent of the vertex data path. Requires setup_pixel_target() to
140 * have been called. On the next processing_function() call the variant
141 * is routed through as_texture_access() and uploaded to the GPU texture
142 * bound at the configured binding.
143 *
144 * Thread-safe. The variant is swapped atomically behind m_pixel_dirty.
145 *
146 * @param variant Pixel data compatible with the format passed to setup_pixel_target().
147 */
148 void set_pixel_data(Kakshya::DataVariant variant);
149
150 /**
151 * @brief Configure the pixel upload path for texture modalities.
152 *
153 * Must be called before the first set_data() when the attached buffer
154 * has a texture modality and is not a TextureBuffer. For TextureBuffer
155 * targets, call this directly
156 * TextureBuffer::set_pixel_data().
157 *
158 * @param width Texture width in texels.
159 * @param height Texture height in texels.
160 * @param format Pixel format.
161 * @param binding Descriptor name to bind the VKImage to on the RenderProcessor.
162 */
163 void setup_pixel_target(
164 uint32_t width,
165 uint32_t height,
166 Portal::Graphics::ImageFormat format,
167 std::string binding = "texSampler");
168
169 /**
170 * @brief Returns the GpuDataFormat resolved on the last texture upload.
171 *
172 * Valid only when the attached buffer has a texture modality and at least
173 * one successful upload has occurred. Returns GpuDataFormat::VEC4_F32
174 * as a default before first upload.
175 */
176 [[nodiscard]] Kakshya::GpuDataFormat last_texture_format() const noexcept;
177
178protected:
179 void on_attach(const std::shared_ptr<Buffer>& buffer) override;
180 void on_detach(const std::shared_ptr<Buffer>& buffer) override;
181 void processing_function(const std::shared_ptr<Buffer>& buffer) override;
182
183private:
185 std::shared_ptr<Core::VKImage> image;
186 std::string binding;
187 };
188
189 std::optional<PendingTexture> m_pending_texture;
190 std::atomic_flag m_texture_dirty;
191
194 std::atomic_flag m_pixel_dirty;
195 bool m_tex_binding_confirmed {};
196
197 uint32_t m_tex_width {};
198 uint32_t m_tex_height {};
199 Portal::Graphics::ImageFormat m_tex_format { Portal::Graphics::ImageFormat::RGBA32F };
200 std::string m_tex_binding;
201 std::shared_ptr<Core::VKImage> m_gpu_texture;
202 std::shared_ptr<VKBuffer> m_image_staging;
203
204 Kakshya::DataModality m_modality { Kakshya::DataModality::UNKNOWN };
205 Kakshya::GpuDataFormat m_last_texture_format { Kakshya::GpuDataFormat::VEC4_F32 };
206 Portal::Graphics::PrimitiveTopology m_topology { Portal::Graphics::PrimitiveTopology::POINT_LIST };
207
208 std::vector<Kakshya::DataVariant> m_data_pending;
209 std::vector<Kakshya::DataVariant> m_active;
210 std::atomic_flag m_data_dirty;
211
212 std::shared_ptr<VKBuffer> m_staging;
213
214 void upload_primary(const std::shared_ptr<VKBuffer>& vk, std::vector<Kakshya::DataVariant>& slots);
215 void upload_secondary(const std::shared_ptr<VKBuffer>& vk, Kakshya::DataVariant& slot);
216 void upload_vertex(const std::shared_ptr<VKBuffer>& vk, std::vector<Kakshya::DataVariant>& slots);
217 void upload_texture(const std::shared_ptr<VKBuffer>& vk, Kakshya::DataVariant& slot);
218 void upload_raw(const std::shared_ptr<VKBuffer>& vk, Kakshya::DataVariant& slot);
219
220 void ensure_capacity(const std::shared_ptr<VKBuffer>& vk, size_t required);
221
222 static bool is_vertex_modality(Kakshya::DataModality m) noexcept;
223 static bool is_texture_modality(Kakshya::DataModality m) noexcept;
224};
225
226} // namespace MayaFlux::Buffers
IO::ImageData image
Definition Decoder.cpp:57
uint32_t width
Definition Decoder.cpp:59
Backend-agnostic interface for sequential data storage and transformation.
Definition Buffer.hpp:37
std::vector< Kakshya::DataVariant > m_data_pending
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::vector< Kakshya::DataVariant > m_active
std::shared_ptr< VKBuffer > m_image_staging
Modality-aware multi-slot write processor for plain VKBuffer.
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
GpuDataFormat
GPU data formats with explicit precision levels.
Definition NDData.hpp:11
ImageFormat
User-friendly image format enum.
PrimitiveTopology
Vertex assembly primitive topology.