MayaFlux 0.5.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 SPIR-V bytecode
312 * @return Vector of SPIR-V words (empty if not preserved)
313 *
314 * Useful for caching, serialization, or re-creation.
315 * Only available if preserve_spirv was enabled during creation.
316 */
317 [[nodiscard]] const std::vector<uint32_t>& get_spirv() const { return m_spirv_code; }
318
319 /**
320 * @brief Set specialization constants
321 * @param constants Map of constant_id → value
322 *
323 * Updates the specialization info used in get_stage_create_info().
324 * Must be called before using the shader in pipeline creation.
325 *
326 * Example:
327 * shader.set_specialization_constants({
328 * {0, 256}, // WORKGROUP_SIZE = 256
329 * {1, 1} // ENABLE_OPTIMIZATION = true
330 * });
331 */
332 void set_specialization_constants(const std::unordered_map<uint32_t, uint32_t>& constants);
333
334 /**
335 * @brief Enable SPIR-V preservation for hot-reload
336 * @param preserve If true, stores SPIR-V bytecode internally
337 *
338 * Increases memory usage but enables recreation without recompilation.
339 */
340 void set_preserve_spirv(bool preserve) { m_preserve_spirv = preserve; }
341
342 /**
343 * @brief Get shader stage type
344 * @return Stage enum (easier than vk::ShaderStageFlagBits for logic)
345 */
346 [[nodiscard]] Stage get_stage_type() const;
347
348 /**
349 * @brief Get vertex input state (vertex shaders only)
350 * @return Vertex input metadata, empty if not a vertex shader
351 */
352 [[nodiscard]] const VertexInputInfo& get_vertex_input() const
353 {
354 return m_vertex_input;
355 }
356
357 /**
358 * @brief Check if vertex input is available
359 */
360 [[nodiscard]] bool has_vertex_input() const
361 {
362 return !m_vertex_input.attributes.empty();
363 }
364
365 /**
366 * @brief Get fragment output state (fragment shaders only)
367 * @return Fragment output metadata, empty if not a fragment shader
368 */
369 [[nodiscard]] const FragmentOutputInfo& get_fragment_output() const
370 {
371 return m_fragment_output;
372 }
373
374 /**
375 * @brief Get detailed push constant info
376 * @return Push constant metadata (replaces simple PushConstantRange)
377 */
378 [[nodiscard]] const std::vector<PushConstantInfo>& get_push_constants() const
379 {
380 return m_push_constants;
381 }
382
383 // NEW: Workgroup size for compute shaders
384 /**
385 * @brief Get compute workgroup size (compute shaders only)
386 * @return {local_size_x, local_size_y, local_size_z} or nullopt
387 */
388 [[nodiscard]] std::optional<std::array<uint32_t, 3>> get_workgroup_size() const
389 {
390 return m_reflection.workgroup_size;
391 }
392
393 /**
394 * @brief Auto-detect shader stage from file extension
395 * @param filepath Path to shader file
396 * @return Detected stage, or nullopt if unknown extension
397 */
398 static std::optional<vk::ShaderStageFlagBits> detect_stage_from_extension(const std::string& filepath);
399
400private:
401 vk::ShaderModule m_module = nullptr;
402 vk::ShaderStageFlagBits m_stage = vk::ShaderStageFlagBits::eCompute;
403 std::string m_entry_point = "main";
404
406 std::vector<uint32_t> m_spirv_code; ///< Preserved SPIR-V (if enabled)
407
408 bool m_preserve_spirv {};
409
410 std::unordered_map<uint32_t, uint32_t> m_specialization_map;
411 std::vector<vk::SpecializationMapEntry> m_specialization_entries;
412 std::vector<uint32_t> m_specialization_data;
413 vk::SpecializationInfo m_specialization_info;
414
417 std::vector<PushConstantInfo> m_push_constants;
418
419 /**
420 * @brief Perform reflection on SPIR-V bytecode
421 * @param spirv_code SPIR-V bytecode
422 * @return true if reflection succeeded
423 *
424 * Uses SPIRV-Cross library to extract bindings, push constants,
425 * workgroup sizes, etc. Falls back to basic parsing if library unavailable.
426 */
427 bool reflect_spirv(const std::vector<uint32_t>& spirv_code);
428
429 /**
430 * @brief Compile GLSL to SPIR-V using shaderc
431 * @param glsl_source GLSL source code
432 * @param stage Shader stage (affects compiler settings)
433 * @param include_directories Include paths
434 * @param defines Preprocessor macros
435 * @return SPIR-V bytecode, or empty vector on failure
436 */
437 std::vector<uint32_t> compile_glsl_to_spirv(
438 const std::string& glsl_source,
439 vk::ShaderStageFlagBits stage,
440 const std::vector<std::string>& include_directories,
441 const std::unordered_map<std::string, std::string>& defines);
442
443 /**
444 * @brief Read binary file into vector
445 * @param filepath Path to file
446 * @return File contents, or empty vector on failure
447 */
448 static std::vector<uint32_t> read_spirv_file(const std::string& filepath);
449
450 /**
451 * @brief Read text file into string
452 * @param filepath Path to file
453 * @return File contents, or empty string on failure
454 */
455 static std::string read_text_file(const std::string& filepath);
456
457 /**
458 * @brief Update specialization info from current map
459 * Called before get_stage_create_info() to ensure fresh data
460 */
461 void update_specialization_info();
462
463 /**
464 * @brief Convert SPIRV-Cross type to Vulkan vertex attribute format
465 * @param type SPIR-V type information
466 * @return Corresponding Vulkan format
467 */
468 static vk::Format spirv_type_to_vk_format(const spirv_cross::SPIRType& type);
469};
470
471} // namespace MayaFlux::Core
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)
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
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