MayaFlux 0.4.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 // Descriptor bindings — raw reader overload
90 // =========================================================================
91
92 /**
93 * @brief Bind a type-erased reader to a descriptor binding.
94 *
95 * Used by Bridge for the same reason as the push constant raw overload.
96 *
97 * @param name Logical binding name.
98 * @param reader Callable returning the current float value each tick.
99 * @param descriptor_name Descriptor name in the shader config.
100 * @param binding_index Vulkan binding index within the descriptor set.
101 * @param set Descriptor set index.
102 * @param role UNIFORM for UBO, STORAGE for SSBO.
103 */
104 void bind_descriptor(
105 const std::string& name,
106 std::function<float()> reader,
107 const std::string& descriptor_name,
108 uint32_t binding_index,
109 uint32_t set,
110 Portal::Graphics::DescriptorRole role = Portal::Graphics::DescriptorRole::UNIFORM);
111
112 // =========================================================================
113 // Push constant bindings — MappedState<T> overload
114
115 /**
116 * @brief Bind a MappedState<T> to a push constant slot on an external
117 * ShaderProcessor.
118 *
119 * @param name Logical binding name for introspection and unbind.
120 * @param state MappedState whose value is read each graphics tick.
121 * @param project Projection from T to float. Called every tick.
122 * the value at @p offset. Any subclass is valid.
123 * @param offset Byte offset in the target's push constant struct.
124 * @param size Byte width of the written value. Defaults to sizeof(float).
125 * @tparam T MappedState value type.
126 */
127 template <typename T>
129 const std::string& name,
130 std::shared_ptr<Portal::Forma::MappedState<T>> state,
131 std::function<float(T)> project,
132 uint32_t offset,
133 size_t size = sizeof(float))
134 {
135 if (!state) {
136 MF_ERROR(Journal::Component::Buffers, Journal::Context::BufferProcessing,
137 "FormaBindingsProcessor::bind_push_constant: null state for '{}'", name);
138 return;
139 }
140
141 auto& b = m_bindings[name];
142 b.kind = TargetKind::PUSH_CONSTANT;
143 b.reader = [s = std::move(state), p = std::move(project)]() {
144 return p(s->value);
145 };
146 b.pc = PushConstantTarget { .offset = offset, .size = size };
147 b.desc.reset();
148 }
149
150 // =========================================================================
151 // Descriptor bindings
152 // =========================================================================
153
154 /**
155 * @brief Bind a MappedState<T> to a descriptor binding on the attached buffer.
156 *
157 * Creates an owned host-visible GPU buffer sized to sizeof(float).
158 * Each tick the projected value is uploaded into this buffer and
159 * registered in the attached buffer's descriptor_buffer_bindings,
160 * following the same path as DescriptorBindingsProcessor.
161 *
162 * @param name Logical binding name.
163 * @param state MappedState whose value is read each graphics tick.
164 * @param project Projection from T to float. Called every tick.
165 * @param descriptor_name Descriptor name in the shader config.
166 * @param binding_index Vulkan binding index within the descriptor set.
167 * @param set Descriptor set index.
168 * @param role UNIFORM for UBO, STORAGE for SSBO.
169 * @tparam T MappedState value type.
170 */
171 template <typename T>
173 const std::string& name,
174 std::shared_ptr<Portal::Forma::MappedState<T>> state,
175 std::function<float(T)> project,
176 const std::string& descriptor_name,
177 uint32_t binding_index,
178 uint32_t set,
179 Portal::Graphics::DescriptorRole role = Portal::Graphics::DescriptorRole::UNIFORM)
180 {
181 if (!state) {
182 MF_ERROR(Journal::Component::Buffers, Journal::Context::BufferProcessing,
183 "FormaBindingsProcessor::bind_descriptor: null state for '{}'", name);
184 return;
185 }
186
187 auto gpu_buf = make_descriptor_buffer(role);
188
189 auto& b = m_bindings[name];
190 b.kind = TargetKind::DESCRIPTOR;
191 b.reader = [s = std::move(state), p = std::move(project)]() {
192 return p(s->value);
193 };
194 b.pc.reset();
195 b.desc = DescriptorTarget {
196 .descriptor_name = descriptor_name,
197 .set_index = set,
198 .binding_index = binding_index,
199 .role = role,
200 .gpu_buffer = gpu_buf,
201 .buffer_size = sizeof(float),
202 };
203
204 bind_buffer(descriptor_name, gpu_buf);
205 m_needs_descriptor_rebuild = true;
206
207 MF_DEBUG(Journal::Component::Buffers, Journal::Context::BufferProcessing,
208 "FormaBindingsProcessor::bind_descriptor: '{}' -> descriptor '{}' set={} binding={}",
209 name, descriptor_name, set, binding_index);
210 }
211
212 // =========================================================================
213 // Introspection
214 // =========================================================================
215
216 /** @brief Returns true if a binding with @p name exists. */
217 [[nodiscard]] bool has_binding(const std::string& name) const;
218
219 /** @brief Returns all registered binding names. */
220 [[nodiscard]] std::vector<std::string> get_binding_names() const;
221
222 /**
223 * @brief Remove a binding by name.
224 * @return True if found and removed.
225 */
226 bool unbind(const std::string& name);
227
228protected:
229 void execute_shader(const std::shared_ptr<VKBuffer>& buffer) override;
230 void initialize_pipeline(const std::shared_ptr<VKBuffer>&) override { }
231 void initialize_descriptors(const std::shared_ptr<VKBuffer>&) override { }
232
233private:
234 enum class TargetKind : uint8_t {
235 PUSH_CONSTANT,
236 DESCRIPTOR,
237 };
238
240 uint32_t offset;
241 size_t size;
242 };
243
245 std::string descriptor_name;
246 uint32_t set_index;
249 std::shared_ptr<VKBuffer> gpu_buffer;
251 };
252
253 struct Binding {
255 std::function<float()> reader;
256 std::optional<PushConstantTarget> pc;
257 std::optional<DescriptorTarget> desc;
258 };
259
260 std::unordered_map<std::string, Binding> m_bindings;
261
262 void flush_push_constant(float value, const PushConstantTarget& pc, const std::shared_ptr<VKBuffer>& buffer);
263
264 void flush_descriptor(float value, const DescriptorTarget& desc,
265 const std::shared_ptr<VKBuffer>& attached);
266
267 [[nodiscard]] static std::shared_ptr<VKBuffer> make_descriptor_buffer(
269};
270
271} // namespace MayaFlux::Buffers
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
size_t b
FormaBindingsProcessor(const FormaBindingsProcessor &)=delete
void initialize_descriptors(const std::shared_ptr< VKBuffer > &) override
FormaBindingsProcessor & operator=(const FormaBindingsProcessor &)=delete
FormaBindingsProcessor(FormaBindingsProcessor &&)=delete
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
ShaderProcessor that reads Forma element state and routes it to push constants or descriptor bindings...
Abstract base class for shader-based buffer processing.
DescriptorRole
Semantic descriptor type — maps to Vulkan descriptor types internally.
Value carrier for a Mapped primitive.
Definition Mapped.hpp:36