MayaFlux 1.0.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
45 if (!m_feeds.empty()) {
46 pump_feeds();
47 }
48
51 if (is_dispatch_pending()) {
52 return;
53 }
54 }
55
58 "on_before_execute() reported failure, skipping shader execution");
59 return;
60 }
61
64 }
65
66 if (!m_initialized) {
69 initialize_pipeline(vk_buffer);
70 note_created_layout(vk_buffer);
71 m_initialized = true;
72 } else if (m_needs_pipeline_rebuild) {
74 initialize_pipeline(vk_buffer);
75 note_created_layout(vk_buffer);
77 }
78
80 initialize_descriptors(vk_buffer);
82 } else {
83 update_descriptors(vk_buffer);
84 }
85
86 execute_shader(vk_buffer);
88}
89
90void ShaderProcessor::on_attach(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 if (m_config.bindings.empty()) {
97 auto_bind_buffer(vk_buffer);
98 }
99
101 "ShaderProcessor attached to VKBuffer (size: {} bytes, modality: {})",
102 vk_buffer->get_size_bytes(),
103 static_cast<int>(vk_buffer->get_modality()));
104}
105
106void ShaderProcessor::on_detach(const std::shared_ptr<Buffer>& buffer)
107{
108 auto vk_buffer = std::dynamic_pointer_cast<VKBuffer>(buffer);
109 if (!vk_buffer)
110 return;
111
112 for (auto it = m_bound_buffers.begin(); it != m_bound_buffers.end();) {
113 if (it->second == vk_buffer) {
114 it = m_bound_buffers.erase(it);
115 } else {
116 ++it;
117 }
118 }
120}
121
122bool ShaderProcessor::is_compatible_with(const std::shared_ptr<Buffer>& buffer) const
123{
124 return std::dynamic_pointer_cast<VKBuffer>(buffer) != nullptr;
125}
126
127//==============================================================================
128// Buffer Binding
129//==============================================================================
130
131void ShaderProcessor::bind_buffer(const std::string& descriptor_name, const std::shared_ptr<VKBuffer>& buffer)
132{
133 if (!buffer) {
135 "Cannot bind null buffer to descriptor '{}'", descriptor_name);
136 return;
137 }
138
140
141 if (m_config.bindings.find(descriptor_name) == m_config.bindings.end()) {
142 uint32_t binding_index = 0;
143 while (std::ranges::any_of(m_config.bindings, [binding_index](const auto& pair) {
144 return pair.second.set == 1 && pair.second.binding == binding_index;
145 })) {
146 ++binding_index;
147 }
148
149 ShaderBinding default_binding;
150 default_binding.set = 1;
151 default_binding.binding = binding_index;
152 default_binding.type = vk::DescriptorType::eStorageBuffer;
153 m_config.bindings[descriptor_name] = default_binding;
154
156 "Created default binding for '{}': set={}, binding={}",
157 descriptor_name, default_binding.set, default_binding.binding);
158 }
159
160 m_bound_buffers[descriptor_name] = buffer;
162
164 "Bound buffer to descriptor '{}' (size: {} bytes)",
165 descriptor_name, buffer->get_size_bytes());
166}
167
168void ShaderProcessor::unbind_buffer(const std::string& descriptor_name)
169{
170 auto it = m_bound_buffers.find(descriptor_name);
171 if (it != m_bound_buffers.end()) {
172 m_bound_buffers.erase(it);
174 }
175}
176
177std::shared_ptr<VKBuffer> ShaderProcessor::get_bound_buffer(const std::string& descriptor_name) const
178{
179 auto it = m_bound_buffers.find(descriptor_name);
180 return it != m_bound_buffers.end() ? it->second : nullptr;
181}
182
183void ShaderProcessor::auto_bind_buffer(const std::shared_ptr<VKBuffer>& buffer)
184{
185 std::string descriptor_name;
186 if (m_auto_bind_index == 0) {
187 descriptor_name = "input";
188 } else if (m_auto_bind_index == 1) {
189 descriptor_name = "output";
190 } else {
191 descriptor_name = "buffer_" + std::to_string(m_auto_bind_index);
192 }
193
194 bind_buffer(descriptor_name, buffer);
196}
197
199 const std::string& descriptor_name,
200 void* data,
201 size_t size,
202 const std::shared_ptr<VKBuffer>& staging) const
203{
204 auto buffer = get_bound_buffer(descriptor_name);
205 if (!buffer) {
207 "download_bound: no buffer bound to descriptor '{}'", descriptor_name);
208 return false;
209 }
210
211 download_from_gpu(buffer, data, size, staging);
212 return true;
213}
214
215//==============================================================================
216// Feeds
217//==============================================================================
218
219void ShaderProcessor::feed(const std::string& name, FeedSource source)
220{
221 if (!source) {
223 "feed: null source for '{}'", name);
224 return;
225 }
226
227 if (m_config.bindings.contains(name)) {
228 auto& entry = m_feeds[name];
229 entry.source = std::move(source);
230 entry.is_storage = true;
231 entry.offset = 0;
232 entry.size = 0;
233 entry.format.reset();
234 entry.descriptor_name = name;
235 entry.buffer.reset();
236 entry.mismatch_logged = false;
237 entry.early = true;
238 return;
239 }
240
242 for (size_t i = 0; i < m_config.pc_fields.size(); ++i) {
243 const auto& field = m_config.pc_fields[i];
244 if (field.name == name) {
245 auto& entry = m_feeds[name];
246 entry.source = std::move(source);
247 entry.is_storage = false;
248 entry.offset = layout.offsets[i];
249 entry.size = Kakshya::gpu_data_format_bytes(field.format);
250 entry.format = field.format;
251 entry.descriptor_name.clear();
252 entry.buffer.reset();
253 entry.mismatch_logged = false;
254 entry.early = true;
255 return;
256 }
257 }
258
260 "feed: '{}' matches no descriptor and no push constant field. "
261 "File-shader processors must supply an explicit offset.",
262 name);
263}
264
265void ShaderProcessor::feed(const std::string& name, FeedSource source, uint32_t offset, size_t size, bool early)
266{
267 if (!source) {
269 "feed: null source for '{}'", name);
270 return;
271 }
272
273 if (size != sizeof(float) && size != sizeof(double)) {
275 "feed: '{}' requests {} bytes, only 4 or 8 are written", name, size);
276 return;
277 }
278
279 auto& entry = m_feeds[name];
280 entry.source = std::move(source);
281 entry.is_storage = false;
282 entry.offset = offset;
283 entry.size = size;
284 entry.format.reset();
285 entry.descriptor_name.clear();
286 entry.buffer.reset();
287 entry.mismatch_logged = false;
288 entry.early = early;
289}
290
291void ShaderProcessor::remove_feed(const std::string& name)
292{
293 auto it = m_feeds.find(name);
294 if (it == m_feeds.end()) {
295 return;
296 }
297
298 if (it->second.is_storage) {
299 unbind_buffer(it->second.descriptor_name);
300 }
301
302 m_feeds.erase(it);
303}
304
305bool ShaderProcessor::has_feed(const std::string& name) const
306{
307 return m_feeds.contains(name);
308}
309
310std::vector<std::string> ShaderProcessor::get_feed_names() const
311{
312 std::vector<std::string> names;
313 names.reserve(m_feeds.size());
314 for (const auto& [name, _] : m_feeds) {
315 names.push_back(name);
316 }
317 return names;
318}
319
321{
322 for (auto& [name, entry] : m_feeds) {
323 if (entry.early != early) {
324 continue;
325 }
326
327 FeedValue value = entry.source();
328
329 if (!entry.is_storage) {
330 if (const auto* typed = std::get_if<Kakshya::GpuValue>(&value)) {
331 if (entry.format && *entry.format != Kakshya::gpu_value_format(*typed)) {
332 if (!entry.mismatch_logged) {
334 "feed '{}' returned a value of a different format than its push constant field", name);
335 entry.mismatch_logged = true;
336 }
337 continue;
338 }
339
340 std::array<std::byte, 64> bytes {};
341 const size_t written = Kakshya::gpu_value_write(*typed, bytes);
342
343 auto& data = get_push_constant_data();
344 const size_t required = entry.offset + written;
345 if (data.size() < required) {
346 data.resize(required);
347 }
348
349 std::memcpy(data.data() + entry.offset, bytes.data(), written);
350 continue;
351 }
352
353 const auto* scalar = std::get_if<double>(&value);
354 if (!scalar) {
355 if (!entry.mismatch_logged) {
357 "feed '{}' writes a push constant but returned a DataVariant", name);
358 entry.mismatch_logged = true;
359 }
360 continue;
361 }
362
363 auto& data = get_push_constant_data();
364 const size_t required = entry.offset + entry.size;
365 if (data.size() < required) {
366 data.resize(required);
367 }
368
369 if (entry.size == sizeof(float)) {
370 const auto narrowed = static_cast<float>(*scalar);
371 std::memcpy(data.data() + entry.offset, &narrowed, sizeof(float));
372 } else {
373 std::memcpy(data.data() + entry.offset, scalar, sizeof(double));
374 }
375 continue;
376 }
377
378 auto* variant = std::get_if<Kakshya::DataVariant>(&value);
379 if (!variant) {
380 if (!entry.mismatch_logged) {
382 "feed '{}' writes a storage descriptor but returned a push constant value", name);
383 entry.mismatch_logged = true;
384 }
385 continue;
386 }
387
389 auto [ptr, bytes, format] = accessor.gpu_buffer();
390
391 if (!ptr || bytes == 0) {
393 "feed '{}' produced no bytes", name);
394 continue;
395 }
396
397 if (!entry.buffer) {
398 entry.buffer = std::make_shared<VKBuffer>(
399 static_cast<size_t>(static_cast<float>(bytes) * 1.5F),
400 m_config.bindings.at(entry.descriptor_name).type == vk::DescriptorType::eUniformBuffer
401 ? VKBuffer::Usage::UNIFORM
402 : VKBuffer::Usage::HOST_STORAGE,
404
405 bind_buffer(entry.descriptor_name, entry.buffer);
406
408 "feed '{}' backing buffer created at {} bytes",
409 name, entry.buffer->get_size_bytes());
410 } else if (entry.buffer->get_size_bytes() < bytes) {
411 const size_t grown = bytes * 3 / 2;
412
414 "feed '{}' backing buffer resized {} to {} bytes",
415 name, entry.buffer->get_size_bytes(), grown);
416
417 entry.buffer->resize(grown, false);
419 }
420
421 upload_host_visible(entry.buffer, *variant);
422 }
423}
424
425//==============================================================================
426// Shader Management
427//==============================================================================
428
430{
432 "Hot-reloading shader: {}", m_config.shader_path);
433
434 auto& foundry = Portal::Graphics::get_shader_foundry();
435 auto new_shader_id = foundry.reload_shader(m_config.shader_path);
436
437 if (new_shader_id == Portal::Graphics::INVALID_SHADER) {
439 "Hot-reload failed for shader: {}", m_config.shader_path);
440 return false;
441 }
442
444 foundry.destroy_shader(m_shader_id);
445 }
446
447 m_shader_id = new_shader_id;
450
452 "Shader hot-reloaded successfully (ID: {})", m_shader_id);
453 return true;
454}
455
456void ShaderProcessor::set_shader(const std::string& shader_path)
457{
458 m_config.shader_path = shader_path;
461}
462
463//==============================================================================
464// Push Constants
465//==============================================================================
466
468{
469 const bool changed = m_config.push_constant_size != size;
472 if (changed) {
474 }
475}
476
478{
481 "Push constant data size {} exceeds configured size {}",
483 return;
484 }
485
487 std::memcpy(m_push_constant_data.data(), data, size);
488}
489
490size_t ShaderProcessor::resolve_push_constant_size(const std::shared_ptr<VKBuffer>& buffer) const
491{
492 size_t size = std::max(m_config.push_constant_size, m_push_constant_data.size());
493
494 for (const auto& entry : buffer->get_pipeline_context().push_constant_bindings) {
495 size = std::max(size, static_cast<size_t>(entry.offset) + entry.data.size());
496 }
497
498 for (const auto& [name, entry] : m_feeds) {
499 if (!entry.is_storage) {
500 size = std::max(size, static_cast<size_t>(entry.offset) + entry.size);
501 }
502 }
503
504 return size;
505}
506
507void ShaderProcessor::resolve_push_constants_into(std::vector<uint8_t>& out, const std::shared_ptr<VKBuffer>& buffer) const
508{
509 out.assign(m_push_constant_data.begin(), m_push_constant_data.end());
511
512 for (const auto& entry : buffer->get_pipeline_context().push_constant_bindings) {
513 std::memcpy(out.data() + entry.offset, entry.data.data(), entry.data.size());
514 }
515}
516
517std::vector<uint8_t> ShaderProcessor::resolve_push_constants(const std::shared_ptr<VKBuffer>& buffer) const
518{
519 std::vector<uint8_t> merged;
521 return merged;
522}
523
524//==============================================================================
525// Submission
526//==============================================================================
527
529{
530 if (!deferred && is_dispatch_pending()) {
532 }
533 m_deferred_submission = deferred;
534}
535
537{
538 if (!is_dispatch_pending()) {
539 return false;
540 }
541
542 auto& foundry = Portal::Graphics::get_shader_foundry();
543
544 if (block) {
545 foundry.wait_for_fence(m_pending_fence);
546 } else if (!foundry.is_fence_signaled(m_pending_fence)) {
547 return false;
548 }
549
550 const auto fence = m_pending_fence;
552
554 m_pending_buffer.reset();
555
557 foundry.release_fence(fence);
558
559 return true;
560}
561
564 const std::shared_ptr<VKBuffer>& buffer)
565{
566 auto& foundry = Portal::Graphics::get_shader_foundry();
567
569 foundry.submit_and_wait(cmd_id);
570 return;
571 }
572
573 m_pending_fence = foundry.submit_async(cmd_id);
574
577 "Deferred submission failed, no fence returned");
578 return;
579 }
580
582}
583
584//==============================================================================
585// Specialization Constants
586//==============================================================================
587
588void ShaderProcessor::set_specialization_constant(uint32_t constant_id, uint32_t value)
589{
592}
593
599
600//==============================================================================
601// Configuration
602//==============================================================================
603
605{
606 m_config = config;
610}
611
612void ShaderProcessor::add_binding(const std::string& descriptor_name, const ShaderBinding& binding)
613{
614 m_config.bindings[descriptor_name] = binding;
616}
617
618//==========================================================================
619// Data movement Queries
620//==========================================================================
621
622[[nodiscard]] ShaderProcessor::BufferUsageHint ShaderProcessor::get_buffer_usage_hint(const std::string& descriptor_name) const
623{
624 if (descriptor_name == "input")
626 if (descriptor_name == "output")
629}
630
631bool ShaderProcessor::is_in_place_operation(const std::string& descriptor_name) const
632{
633 auto hint = get_buffer_usage_hint(descriptor_name);
634 return hint == BufferUsageHint::BIDIRECTIONAL;
635}
636
637bool ShaderProcessor::has_binding(const std::string& descriptor_name) const
638{
639 return m_config.bindings.find(descriptor_name) != m_config.bindings.end();
640}
641
642std::vector<std::string> ShaderProcessor::get_binding_names() const
643{
644 std::vector<std::string> names;
645 names.reserve(m_config.bindings.size());
646 for (const auto& [name, _] : m_config.bindings) {
647 names.push_back(name);
648 }
649 return names;
650}
651
653{
654 return std::ranges::all_of(
656 [this](const auto& pair) {
657 return m_bound_buffers.find(pair.first) != m_bound_buffers.end();
658 });
659}
660
661//==============================================================================
662// Protected Hooks
663//==============================================================================
664
665void ShaderProcessor::on_before_compile(const std::string&) { }
670bool ShaderProcessor::on_before_execute(Portal::Graphics::CommandBufferID, const std::shared_ptr<VKBuffer>&) { return true; }
672void ShaderProcessor::on_dispatch_complete(const std::shared_ptr<VKBuffer>&) { }
673
674//==============================================================================
675// Private Implementation
676//==============================================================================
677
703
704uint64_t ShaderProcessor::layout_fingerprint(const std::shared_ptr<VKBuffer>& buffer) const
705{
706 const auto mix = [](uint64_t seed, uint64_t value) {
707 return seed ^ (value + 0x9e3779b97f4a7c15ULL + (seed << 6) + (seed >> 2));
708 };
709 const auto slot = [&mix](uint32_t set, uint32_t binding, vk::DescriptorType type, uint32_t count) {
710 uint64_t hash = mix(0, set);
711 hash = mix(hash, binding);
712 hash = mix(hash, static_cast<uint64_t>(type));
713 return mix(hash, count);
714 };
715
716 const auto& staged = buffer->get_pipeline_context().descriptor_buffer_bindings;
717 const auto find_staged = [&staged](uint32_t set, uint32_t binding) {
718 return std::ranges::find_if(staged, [set, binding](const auto& entry) {
719 return entry.set == set && entry.binding == binding;
720 });
721 };
722
723 uint64_t sum = 0;
724 for (const auto& [name, binding] : m_config.bindings) {
725 const auto override_it = find_staged(binding.set, binding.binding);
726 sum += override_it != staged.end()
727 ? slot(binding.set, binding.binding, override_it->type, override_it->count)
728 : slot(binding.set, binding.binding, binding.type, binding.count);
729 }
730
731 for (auto it = staged.begin(); it != staged.end(); ++it) {
732 const bool configured = std::ranges::any_of(m_config.bindings, [it](const auto& pair) {
733 return pair.second.set == it->set && pair.second.binding == it->binding;
734 });
735 const bool repeated = find_staged(it->set, it->binding) != it;
736 if (!configured && !repeated) {
737 sum += slot(it->set, it->binding, it->type, it->count);
738 }
739 }
740
741 return mix(sum, resolve_push_constant_size(buffer));
742}
743
744void ShaderProcessor::note_created_layout(const std::shared_ptr<VKBuffer>& buffer)
745{
748 }
749}
750
751std::optional<uint32_t> ShaderProcessor::resolve_ds_index(uint32_t set) const
752{
754 if (set == 0)
755 return std::nullopt;
756 const uint32_t idx = set - 1;
757 if (idx >= m_descriptor_set_ids.size())
758 return std::nullopt;
759 return idx;
760 }
761 if (set >= m_descriptor_set_ids.size())
762 return std::nullopt;
763 return set;
764}
765
766void ShaderProcessor::update_descriptors(const std::shared_ptr<VKBuffer>& buffer)
767{
768 if (m_descriptor_set_ids.empty()) {
769 return;
770 }
771
772 auto& foundry = Portal::Graphics::get_shader_foundry();
773 auto& descriptor_bindings = buffer->get_pipeline_context().descriptor_buffer_bindings;
774
775 std::set<std::pair<uint32_t, uint32_t>> updated_pairs;
776
777 for (const auto& binding : descriptor_bindings) {
778 auto ds_index = resolve_ds_index(binding.set);
779 if (!ds_index) {
781 "Descriptor set index {} out of range or reserved", binding.set);
782 continue;
783 }
784
785 foundry.update_descriptor_buffer(
786 m_descriptor_set_ids[*ds_index],
787 binding.binding,
788 binding.type,
789 binding.buffer_info.buffer,
790 binding.buffer_info.offset,
791 binding.buffer_info.range);
792
793 updated_pairs.emplace(binding.set, binding.binding);
794 }
795
796 for (const auto& [descriptor_name, buf] : m_bound_buffers) {
797 auto binding_it = m_config.bindings.find(descriptor_name);
798 if (binding_it == m_config.bindings.end()) {
799 continue;
800 }
801
802 const auto& binding = binding_it->second;
803 auto key = std::make_pair(binding.set, binding.binding);
804
805 if (updated_pairs.count(key)) {
806 continue;
807 }
808
809 auto ds_index = resolve_ds_index(binding.set);
810 if (!ds_index) {
812 "Invalid descriptor set index {} for binding '{}'",
813 binding.set, descriptor_name);
814 continue;
815 }
816
817 foundry.update_descriptor_buffer(
818 m_descriptor_set_ids[*ds_index],
819 binding.binding,
820 binding.type,
821 buf->get_buffer(),
822 0,
823 buf->get_size_bytes());
824 }
825}
826
828{
830 auto& foundry = Portal::Graphics::get_shader_foundry();
831 auto& compute_press = Portal::Graphics::get_compute_press();
832
834 foundry.destroy_shader(m_shader_id);
836 }
837
838 m_descriptor_set_ids.clear();
839 m_bound_buffers.clear();
840 m_feeds.clear();
841 m_created_layout.reset();
842 m_initialized = false;
843}
844
845} // namespace MayaFlux::Buffers
#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,...)
vk::Fence fence
uint64_t offset
Definition Composite.cpp:10
bool format
bool size
std::shared_ptr< NetworkGeometryBuffer > buffer
uint32_t scalar
std::vector< float > * out
glm::vec3 value
vk::PhysicalDeviceType type
Definition VKDevice.cpp:126
std::string name
Definition VKDevice.cpp:123
size_t count
const uint8_t * ptr
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.
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
virtual bool tracks_pipeline_layout() const
Whether the owner builds its pipeline layout from this processor's bindings, staged descriptors and p...
uint64_t layout_fingerprint(const std::shared_ptr< VKBuffer > &buffer) const
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
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)
void resolve_push_constants_into(std::vector< uint8_t > &out, const std::shared_ptr< VKBuffer > &buffer) const
resolve_push_constants() written into out, reusing its storage.
virtual void on_before_compile(const std::string &shader_path)
Called before shader compilation.
std::optional< uint64_t > m_created_layout
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.
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.
void note_created_layout(const std::shared_ptr< VKBuffer > &buffer)
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.
void pump_feeds(bool early=true)
Pull every feed and write its result.
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)
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.
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::variant< double, Kakshya::GpuValue, Kakshya::DataVariant > FeedValue
What a feed callable returns.
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 ensure_initialized(const std::shared_ptr< VKBuffer > &buffer)
Definition VKBuffer.cpp:514
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.
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.
constexpr ShaderID INVALID_SHADER
constexpr FenceID INVALID_FENCE
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
Kakshya::GpuBlockLayout push_constant_layout(const std::vector< PushConstantField > &fields)
Offsets and total size of a push constant block, under std430.
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
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