MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ShaderProcessor.cpp
Go to the documentation of this file.
1#include "ShaderProcessor.hpp"
2
4
5namespace MayaFlux::Buffers {
6
7//==============================================================================
8// Construction
9//==============================================================================
10
11ShaderProcessor::ShaderProcessor(const std::string& shader_path)
12 : m_config({ shader_path })
13{
14 m_processing_token = ProcessingToken::GRAPHICS_BACKEND;
15 initialize_buffer_service();
16 initialize_compute_service();
17}
18
26
31
32//==============================================================================
33// BufferProcessor Interface
34//==============================================================================
35
36void ShaderProcessor::processing_function(const std::shared_ptr<Buffer>& buffer)
37{
38 auto vk_buffer = std::dynamic_pointer_cast<VKBuffer>(buffer);
39 if (!vk_buffer) {
41 "ShaderProcessor can only process VKBuffers");
42 return;
43 }
44
47 "on_before_execute() reported failure, skipping shader execution");
48 return;
49 }
50
51 if (!m_initialized) {
53 initialize_pipeline(vk_buffer);
54 m_initialized = true;
55 }
56
58 initialize_pipeline(vk_buffer);
61 }
62
64 initialize_descriptors(vk_buffer);
66 } else {
67 update_descriptors(vk_buffer);
68 }
69
70 execute_shader(vk_buffer);
72}
73
74void ShaderProcessor::on_attach(const std::shared_ptr<Buffer>& buffer)
75{
76 auto vk_buffer = std::dynamic_pointer_cast<VKBuffer>(buffer);
77 if (!vk_buffer)
78 return;
79
80 if (m_config.bindings.empty()) {
81 auto_bind_buffer(vk_buffer);
82 }
83
85 "ShaderProcessor attached to VKBuffer (size: {} bytes, modality: {})",
86 vk_buffer->get_size_bytes(),
87 static_cast<int>(vk_buffer->get_modality()));
88}
89
90void ShaderProcessor::on_detach(const std::shared_ptr<Buffer>& buffer)
91{
92 auto vk_buffer = std::dynamic_pointer_cast<VKBuffer>(buffer);
93 if (!vk_buffer)
94 return;
95
96 for (auto it = m_bound_buffers.begin(); it != m_bound_buffers.end();) {
97 if (it->second == vk_buffer) {
98 it = m_bound_buffers.erase(it);
99 } else {
100 ++it;
101 }
102 }
104}
105
106bool ShaderProcessor::is_compatible_with(const std::shared_ptr<Buffer>& buffer) const
107{
108 return std::dynamic_pointer_cast<VKBuffer>(buffer) != nullptr;
109}
110
111//==============================================================================
112// Buffer Binding
113//==============================================================================
114
115void ShaderProcessor::bind_buffer(const std::string& descriptor_name, const std::shared_ptr<VKBuffer>& buffer)
116{
117 if (!buffer) {
119 "Cannot bind null buffer to descriptor '{}'", descriptor_name);
120 return;
121 }
122
123 ensure_initialized(buffer);
124
125 if (m_config.bindings.find(descriptor_name) == m_config.bindings.end()) {
126 auto user_binding_count = static_cast<uint32_t>(
127 std::ranges::count_if(m_config.bindings, [](const auto& pair) {
128 return pair.second.set == 1;
129 }));
130
131 ShaderBinding default_binding;
132 default_binding.set = 1;
133 default_binding.binding = user_binding_count;
134 default_binding.type = vk::DescriptorType::eStorageBuffer;
135 m_config.bindings[descriptor_name] = default_binding;
136
138 "Created default binding for '{}': set={}, binding={}",
139 descriptor_name, default_binding.set, default_binding.binding);
140 }
142 m_bound_buffers[descriptor_name] = buffer;
144
146 "Bound buffer to descriptor '{}' (size: {} bytes)",
147 descriptor_name, buffer->get_size_bytes());
148}
149
150void ShaderProcessor::unbind_buffer(const std::string& descriptor_name)
151{
152 auto it = m_bound_buffers.find(descriptor_name);
153 if (it != m_bound_buffers.end()) {
154 m_bound_buffers.erase(it);
156 }
157}
158
159std::shared_ptr<VKBuffer> ShaderProcessor::get_bound_buffer(const std::string& descriptor_name) const
160{
161 auto it = m_bound_buffers.find(descriptor_name);
162 return it != m_bound_buffers.end() ? it->second : nullptr;
163}
164
165void ShaderProcessor::auto_bind_buffer(const std::shared_ptr<VKBuffer>& buffer)
166{
167 std::string descriptor_name;
168 if (m_auto_bind_index == 0) {
169 descriptor_name = "input";
170 } else if (m_auto_bind_index == 1) {
171 descriptor_name = "output";
172 } else {
173 descriptor_name = "buffer_" + std::to_string(m_auto_bind_index);
174 }
175
176 bind_buffer(descriptor_name, buffer);
178}
179
180//==============================================================================
181// Shader Management
182//==============================================================================
183
185{
187 "Hot-reloading shader: {}", m_config.shader_path);
188
189 auto& foundry = Portal::Graphics::get_shader_foundry();
190 auto new_shader_id = foundry.reload_shader(m_config.shader_path);
191
192 if (new_shader_id == Portal::Graphics::INVALID_SHADER) {
194 "Hot-reload failed for shader: {}", m_config.shader_path);
195 return false;
196 }
197
199 foundry.destroy_shader(m_shader_id);
200 }
201
202 m_shader_id = new_shader_id;
205
207 "Shader hot-reloaded successfully (ID: {})", m_shader_id);
208 return true;
209}
210
211void ShaderProcessor::set_shader(const std::string& shader_path)
212{
213 m_config.shader_path = shader_path;
216}
217
218//==============================================================================
219// Push Constants
220//==============================================================================
221
228
229void ShaderProcessor::set_push_constant_data_raw(const void* data, size_t size)
230{
231 if (size > m_config.push_constant_size) {
233 "Push constant data size {} exceeds configured size {}",
235 return;
236 }
237
238 m_push_constant_data.resize(size);
239 std::memcpy(m_push_constant_data.data(), data, size);
240}
241
242size_t ShaderProcessor::resolve_push_constant_size(const std::shared_ptr<VKBuffer>& buffer) const
243{
244 size_t size = std::max(m_config.push_constant_size, m_push_constant_data.size());
245
246 for (const auto& entry : buffer->get_pipeline_context().push_constant_bindings) {
247 size = std::max(size, static_cast<size_t>(entry.offset) + entry.data.size());
248 }
249
250 return size;
251}
252
253std::vector<uint8_t> ShaderProcessor::resolve_push_constants(const std::shared_ptr<VKBuffer>& buffer) const
254{
255 std::vector<uint8_t> merged = m_push_constant_data;
256 merged.resize(resolve_push_constant_size(buffer));
257
258 for (const auto& entry : buffer->get_pipeline_context().push_constant_bindings) {
259 std::memcpy(merged.data() + entry.offset, entry.data.data(), entry.data.size());
260 }
261
262 return merged;
263}
264
265//==============================================================================
266// Specialization Constants
267//==============================================================================
268
269void ShaderProcessor::set_specialization_constant(uint32_t constant_id, uint32_t value)
270{
273}
274
280
281//==============================================================================
282// Configuration
283//==============================================================================
284
286{
287 m_config = config;
291}
292
293void ShaderProcessor::add_binding(const std::string& descriptor_name, const ShaderBinding& binding)
294{
295 m_config.bindings[descriptor_name] = binding;
297}
298
299//==========================================================================
300// Data movement Queries
301//==========================================================================
302
303[[nodiscard]] ShaderProcessor::BufferUsageHint ShaderProcessor::get_buffer_usage_hint(const std::string& descriptor_name) const
304{
305 if (descriptor_name == "input")
307 if (descriptor_name == "output")
310}
311
312bool ShaderProcessor::is_in_place_operation(const std::string& descriptor_name) const
313{
314 auto hint = get_buffer_usage_hint(descriptor_name);
315 return hint == BufferUsageHint::BIDIRECTIONAL;
316}
317
318bool ShaderProcessor::has_binding(const std::string& descriptor_name) const
319{
320 return m_config.bindings.find(descriptor_name) != m_config.bindings.end();
321}
322
323std::vector<std::string> ShaderProcessor::get_binding_names() const
324{
325 std::vector<std::string> names;
326 names.reserve(m_config.bindings.size());
327 for (const auto& [name, _] : m_config.bindings) {
328 names.push_back(name);
329 }
330 return names;
331}
332
334{
335 return std::ranges::all_of(
337 [this](const auto& pair) {
338 return m_bound_buffers.find(pair.first) != m_bound_buffers.end();
339 });
340}
341
342//==============================================================================
343// Protected Hooks
344//==============================================================================
345
346void ShaderProcessor::on_before_compile(const std::string&) { }
351bool ShaderProcessor::on_before_execute(Portal::Graphics::CommandBufferID, const std::shared_ptr<VKBuffer>&) { return true; }
353
354//==============================================================================
355// Private Implementation
356//==============================================================================
357
383
384std::optional<uint32_t> ShaderProcessor::resolve_ds_index(uint32_t set) const
385{
387 if (set == 0)
388 return std::nullopt;
389 const uint32_t idx = set - 1;
390 if (idx >= m_descriptor_set_ids.size())
391 return std::nullopt;
392 return idx;
393 }
394 if (set >= m_descriptor_set_ids.size())
395 return std::nullopt;
396 return set;
397}
398
399void ShaderProcessor::update_descriptors(const std::shared_ptr<VKBuffer>& buffer)
400{
401 if (m_descriptor_set_ids.empty()) {
402 return;
403 }
404
405 auto& foundry = Portal::Graphics::get_shader_foundry();
406 auto& descriptor_bindings = buffer->get_pipeline_context().descriptor_buffer_bindings;
407
408 std::set<std::pair<uint32_t, uint32_t>> updated_pairs;
409
410 for (const auto& binding : descriptor_bindings) {
411 auto ds_index = resolve_ds_index(binding.set);
412 if (!ds_index) {
414 "Descriptor set index {} out of range or reserved", binding.set);
415 continue;
416 }
417
418 foundry.update_descriptor_buffer(
419 m_descriptor_set_ids[*ds_index],
420 binding.binding,
421 binding.type,
422 binding.buffer_info.buffer,
423 binding.buffer_info.offset,
424 binding.buffer_info.range);
425
426 updated_pairs.emplace(binding.set, binding.binding);
427 }
428
429 for (const auto& [descriptor_name, buf] : m_bound_buffers) {
430 auto binding_it = m_config.bindings.find(descriptor_name);
431 if (binding_it == m_config.bindings.end()) {
432 continue;
433 }
434
435 const auto& binding = binding_it->second;
436 auto key = std::make_pair(binding.set, binding.binding);
437
438 if (updated_pairs.count(key)) {
439 continue;
440 }
441
442 auto ds_index = resolve_ds_index(binding.set);
443 if (!ds_index) {
445 "Invalid descriptor set index {} for binding '{}'",
446 binding.set, descriptor_name);
447 continue;
448 }
449
450 foundry.update_descriptor_buffer(
451 m_descriptor_set_ids[*ds_index],
452 binding.binding,
453 binding.type,
454 buf->get_buffer(),
455 0,
456 buf->get_size_bytes());
457 }
458}
459
461{
462 auto& foundry = Portal::Graphics::get_shader_foundry();
463 auto& compute_press = Portal::Graphics::get_compute_press();
464
466 foundry.destroy_shader(m_shader_id);
468 }
469
470 m_descriptor_set_ids.clear();
471 m_bound_buffers.clear();
472 m_initialized = false;
473}
474
475} // namespace MayaFlux::Buffers
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_TRACE(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
#define MF_RT_DEBUG(comp, ctx,...)
float value
virtual void initialize_pipeline(const std::shared_ptr< VKBuffer > &buffer)=0
ShaderProcessor(const std::string &shader_path)
Construct processor with shader path.
Portal::Graphics::CommandBufferID m_last_command_buffer
bool are_bindings_complete() const
Check if all required bindings are satisfied.
std::optional< uint32_t > resolve_ds_index(uint32_t set) const
Resolve logical descriptor set index to actual index.
virtual void execute_shader(const std::shared_ptr< VKBuffer > &buffer)=0
std::unordered_map< std::string, std::shared_ptr< VKBuffer > > m_bound_buffers
void unbind_buffer(const std::string &descriptor_name)
Unbind a buffer from a descriptor.
Portal::Graphics::ShaderID m_shader_id
std::vector< uint8_t > m_push_constant_data
virtual void on_after_execute(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr< VKBuffer > &buffer)
Called after each process callback.
virtual void on_descriptors_created()
Called after descriptor sets are created.
virtual void initialize_descriptors(const std::shared_ptr< VKBuffer > &buffer)=0
std::shared_ptr< VKBuffer > get_bound_buffer(const std::string &descriptor_name) const
Get bound buffer for a descriptor name.
size_t resolve_push_constant_size(const std::shared_ptr< VKBuffer > &buffer) const
Byte width of this processor's push constant block, extended to cover any fragment staged on the buff...
virtual void update_descriptors(const std::shared_ptr< VKBuffer > &buffer)
virtual void on_before_compile(const std::string &shader_path)
Called before shader compilation.
virtual bool on_before_execute(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr< VKBuffer > &buffer)
Called before each process callback.
void on_detach(const std::shared_ptr< Buffer > &buffer) override
Called when this processor is detached from a buffer.
void auto_bind_buffer(const std::shared_ptr< VKBuffer > &buffer)
Auto-bind buffer based on attachment order.
bool has_binding(const std::string &descriptor_name) const
Check if a descriptor binding exists.
void set_push_constant_size()
Set push constant size from type.
void processing_function(const std::shared_ptr< Buffer > &buffer) override
The core processing function that must be implemented by derived classes.
virtual void on_pipeline_created(Portal::Graphics::ComputePipelineID pipeline_id)
Called after pipeline is created.
virtual bool is_in_place_operation(const std::string &descriptor_name) const
Check if shader modifies a specific buffer in-place.
virtual void on_shader_loaded(Portal::Graphics::ShaderID shader_id)
Called after shader is loaded.
void add_binding(const std::string &descriptor_name, const ShaderBinding &binding)
Add descriptor binding configuration.
bool hot_reload_shader()
Hot-reload shader from ShaderFoundry.
virtual void on_before_descriptors_create()
Called before descriptor sets are created.
void set_config(const ShaderConfig &config)
Update entire configuration.
void set_shader(const std::string &shader_path)
Update shader path and reload.
bool m_engine_owns_set_zero
Whether the engine reserves set=0 for global resources.
bool is_compatible_with(const std::shared_ptr< Buffer > &buffer) const override
Checks if this processor can handle the specified buffer type.
virtual BufferUsageHint get_buffer_usage_hint(const std::string &descriptor_name) const
Get buffer usage hint for a descriptor.
BufferUsageHint
Get buffer usage characteristics needed for safe data flow.
@ OUTPUT_WRITE
Shader writes output (modifies)
std::vector< std::string > get_binding_names() const
Get all configured descriptor names.
void set_specialization_constant(uint32_t constant_id, uint32_t value)
Set specialization constant.
void on_attach(const std::shared_ptr< Buffer > &buffer) override
Called when this processor is attached to a buffer.
void bind_buffer(const std::string &descriptor_name, const std::shared_ptr< VKBuffer > &buffer)
Bind a VKBuffer to a named shader descriptor.
std::vector< Portal::Graphics::DescriptorSetID > m_descriptor_set_ids
void clear_specialization_constants()
Clear all specialization constants.
std::vector< uint8_t > resolve_push_constants(const std::shared_ptr< VKBuffer > &buffer) const
This processor's push constant data with buffer-staged fragments overlaid at their declared offsets.
virtual void set_push_constant_data_raw(const void *data, size_t size)
Update push constant data (raw bytes)
void ensure_initialized(const std::shared_ptr< VKBuffer > &buffer)
Definition VKBuffer.cpp:448
@ GRAPHICS_BACKEND
Standard graphics processing backend configuration.
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.
constexpr ShaderID INVALID_SHADER
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
MAYAFLUX_API ComputePress & get_compute_press()
uint32_t binding
Binding point within set.
uint32_t set
Descriptor set index.
Describes how a VKBuffer binds to a shader descriptor.
std::string shader_path
Path to shader file.
std::unordered_map< uint32_t, uint32_t > specialization_constants
std::unordered_map< std::string, ShaderBinding > bindings
Portal::Graphics::ShaderStage stage
Portal::Graphics::ShaderID shader_id