MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VKShaderModule.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <vulkan/vulkan.hpp>
4
5namespace spirv_cross {
6class Compiler;
7struct SPIRType;
8}
9
10namespace MayaFlux::Core {
11
12/**
13 * @enum ShaderType
14 * @brief High-level shader type enumeration
15 *
16 * Used for specifying shader types in a generic way.
17 */
18enum class Stage : uint8_t {
19 COMPUTE,
20 VERTEX,
25 MESH,
26 TASK
27};
28
30 std::vector<vk::Format> color_formats;
31 vk::Format depth_format;
32 vk::Format stencil_format;
33};
34
36 std::vector<vk::VertexInputBindingDescription> bindings;
37 std::vector<vk::VertexInputAttributeDescription> attributes;
38};
39
41 struct Attribute {
42 uint32_t location; // layout(location = N)
43 vk::Format format; // vec3 -> eR32G32B32Sfloat
44 uint32_t offset; // byte offset in vertex
45 std::string name; // variable name (from reflection)
46 };
47
48 struct Binding {
49 uint32_t binding; // vertex buffer binding point
50 uint32_t stride; // bytes per vertex
51 vk::VertexInputRate rate; // per-vertex or per-instance
52 };
53
54 std::vector<Attribute> attributes;
55 std::vector<Binding> bindings;
56};
57
59 struct Attachment {
60 uint32_t location; // layout(location = N)
61 vk::Format format; // vec4 -> eR32G32B32A32Sfloat
62 std::string name; // output variable name
63 };
64
65 std::vector<Attachment> color_attachments;
66 bool has_depth_output = false;
67 bool has_stencil_output = false;
68};
69
71 uint32_t offset;
72 uint32_t size;
73 std::string name; // struct name (if any)
74 vk::ShaderStageFlags stages; // which stages use it
75};
76
77/**
78 * @struct ShaderReflection
79 * @brief Metadata extracted from shader module
80 *
81 * Contains information about shader resources for descriptor set layout creation
82 * and pipeline configuration. Extracted via SPIRV-Cross or manual parsing.
83 */
86 uint32_t set; ///< Descriptor set index
87 uint32_t binding; ///< Binding point within set
88 vk::DescriptorType type; ///< Type (uniform buffer, storage buffer, etc.)
89 vk::ShaderStageFlags stage; ///< Stage visibility
90 uint32_t count; ///< Array size (1 for non-arrays)
91 std::string name; ///< Variable name in shader
92 };
93
95 vk::ShaderStageFlags stage; ///< Stage visibility
96 uint32_t offset; ///< Offset in push constant block
97 uint32_t size; ///< Size in bytes
98 };
99
101 uint32_t constant_id; ///< Specialization constant ID
102 uint32_t size; ///< Size in bytes
103 std::string name; ///< Variable name in shader
104 };
105
106 std::vector<DescriptorBinding> bindings;
107 std::vector<PushConstantRange> push_constants;
108 std::vector<SpecializationConstant> specialization_constants;
109
110 std::optional<std::array<uint32_t, 3>> workgroup_size; ///< local_size_x/y/z
111
112 std::vector<vk::VertexInputAttributeDescription> vertex_attributes;
113 std::vector<vk::VertexInputBindingDescription> vertex_bindings;
114};
115
116/**
117 * @class VKShaderModule
118 * @brief Wrapper for Vulkan shader module with lifecycle and reflection
119 *
120 * Responsibilities:
121 * - Create vk::ShaderModule from SPIR-V binary or GLSL source
122 * - Load shaders from disk or memory
123 * - Extract shader metadata via reflection
124 * - Provide pipeline stage info for pipeline creation
125 * - Enable hot-reload support (recreation)
126 *
127 * Does NOT handle:
128 * - Pipeline creation (that's VKComputePipeline/VKGraphicsPipeline)
129 * - Descriptor set allocation (that's VKDescriptorManager)
130 * - Shader compilation (delegates to external compiler)
131 *
132 * Integration points:
133 * - VKComputePipeline/VKGraphicsPipeline: uses get_stage_create_info()
134 * - VKDescriptorManager: uses get_reflection() for layout creation
135 * - VKBufferProcessor: subclasses use this to load compute shaders
136 */
137class MAYAFLUX_API VKShaderModule {
138public:
139 VKShaderModule() = default;
141
144 VKShaderModule(VKShaderModule&&) noexcept;
145 VKShaderModule& operator=(VKShaderModule&&) noexcept;
146
147 /**
148 * @brief Create shader module from SPIR-V binary
149 * @param device Logical device
150 * @param spirv_code SPIR-V bytecode (must be aligned to uint32_t)
151 * @param stage Shader stage (compute, vertex, fragment, etc.)
152 * @param entry_point Entry point function name (default: "main")
153 * @param enable_reflection Extract descriptor bindings and resources
154 * @return true if creation succeeded
155 *
156 * This is the lowest-level creation method. All other create methods
157 * eventually funnel through this one.
158 */
159 bool create_from_spirv(
160 vk::Device device,
161 const std::vector<uint32_t>& spirv_code,
162 vk::ShaderStageFlagBits stage,
163 const std::string& entry_point = "main",
164 bool enable_reflection = true);
165
166 /**
167 * @brief Create shader module from SPIR-V file
168 * @param device Logical device
169 * @param spirv_path Path to .spv file
170 * @param stage Shader stage
171 * @param entry_point Entry point function name
172 * @param enable_reflection Extract metadata
173 * @return true if creation succeeded
174 *
175 * Reads binary file and calls create_from_spirv().
176 */
177 bool create_from_spirv_file(
178 vk::Device device,
179 const std::string& spirv_path,
180 vk::ShaderStageFlagBits stage,
181 const std::string& entry_point = "main",
182 bool enable_reflection = true);
183
184 /**
185 * @brief Create shader module from SPIR-V assembly text.
186 *
187 * Assembles human-readable SPIR-V opcodes to binary via spvTextToBinary
188 * targeting Vulkan 1.3, then delegates to create_from_spirv(). Does not
189 * require shaderc or any GLSL toolchain.
190 *
191 * @param device Logical device.
192 * @param spirv_asm SPIR-V assembly text.
193 * @param stage Shader stage.
194 * @param entry_point Entry point function name (default: "main").
195 * @param enable_reflection Extract descriptor bindings and resources.
196 * @return true if assembly and module creation succeeded.
197 */
198 bool create_from_spirv_asm(
199 vk::Device device,
200 const std::string& spirv_asm,
201 vk::ShaderStageFlagBits stage,
202 const std::string& entry_point = "main",
203 bool enable_reflection = true);
204
205 /**
206 * @brief Create shader module from GLSL source string
207 * @param device Logical device
208 * @param glsl_source GLSL source code
209 * @param stage Shader stage (determines compiler mode)
210 * @param entry_point Entry point function name
211 * @param enable_reflection Extract metadata
212 * @param include_directories Paths for #include resolution
213 * @param defines Preprocessor definitions (e.g., {"DEBUG", "MAX_LIGHTS=4"})
214 * @return true if creation succeeded
215 *
216 * Compiles GLSL → SPIR-V using shaderc, then calls create_from_spirv().
217 * Requires shaderc library to be available.
218 */
219 bool create_from_glsl(
220 vk::Device device,
221 const std::string& glsl_source,
222 vk::ShaderStageFlagBits stage,
223 const std::string& entry_point = "main",
224 bool enable_reflection = true,
225 const std::vector<std::string>& include_directories = {},
226 const std::unordered_map<std::string, std::string>& defines = {});
227
228 /**
229 * @brief Create shader module from GLSL file
230 * @param device Logical device
231 * @param glsl_path Path to .comp/.vert/.frag/.geom file
232 * @param stage Shader stage (auto-detected from extension if not specified)
233 * @param entry_point Entry point function name
234 * @param enable_reflection Extract metadata
235 * @param include_directories Paths for #include resolution
236 * @param defines Preprocessor definitions
237 * @return true if creation succeeded
238 *
239 * Reads file, compiles GLSL → SPIR-V, calls create_from_spirv().
240 * Stage auto-detection:
241 * .comp → Compute
242 * .vert → Vertex
243 * .frag → Fragment
244 * .geom → Geometry
245 * .tesc → Tessellation Control
246 * .tese → Tessellation Evaluation
247 */
248 bool create_from_glsl_file(
249 vk::Device device,
250 const std::string& glsl_path,
251 std::optional<vk::ShaderStageFlagBits> stage = std::nullopt,
252 const std::string& entry_point = "main",
253 bool enable_reflection = true,
254 const std::vector<std::string>& include_directories = {},
255 const std::unordered_map<std::string, std::string>& defines = {});
256
257 /**
258 * @brief Cleanup shader module
259 * @param device Logical device (must match creation device)
260 *
261 * Destroys vk::ShaderModule and clears metadata.
262 * Safe to call multiple times or on uninitialized modules.
263 */
264 void cleanup(vk::Device device);
265
266 /**
267 * @brief Check if module is valid
268 * @return true if shader module was successfully created
269 */
270 [[nodiscard]] bool is_valid() const { return m_module != nullptr; }
271
272 /**
273 * @brief Get raw Vulkan shader module handle
274 * @return vk::ShaderModule handle
275 */
276 [[nodiscard]] vk::ShaderModule get() const { return m_module; }
277
278 /**
279 * @brief Get shader stage
280 * @return Stage flags (compute, vertex, fragment, etc.)
281 */
282 [[nodiscard]] vk::ShaderStageFlagBits get_stage() const { return m_stage; }
283
284 /**
285 * @brief Get entry point function name
286 * @return Entry point string (typically "main")
287 */
288 [[nodiscard]] const std::string& get_entry_point() const { return m_entry_point; }
289
290 /**
291 * @brief Get pipeline shader stage create info
292 * @return vk::PipelineShaderStageCreateInfo for pipeline creation
293 *
294 * This is the primary integration point with pipeline builders.
295 * Usage:
296 * auto stage_info = shader_module.get_stage_create_info();
297 * pipeline_builder.add_shader_stage(stage_info);
298 */
299 [[nodiscard]] vk::PipelineShaderStageCreateInfo get_stage_create_info() const;
300
301 /**
302 * @brief Get shader reflection metadata
303 * @return Const reference to extracted metadata
304 *
305 * Used by descriptor managers and pipeline builders to automatically
306 * configure layouts and bindings without manual specification.
307 */
308 [[nodiscard]] const ShaderReflection& get_reflection() const { return m_reflection; }
309
310 /**
311 * @brief Get the descriptor bindings the entry point statically uses
312 * @return Bindings reached by the entry point, excluding declared but unused resources
313 *
314 * Unlike get_reflection(), which lists every declared resource, this lists only
315 * the resources a pipeline layout must contain for the stage to be valid.
316 */
317 [[nodiscard]] const std::vector<ShaderReflection::DescriptorBinding>& get_active_bindings() const { return m_active_bindings; }
318
319 /**
320 * @brief Get SPIR-V bytecode
321 * @return Vector of SPIR-V words (empty if not preserved)
322 *
323 * Useful for caching, serialization, or re-creation.
324 * Only available if preserve_spirv was enabled during creation.
325 */
326 [[nodiscard]] const std::vector<uint32_t>& get_spirv() const { return m_spirv_code; }
327
328 /**
329 * @brief Set specialization constants
330 * @param constants Map of constant_id → value
331 *
332 * Updates the specialization info used in get_stage_create_info().
333 * Must be called before using the shader in pipeline creation.
334 *
335 * Example:
336 * shader.set_specialization_constants({
337 * {0, 256}, // WORKGROUP_SIZE = 256
338 * {1, 1} // ENABLE_OPTIMIZATION = true
339 * });
340 */
341 void set_specialization_constants(const std::unordered_map<uint32_t, uint32_t>& constants);
342
343 /**
344 * @brief Enable SPIR-V preservation for hot-reload
345 * @param preserve If true, stores SPIR-V bytecode internally
346 *
347 * Increases memory usage but enables recreation without recompilation.
348 */
349 void set_preserve_spirv(bool preserve) { m_preserve_spirv = preserve; }
350
351 /**
352 * @brief Get shader stage type
353 * @return Stage enum (easier than vk::ShaderStageFlagBits for logic)
354 */
355 [[nodiscard]] Stage get_stage_type() const;
356
357 /**
358 * @brief Get vertex input state (vertex shaders only)
359 * @return Vertex input metadata, empty if not a vertex shader
360 */
361 [[nodiscard]] const VertexInputInfo& get_vertex_input() const
362 {
363 return m_vertex_input;
364 }
365
366 /**
367 * @brief Check if vertex input is available
368 */
369 [[nodiscard]] bool has_vertex_input() const
370 {
371 return !m_vertex_input.attributes.empty();
372 }
373
374 /**
375 * @brief Get fragment output state (fragment shaders only)
376 * @return Fragment output metadata, empty if not a fragment shader
377 */
378 [[nodiscard]] const FragmentOutputInfo& get_fragment_output() const
379 {
380 return m_fragment_output;
381 }
382
383 /**
384 * @brief Get detailed push constant info
385 * @return Push constant metadata (replaces simple PushConstantRange)
386 */
387 [[nodiscard]] const std::vector<PushConstantInfo>& get_push_constants() const
388 {
389 return m_push_constants;
390 }
391
392 // NEW: Workgroup size for compute shaders
393 /**
394 * @brief Get compute workgroup size (compute shaders only)
395 * @return {local_size_x, local_size_y, local_size_z} or nullopt
396 */
397 [[nodiscard]] std::optional<std::array<uint32_t, 3>> get_workgroup_size() const
398 {
399 return m_reflection.workgroup_size;
400 }
401
402 /**
403 * @brief Auto-detect shader stage from file extension
404 * @param filepath Path to shader file
405 * @return Detected stage, or nullopt if unknown extension
406 */
407 static std::optional<vk::ShaderStageFlagBits> detect_stage_from_extension(const std::string& filepath);
408
409private:
410 vk::ShaderModule m_module = nullptr;
411 vk::ShaderStageFlagBits m_stage = vk::ShaderStageFlagBits::eCompute;
412 std::string m_entry_point = "main";
413
415 std::vector<ShaderReflection::DescriptorBinding> m_active_bindings;
416 std::vector<uint32_t> m_spirv_code; ///< Preserved SPIR-V (if enabled)
417
418 bool m_preserve_spirv {};
419
420 std::unordered_map<uint32_t, uint32_t> m_specialization_map;
421 std::vector<vk::SpecializationMapEntry> m_specialization_entries;
422 std::vector<uint32_t> m_specialization_data;
423 vk::SpecializationInfo m_specialization_info;
424
427 std::vector<PushConstantInfo> m_push_constants;
428
429 /**
430 * @brief Perform reflection on SPIR-V bytecode
431 * @param spirv_code SPIR-V bytecode
432 * @return true if reflection succeeded
433 *
434 * Uses SPIRV-Cross library to extract bindings, push constants,
435 * workgroup sizes, etc. Falls back to basic parsing if library unavailable.
436 */
437 bool reflect_spirv(const std::vector<uint32_t>& spirv_code);
438
439 /**
440 * @brief Compile GLSL to SPIR-V using shaderc
441 * @param glsl_source GLSL source code
442 * @param stage Shader stage (affects compiler settings)
443 * @param include_directories Include paths
444 * @param defines Preprocessor macros
445 * @return SPIR-V bytecode, or empty vector on failure
446 */
447 std::vector<uint32_t> compile_glsl_to_spirv(
448 const std::string& glsl_source,
449 vk::ShaderStageFlagBits stage,
450 const std::vector<std::string>& include_directories,
451 const std::unordered_map<std::string, std::string>& defines);
452
453 /**
454 * @brief Read binary file into vector
455 * @param filepath Path to file
456 * @return File contents, or empty vector on failure
457 */
458 static std::vector<uint32_t> read_spirv_file(const std::string& filepath);
459
460 /**
461 * @brief Read text file into string
462 * @param filepath Path to file
463 * @return File contents, or empty string on failure
464 */
465 static std::string read_text_file(const std::string& filepath);
466
467 /**
468 * @brief Update specialization info from current map
469 * Called before get_stage_create_info() to ensure fresh data
470 */
471 void update_specialization_info();
472
473 /**
474 * @brief Convert SPIRV-Cross type to Vulkan vertex attribute format
475 * @param type SPIR-V type information
476 * @return Corresponding Vulkan format
477 */
478 static vk::Format spirv_type_to_vk_format(const spirv_cross::SPIRType& type);
479};
480
481} // namespace MayaFlux::Core
vk::PhysicalDevice device
Definition VKDevice.cpp:121
vk::PhysicalDeviceType type
Definition VKDevice.cpp:126
vk::SpecializationInfo m_specialization_info
const std::vector< uint32_t > & get_spirv() const
Get SPIR-V bytecode.
std::optional< std::array< uint32_t, 3 > > get_workgroup_size() const
Get compute workgroup size (compute shaders only)
const std::vector< ShaderReflection::DescriptorBinding > & get_active_bindings() const
Get the descriptor bindings the entry point statically uses.
bool is_valid() const
Check if module is valid.
bool has_vertex_input() const
Check if vertex input is available.
std::vector< vk::SpecializationMapEntry > m_specialization_entries
vk::ShaderModule get() const
Get raw Vulkan shader module handle.
VKShaderModule & operator=(const VKShaderModule &)=delete
void set_preserve_spirv(bool preserve)
Enable SPIR-V preservation for hot-reload.
VKShaderModule(const VKShaderModule &)=delete
const FragmentOutputInfo & get_fragment_output() const
Get fragment output state (fragment shaders only)
std::vector< PushConstantInfo > m_push_constants
std::vector< uint32_t > m_specialization_data
std::vector< ShaderReflection::DescriptorBinding > m_active_bindings
const std::vector< PushConstantInfo > & get_push_constants() const
Get detailed push constant info.
std::unordered_map< uint32_t, uint32_t > m_specialization_map
const std::string & get_entry_point() const
Get entry point function name.
const ShaderReflection & get_reflection() const
Get shader reflection metadata.
std::vector< uint32_t > m_spirv_code
Preserved SPIR-V (if enabled)
const VertexInputInfo & get_vertex_input() const
Get vertex input state (vertex shaders only)
vk::ShaderStageFlagBits get_stage() const
Get shader stage.
Wrapper for Vulkan shader module with lifecycle and reflection.
int main(int argc, char **argv)
Main entry point for the Lila server binary.
std::vector< Attachment > color_attachments
std::vector< vk::Format > color_formats
vk::ShaderStageFlags stage
Stage visibility.
uint32_t count
Array size (1 for non-arrays)
vk::DescriptorType type
Type (uniform buffer, storage buffer, etc.)
vk::ShaderStageFlags stage
Stage visibility.
uint32_t offset
Offset in push constant block.
std::vector< SpecializationConstant > specialization_constants
std::vector< vk::VertexInputBindingDescription > vertex_bindings
std::vector< DescriptorBinding > bindings
std::vector< PushConstantRange > push_constants
std::vector< vk::VertexInputAttributeDescription > vertex_attributes
std::optional< std::array< uint32_t, 3 > > workgroup_size
local_size_x/y/z
Metadata extracted from shader module.
std::vector< Binding > bindings
std::vector< Attribute > attributes
std::vector< vk::VertexInputAttributeDescription > attributes
std::vector< vk::VertexInputBindingDescription > bindings