MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
FormaBindingsProcessor.cpp
Go to the documentation of this file.
2
8
9namespace MayaFlux::Buffers {
10
12 : ShaderProcessor(shader_path)
13{
14}
15
20
21// =============================================================================
22// Raw reader overloads
23// =============================================================================
24
26 const std::string& name,
27 std::function<float()> reader,
28 uint32_t offset,
29 size_t size)
30{
31 if (!reader) {
33 "FormaBindingsProcessor::bind_push_constant: null reader for '{}'", name);
34 return;
35 }
36
38 auto& b = m_bindings[name];
40 b.reader = std::move(reader);
41 b.typed_reader = nullptr;
42 b.pc = PushConstantTarget { .offset = offset, .size = size };
43 b.desc.reset();
44}
45
47 const std::string& name,
48 std::function<float()> reader,
49 uint32_t offset,
51{
52 if (!reader) {
54 "FormaBindingsProcessor::bind_push_constant: null reader for '{}'", name);
55 return;
56 }
57
60 "FormaBindingsProcessor::bind_push_constant: '{}' needs a single component format",
61 name);
62 return;
63 }
64
66 auto& b = m_bindings[name];
68 b.reader = std::move(reader);
69 b.typed_reader = nullptr;
71 b.desc.reset();
72}
73
75 const std::string& name,
76 std::function<Kakshya::GpuValue()> reader,
77 uint32_t offset)
78{
79 if (!reader) {
81 "FormaBindingsProcessor::bind_typed_push_constant: null reader for '{}'", name);
82 return;
83 }
84
86 auto& b = m_bindings[name];
88 b.reader = nullptr;
89 b.typed_reader = std::move(reader);
90 b.pc = PushConstantTarget { .offset = offset, .size = 0 };
91 b.desc.reset();
92}
93
95 const std::string& name,
96 std::function<float()> reader,
97 const std::string& descriptor_name,
98 uint32_t binding_index,
99 uint32_t set,
101{
102 if (!reader) {
104 "FormaBindingsProcessor::bind_descriptor: null reader for '{}'", name);
105 return;
106 }
107
108 release(name);
109 auto& b = m_bindings[name];
111 b.reader = std::move(reader);
112 b.typed_reader = nullptr;
113 b.pc.reset();
114
115 auto gpu_buf = make_descriptor_buffer(role);
116
117 b.desc = DescriptorTarget {
118 .descriptor_name = descriptor_name,
119 .set_index = set,
120 .binding_index = binding_index,
121 .role = role,
122 .gpu_buffer = gpu_buf,
123 .buffer_size = sizeof(float),
124 };
125
126 bind_buffer(descriptor_name, gpu_buf);
128}
129
130// =============================================================================
131// Introspection
132// =============================================================================
133
134bool FormaBindingsProcessor::has_binding(const std::string& name) const
135{
136 return m_bindings.contains(name);
137}
138
139std::vector<std::string> FormaBindingsProcessor::get_binding_names() const
140{
141 std::vector<std::string> names;
142 names.reserve(m_bindings.size());
143 for (const auto& [k, _] : m_bindings) {
144 names.push_back(k);
145 }
146 return names;
147}
148
149bool FormaBindingsProcessor::unbind(const std::string& name)
150{
151 if (!m_bindings.contains(name))
152 return false;
153
154 release(name);
155 m_bindings.erase(name);
156 return true;
157}
158
159void FormaBindingsProcessor::release(const std::string& name)
160{
161 const auto found = m_bindings.find(name);
162 if (found == m_bindings.end())
163 return;
164
165 const auto& binding = found->second;
166 std::erase_if(m_targets, [](const auto& target) { return target.expired(); });
167
168 for (const auto& weak : m_targets) {
169 const auto target = weak.lock();
170 auto& context = target->get_pipeline_context();
171
172 if (binding.pc) {
173 std::erase_if(context.push_constant_bindings, [&](const auto& entry) {
174 return entry.offset == binding.pc->offset && entry.name == name;
175 });
176 }
177
178 if (binding.desc) {
179 const auto handle = binding.desc->gpu_buffer->get_buffer();
180 std::erase_if(context.descriptor_buffer_bindings, [&](const auto& entry) {
181 return entry.set == binding.desc->set_index
182 && entry.binding == binding.desc->binding_index
183 && entry.buffer_info.buffer == handle;
184 });
185 }
186 }
187
188 if (binding.desc)
189 unbind_buffer(binding.desc->descriptor_name);
190}
191
192void FormaBindingsProcessor::remember_target(const std::shared_ptr<VKBuffer>& buffer)
193{
194 const bool known = std::ranges::any_of(m_targets, [&](const auto& weak) {
195 return weak.lock() == buffer;
196 });
197 if (!known)
198 m_targets.push_back(buffer);
199}
200
201// =============================================================================
202// ShaderProcessor hook
203// =============================================================================
204
205void FormaBindingsProcessor::execute_shader(const std::shared_ptr<VKBuffer>& buffer)
206{
208
209 for (const auto& [name, b] : m_bindings) {
210 if (b.typed_reader) {
211 if (b.pc)
212 flush_typed_push_constant(name, b.typed_reader(), *b.pc, buffer);
213 continue;
214 }
215
216 if (!b.reader) {
218 "FormaBindingsProcessor: binding '{}' has no reader", name);
219 continue;
220 }
221
222 const float value = b.reader();
223 switch (b.kind) {
225 if (b.pc)
227 break;
229 if (b.desc)
231 break;
232 }
233 }
234}
235
236// =============================================================================
237// Private
238// =============================================================================
239
241 const std::string& name,
242 float value,
243 const PushConstantTarget& pc,
244 const std::shared_ptr<VKBuffer>& buffer)
245{
246 auto& entries = buffer->get_pipeline_context().push_constant_bindings;
247
248 auto it = std::ranges::find_if(entries, [&](const auto& e) {
249 return e.offset == pc.offset;
250 });
251
252 if (it == entries.end()) {
254 .offset = pc.offset,
255 .data = std::vector<uint8_t>(pc.size),
256 .name = name });
257 it = std::prev(entries.end());
258 }
259
260 if (pc.format) {
261 const bool packed = Kakshya::pack_gpu_value(*pc.format, value,
262 std::span<std::byte>(reinterpret_cast<std::byte*>(it->data.data()), it->data.size()));
263 if (!packed) {
265 "FormaBindingsProcessor: binding '{}' does not fit its push constant slot", name);
266 }
267 } else if (pc.size == sizeof(float)) {
268 std::memcpy(it->data.data(), &value, sizeof(float));
269 } else if (pc.size == sizeof(double)) {
270 auto promoted = static_cast<double>(value);
271 std::memcpy(it->data.data(), &promoted, sizeof(double));
272 }
273
275 "FormaBindingsProcessor: PC write offset={} value={}", pc.offset, value);
276}
277
279 const std::string& name,
281 const PushConstantTarget& pc,
282 const std::shared_ptr<VKBuffer>& buffer)
283{
284 auto& entries = buffer->get_pipeline_context().push_constant_bindings;
286
287 auto it = std::ranges::find_if(entries, [&](const auto& e) {
288 return e.offset == pc.offset;
289 });
290
291 if (it == entries.end()) {
293 .offset = pc.offset,
294 .data = std::vector<uint8_t>(width),
295 .name = name });
296 it = std::prev(entries.end());
297 } else if (it->data.size() != width) {
298 it->data.resize(width);
299 }
300
302 std::span<std::byte>(reinterpret_cast<std::byte*>(it->data.data()), it->data.size()));
303}
304
306 float value,
307 const DescriptorTarget& desc,
308 const std::shared_ptr<VKBuffer>& attached)
309{
310 upload_to_gpu(&value, sizeof(float), desc.gpu_buffer, nullptr);
311
312 auto& bindings_list = attached->get_pipeline_context().descriptor_buffer_bindings;
313
314 for (auto& entry : bindings_list) {
315 if (entry.set == desc.set_index && entry.binding == desc.binding_index) {
316 entry.buffer_info.buffer = desc.gpu_buffer->get_buffer();
317 entry.buffer_info.offset = 0;
318 entry.buffer_info.range = sizeof(float);
319 return;
320 }
321 }
322
323 bindings_list.push_back({
324 .set = desc.set_index,
325 .binding = desc.binding_index,
327 ? vk::DescriptorType::eUniformBuffer
328 : vk::DescriptorType::eStorageBuffer,
329 .buffer_info = vk::DescriptorBufferInfo {
330 desc.gpu_buffer->get_buffer(), 0, sizeof(float) },
331 });
332}
333
336{
338 ? VKBuffer::Usage::UNIFORM
339 : VKBuffer::Usage::COMPUTE;
340
341 auto buf = std::make_shared<VKBuffer>(
342 sizeof(float),
343 usage,
345
348
349 if (!svc) {
350 error<std::runtime_error>(
353 std::source_location::current(),
354 "FormaBindingsProcessor: no BufferService available");
355 }
356
357 svc->initialize_buffer(buf);
358 return buf;
359}
360
361} // namespace MayaFlux::Buffers
#define MF_ERROR(comp, ctx,...)
#define MF_RT_WARN(comp, ctx,...)
#define MF_RT_DEBUG(comp, ctx,...)
uint64_t offset
Definition Composite.cpp:10
bool format
bool size
std::shared_ptr< NetworkGeometryBuffer > buffer
size_t b
uint16_t usage
glm::vec3 value
std::string name
Definition VKDevice.cpp:123
uint32_t width
void flush_descriptor(float value, const DescriptorTarget &desc, const std::shared_ptr< VKBuffer > &attached)
void bind_typed_push_constant(const std::string &name, std::function< Kakshya::GpuValue()> reader, uint32_t offset)
Bind a typed reader to a push constant slot.
void remember_target(const std::shared_ptr< VKBuffer > &buffer)
void bind_descriptor(const std::string &name, std::function< float()> reader, const std::string &descriptor_name, uint32_t binding_index, uint32_t set, Portal::Graphics::DescriptorRole role=Portal::Graphics::DescriptorRole::UNIFORM)
Bind a type-erased reader to a descriptor binding.
void flush_typed_push_constant(const std::string &name, const Kakshya::GpuValue &value, const PushConstantTarget &pc, const std::shared_ptr< VKBuffer > &buffer)
static std::shared_ptr< VKBuffer > make_descriptor_buffer(Portal::Graphics::DescriptorRole role)
void flush_push_constant(const std::string &name, float value, const PushConstantTarget &pc, const std::shared_ptr< VKBuffer > &buffer)
bool unbind(const std::string &name)
Remove a binding by name.
bool has_binding(const std::string &name) const
Returns true if a binding with name exists.
void execute_shader(const std::shared_ptr< VKBuffer > &buffer) override
std::vector< std::string > get_binding_names() const
Returns all registered binding names.
FormaBindingsProcessor(const std::string &shader_path)
std::unordered_map< std::string, Binding > m_bindings
std::vector< std::weak_ptr< VKBuffer > > m_targets
void bind_push_constant(const std::string &name, std::function< float()> reader, uint32_t offset, size_t size=sizeof(float))
Bind a type-erased reader to a push constant slot.
void unbind_buffer(const std::string &descriptor_name)
Unbind a buffer from a descriptor.
void bind_buffer(const std::string &descriptor_name, const std::shared_ptr< VKBuffer > &buffer)
Bind a VKBuffer to a named shader descriptor.
Abstract base class for shader-based buffer processing.
Interface * get_service()
Query for a backend service.
static BackendRegistry & instance()
Get the global registry instance.
void upload_to_gpu(const void *data, size_t size, const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging, size_t dst_offset)
Upload raw data to GPU buffer (auto-detects host-visible vs device-local)
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.
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.
size_t gpu_data_format_bytes(GpuDataFormat fmt) noexcept
Byte size of one element of a GpuDataFormat.
size_t gpu_data_format_components(GpuDataFormat fmt) noexcept
Number of scalar components in one element of a GpuDataFormat.
@ UNKNOWN
Unknown or undefined modality.
GpuDataFormat gpu_value_format(const GpuValue &value) noexcept
The GpuDataFormat of the value held.
size_t gpu_value_write(const GpuValue &value, std::span< std::byte > out) noexcept
Copies the held value's bytes into out, unconverted.
bool pack_gpu_value(GpuDataFormat dst, const T &value, std::span< std::byte > out) noexcept
Writes a typed value into out as one element of dst.
GpuDataFormat
GPU data formats with explicit precision levels.
DescriptorRole
Semantic descriptor type — maps to Vulkan descriptor types internally.
@ UNIFORM
Small, read-only, frequently updated (scalars, param structs)
BufferUsageHint
Semantic usage hint for buffer allocation and memory properties.
Self-describing push constant fragment staged on a VKBuffer.
Backend buffer management service interface.