Perform reflection on SPIR-V bytecode.
Uses SPIRV-Cross library to extract bindings, push constants, workgroup sizes, etc. Falls back to basic parsing if library unavailable.
370{
371 try {
372 spirv_cross::Compiler compiler(spirv_code);
373 spirv_cross::ShaderResources resources = compiler.get_shader_resources();
374
375 auto reflect_resources = [&](const spirv_cross::SmallVector<spirv_cross::Resource>& res_vec,
376 bool is_storage = false) {
377 for (const auto& resource : res_vec) {
378 ShaderReflection::DescriptorBinding desc;
379 desc.set = compiler.get_decoration(resource.id, spv::DecorationDescriptorSet);
380 desc.binding = compiler.get_decoration(resource.id, spv::DecorationBinding);
382 desc.name = resource.name;
383
384 const auto& type = compiler.get_type(resource.type_id);
385 desc.count = type.array.empty() ? 1 : type.array[0];
386 desc.type = spirv_to_vk_descriptor_type(type.basetype, type, is_storage);
387
389 }
390 };
391
392 reflect_resources(resources.uniform_buffers, false);
393
394 reflect_resources(resources.storage_buffers, true);
395
396 reflect_resources(resources.sampled_images, false);
397
398 reflect_resources(resources.storage_images, true);
399
400 reflect_resources(resources.separate_images, false);
401 reflect_resources(resources.separate_samplers, false);
402
406 }
407
408 for (const auto& pc_buffer : resources.push_constant_buffers) {
409 const auto& type = compiler.get_type(pc_buffer.type_id);
410
411 ShaderReflection::PushConstantRange
range;
414 range.size =
static_cast<uint32_t
>(compiler.get_declared_struct_size(type));
415
417 }
418
422 }
423
424 auto spec_constants = compiler.get_specialization_constants();
425 for (const auto& spec : spec_constants) {
426 ShaderReflection::SpecializationConstant sc;
427 sc.constant_id = spec.constant_id;
428 sc.name = compiler.get_name(spec.id);
429
430 const auto& type = compiler.get_type(compiler.get_constant(spec.id).constant_type);
431 sc.size = static_cast<uint32_t>(compiler.get_declared_struct_size(type));
432
434 }
435
438 "Reflected {} specialization constants",
440 }
441
442 if (
m_stage == vk::ShaderStageFlagBits::eCompute
443 ||
m_stage == vk::ShaderStageFlagBits::eMeshEXT
444 ||
m_stage == vk::ShaderStageFlagBits::eTaskEXT) {
445 auto entry_points = compiler.get_entry_points_and_stages();
446
447 for (const auto& ep : entry_points) {
448 if (ep.name ==
m_entry_point && ep.execution_model == spv::ExecutionModelGLCompute) {
449
450 std::array<uint32_t, 3> workgroup_size {
451 compiler.get_execution_mode_argument(spv::ExecutionModeLocalSize, 0),
452 compiler.get_execution_mode_argument(spv::ExecutionModeLocalSize, 1),
453 compiler.get_execution_mode_argument(spv::ExecutionModeLocalSize, 2)
454 };
455
456 if (!workgroup_size.empty() && workgroup_size.size() >= 3) {
458 workgroup_size[0],
459 workgroup_size[1],
460 workgroup_size[2]
461 };
462
464 "Compute shader workgroup size: [{}, {}, {}]",
465 workgroup_size[0], workgroup_size[1], workgroup_size[2]);
466 }
467 break;
468 }
469 }
470 }
471
472 if (
m_stage == vk::ShaderStageFlagBits::eVertex) {
473 for (const auto& input : resources.stage_inputs) {
474 uint32_t location = compiler.get_decoration(input.id, spv::DecorationLocation);
475 const auto& type = compiler.get_type(input.type_id);
476
477 vk::VertexInputAttributeDescription attr;
478 attr.location = location;
479 attr.binding = 0;
481 attr.offset = 0;
482
484 }
485
488 "Reflected {} vertex input attributes",
490 }
491 }
492
493 return true;
494
495 } catch (const spirv_cross::CompilerError& e) {
497 "SPIRV-Cross reflection failed: {}", e.what());
498 return false;
499 }
500}
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
vk::ShaderStageFlagBits m_stage
ShaderReflection m_reflection
static vk::Format spirv_type_to_vk_format(const spirv_cross::SPIRType &type)
Convert SPIRV-Cross type to Vulkan vertex attribute format.
std::string m_entry_point
@ GraphicsBackend
Graphics/visual rendering backend (Vulkan, OpenGL)
@ Core
Core engine, backend, subsystems.
std::vector< double > range(std::span< const double > data, size_t n_windows, uint32_t hop_size, uint32_t window_size)
Value range (max - min) per window.
std::vector< SpecializationConstant > specialization_constants
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