12 : m_config({ shader_path })
15 initialize_buffer_service();
16 initialize_compute_service();
20 : m_config(
std::move(config))
38 auto vk_buffer = std::dynamic_pointer_cast<VKBuffer>(buffer);
41 "ShaderProcessor can only process VKBuffers");
58 "on_before_execute() reported failure, skipping shader execution");
88 auto vk_buffer = std::dynamic_pointer_cast<VKBuffer>(buffer);
97 "ShaderProcessor attached to VKBuffer (size: {} bytes, modality: {})",
98 vk_buffer->get_size_bytes(),
99 static_cast<int>(vk_buffer->get_modality()));
104 auto vk_buffer = std::dynamic_pointer_cast<VKBuffer>(buffer);
109 if (it->second == vk_buffer) {
120 return std::dynamic_pointer_cast<VKBuffer>(buffer) !=
nullptr;
131 "Cannot bind null buffer to descriptor '{}'", descriptor_name);
138 auto user_binding_count =
static_cast<uint32_t
>(
140 return pair.second.set == 1;
144 default_binding.
set = 1;
146 default_binding.
type = vk::DescriptorType::eStorageBuffer;
150 "Created default binding for '{}': set={}, binding={}",
151 descriptor_name, default_binding.
set, default_binding.
binding);
158 "Bound buffer to descriptor '{}' (size: {} bytes)",
159 descriptor_name, buffer->get_size_bytes());
179 std::string descriptor_name;
181 descriptor_name =
"input";
183 descriptor_name =
"output";
193 const std::string& descriptor_name,
196 const std::shared_ptr<VKBuffer>& staging)
const
201 "download_bound: no buffer bound to descriptor '{}'", descriptor_name);
217 "feed: null source for '{}'",
name);
223 entry.source = std::move(source);
224 entry.is_storage =
true;
227 entry.descriptor_name =
name;
228 entry.buffer.reset();
229 entry.mismatch_logged =
false;
236 if (field.name ==
name) {
238 entry.source = std::move(source);
239 entry.is_storage =
false;
242 entry.descriptor_name.clear();
243 entry.buffer.reset();
244 entry.mismatch_logged =
false;
251 "feed: '{}' matches no descriptor and no push constant field. "
252 "File-shader processors must supply an explicit offset.",
260 "feed: null source for '{}'",
name);
264 if (size !=
sizeof(
float) && size !=
sizeof(
double)) {
266 "feed: '{}' requests {} bytes, only 4 or 8 are written",
name, size);
271 entry.source = std::move(source);
272 entry.is_storage =
false;
275 entry.descriptor_name.clear();
276 entry.buffer.reset();
277 entry.mismatch_logged =
false;
287 if (it->second.is_storage) {
301 std::vector<std::string> names;
304 names.push_back(
name);
314 if (!entry.is_storage) {
315 const auto* scalar = std::get_if<double>(&
value);
317 if (!entry.mismatch_logged) {
319 "feed '{}' writes a push constant but returned a DataVariant",
name);
320 entry.mismatch_logged =
true;
326 const size_t required = entry.offset + entry.size;
327 if (data.size() < required) {
328 data.resize(required);
331 if (entry.size ==
sizeof(
float)) {
332 const auto narrowed =
static_cast<float>(*scalar);
333 std::memcpy(data.data() + entry.offset, &narrowed,
sizeof(
float));
335 std::memcpy(data.data() + entry.offset, scalar,
sizeof(
double));
340 auto* variant = std::get_if<Kakshya::DataVariant>(&
value);
342 if (!entry.mismatch_logged) {
344 "feed '{}' writes a storage descriptor but returned a double",
name);
345 entry.mismatch_logged =
true;
353 if (!
ptr || bytes == 0) {
355 "feed '{}' produced no bytes",
name);
360 entry.buffer = std::make_shared<VKBuffer>(
361 static_cast<size_t>(
static_cast<float>(bytes) * 1.5F),
362 m_config.
bindings.at(entry.descriptor_name).type == vk::DescriptorType::eUniformBuffer
363 ? VKBuffer::Usage::UNIFORM
364 : VKBuffer::Usage::HOST_STORAGE,
370 "feed '{}' backing buffer created at {} bytes",
371 name, entry.buffer->get_size_bytes());
372 }
else if (entry.buffer->get_size_bytes() < bytes) {
373 const size_t grown = bytes * 3 / 2;
376 "feed '{}' backing buffer resized {} to {} bytes",
377 name, entry.buffer->get_size_bytes(), grown);
379 entry.buffer->resize(grown,
false);
414 "Shader hot-reloaded successfully (ID: {})",
m_shader_id);
440 "Push constant data size {} exceeds configured size {}",
453 for (
const auto& entry : buffer->get_pipeline_context().push_constant_bindings) {
454 size = std::max(size,
static_cast<size_t>(entry.offset) + entry.data.size());
465 for (
const auto& entry : buffer->get_pipeline_context().push_constant_bindings) {
466 std::memcpy(merged.data() + entry.offset, entry.data.data(), entry.data.size());
505 foundry.release_fence(
fence);
512 const std::shared_ptr<VKBuffer>& buffer)
517 foundry.submit_and_wait(cmd_id);
525 "Deferred submission failed, no fence returned");
572 if (descriptor_name ==
"input")
574 if (descriptor_name ==
"output")
592 std::vector<std::string> names;
595 names.push_back(
name);
602 return std::ranges::all_of(
604 [
this](
const auto& pair) {
605 return m_bound_buffers.find(pair.first) != m_bound_buffers.end();
640 "Failed to load shader: {}",
648 "Shader loaded: {} (ID: {})",
657 const uint32_t idx = set - 1;
674 auto& descriptor_bindings = buffer->get_pipeline_context().descriptor_buffer_bindings;
676 std::set<std::pair<uint32_t, uint32_t>> updated_pairs;
678 for (
const auto& binding : descriptor_bindings) {
682 "Descriptor set index {} out of range or reserved", binding.set);
686 foundry.update_descriptor_buffer(
690 binding.buffer_info.buffer,
691 binding.buffer_info.offset,
692 binding.buffer_info.range);
694 updated_pairs.emplace(binding.set, binding.binding);
703 const auto& binding = binding_it->second;
704 auto key = std::make_pair(binding.set, binding.binding);
706 if (updated_pairs.count(key)) {
713 "Invalid descriptor set index {} for binding '{}'",
714 binding.set, descriptor_name);
718 foundry.update_descriptor_buffer(
724 buf->get_size_bytes());
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_RT_WARN(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_TRACE(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
#define MF_RT_DEBUG(comp, ctx,...)
ProcessingToken m_processing_token
bool download_bound(const std::string &descriptor_name, void *data, size_t size, const std::shared_ptr< VKBuffer > &staging=nullptr) const
Download the buffer currently bound to a named descriptor.
virtual void initialize_pipeline(const std::shared_ptr< VKBuffer > &buffer)=0
const std::vector< uint8_t > & get_push_constant_data() const
Get current push constant data.
bool has_feed(const std::string &name) const
Whether a feed of this name is registered.
ShaderProcessor(const std::string &shader_path)
Construct processor with shader path.
void pump_feeds()
Pull every feed and write its result.
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
std::vector< std::string > get_feed_names() const
Names of every registered feed.
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
bool m_needs_descriptor_rebuild
std::shared_ptr< VKBuffer > m_pending_buffer
Buffer retained for the outstanding submission.
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.
~ShaderProcessor() override
Portal::Graphics::FenceID m_pending_fence
Outstanding async submission, if any.
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 submit_recorded(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr< VKBuffer > &buffer)
Submit a recorded command buffer honoring the submission mode.
void add_binding(const std::string &descriptor_name, const ShaderBinding &binding)
Add descriptor binding configuration.
bool m_deferred_submission
False submits synchronously, preserving pre-existing behaviour.
std::variant< double, Kakshya::DataVariant > FeedValue
What a feed callable returns.
void set_deferred_submission(bool deferred)
Submit asynchronously and resolve at the top of a later cycle.
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 void on_dispatch_complete(const std::shared_ptr< VKBuffer > &buffer)
Called once when an asynchronous submission is observed complete.
virtual BufferUsageHint get_buffer_usage_hint(const std::string &descriptor_name) const
Get buffer usage hint for a descriptor.
bool resolve_pending_dispatch(bool block)
Resolve an outstanding asynchronous submission.
BufferUsageHint
Get buffer usage characteristics needed for safe data flow.
@ OUTPUT_WRITE
Shader writes output (modifies)
@ INPUT_READ
Shader reads input.
std::vector< std::string > get_binding_names() const
Get all configured descriptor names.
bool is_dispatch_pending() const
True while an asynchronous submission is outstanding.
std::unordered_map< std::string, Feed > m_feeds
void remove_feed(const std::string &name)
Remove a feed.
void set_specialization_constant(uint32_t constant_id, uint32_t value)
Set specialization constant.
std::function< FeedValue()> FeedSource
A callable pulled once per processing cycle.
bool m_needs_pipeline_rebuild
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.
void feed(const std::string &name, FeedSource source)
Supply a shader input from a callable, resolved by name.
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 initialize_compute_service()
void initialize_buffer_service()
void ensure_initialized(const std::shared_ptr< VKBuffer > &buffer)
auto gpu_buffer() const
Get raw buffer info for GPU upload.
Type-erased accessor for NDData with semantic view construction.
@ GRAPHICS_BACKEND
Standard graphics processing backend configuration.
void download_from_gpu(const std::shared_ptr< VKBuffer > &source, void *data, size_t size, const std::shared_ptr< VKBuffer > &staging)
Download from GPU buffer to raw data (auto-detects host-visible vs device-local)
void upload_host_visible(const std::shared_ptr< VKBuffer > &target, const Kakshya::DataVariant &data, size_t dst_offset)
Upload data to a host-visible buffer.
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.
size_t gpu_data_format_bytes(GpuDataFormat fmt) noexcept
Byte size of one element of a GpuDataFormat.
@ UNKNOWN
Unknown or undefined modality.
uint64_t ComputePipelineID
constexpr ShaderID INVALID_SHADER
constexpr FenceID INVALID_FENCE
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
size_t push_constant_size
Portal::Graphics::ShaderStage stage
std::vector< Portal::Graphics::PushConstantField > pc_fields
Retained from a ShaderSpec so feeds can resolve a field name to an offset. Empty for file shaders.
Portal::Graphics::ShaderID shader_id