MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ShaderFoundry.hpp
Go to the documentation of this file.
1#pragma once
2
3#include "ShaderUtils.hpp"
4
5namespace MayaFlux::Core {
6class VulkanBackend;
7class VKShaderModule;
8class VKComputePipeline;
9class VKDescriptorManager;
10}
11
13
14class ComputePress;
15class RenderFlow;
16
17/**
18 * @class ShaderFoundry
19 * @brief Portal-level shader compilation and caching
20 *
21 * ShaderFoundry is a thin glue layer that:
22 * - Wraps Core::VKShaderModule for convenient shader creation
23 * - Provides caching to avoid redundant compilation
24 * - Supports hot-reload workflows (watch files, recompile)
25 * - Returns VKShaderModule directly for use in pipelines
26 *
27 * Design Philosophy:
28 * - Manages compilation, NOT execution (that's Pipeline/Compute)
29 * - Returns VKShaderModule directly (no wrapping)
30 * - Simple, focused API aligned with VKShaderModule capabilities
31 * - Integrates with existing Core shader infrastructure
32 *
33 * Consumers:
34 * - VKBufferProcessor subclasses (compute shaders)
35 * - Future Yantra::ComputePipeline (compute shaders)
36 * - Future Yantra::RenderPipeline (graphics shaders)
37 * - Future DataProcessors (image processing shaders)
38 *
39 * Usage:
40 * auto& compiler = Portal::Graphics::ShaderFoundry::instance();
41 *
42 * // Compile from file
43 * auto shader = compiler.compile_from_file("shaders/my_kernel.comp");
44 *
45 * // Compile from string
46 * auto shader = compiler.compile_from_source(glsl_code, ShaderStage::COMPUTE);
47 *
48 * // Use in pipeline
49 * my_buffer_processor->set_shader(shader);
50 * my_compute_pipeline->set_shader(shader);
51 */
52class MAYAFLUX_API ShaderFoundry {
53public:
54 enum class CommandBufferType : uint8_t {
56 COMPUTE,
57 TRANSFER
58 };
59
60 enum class CommandBufferLevel : uint8_t {
61 PRIMARY,
62 SECONDARY
63 };
64
65private:
67 vk::DescriptorSet descriptor_set;
68 };
69
71 vk::CommandBuffer cmd;
73 CommandBufferLevel level { CommandBufferLevel::PRIMARY };
75 vk::QueryPool timestamp_pool;
76 std::unordered_map<std::string, uint32_t> timestamp_queries;
77 };
78
79 struct FenceState {
80 vk::Fence fence;
82 };
83
85 vk::Semaphore semaphore;
86 };
87
88 struct ShaderState {
89 std::shared_ptr<Core::VKShaderModule> module;
90 std::string filepath;
92 std::string entry_point;
93 };
94
95public:
97 {
98 static ShaderFoundry compiler;
99 return compiler;
100 }
101
102 ShaderFoundry(const ShaderFoundry&) = delete;
104 ShaderFoundry(ShaderFoundry&&) noexcept = delete;
105 ShaderFoundry& operator=(ShaderFoundry&&) noexcept = delete;
106
107 /**
108 * @brief Initialize shader compiler
109 * @param backend Shared pointer to VulkanBackend
110 * @param config Compiler configuration
111 * @return True if initialization succeeded
112 *
113 * Must be called before compiling any shaders.
114 */
115 bool initialize(
116 const std::shared_ptr<Core::VulkanBackend>& backend,
117 const ShaderCompilerConfig& config = {});
118
119 /**
120 * @brief Stop active command recording and free command buffers
121 *
122 * Frees all command buffers back to pool and destroys query pools.
123 * Call this BEFORE destroying pipelines/resources that command buffers reference.
124 * Does NOT destroy the command pool itself - that happens in shutdown().
125 */
126 void stop();
127
128 /**
129 * @brief Shutdown and cleanup all ShaderFoundry resources
130 *
131 * Destroys sync objects, descriptor resources, and shader modules.
132 * Must be called AFTER stop() and AFTER pipeline consumers (RenderFlow/ComputePress) shutdown.
133 */
134 void shutdown();
135
136 /**
137 * @brief Check if compiler is initialized
138 */
139 [[nodiscard]] bool is_initialized() const { return m_backend != nullptr; }
140
141 //==========================================================================
142 // Shader Compilation - Primary API
143 //==========================================================================
144
145 /**
146 * @brief Universal shader loader - auto-detects source type
147 * @param content File path, GLSL source string, or SPIR-V path
148 * @param stage Optional stage override (auto-detected if omitted)
149 * @param entry_point Entry point function name (default: "main")
150 * @return ShaderID, or INVALID_SHADER on failure
151 *
152 * Supports:
153 * - GLSL files: .comp, .vert, .frag, .geom, .tesc, .tese
154 * - SPIR-V files: .spv
155 *
156 * Stage auto-detection:
157 * .comp → COMPUTE
158 * .vert → VERTEX
159 * .frag → FRAGMENT
160 * .geom → GEOMETRY
161 * .tesc → TESS_CONTROL
162 * .tese → TESS_EVALUATION
163 * .mesh → MESH
164 * .task → TASK
165 *
166 * Examples:
167 * load_shader("shaders/kernel.comp"); // File
168 * load_shader("shaders/kernel.spv", COMPUTE); // SPIR-V
169 * load_shader("#version 450\nvoid main(){}", COMPUTE); // Source
170 */
171 ShaderID load_shader(
172 const std::string& content,
173 std::optional<ShaderStage> stage = std::nullopt,
174 const std::string& entry_point = "main");
175
176 /**
177 * @brief Load shader from explicit ShaderSource descriptor
178 * @param source Complete shader source specification
179 * @return ShaderID, or INVALID_SHADER on failure
180 */
181 ShaderID load_shader(const ShaderSource& source);
182
183 /**
184 * @brief Hot-reload shader (returns new ID)
185 */
186 ShaderID reload_shader(const std::string& filepath);
187
188 /**
189 * @brief Destroy shader (cleanup internal state)
190 */
191 void destroy_shader(ShaderID shader_id);
192
193 /**
194 * @brief Compile shader from ShaderSource descriptor
195 * @param shader_source Shader descriptor (path or source + type + stage)
196 * @return Compiled shader module, or nullptr on failure
197 *
198 * Unified interface that dispatches to appropriate compile method.
199 * Useful for abstracted shader loading pipelines.
200 */
201 std::shared_ptr<Core::VKShaderModule> compile(const ShaderSource& shader_source);
202
203 //==========================================================================
204 // Shader Introspection
205 //==========================================================================
206
207 /**
208 * @brief Get reflection info for compiled shader
209 * @param shader_id ID of compiled shader
210 * @return Reflection information
211 *
212 * Extracted during compilation if enabled in config.
213 * Includes descriptor bindings, push constant ranges, workgroup size, etc.
214 */
215 ShaderReflectionInfo get_shader_reflection(ShaderID shader_id);
216
217 /**
218 * @brief Get shader stage for compiled shader
219 * @param shader_id ID of compiled shader
220 * @return Shader stage (COMPUTE, VERTEX, FRAGMENT, etc.)
221 */
222 ShaderStage get_shader_stage(ShaderID shader_id);
223
224 /**
225 * @brief Get entry point name for compiled shader
226 * @param shader_id ID of compiled shader
227 * @return Entry point function name
228 */
229 std::string get_shader_entry_point(ShaderID shader_id);
230
231 //==========================================================================
232 // Hot-Reload Support
233 //==========================================================================
234
235 /**
236 * @brief Invalidate cache for specific shader
237 * @param cache_key File path or cache key
238 *
239 * Forces next compilation to recompile from source.
240 * Useful for hot-reload workflows.
241 */
242 void invalidate_cache(const std::string& cache_key);
243
244 /**
245 * @brief Invalidate entire shader cache
246 *
247 * Forces all subsequent compilations to recompile.
248 * Does NOT destroy existing shader modules (they remain valid).
249 */
250 void clear_cache();
251
252 /**
253 * @brief Hot-reload a shader from file
254 * @param filepath Path to shader file
255 * @return Recompiled shader module, or nullptr on failure
256 *
257 * Convenience method: invalidate_cache() + compile_from_file().
258 * Returns new shader module; consumers must update references.
259 */
260 std::shared_ptr<Core::VKShaderModule> hot_reload(const std::string& filepath);
261
262 //==========================================================================
263 // Configuration
264 //==========================================================================
265
266 /**
267 * @brief Update compiler configuration
268 * @param config New configuration
269 *
270 * Affects future compilations.
271 * Does NOT recompile existing shaders.
272 */
273 void set_config(const ShaderCompilerConfig& config);
274
275 /**
276 * @brief Get current compiler configuration
277 */
278 [[nodiscard]] const ShaderCompilerConfig& get_config() const { return m_config; }
279
280 /**
281 * @brief Add include directory for shader compilation
282 * @param directory Path to directory containing shader includes
283 *
284 * Used for #include resolution in GLSL files.
285 */
286 void add_include_directory(const std::string& directory);
287
288 /**
289 * @brief Add preprocessor define for shader compilation
290 * @param name Macro name
291 * @param value Macro value (optional)
292 *
293 * Example: define("DEBUG", "1") → #define DEBUG 1
294 */
295 void add_define(const std::string& name, const std::string& value = "");
296
297 //==========================================================================
298 // Introspection
299 //==========================================================================
300
301 /**
302 * @brief Check if shader is cached
303 * @param cache_key File path or cache key
304 */
305 [[nodiscard]] bool is_cached(const std::string& cache_key) const;
306
307 /**
308 * @brief Get all cached shader keys
309 */
310 [[nodiscard]] std::vector<std::string> get_cached_keys() const;
311
312 /**
313 * @brief Get number of cached shaders
314 */
315 [[nodiscard]] size_t get_cache_size() const { return m_shader_cache.size(); }
316
317 //==========================================================================
318 // Descriptor Set Management - ShaderFoundry allocates and tracks
319 //==========================================================================
320
321 /**
322 * @brief Allocate descriptor set for a pipeline
323 * @param pipeline_id Which pipeline this is for
324 * @param set_index Which descriptor set (0, 1, 2...)
325 * @return Descriptor set ID
326 */
327 DescriptorSetID allocate_descriptor_set(vk::DescriptorSetLayout layout);
328
329 /**
330 * @brief Update descriptor set with buffer binding
331 * @param descriptor_set_id ID of descriptor set to update
332 * @param binding Binding index within the descriptor set
333 * @param type Descriptor type (e.g., eStorageBuffer, eUniformBuffer)
334 * @param buffer Vulkan buffer to bind
335 * @param offset Offset within the buffer
336 * @param size Size of the buffer region
337 */
338 void update_descriptor_buffer(
339 DescriptorSetID descriptor_set_id,
340 uint32_t binding,
341 vk::DescriptorType type,
342 vk::Buffer buffer,
343 size_t offset,
344 size_t size);
345
346 /**
347 * @brief Update descriptor set with image binding
348 * @param descriptor_set_id ID of descriptor set to update
349 * @param binding Binding index within the descriptor set
350 * @param image_view Vulkan image view to bind
351 * @param sampler Vulkan sampler to bind
352 * @param layout Image layout (default: eShaderReadOnlyOptimal)
353 * @param array_element Array index for array bindings (default: 0)
354 */
355 void update_descriptor_image(
356 DescriptorSetID descriptor_set_id,
357 uint32_t binding,
358 vk::ImageView image_view,
359 vk::Sampler sampler,
360 vk::ImageLayout layout = vk::ImageLayout::eShaderReadOnlyOptimal,
361 uint32_t array_element = 0);
362
363 /**
364 * @brief Update descriptor set with storage image binding
365 * @param descriptor_set_id ID of descriptor set to update
366 * @param binding Binding index within the descriptor set
367 * @param image_view Vulkan image view to bind
368 * @param layout Image layout (default: eGeneral)
369 */
370 void update_descriptor_storage_image(
371 DescriptorSetID descriptor_set_id,
372 uint32_t binding,
373 vk::ImageView image_view,
374 vk::ImageLayout layout = vk::ImageLayout::eGeneral);
375
376 /**
377 * @brief Get Vulkan descriptor set handle from DescriptorSetID
378 * @param descriptor_set_id Descriptor set ID
379 * @return Vulkan descriptor set handle
380 */
381 vk::DescriptorSet get_descriptor_set(DescriptorSetID descriptor_set_id);
382
383 //==========================================================================
384 // Command Recording - ShaderFoundry manages command buffers
385 //==========================================================================
386
387 /**
388 * @brief Begin recording command buffer
389 * @param type Command buffer type (GRAPHICS, COMPUTE, TRANSFER)
390 * @return Command buffer ID
391 */
392 CommandBufferID begin_commands(CommandBufferType type);
393
394 /**
395 * @brief Begin recording a secondary command buffer for dynamic rendering
396 * @param color_format Format of the color attachment (from swapchain)
397 * @return Command buffer ID
398 *
399 * With dynamic rendering, secondary buffers don't need render pass objects.
400 * They only need to know the attachment formats they'll render to.
401 */
402 CommandBufferID begin_secondary_commands(
403 vk::Format color_format,
404 vk::Format depth_format = vk::Format::eUndefined);
405
406 /**
407 * @brief Get Vulkan command buffer handle from CommandBufferID
408 * @param cmd_id Command buffer ID
409 */
410 vk::CommandBuffer get_command_buffer(CommandBufferID cmd_id);
411
412 /**
413 * @brief End recording command buffer
414 * @param cmd_id Command buffer ID to end
415 * @return True if successful, false if invalid ID or not active
416 */
417 bool end_commands(CommandBufferID cmd_id);
418
419 /**
420 * @brief Free all allocated command buffers
421 */
422 void free_all_command_buffers();
423
424 //==========================================================================
425 // Memory Barriers and Synchronization
426 //==========================================================================
427
428 /**
429 * @brief Submit command buffer and wait for completion
430 * @param cmd_id Command buffer ID to submit
431 */
432 void submit_and_wait(CommandBufferID cmd_id);
433
434 /**
435 * @brief Submit command buffer asynchronously, returning a fence
436 * @param cmd_id Command buffer ID to submit
437 * @return Fence ID to wait on later
438 */
439 FenceID submit_async(CommandBufferID cmd_id);
440
441 /**
442 * @brief Submit command buffer asynchronously, returning a semaphore
443 * @param cmd_id Command buffer ID to submit
444 * @return Semaphore ID to wait on later
445 */
446 SemaphoreID submit_with_signal(CommandBufferID cmd_id);
447
448 /**
449 * @brief Wait for fence to be signaled
450 * @param fence_id Fence ID to wait on
451 */
452 void wait_for_fence(FenceID fence_id);
453
454 /**
455 * @brief Wait for multiple fences to be signaled
456 * @param fence_ids Vector of fence IDs to wait on
457 */
458 void wait_for_fences(const std::vector<FenceID>& fence_ids);
459
460 /**
461 * @brief Check if fence is signaled
462 * @param fence_id Fence ID to check
463 * @return True if fence is signaled, false otherwise
464 */
465 bool is_fence_signaled(FenceID fence_id);
466
467 /**
468 * @brief Begin command buffer that waits on a semaphore
469 * @param type Command buffer type (GRAPHICS, COMPUTE, TRANSFER)
470 * @param wait_semaphore Semaphore ID to wait on
471 * @param wait_stage Pipeline stage to wait at
472 * @return Command buffer ID
473 */
474 CommandBufferID begin_commands_with_wait(
475 CommandBufferType type,
476 SemaphoreID wait_semaphore,
477 vk::PipelineStageFlags wait_stage);
478
479 /**
480 * @brief Get Vulkan fence handle from FenceID
481 * @param fence_id Fence ID
482 */
483 vk::Semaphore get_semaphore_handle(SemaphoreID semaphore_id);
484
485 /**
486 * @brief Insert buffer memory barrier
487 */
488 void buffer_barrier(
489 CommandBufferID cmd_id,
490 vk::Buffer buffer,
491 vk::AccessFlags src_access,
492 vk::AccessFlags dst_access,
493 vk::PipelineStageFlags src_stage,
494 vk::PipelineStageFlags dst_stage);
495
496 /**
497 * @brief Insert image memory barrier
498 */
499 void image_barrier(
500 CommandBufferID cmd_id,
501 vk::Image image,
502 vk::ImageLayout old_layout,
503 vk::ImageLayout new_layout,
504 vk::AccessFlags src_access,
505 vk::AccessFlags dst_access,
506 vk::PipelineStageFlags src_stage,
507 vk::PipelineStageFlags dst_stage);
508
509 //==========================================================================
510 // Queue Management
511 //==========================================================================
512
513 /**
514 * @brief Set Vulkan queues for command submission
515 */
516 void set_graphics_queue(vk::Queue queue);
517
518 /**
519 * @brief Set Vulkan queues for command submission
520 */
521 void set_compute_queue(vk::Queue queue);
522
523 /**
524 * @brief Set Vulkan queues for command submission
525 */
526 void set_transfer_queue(vk::Queue queue);
527
528 /**
529 * @brief Get Vulkan graphics queue
530 */
531 [[nodiscard]] vk::Queue get_graphics_queue() const;
532
533 /**
534 * @brief Get Vulkan compute queue
535 */
536 [[nodiscard]] vk::Queue get_compute_queue() const;
537
538 /**
539 * @brief Get Vulkan transfer queue
540 */
541 [[nodiscard]] vk::Queue get_transfer_queue() const;
542
543 //==========================================================================
544 // Profiling
545 //==========================================================================
546
547 void begin_timestamp(CommandBufferID cmd_id, const std::string& label = "");
548 void end_timestamp(CommandBufferID cmd_id, const std::string& label = "");
549
551 std::string label;
552 uint64_t duration_ns;
553 bool valid;
554 };
555
556 TimestampResult get_timestamp_result(CommandBufferID cmd_id, const std::string& label);
557
558 //==========================================================================
559 // Utilities
560 //==========================================================================
561
562 /**
563 * @brief Convert Portal ShaderStage to Vulkan ShaderStageFlagBits
564 */
565 static vk::ShaderStageFlagBits to_vulkan_stage(ShaderStage stage);
566
567 /**
568 * @brief Auto-detect shader stage from file extension
569 * @param filepath Path to shader file
570 * @return Detected stage, or nullopt if unknown
571 *
572 * Delegates to VKShaderModule::detect_stage_from_extension().
573 */
574 static std::optional<ShaderStage> detect_stage_from_extension(const std::string& filepath);
575
576 //==========================================================================
577 // Device Access
578 //==========================================================================
579
580 /**
581 * @brief Get logical device handle
582 */
583 [[nodiscard]] vk::Device get_device() const;
584
585 /**
586 * @brief Get physical device handle
587 *
588 * Required by consumers that allocate raw Vulkan buffers directly
589 * (e.g. GpuComputeOperation) and need memory type selection via
590 * vk::PhysicalDevice::getMemoryProperties().
591 */
592 [[nodiscard]] vk::PhysicalDevice get_physical_device() const;
593
594private:
595 /**
596 * @enum DetectedSourceType
597 * @brief Internal enum for source type detection
598 */
599 enum class DetectedSourceType : uint8_t {
600 FILE_GLSL,
601 FILE_SPIRV,
602 SOURCE_STRING,
603 UNKNOWN
604 };
605
606 ShaderFoundry() = default;
607 ~ShaderFoundry() { shutdown(); }
608
609 std::shared_ptr<Core::VulkanBackend> m_backend;
611
612 std::unordered_map<std::string, std::shared_ptr<Core::VKShaderModule>> m_shader_cache;
613 std::unordered_map<ShaderID, ShaderState> m_shaders;
614 std::unordered_map<std::string, ShaderID> m_shader_filepath_cache;
615
616 std::shared_ptr<Core::VKDescriptorManager> m_global_descriptor_manager;
617 std::unordered_map<DescriptorSetID, DescriptorSetState> m_descriptor_sets;
618
619 std::unordered_map<CommandBufferID, CommandBufferState> m_command_buffers;
620 std::unordered_map<FenceID, FenceState> m_fences;
621 std::unordered_map<SemaphoreID, SemaphoreState> m_semaphores;
622
626
627 std::atomic<uint64_t> m_next_shader_id { 1 };
628 std::atomic<uint64_t> m_next_descriptor_set_id { 1 };
629 std::atomic<uint64_t> m_next_command_id { 1 };
630 std::atomic<uint64_t> m_next_fence_id { 1 };
631 std::atomic<uint64_t> m_next_semaphore_id { 1 };
632
633 DetectedSourceType detect_source_type(const std::string& content) const;
634 std::optional<std::filesystem::path> resolve_shader_path(const std::string& filepath) const;
635 std::string generate_source_cache_key(const std::string& source, ShaderStage stage) const;
636
637 std::shared_ptr<Core::VKShaderModule> create_shader_module();
638
639 void cleanup_sync_objects();
640 void cleanup_descriptor_resources();
641 void cleanup_shader_modules();
642
643 //==========================================================================
644 // INTERNAL Shader Compilation Methods
645 //==========================================================================
646
647 std::shared_ptr<Core::VKShaderModule> compile_from_file(
648 const std::string& filepath,
649 std::optional<ShaderStage> stage = std::nullopt,
650 const std::string& entry_point = "main");
651
652 std::shared_ptr<Core::VKShaderModule> compile_from_source(
653 const std::string& source,
654 ShaderStage stage,
655 const std::string& entry_point = "main");
656
657 std::shared_ptr<Core::VKShaderModule> compile_from_source_cached(
658 const std::string& source,
659 ShaderStage stage,
660 const std::string& cache_key,
661 const std::string& entry_point = "main");
662
663 std::shared_ptr<Core::VKShaderModule> compile_from_spirv(
664 const std::string& spirv_path,
665 ShaderStage stage,
666 const std::string& entry_point = "main");
667
668 std::shared_ptr<Core::VKShaderModule> get_vk_shader_module(ShaderID shader_id);
669
670 friend class ComputePress;
671 friend class RenderFlow;
672
673 static bool s_initialized;
674};
675
676/**
677 * @brief Get the global shader compiler instance
678 * @return Reference to singleton shader compiler
679 *
680 * Must call initialize() before first use.
681 * Thread-safe after initialization.
682 */
683inline MAYAFLUX_API ShaderFoundry& get_shader_foundry()
684{
686}
687
688} // namespace MayaFlux::Portal::Graphics
IO::ImageData image
Definition Decoder.cpp:57
Compute-specific pipeline and dispatch orchestration.
Graphics pipeline orchestration for dynamic rendering.
ShaderFoundry(const ShaderFoundry &)=delete
ShaderFoundry & operator=(const ShaderFoundry &)=delete
size_t get_cache_size() const
Get number of cached shaders.
void set_compute_queue(vk::Queue queue)
Set Vulkan queues for command submission.
DetectedSourceType
Internal enum for source type detection.
std::unordered_map< std::string, ShaderID > m_shader_filepath_cache
std::unordered_map< FenceID, FenceState > m_fences
void set_graphics_queue(vk::Queue queue)
Set Vulkan queues for command submission.
void set_transfer_queue(vk::Queue queue)
Set Vulkan queues for command submission.
ShaderFoundry(ShaderFoundry &&) noexcept=delete
std::unordered_map< std::string, std::shared_ptr< Core::VKShaderModule > > m_shader_cache
std::unordered_map< DescriptorSetID, DescriptorSetState > m_descriptor_sets
std::unordered_map< CommandBufferID, CommandBufferState > m_command_buffers
std::shared_ptr< Core::VKDescriptorManager > m_global_descriptor_manager
bool is_initialized() const
Check if compiler is initialized.
std::shared_ptr< Core::VulkanBackend > m_backend
std::unordered_map< SemaphoreID, SemaphoreState > m_semaphores
const ShaderCompilerConfig & get_config() const
Get current compiler configuration.
std::unordered_map< ShaderID, ShaderState > m_shaders
Portal-level shader compilation and caching.
void initialize()
Definition main.cpp:11
void stop()
Stop all Portal::Graphics operations.
Definition Graphics.cpp:69
ShaderStage
User-friendly shader stage enum.
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
@ GRAPHICS
Standard real-time graphics processing domain.
Definition Domain.hpp:55
Configuration for shader compilation.
std::unordered_map< std::string, uint32_t > timestamp_queries
std::shared_ptr< Core::VKShaderModule > std::string filepath
Extracted reflection information from compiled shader.
Shader source descriptor for compilation.