MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ShaderProcessor.hpp
Go to the documentation of this file.
1#pragma once
2
4
6
7namespace MayaFlux::Buffers {
8
9/**
10 * @struct ShaderBinding
11 * @brief Describes how a VKBuffer binds to a shader descriptor
12 */
14 uint32_t set = 0; ///< Descriptor set index
15 uint32_t binding = 0; ///< Binding point within set
16 vk::DescriptorType type = vk::DescriptorType::eStorageBuffer;
17 uint32_t count = 1; ///< Array count for array descriptors (default 1)
18
19 ShaderBinding() = default;
20
21 /**
22 * @brief Construct with semantic role — preferred public API.
23 */
25 : set(s)
26 , binding(b)
27 , type(to_vk_descriptor_type(role))
28 , count(c)
29 {
30 }
31
32 /**
33 * @brief Construct with explicit Vulkan type — internal / advanced use only.
34 */
35 ShaderBinding(uint32_t s, uint32_t b, vk::DescriptorType t, uint32_t c = 1)
36 : set(s)
37 , binding(b)
38 , type(t)
39 , count(c)
40 {
41 }
42};
43
44/**
45 * @struct ShaderProcessorConfig
46 * @brief Complete configuration for shader processor
47 */
49 std::string shader_path; ///< Path to shader file
51 std::string entry_point = "main";
53
54 std::unordered_map<std::string, ShaderBinding> bindings;
55
57
58 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.
59
60 std::unordered_map<uint32_t, uint32_t> specialization_constants;
61
62 ShaderConfig() = default;
63 ShaderConfig(std::string path)
64 : shader_path(std::move(path))
65 {
66 }
68 : shader_id(Portal::Graphics::get_shader_foundry().load_shader(spec))
69 , push_constant_size(spec.push_constant_bytes)
70 , pc_fields(spec.pc_fields)
71 {
72 }
73};
74
75/**
76 * @class ShaderProcessor
77 * @brief Abstract base class for shader-based buffer processing
78 *
79 * ShaderProcessor provides the foundational infrastructure for managing shader resources,
80 * descriptor sets, and buffer bindings. It is designed to be stage-agnostic, serving as
81 * the common parent for specialized processors like ComputeProcessor and RenderProcessor.
82 *
83 * Core Responsibilities:
84 * - **Shader Management:** Loads and manages shader modules via Portal::Graphics::ShaderFoundry.
85 * - **Descriptor Management:** Handles descriptor set allocation, updates, and binding.
86 * - **Buffer Binding:** Maps logical names (e.g., "input", "output") to physical VKBuffers.
87 * - **Constants:** Manages push constants and specialization constants.
88 * - **Hot-Reload:** Supports runtime shader reloading and pipeline invalidation.
89 *
90 * It does NOT define specific pipeline creation or execution logic (e.g., dispatch vs draw),
91 * leaving those details to derived classes (ComputeProcessor, RenderProcessor).
92 *
93 * Quality-of-life features:
94 * - **Data movement hints:** Query buffer usage (input/output/in-place) for automation.
95 * - **Binding introspection:** Validate if required bindings are satisfied.
96 * - **State queries:** Track processing state for chain management.
97 *
98 * Design Philosophy:
99 * - **Inheritance-focused**: Provides the "plumbing" for shader processors without dictating the pipeline type.
100 * - **Buffer-agnostic**: Works with any VKBuffer modality/usage.
101 * - **Flexible binding**: Decouples logical shader parameters from physical buffers.
102 *
103 * Integration:
104 * - Base class for `ComputeProcessor` (Compute Pipelines)
105 * - Base class for `RenderProcessor` (Graphics Pipelines)
106 * - Base class for `NodeBindingsProcessor` (Node-driven parameters)
107 *
108 * Usage (via derived classes):
109 * // Compute example
110 * auto compute = std::make_shared<ComputeProcessor>("shaders/kernel.comp");
111 * compute->bind_buffer("data", buffer);
112 *
113 * // Graphics example
114 * auto render = std::make_shared<RenderProcessor>(config);
115 * render->bind_buffer("vertices", vertex_buffer);
116 */
117class MAYAFLUX_API ShaderProcessor : public VKBufferProcessor {
118public:
119 /**
120 * @brief Get buffer usage characteristics needed for safe data flow
121 *
122 * Returns flags indicating:
123 * - Does compute read from input? (HOST_TO_DEVICE upload needed?)
124 * - Does compute write to output? (DEVICE_TO_HOST readback needed?)
125 *
126 * This lets ComputeProcessingChain auto-determine staging needs.
127 */
128 enum class BufferUsageHint : uint8_t {
129 NONE = 0,
130 INPUT_READ = 1 << 0, ///< Shader reads input
131 OUTPUT_WRITE = 1 << 1, ///< Shader writes output (modifies)
132 BIDIRECTIONAL = INPUT_READ | OUTPUT_WRITE
133 };
134
135 /**
136 * @brief Construct processor with shader path
137 * @param shader_path Path to shader file (e.g., .comp, .vert, .frag, .spv)
138 */
139 explicit ShaderProcessor(const std::string& shader_path);
140
141 /**
142 * @brief Construct processor with full configuration
143 * @param config Complete shader processor configuration
144 */
145 explicit ShaderProcessor(ShaderConfig config);
146
147 ~ShaderProcessor() override;
148
149 //==========================================================================
150 // BufferProcessor Interface
151 //==========================================================================
152
153 void processing_function(const std::shared_ptr<Buffer>& buffer) override;
154 void on_attach(const std::shared_ptr<Buffer>& buffer) override;
155 void on_detach(const std::shared_ptr<Buffer>& buffer) override;
156
157 [[nodiscard]] bool is_compatible_with(const std::shared_ptr<Buffer>& buffer) const override;
158
159 //==========================================================================
160 // Buffer Binding - Multi-buffer Support
161 //==========================================================================
162
163 /**
164 * @brief Bind a VKBuffer to a named shader descriptor
165 * @param descriptor_name Logical name (e.g., "input", "output")
166 * @param buffer VKBuffer to bind
167 *
168 * Registers the buffer for descriptor set binding.
169 * The descriptor_name must match a key in config.bindings.
170 */
171 void bind_buffer(const std::string& descriptor_name, const std::shared_ptr<VKBuffer>& buffer);
172
173 /**
174 * @brief Unbind a buffer from a descriptor
175 * @param descriptor_name Logical name to unbind
176 */
177 void unbind_buffer(const std::string& descriptor_name);
178
179 /**
180 * @brief Get bound buffer for a descriptor name
181 * @param descriptor_name Logical name
182 * @return Bound buffer, or nullptr if not bound
183 */
184 [[nodiscard]] std::shared_ptr<VKBuffer> get_bound_buffer(const std::string& descriptor_name) const;
185
186 /**
187 * @brief Auto-bind buffer based on attachment order
188 * @param buffer Buffer to auto-bind
189 *
190 * First attachment -> "input" or first binding
191 * Second attachment -> "output" or second binding
192 * Useful for simple single-buffer or input/output patterns.
193 */
194 void auto_bind_buffer(const std::shared_ptr<VKBuffer>& buffer);
195
196 /**
197 * @brief Download the buffer currently bound to a named descriptor.
198 * @param descriptor_name Logical name previously bound via bind_buffer.
199 * @param data Destination pointer, at least the bound buffer's size in bytes.
200 * @param size Byte count to copy.
201 * @param staging Optional staging buffer, forwarded to download_from_gpu.
202 * @return True if the name resolved to a bound buffer and the download ran.
203 */
204 bool download_bound(
205 const std::string& descriptor_name,
206 void* data,
207 size_t size,
208 const std::shared_ptr<VKBuffer>& staging = nullptr) const;
209
210 template <typename T>
211 bool download_bound(const std::string& descriptor_name, std::vector<T>& data) const
212 {
213 auto buffer = get_bound_buffer(descriptor_name);
214 if (!buffer) {
215 MF_ERROR(Journal::Component::Buffers, Journal::Context::BufferProcessing,
216 "download_bound: no buffer bound to descriptor '{}'", descriptor_name);
217 return false;
218 }
220 return true;
221 }
222
223 //==========================================================================
224 // Feeds
225 //==========================================================================
226
227 /**
228 * @brief What a feed callable returns.
229 *
230 * A push constant entry takes a double, written at the entry's width, or
231 * a GpuValue, written as its own format's bytes at the offset. A storage
232 * descriptor entry takes a DataVariant. Which one an entry expects is
233 * fixed when it is registered by the name it resolves to.
234 *
235 * A float or int literal converts to the double alternative; wrap a
236 * value in Kakshya::GpuValue to feed it as its own format.
237 */
238 using FeedValue = std::variant<double, Kakshya::GpuValue, Kakshya::DataVariant>;
239
240 /** @brief A callable pulled once per processing cycle. */
241 using FeedSource = std::function<FeedValue()>;
242
243 /**
244 * @brief Supply a shader input from a callable, resolved by name.
245 * @param name A name already declared on this shader: a descriptor in
246 * config.bindings, or a push constant field from the ShaderSpec
247 * this processor was constructed from.
248 * @param source Callable pulled each cycle. Returns a DataVariant for a
249 * descriptor name, a double for a push constant field.
250 *
251 * Resolution happens once, here. Descriptor names take precedence; a
252 * name matching neither is an error and registers nothing. File-shader
253 * processors have no record of push constant field names, so their
254 * constant feeds must use the explicit overload.
255 */
256 void feed(const std::string& name, FeedSource source);
257
258 /**
259 * @brief Supply a push constant from a callable at an explicit offset.
260 * @param name Logical name, used for removal and error reporting. Need
261 * not match anything on the shader.
262 * @param source Callable returning a double or a GpuValue.
263 * @param offset Byte offset in the push constant block.
264 * @param size Byte width written for a double. Four narrows to float,
265 * eight keeps double. Other values are rejected. A GpuValue
266 * writes its own width and ignores this.
267 * @param early True writes at the top of processing_function. False
268 * writes after the final on_before_execute of a compute dispatch,
269 * which is the only way a value survives a processor that rewrites
270 * its whole push constant block there. Other dispatch kinds
271 * ignore false.
272 *
273 * The form hand-written shaders need, since their push constant block
274 * is described nowhere the processor can read.
275 */
276 void feed(const std::string& name, FeedSource source, uint32_t offset, size_t size = sizeof(float), bool early = true);
277
278 /** @brief Remove a feed. Any buffer it created is released. */
279 void remove_feed(const std::string& name);
280
281 /** @brief Whether a feed of this name is registered. */
282 [[nodiscard]] bool has_feed(const std::string& name) const;
283
284 /** @brief Names of every registered feed. */
285 [[nodiscard]] std::vector<std::string> get_feed_names() const;
286
287 //==========================================================================
288 // Shader Management
289 //==========================================================================
290
291 /**
292 * @brief Hot-reload shader from ShaderFoundry
293 * @return True if reload succeeded
294 *
295 * Invalidates cached shader and rebuilds pipeline.
296 * Existing descriptor sets are preserved if compatible.
297 */
298 bool hot_reload_shader();
299
300 /**
301 * @brief Update shader path and reload
302 * @param shader_path New shader path
303 */
304 void set_shader(const std::string& shader_path);
305
306 /**
307 * @brief Get current shader path
308 */
309 [[nodiscard]] const std::string& get_shader_path() const { return m_config.shader_path; }
310
311 //==========================================================================
312 // Push Constants
313 //==========================================================================
314
315 /**
316 * @brief Set push constant size
317 * @param size Size in bytes
318 */
319 void set_push_constant_size(size_t size);
320
321 /**
322 * @brief Set push constant size from type
323 * @tparam T Push constant struct type
324 */
325 template <typename T>
327 {
328 set_push_constant_size(sizeof(T));
329 }
330
331 /**
332 * @brief Update push constant data (type-safe)
333 * @tparam T Push constant struct type
334 * @param data Push constant data
335 *
336 * Data is copied and uploaded during next process() call.
337 */
338 template <typename T>
339 void set_push_constant_data(const T& data);
340
341 /**
342 * @brief Update push constant data (raw bytes)
343 * @param data Pointer to data
344 * @param size Size in bytes
345 */
346 virtual void set_push_constant_data_raw(const void* data, size_t size);
347
348 /**
349 * @brief Get current push constant data
350 */
351 [[nodiscard]] const std::vector<uint8_t>& get_push_constant_data() const { return m_push_constant_data; }
352 [[nodiscard]] std::vector<uint8_t>& get_push_constant_data() { return m_push_constant_data; }
353
354 //==========================================================================
355 // Specialization Constants
356 //==========================================================================
357
358 /**
359 * @brief Set specialization constant
360 * @param constant_id Specialization constant ID
361 * @param value Value to set
362 *
363 * Requires pipeline recreation to take effect.
364 */
365 void set_specialization_constant(uint32_t constant_id, uint32_t value);
366
367 /**
368 * @brief Clear all specialization constants
369 */
370 void clear_specialization_constants();
371
372 //==========================================================================
373 // Configuration
374 //==========================================================================
375
376 /**
377 * @brief Update entire configuration
378 * @param config New configuration
379 *
380 * Triggers pipeline recreation.
381 */
382 void set_config(const ShaderConfig& config);
383
384 /**
385 * @brief Get current configuration
386 */
387 [[nodiscard]] const ShaderConfig& get_config() const { return m_config; }
388
389 /**
390 * @brief Add descriptor binding configuration
391 * @param descriptor_name Logical name
392 * @param binding Shader binding info
393 */
394 void add_binding(const std::string& descriptor_name, const ShaderBinding& binding);
395
396 //==========================================================================
397 // Data movement hints
398 //==========================================================================
399
400 /**
401 * @brief Get buffer usage hint for a descriptor
402 * @param descriptor_name Binding name
403 * @return BufferUsageHint flags
404 */
405 [[nodiscard]] virtual BufferUsageHint get_buffer_usage_hint(const std::string& descriptor_name) const;
406
407 /**
408 * @brief Check if shader modifies a specific buffer in-place
409 * @param descriptor_name Binding name
410 * @return True if shader both reads and writes this buffer
411 */
412 [[nodiscard]] virtual bool is_in_place_operation(const std::string& descriptor_name) const;
413
414 /**
415 * @brief Check if a descriptor binding exists
416 * @param descriptor_name Name of the binding (e.g., "input", "output")
417 * @return True if binding is configured
418 */
419 [[nodiscard]] bool has_binding(const std::string& descriptor_name) const;
420
421 /**
422 * @brief Get all configured descriptor names
423 * @return Vector of binding names
424 *
425 * Useful for introspection: which buffers does this shader expect?
426 */
427 [[nodiscard]] std::vector<std::string> get_binding_names() const;
428
429 /**
430 * @brief Check if all required bindings are satisfied
431 * @return True if all configured bindings have buffers bound
432 */
433 [[nodiscard]] bool are_bindings_complete() const;
434
435 //==========================================================================
436 // State Queries
437 //==========================================================================
438
439 /**
440 * @brief Check if shader is loaded
441 */
442 [[nodiscard]] bool is_shader_loaded() const { return m_shader_id != Portal::Graphics::INVALID_SHADER; }
443
444 /**
445 * @brief Check if descriptors are initialized
446 */
447 [[nodiscard]] bool are_descriptors_ready() const { return !m_descriptor_set_ids.empty(); }
448
449 /**
450 * @brief Get number of bound buffers
451 */
452 [[nodiscard]] size_t get_bound_buffer_count() const { return m_bound_buffers.size(); }
453
454 /**
455 * @brief Get the output buffer after compute dispatch
456 *
457 * Returns the buffer that was last processed (input/output depends on
458 * shader and binding configuration). Used by ComputeProcessingChain
459 * to determine where compute results ended up.
460 *
461 * Typically the buffer passed to processing_function(), but can be
462 * overridden by subclasses if compute modifies different buffers.
463 */
464 [[nodiscard]] virtual std::shared_ptr<VKBuffer> get_output_buffer() const { return m_last_processed_buffer; }
465
466 /**
467 * @brief Submit asynchronously and resolve at the top of a later cycle.
468 * @param deferred True to submit via submit_async, false for submit_and_wait.
469 *
470 * Only meaningful for children that submit their own command buffers.
471 * Children that record without submitting, such as RenderProcessor,
472 * are unaffected. Switching back to synchronous while a submission is
473 * outstanding waits for and releases it first.
474 */
475 void set_deferred_submission(bool deferred);
476
477 /** @brief Whether this processor submits asynchronously. */
478 [[nodiscard]] bool is_deferred_submission() const { return m_deferred_submission; }
479
480 /** @brief True while an asynchronous submission is outstanding. */
481 [[nodiscard]] bool is_dispatch_pending() const
482 {
483 return m_pending_fence != Portal::Graphics::INVALID_FENCE;
484 }
485
486 /**
487 * @brief Resolve an outstanding asynchronous submission.
488 * @param block True to wait for the fence, false to return immediately
489 * when it is not yet signaled.
490 * @return True if a submission was resolved by this call.
491 *
492 * On resolution invokes on_dispatch_complete, then release_fence, which
493 * also frees the associated command buffer. Invoked with block=false at
494 * the top of processing_function and with block=true from cleanup.
495 */
496 bool resolve_pending_dispatch(bool block);
497
498 /**
499 * @brief Check if compute has been executed at least once
500 * @return True if processing_function() has been called
501 */
502 [[nodiscard]] virtual inline bool has_executed() const
503 {
504 return m_last_command_buffer != Portal::Graphics::INVALID_COMMAND_BUFFER;
505 }
506
507protected:
508 /**
509 * @brief Byte width of this processor's push constant block, extended to
510 * cover any fragment staged on the buffer.
511 */
512 [[nodiscard]] size_t resolve_push_constant_size(const std::shared_ptr<VKBuffer>& buffer) const;
513
514 /**
515 * @brief This processor's push constant data with buffer-staged fragments
516 * overlaid at their declared offsets.
517 */
518 [[nodiscard]] std::vector<uint8_t> resolve_push_constants(const std::shared_ptr<VKBuffer>& buffer) const;
519
520 /**
521 * @brief resolve_push_constants() written into @p out, reusing its storage.
522 */
523 void resolve_push_constants_into(std::vector<uint8_t>& out, const std::shared_ptr<VKBuffer>& buffer) const;
524
525 /**
526 * @brief Whether the owner builds its pipeline layout from this processor's
527 * bindings, staged descriptors and push range.
528 *
529 * When true, processing_function requests a new pipeline as soon as that
530 * effective layout differs from the one the previous initialize_pipeline
531 * call saw. Defaults to false for processors that create no pipeline.
532 */
533 [[nodiscard]] virtual bool tracks_pipeline_layout() const { return false; }
534
535 /**
536 * @brief Pull every feed and write its result.
537 *
538 * Called at the top of processing_function, before descriptors are
539 * rebuilt, so a storage feed creating its buffer binds the same cycle.
540 * Callables run on whichever thread drives the chain.
541 *
542 * @param early Only feeds registered with this timing are pulled.
543 */
544 void pump_feeds(bool early = true);
545
546 //==========================================================================
547 // Overridable Hooks for Specialized Processors
548 //==========================================================================
549
550 /**
551 * @brief Called before shader compilation
552 * @param shader_path Path to shader
553 *
554 * Override to modify shader compilation (e.g., add defines, includes).
555 */
556 virtual void on_before_compile(const std::string& shader_path);
557
558 /**
559 * @brief Called after shader is loaded
560 * @param shader Loaded shader module
561 *
562 * Override to extract reflection data or validate shader.
563 */
564 virtual void on_shader_loaded(Portal::Graphics::ShaderID shader_id);
565
566 /**
567 * @brief Called before pipeline creation
568 * @param config Pipeline configuration
569 *
570 * Override to modify pipeline configuration.
571 */
573
574 /**
575 * @brief Called after pipeline is created
576 * @param pipeline Created pipeline
577 *
578 * Override for post-pipeline setup.
579 */
580 virtual void on_pipeline_created(Portal::Graphics::ComputePipelineID pipeline_id);
581
582 /**
583 * @brief Called before descriptor sets are created
584 *
585 * Override to add custom descriptor bindings.
586 */
587 virtual void on_before_descriptors_create();
588
589 /**
590 * @brief Called after descriptor sets are created
591 *
592 * Override for custom descriptor updates.
593 */
594 virtual void on_descriptors_created();
595
596 /**
597 * @brief Called before each process callback
598 * @param cmd Command buffer
599 * @param buffer Currently processing buffer
600 * @return True to proceed with execution, false to skip
601 *
602 * Override to update push constants or dynamic descriptors.
603 */
604 virtual bool on_before_execute(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr<VKBuffer>& buffer);
605
606 /**
607 * @brief Called after each process callback
608 * @param cmd Command buffer
609 * @param buffer Currently processed buffer
610 *
611 * Override for post-dispatch synchronization or state updates.
612 */
613 virtual void on_after_execute(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr<VKBuffer>& buffer);
614
615 /**
616 * @brief Called once when an asynchronous submission is observed complete.
617 * @param buffer Buffer processed by the completed submission.
618 *
619 * The correct place for device-to-host readback under deferred
620 * submission, since on_after_execute runs at record time, before the
621 * GPU has executed anything. Never invoked under synchronous
622 * submission, where submit_and_wait already precedes the return.
623 */
624 virtual void on_dispatch_complete(const std::shared_ptr<VKBuffer>& buffer);
625
626 /**
627 * @brief Submit a recorded command buffer honoring the submission mode.
628 * @param cmd_id Command buffer to submit.
629 * @param buffer Buffer being processed. Retained until resolution when
630 * deferred, so on_dispatch_complete receives the same instance.
631 *
632 * Synchronous submission calls submit_and_wait and returns. Deferred
633 * submission calls submit_async and stores the fence; a failed
634 * submission falls back to leaving nothing pending.
635 */
636 void submit_recorded(Portal::Graphics::CommandBufferID cmd_id, const std::shared_ptr<VKBuffer>& buffer);
637
638 /**
639 * @brief Resolve logical descriptor set index to actual index
640 * @param set Logical set index from ShaderBinding
641 * @return Resolved set index, or std::nullopt if invalid
642 *
643 * Handles cases where the engine reserves set 0 for global resources.
644 * If m_engine_owns_set_zero is true, logical set 0 maps to no descriptor,
645 * and logical sets are offset by +1 in the actual descriptor sets.
646 */
647 [[nodiscard]] std::optional<uint32_t> resolve_ds_index(uint32_t set) const;
648
649 //==========================================================================
650 // Protected State - Available to Subclasses
651 //==========================================================================
652
654
655 Portal::Graphics::ShaderID m_shader_id = Portal::Graphics::INVALID_SHADER;
656 std::vector<Portal::Graphics::DescriptorSetID> m_descriptor_set_ids;
657 Portal::Graphics::CommandBufferID m_last_command_buffer = Portal::Graphics::INVALID_COMMAND_BUFFER;
658
659 std::unordered_map<std::string, std::shared_ptr<VKBuffer>> m_bound_buffers;
660 std::shared_ptr<VKBuffer> m_last_processed_buffer;
661
662 bool m_deferred_submission {}; ///< False submits synchronously, preserving pre-existing behaviour.
663 Portal::Graphics::FenceID m_pending_fence { Portal::Graphics::INVALID_FENCE }; ///< Outstanding async submission, if any.
664 std::shared_ptr<VKBuffer> m_pending_buffer; ///< Buffer retained for the outstanding submission.
665
666 std::vector<uint8_t> m_push_constant_data;
667
668 bool m_initialized {};
669 bool m_needs_pipeline_rebuild = true;
670 bool m_needs_descriptor_rebuild = true;
671
672 size_t m_auto_bind_index {};
673
674 /**
675 * @brief Whether the engine reserves set=0 for global resources
676 *
677 * Defaults to false. Only RenderProcessor sets this to true in its
678 * constructor. When true, resolve_ds_index() maps logical set=0 to
679 * nullopt (no user descriptor) and offsets all other sets by -1.
680 * Compute subclasses leave this false: their descriptor sets are
681 * numbered from set=0 with no engine reservation.
682 *
683 * A future subclass that needs engine-owned sets must set this
684 * explicitly and be aware of the index offset applied by resolve_ds_index.
685 */
686 bool m_engine_owns_set_zero {};
687
688 virtual void initialize_pipeline(const std::shared_ptr<VKBuffer>& buffer) = 0;
689 virtual void initialize_descriptors(const std::shared_ptr<VKBuffer>& buffer) = 0;
690 virtual void execute_shader(const std::shared_ptr<VKBuffer>& buffer) = 0;
691
692 virtual void update_descriptors(const std::shared_ptr<VKBuffer>& buffer);
693 virtual void cleanup();
694
695private:
696 //==========================================================================
697 // Internal Implementation
698 //==========================================================================
699
700 /**
701 * @struct Feed
702 * @brief One registered callable and where its result is written.
703 */
704 struct Feed {
706 bool is_storage {};
707 uint32_t offset {};
708 size_t size {};
709 std::optional<Kakshya::GpuDataFormat> format;
710 std::string descriptor_name;
711 std::shared_ptr<VKBuffer> buffer;
712 bool mismatch_logged {};
713 bool early { true };
714 };
715
716 std::unordered_map<std::string, Feed> m_feeds;
717
718 std::optional<uint64_t> m_created_layout;
719
720 void initialize_shader();
721 [[nodiscard]] uint64_t layout_fingerprint(const std::shared_ptr<VKBuffer>& buffer) const;
722 void note_created_layout(const std::shared_ptr<VKBuffer>& buffer);
723};
724
725template <typename T>
727{
728 const auto size = sizeof(T);
729 static_assert(size <= 128, "Push constants typically limited to 128 bytes");
730 if (m_push_constant_data.size() < size) {
732 }
733
734 std::memcpy(m_push_constant_data.data(), &data, size);
735}
736
737} // namespace MayaFlux::Buffers
#define MF_ERROR(comp, ctx,...)
uint64_t offset
Definition Composite.cpp:10
bool size
std::shared_ptr< NetworkGeometryBuffer > buffer
size_t b
std::vector< float > * out
glm::vec3 value
std::string name
Definition VKDevice.cpp:123
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.
size_t get_bound_buffer_count() const
Get number of bound buffers.
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...
std::vector< uint8_t > m_push_constant_data
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::vector< uint8_t > & get_push_constant_data()
std::optional< uint64_t > m_created_layout
bool is_deferred_submission() const
Whether this processor submits asynchronously.
const std::string & get_shader_path() const
Get current shader path.
void set_push_constant_size()
Set push constant size from type.
const ShaderConfig & get_config() const
Get current configuration.
virtual bool has_executed() const
Check if compute has been executed at least once.
void set_push_constant_data(const T &data)
Update push constant data (type-safe)
bool are_descriptors_ready() const
Check if descriptors are initialized.
std::shared_ptr< VKBuffer > m_last_processed_buffer
bool is_shader_loaded() const
Check if shader is loaded.
virtual std::shared_ptr< VKBuffer > get_output_buffer() const
Get the output buffer after compute dispatch.
BufferUsageHint
Get buffer usage characteristics needed for safe data flow.
virtual void on_before_pipeline_create(Portal::Graphics::ComputePipelineID pipeline_id)
Called before pipeline creation.
bool is_dispatch_pending() const
True while an asynchronous submission is outstanding.
std::unordered_map< std::string, Feed > m_feeds
std::function< FeedValue()> FeedSource
A callable pulled once per processing cycle.
bool download_bound(const std::string &descriptor_name, std::vector< T > &data) const
std::variant< double, Kakshya::GpuValue, Kakshya::DataVariant > FeedValue
What a feed callable returns.
std::vector< Portal::Graphics::DescriptorSetID > m_descriptor_set_ids
Abstract base class for shader-based buffer processing.
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)
constexpr ShaderID INVALID_SHADER
ShaderStage
User-friendly shader stage enum.
DescriptorRole
Semantic descriptor type — maps to Vulkan descriptor types internally.
@ STORAGE
Large arrays or buffers the shader may write (SSBO)
static constexpr DomainSpec Graphics
Domain constant for Graphics domain.
Definition Creator.hpp:444
uint32_t binding
Binding point within set.
ShaderBinding(uint32_t s, uint32_t b, vk::DescriptorType t, uint32_t c=1)
Construct with explicit Vulkan type — internal / advanced use only.
uint32_t set
Descriptor set index.
ShaderBinding(uint32_t s, uint32_t b, Portal::Graphics::DescriptorRole role=Portal::Graphics::DescriptorRole::STORAGE, uint32_t c=1)
Construct with semantic role — preferred public API.
uint32_t count
Array count for array descriptors (default 1)
Describes how a VKBuffer binds to a shader descriptor.
ShaderConfig(const Portal::Graphics::ShaderSpec &spec)
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
std::optional< Kakshya::GpuDataFormat > format
One registered callable and where its result is written.
Complete declarative description of a generated compute shader.