MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
FormaBindingsProcessor.hpp
Go to the documentation of this file.
1#pragma once
2
4
6
8
9namespace MayaFlux::Buffers {
10
11/**
12 * @class FormaBindingsProcessor
13 * @brief ShaderProcessor that reads Forma element state and routes it to push
14 * constants or descriptor bindings on external pipelines each graphics tick.
15 *
16 * Attaches to a FormaBuffer as a secondary processor — never as the primary.
17 * Owns no pipeline or shader of its own; initialize_pipeline() and
18 * initialize_descriptors() are no-ops, matching NodeBindingsProcessor and
19 * DescriptorBindingsProcessor.
20 *
21 * The processor is the automation layer. Each binding registers:
22 * - A type-erased reader: std::function<float()> that closes over a
23 * shared_ptr<MappedState<T>> and a projection function T -> float.
24 * The reader is called every graphics tick regardless of version — the
25 * graphics tick IS the sampling boundary.
26 * - A target: either a push constant slot on an external ShaderProcessor,
27 * or a descriptor buffer registered on the attached buffer's
28 * pipeline_context.
29 *
30 * Templated bind overloads accept any MappedState<T> and a projection
31 * function, constructing the type-erased reader internally. The processor
32 * itself holds no template parameters.
33 *
34 * Usage:
35 * @code
36 * // Fader state drives a push constant on a compute processor
37 * forma_bindings->bind_push_constant(
38 * "cutoff",
39 * fader.state,
40 * [](float v) { return v; },
41 * compute_proc,
42 * offsetof(MyPC, cutoff));
43 *
44 * // 2D picker x-axis drives a descriptor uniform
45 * forma_bindings->bind_descriptor(
46 * "pan",
47 * picker.state,
48 * [](glm::vec2 v) { return v.x; },
49 * "pan_ubo",
50 * 0,
51 * Portal::Graphics::DescriptorRole::UNIFORM);
52 *
53 * forma_buffer->add_processor(forma_bindings);
54 * @endcode
55 */
56class MAYAFLUX_API FormaBindingsProcessor : public ShaderProcessor {
57public:
58 explicit FormaBindingsProcessor(const std::string& shader_path);
59 explicit FormaBindingsProcessor(ShaderConfig config);
60 ~FormaBindingsProcessor() override = default;
61
66
67 // =========================================================================
68 // Push constant bindings — raw reader overload
69 // =========================================================================
70
71 /**
72 * @brief Bind a type-erased reader to a push constant slot.
73 *
74 * Used by Bridge, which holds readers as std::function<float()> after
75 * type-erasing MappedState<T> at register_element time.
76 *
77 * @param name Logical binding name.
78 * @param reader Callable returning the current float value each tick.
79 * @param offset Byte offset in the push constant struct.
80 * @param size Byte width. Defaults to sizeof(float).
81 */
82 void bind_push_constant(
83 const std::string& name,
84 std::function<float()> reader,
85 uint32_t offset,
86 size_t size = sizeof(float));
87
88 /**
89 * @brief Bind a type-erased reader to a push constant slot described by
90 * a GpuDataFormat.
91 *
92 * The reader's value is converted to the format; integers saturate. The
93 * format must have a single component, since the reader produces one
94 * value.
95 *
96 * @param name Logical binding name.
97 * @param reader Callable returning the current value each tick.
98 * @param offset Byte offset in the push constant struct.
99 * @param format Layout written at the offset.
100 */
101 void bind_push_constant(
102 const std::string& name,
103 std::function<float()> reader,
104 uint32_t offset,
106
107 /**
108 * @brief Bind a typed reader to a push constant slot.
109 *
110 * The value is written as its own format's bytes at the offset, with no
111 * conversion, so vectors and matrices keep every component. The slot is
112 * as wide as the value each tick.
113 *
114 * @param name Logical binding name.
115 * @param reader Callable returning the current value each tick.
116 * @param offset Byte offset in the push constant struct.
117 */
118 void bind_typed_push_constant(
119 const std::string& name,
120 std::function<Kakshya::GpuValue()> reader,
121 uint32_t offset);
122
123 // =========================================================================
124 // Descriptor bindings — raw reader overload
125 // =========================================================================
126
127 /**
128 * @brief Bind a type-erased reader to a descriptor binding.
129 *
130 * Used by Bridge for the same reason as the push constant raw overload.
131 *
132 * @param name Logical binding name.
133 * @param reader Callable returning the current float value each tick.
134 * @param descriptor_name Descriptor name in the shader config.
135 * @param binding_index Vulkan binding index within the descriptor set.
136 * @param set Descriptor set index.
137 * @param role UNIFORM for UBO, STORAGE for SSBO.
138 */
139 void bind_descriptor(
140 const std::string& name,
141 std::function<float()> reader,
142 const std::string& descriptor_name,
143 uint32_t binding_index,
144 uint32_t set,
145 Portal::Graphics::DescriptorRole role = Portal::Graphics::DescriptorRole::UNIFORM);
146
147 // =========================================================================
148 // Push constant bindings — MappedState<T> overload
149
150 /**
151 * @brief Bind a MappedState<T> to a push constant slot on an external
152 * ShaderProcessor.
153 *
154 * @param name Logical binding name for introspection and unbind.
155 * @param state MappedState whose value is read each graphics tick.
156 * @param project Projection from T to float. Called every tick.
157 * the value at @p offset. Any subclass is valid.
158 * @param offset Byte offset in the target's push constant struct.
159 * @param size Byte width of the written value. Defaults to sizeof(float).
160 * @tparam T MappedState value type.
161 */
162 template <typename T>
164 const std::string& name,
165 std::shared_ptr<Portal::Forma::MappedState<T>> state,
166 std::function<float(T)> project,
167 uint32_t offset,
168 size_t size = sizeof(float))
169 {
170 if (!state) {
171 MF_ERROR(Journal::Component::Buffers, Journal::Context::BufferProcessing,
172 "FormaBindingsProcessor::bind_push_constant: null state for '{}'", name);
173 return;
174 }
175
176 release(name);
177 auto& b = m_bindings[name];
178 b.kind = TargetKind::PUSH_CONSTANT;
179 b.reader = [s = std::move(state), p = std::move(project)]() {
180 return p(s->value);
181 };
182 b.pc = PushConstantTarget { .offset = offset, .size = size };
183 b.desc.reset();
184 }
185
186 /**
187 * @brief Bind a MappedState<T> to a push constant slot described by a
188 * GpuDataFormat.
189 *
190 * @param format Layout written at the offset.
191 * @tparam T MappedState value type.
192 */
193 template <typename T>
195 const std::string& name,
196 std::shared_ptr<Portal::Forma::MappedState<T>> state,
197 std::function<float(T)> project,
198 uint32_t offset,
200 {
201 if (!state) {
202 MF_ERROR(Journal::Component::Buffers, Journal::Context::BufferProcessing,
203 "FormaBindingsProcessor::bind_push_constant: null state for '{}'", name);
204 return;
205 }
206
207 bind_push_constant(
208 name,
209 std::function<float()>([s = std::move(state), p = std::move(project)]() {
210 return p(s->value);
211 }),
212 offset, format);
213 }
214
215 // =========================================================================
216 // Descriptor bindings
217 // =========================================================================
218
219 /**
220 * @brief Bind a MappedState<T> to a descriptor binding on the attached buffer.
221 *
222 * Creates an owned host-visible GPU buffer sized to sizeof(float).
223 * Each tick the projected value is uploaded into this buffer and
224 * registered in the attached buffer's descriptor_buffer_bindings,
225 * following the same path as DescriptorBindingsProcessor.
226 *
227 * @param name Logical binding name.
228 * @param state MappedState whose value is read each graphics tick.
229 * @param project Projection from T to float. Called every tick.
230 * @param descriptor_name Descriptor name in the shader config.
231 * @param binding_index Vulkan binding index within the descriptor set.
232 * @param set Descriptor set index.
233 * @param role UNIFORM for UBO, STORAGE for SSBO.
234 * @tparam T MappedState value type.
235 */
236 template <typename T>
238 const std::string& name,
239 std::shared_ptr<Portal::Forma::MappedState<T>> state,
240 std::function<float(T)> project,
241 const std::string& descriptor_name,
242 uint32_t binding_index,
243 uint32_t set,
244 Portal::Graphics::DescriptorRole role = Portal::Graphics::DescriptorRole::UNIFORM)
245 {
246 if (!state) {
247 MF_ERROR(Journal::Component::Buffers, Journal::Context::BufferProcessing,
248 "FormaBindingsProcessor::bind_descriptor: null state for '{}'", name);
249 return;
250 }
251
252 release(name);
253 auto gpu_buf = make_descriptor_buffer(role);
254
255 auto& b = m_bindings[name];
256 b.kind = TargetKind::DESCRIPTOR;
257 b.reader = [s = std::move(state), p = std::move(project)]() {
258 return p(s->value);
259 };
260 b.pc.reset();
261 b.desc = DescriptorTarget {
262 .descriptor_name = descriptor_name,
263 .set_index = set,
264 .binding_index = binding_index,
265 .role = role,
266 .gpu_buffer = gpu_buf,
267 .buffer_size = sizeof(float),
268 };
269
270 bind_buffer(descriptor_name, gpu_buf);
271 m_needs_descriptor_rebuild = true;
272
273 MF_DEBUG(Journal::Component::Buffers, Journal::Context::BufferProcessing,
274 "FormaBindingsProcessor::bind_descriptor: '{}' -> descriptor '{}' set={} binding={}",
275 name, descriptor_name, set, binding_index);
276 }
277
278 // =========================================================================
279 // Introspection
280 // =========================================================================
281
282 /** @brief Returns true if a binding with @p name exists. */
283 [[nodiscard]] bool has_binding(const std::string& name) const;
284
285 /** @brief Returns all registered binding names. */
286 [[nodiscard]] std::vector<std::string> get_binding_names() const;
287
288 /**
289 * @brief Remove a binding by name.
290 * @return True if found and removed.
291 */
292 bool unbind(const std::string& name);
293
294protected:
295 void execute_shader(const std::shared_ptr<VKBuffer>& buffer) override;
296 void initialize_pipeline(const std::shared_ptr<VKBuffer>&) override { }
297 void initialize_descriptors(const std::shared_ptr<VKBuffer>&) override { }
298
299 /**
300 * @brief Drop the loaded shader id without destroying it.
301 *
302 * The shader belongs to the target buffer's pipeline and is shared by
303 * path, so cleanup must not destroy it.
304 */
305 void on_shader_loaded(Portal::Graphics::ShaderID) override { m_shader_id = Portal::Graphics::INVALID_SHADER; }
306
307private:
308 enum class TargetKind : uint8_t {
309 PUSH_CONSTANT,
310 DESCRIPTOR,
311 };
312
314 uint32_t offset;
315 size_t size;
316 std::optional<Kakshya::GpuDataFormat> format {};
317 };
318
320 std::string descriptor_name;
321 uint32_t set_index;
324 std::shared_ptr<VKBuffer> gpu_buffer;
326 };
327
328 struct Binding {
330 std::function<float()> reader;
332 std::optional<PushConstantTarget> pc;
333 std::optional<DescriptorTarget> desc;
334 };
335
336 std::unordered_map<std::string, Binding> m_bindings;
337 std::vector<std::weak_ptr<VKBuffer>> m_targets;
338
339 void release(const std::string& name);
340
341 void remember_target(const std::shared_ptr<VKBuffer>& buffer);
342
343 void flush_push_constant(const std::string& name, float value, const PushConstantTarget& pc, const std::shared_ptr<VKBuffer>& buffer);
344
345 void flush_typed_push_constant(const std::string& name, const Kakshya::GpuValue& value, const PushConstantTarget& pc, const std::shared_ptr<VKBuffer>& buffer);
346
347 void flush_descriptor(float value, const DescriptorTarget& desc,
348 const std::shared_ptr<VKBuffer>& attached);
349
350 [[nodiscard]] static std::shared_ptr<VKBuffer> make_descriptor_buffer(
352};
353
354} // namespace MayaFlux::Buffers
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
uint64_t offset
Definition Composite.cpp:10
bool format
bool size
std::shared_ptr< NetworkGeometryBuffer > buffer
size_t b
glm::vec3 value
std::string name
Definition VKDevice.cpp:123
FormaBindingsProcessor(const FormaBindingsProcessor &)=delete
void on_shader_loaded(Portal::Graphics::ShaderID) override
Drop the loaded shader id without destroying it.
void initialize_descriptors(const std::shared_ptr< VKBuffer > &) override
FormaBindingsProcessor & operator=(const FormaBindingsProcessor &)=delete
FormaBindingsProcessor(FormaBindingsProcessor &&)=delete
void bind_push_constant(const std::string &name, std::shared_ptr< Portal::Forma::MappedState< T > > state, std::function< float(T)> project, uint32_t offset, Kakshya::GpuDataFormat format)
Bind a MappedState<T> to a push constant slot described by a GpuDataFormat.
void bind_descriptor(const std::string &name, std::shared_ptr< Portal::Forma::MappedState< T > > state, std::function< float(T)> project, const std::string &descriptor_name, uint32_t binding_index, uint32_t set, Portal::Graphics::DescriptorRole role=Portal::Graphics::DescriptorRole::UNIFORM)
Bind a MappedState<T> to a descriptor binding on the attached buffer.
std::unordered_map< std::string, Binding > m_bindings
void bind_push_constant(const std::string &name, std::shared_ptr< Portal::Forma::MappedState< T > > state, std::function< float(T)> project, uint32_t offset, size_t size=sizeof(float))
Bind a MappedState<T> to a push constant slot on an external ShaderProcessor.
void initialize_pipeline(const std::shared_ptr< VKBuffer > &) override
FormaBindingsProcessor & operator=(FormaBindingsProcessor &&)=delete
std::vector< std::weak_ptr< VKBuffer > > m_targets
ShaderProcessor that reads Forma element state and routes it to push constants or descriptor bindings...
Abstract base class for shader-based buffer processing.
std::variant< float, glm::vec2, glm::vec3, glm::vec4, double, glm::dvec2, glm::dvec3, glm::dvec4, int32_t, uint32_t, uint8_t, uint16_t, glm::mat4 > GpuValue
One value of any GpuDataFormat.
GpuDataFormat
GPU data formats with explicit precision levels.
DescriptorRole
Semantic descriptor type — maps to Vulkan descriptor types internally.
Value carrier for a Mapped primitive.
Definition Mapped.hpp:40