diff --git a/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp b/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp index 5b27265e..0045bab4 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp @@ -11,65 +11,194 @@ namespace MobileGL { namespace MG_Util { namespace ShaderTranspiler { - SpvcSession::SpvcSession(const Vector& spirv) { - const SpvId* p_spirv = spirv.data(); - size_t word_count = spirv.size(); - spvc_context_create(&context); - spvc_context_parse_spirv(context, p_spirv, word_count, &ir); - spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, - &compiler); - spvc_compiler_create_shader_resources(compiler, &resources); + static spvc_basetype MapReflectToSpvcBasetype(const SpvReflectBlockVariable& member) { + if (!member.type_description) return SPVC_BASETYPE_UNKNOWN; + auto flags = member.type_description->type_flags; + auto width = member.numeric.scalar.width; + auto signedness = member.numeric.scalar.signedness; + if (flags & SPV_REFLECT_TYPE_FLAG_FLOAT) { + switch (width) { + case 16: return SPVC_BASETYPE_FP16; + case 32: return SPVC_BASETYPE_FP32; + case 64: return SPVC_BASETYPE_FP64; + default: return SPVC_BASETYPE_UNKNOWN; + } + } else if (flags & SPV_REFLECT_TYPE_FLAG_INT) { + if (signedness) { + switch (width) { + case 8: return SPVC_BASETYPE_INT8; + case 16: return SPVC_BASETYPE_INT16; + case 32: return SPVC_BASETYPE_INT32; + case 64: return SPVC_BASETYPE_INT64; + default: return SPVC_BASETYPE_UNKNOWN; + } + } else { + switch (width) { + case 8: return SPVC_BASETYPE_UINT8; + case 16: return SPVC_BASETYPE_UINT16; + case 32: return SPVC_BASETYPE_UINT32; + case 64: return SPVC_BASETYPE_UINT64; + default: return SPVC_BASETYPE_UNKNOWN; + } + } + } else if (flags & SPV_REFLECT_TYPE_FLAG_BOOL) { + return SPVC_BASETYPE_BOOLEAN; + } + return SPVC_BASETYPE_UNKNOWN; + } + + SpvcSession::SpvcSession(const Vector& spirv, Flags usage) + : usage(usage) { + if (usage & SessionUsageBit::Transpile) { + const SpvId* p_spirv = spirv.data(); + size_t word_count = spirv.size(); + + spvc_context_create(&context); + spvc_context_parse_spirv(context, p_spirv, word_count, &ir); + spvc_context_create_compiler(context, SPVC_BACKEND_GLSL, ir, SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, + &compiler); + spvc_compiler_create_shader_resources(compiler, &resources); + } else if (usage & SessionUsageBit::Reflection) { + SpvReflectResult result = spvReflectCreateShaderModule( + spirv.size() * sizeof(uint32_t), spirv.data(), &reflectModule); + reflectModuleValid = (result == SPV_REFLECT_RESULT_SUCCESS); + } } SpvcSession::SpvcSession(SpvcSession&& that) { + std::swap(this->usage, that.usage); std::swap(this->context, that.context); std::swap(this->compiler, that.compiler); std::swap(this->ir, that.ir); std::swap(this->compiler_options, that.compiler_options); std::swap(this->resources, that.resources); + std::swap(this->reflectModule, that.reflectModule); + std::swap(this->reflectModuleValid, that.reflectModuleValid); } SpvcSession& SpvcSession::operator=(SpvcSession&& that) { + std::swap(this->usage, that.usage); std::swap(this->context, that.context); std::swap(this->compiler, that.compiler); std::swap(this->ir, that.ir); std::swap(this->compiler_options, that.compiler_options); std::swap(this->resources, that.resources); + std::swap(this->reflectModule, that.reflectModule); + std::swap(this->reflectModuleValid, that.reflectModuleValid); return *this; } spvc_result SpvcSession::CreateOptions(spvc_compiler_options* options) { + if (!(usage & SessionUsageBit::Transpile)) return SPVC_ERROR_INVALID_ARGUMENT; return spvc_compiler_create_compiler_options(compiler, options); } spvc_result SpvcSession::SetOptions(spvc_compiler_options options) { + if (!(usage & SessionUsageBit::Transpile)) return SPVC_ERROR_INVALID_ARGUMENT; compiler_options = options; return spvc_compiler_install_compiler_options(compiler, options); } Vector SpvcSession::GetShaderInterface(spvc_resource_type resource_type) const { - const spvc_reflected_resource* list = nullptr; - size_t count = 0; - spvc_resources_get_resource_list_for_type(resources, resource_type, &list, &count); + if (usage & SessionUsageBit::Transpile) { + // SPIRV-Cross path + const spvc_reflected_resource* list = nullptr; + size_t count = 0; + spvc_resources_get_resource_list_for_type(resources, resource_type, &list, &count); + + Vector variables; + for (size_t i = 0; i < count; ++i) { + if (spvc_compiler_has_decoration(compiler, list[i].id, SpvDecorationBuiltIn)) { + continue; + } + + InterfaceVariable var; + var.name = list[i].name; + var.location = spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationLocation); + variables.push_back(var); + } + std::sort(variables.begin(), variables.end()); + return variables; + } + + // SPIRV-Reflect path (Reflection only, no Transpile) + if (!reflectModuleValid) return {}; Vector variables; - for (size_t i = 0; i < count; ++i) { - if (spvc_compiler_has_decoration(compiler, list[i].id, SpvDecorationBuiltIn)) { - continue; + switch (resource_type) { + case SPVC_RESOURCE_TYPE_STAGE_INPUT: { + uint32_t count = 0; + spvReflectEnumerateInputVariables(&reflectModule, &count, nullptr); + Vector vars(count); + spvReflectEnumerateInputVariables(&reflectModule, &count, vars.data()); + for (uint32_t i = 0; i < count; ++i) { + if (vars[i]->decoration_flags & SPV_REFLECT_DECORATION_BUILT_IN) continue; + InterfaceVariable var; + var.name = vars[i]->name; + var.location = vars[i]->location; + variables.push_back(var); } - - InterfaceVariable var; - var.name = list[i].name; - var.location = spvc_compiler_get_decoration(compiler, list[i].id, SpvDecorationLocation); - variables.push_back(var); + break; + } + case SPVC_RESOURCE_TYPE_STAGE_OUTPUT: { + uint32_t count = 0; + spvReflectEnumerateOutputVariables(&reflectModule, &count, nullptr); + Vector vars(count); + spvReflectEnumerateOutputVariables(&reflectModule, &count, vars.data()); + for (uint32_t i = 0; i < count; ++i) { + if (vars[i]->decoration_flags & SPV_REFLECT_DECORATION_BUILT_IN) continue; + InterfaceVariable var; + var.name = vars[i]->name; + var.location = vars[i]->location; + variables.push_back(var); + } + break; + } + case SPVC_RESOURCE_TYPE_SAMPLED_IMAGE: { + uint32_t count = 0; + spvReflectEnumerateDescriptorBindings(&reflectModule, &count, nullptr); + Vector bindings(count); + spvReflectEnumerateDescriptorBindings(&reflectModule, &count, bindings.data()); + for (uint32_t i = 0; i < count; ++i) { + if (bindings[i]->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER || + bindings[i]->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_SAMPLED_IMAGE) { + InterfaceVariable var; + var.name = bindings[i]->name; + var.location = bindings[i]->binding; + variables.push_back(var); + } + } + break; + } + case SPVC_RESOURCE_TYPE_UNIFORM_BUFFER: { + uint32_t count = 0; + spvReflectEnumerateDescriptorBindings(&reflectModule, &count, nullptr); + Vector bindings(count); + spvReflectEnumerateDescriptorBindings(&reflectModule, &count, bindings.data()); + for (uint32_t i = 0; i < count; ++i) { + if (bindings[i]->descriptor_type == SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER) { + InterfaceVariable var; + var.name = bindings[i]->name; + var.location = bindings[i]->binding; + variables.push_back(var); + } + } + break; + } + case SPVC_RESOURCE_TYPE_GL_PLAIN_UNIFORM: + // GL plain uniforms are a SPIRV-Cross-specific concept. + // In reflection-only mode, not available. + break; + default: + break; } std::sort(variables.begin(), variables.end()); return variables; } spvc_result SpvcSession::SetVertexAttribLocation(const UnorderedMap& location) { - // TODO: We should assert we're really dealing with vertex shader here + if (!(usage & SessionUsageBit::Transpile)) return SPVC_ERROR_INVALID_ARGUMENT; SPVC_CHK_INIT const spvc_reflected_resource* list = nullptr; @@ -80,8 +209,6 @@ namespace MobileGL { auto& resource = list[i]; auto it = location.find(resource.name); if (it != location.end()) { - // realize glBindVertexAttribLocation here - // it->second should be the location explicitly requested spvc_compiler_set_decoration(compiler, resource.id, SpvDecorationLocation, it->second); } } @@ -89,59 +216,99 @@ namespace MobileGL { } spvc_result SpvcSession::Compile(const char** result) { + if (!(usage & SessionUsageBit::Transpile)) return SPVC_ERROR_INVALID_ARGUMENT; SPVC_CHK_INIT SPVC_CHK_RESULT(spvc_compiler_compile(compiler, result)); - // SPVC_CHK_RESULT(ParseMetaData()); SPVC_CHK_RETURN } spvc_result SpvcSession::ParseMetaData() { - SPVC_CHK_INIT + if (usage & SessionUsageBit::Transpile) { + // SPIRV-Cross path + SPVC_CHK_INIT + + metadata = SpvcMetadata(); + + const spvc_reflected_resource* list = nullptr; + size_t count = 0; + + SPVC_CHK_RESULT(spvc_resources_get_resource_list_for_type( + resources, SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, &list, &count);) + for (size_t i = 0; i < count; ++i) { + if (spvc_compiler_has_decoration(compiler, list[i].id, SpvDecorationBuiltIn)) { + continue; + } + + if (strcmp(list[i].name, GLOBAL_UBO_NAME) == 0) { + spvc_type type = spvc_compiler_get_type_handle(compiler, list[i].base_type_id); + spvc_compiler_get_declared_struct_size(compiler, type, &metadata.globalUboSize); + size_t num_members = spvc_type_get_num_member_types(type); + for (size_t j = 0; j < num_members; ++j) { + const char* memberName = + spvc_compiler_get_member_name(compiler, list[i].base_type_id, j); + + unsigned memberOffset = 0; + SPVC_CHK_RESULT( + spvc_compiler_type_struct_member_offset(compiler, type, j, &memberOffset);) + metadata.plainUniformOffsetsInUBO[memberName] = memberOffset; + SizeT memberSize = 0; + SPVC_CHK_RESULT( + spvc_compiler_get_declared_struct_member_size(compiler, type, j, &memberSize);) + metadata.plainUniformMemberSizesInBytes[memberName] = memberSize; + + auto memberTypeId = spvc_type_get_member_type(type, j); + spvc_type memberType = spvc_compiler_get_type_handle(compiler, memberTypeId); + spvc_basetype basetype = spvc_type_get_basetype(memberType); + auto vectorSize = spvc_type_get_vector_size(memberType); + auto matCol = spvc_type_get_columns(memberType); + metadata.plainUniformMemberTypes[memberName] = { + .basetype = basetype, + .vectorSize = vectorSize, + .matCol = matCol, + }; + } + SPVC_CHK_RETURN + } + } + return SPVC_ERROR_INVALID_SPIRV; + } + + // SPIRV-Reflect path (Reflection only) + if (!reflectModuleValid) return SPVC_ERROR_INVALID_SPIRV; metadata = SpvcMetadata(); - const spvc_reflected_resource* list = nullptr; - size_t count = 0; + uint32_t bindingCount = 0; + spvReflectEnumerateDescriptorBindings(&reflectModule, &bindingCount, nullptr); + Vector bindings(bindingCount); + spvReflectEnumerateDescriptorBindings(&reflectModule, &bindingCount, bindings.data()); - SPVC_CHK_RESULT(spvc_resources_get_resource_list_for_type(resources, SPVC_RESOURCE_TYPE_UNIFORM_BUFFER, - &list, &count);) - for (size_t i = 0; i < count; ++i) { - if (spvc_compiler_has_decoration(compiler, list[i].id, SpvDecorationBuiltIn)) { - continue; - } + for (uint32_t i = 0; i < bindingCount; ++i) { + auto* binding = bindings[i]; + if (binding->descriptor_type != SPV_REFLECT_DESCRIPTOR_TYPE_UNIFORM_BUFFER) continue; + if (strcmp(binding->name, GLOBAL_UBO_NAME) != 0) continue; - if (strcmp(list[i].name, GLOBAL_UBO_NAME) == 0) { - spvc_type type = spvc_compiler_get_type_handle(compiler, list[i].base_type_id); - spvc_compiler_get_declared_struct_size(compiler, type, &metadata.globalUboSize); - size_t num_members = spvc_type_get_num_member_types(type); - for (size_t j = 0; j < num_members; ++j) { - const char* memberName = spvc_compiler_get_member_name(compiler, list[i].base_type_id, j); + auto& block = binding->block; + metadata.globalUboSize = block.size; - unsigned memberOffset = 0; - SPVC_CHK_RESULT(spvc_compiler_type_struct_member_offset(compiler, type, j, &memberOffset);) - metadata.plainUniformOffsetsInUBO[memberName] = memberOffset; - SizeT memberSize = 0; - SPVC_CHK_RESULT( - spvc_compiler_get_declared_struct_member_size(compiler, type, j, &memberSize);) - metadata.plainUniformMemberSizesInBytes[memberName] = memberSize; + for (uint32_t j = 0; j < block.member_count; ++j) { + auto& member = block.members[j]; + metadata.plainUniformOffsetsInUBO[member.name] = member.offset; + metadata.plainUniformMemberSizesInBytes[member.name] = member.size; - auto memberTypeId = spvc_type_get_member_type(type, j); - spvc_type memberType = spvc_compiler_get_type_handle(compiler, memberTypeId); - spvc_basetype basetype = spvc_type_get_basetype(memberType); - auto vectorSize = spvc_type_get_vector_size(memberType); - auto matCol = spvc_type_get_columns(memberType); - // auto dim = spvc_type_get_num_array_dimensions(type); - metadata.plainUniformMemberTypes[memberName] = { - .basetype = basetype, - .vectorSize = vectorSize, - .matCol = matCol, - }; - } - SPVC_CHK_RETURN + Uint32 vectorSize = member.numeric.vector.component_count; + if (vectorSize == 0) vectorSize = 1; + Uint32 matCol = member.numeric.matrix.column_count; + if (matCol == 0) matCol = 1; + + metadata.plainUniformMemberTypes[member.name] = { + .basetype = MapReflectToSpvcBasetype(member), + .vectorSize = vectorSize, + .matCol = matCol, + }; } + return SPVC_SUCCESS; } - // This means this spv binary does not have - // auto-generated UBO in it return SPVC_ERROR_INVALID_SPIRV; } @@ -150,11 +317,17 @@ namespace MobileGL { } const char* SpvcSession::GetLastErrorString() const { - return spvc_context_get_last_error_string(context); + if (context) { + return spvc_context_get_last_error_string(context); + } + return ""; } SpvcSession::~SpvcSession() { spvc_context_destroy(context); + if (reflectModuleValid) { + spvReflectDestroyShaderModule(&reflectModule); + } } } // namespace ShaderTranspiler } // namespace MG_Util diff --git a/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h b/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h index 7696c8b2..f30143bb 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h +++ b/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include "Types.h" #define SPVC_CHK_INIT auto __r = SPVC_SUCCESS; @@ -55,6 +56,11 @@ namespace MobileGL { } }; + enum class SessionUsageBit { + Reflection = 1 << 0, + Transpile = 1 << 1, + }; + struct SpvcMetadata { UnorderedMap plainUniformOffsetsInUBO; UnorderedMap plainUniformMemberSizesInBytes; @@ -66,7 +72,8 @@ namespace MobileGL { public: SpvcSession() {} - explicit SpvcSession(const Vector& spirv); + explicit SpvcSession(const Vector& spirv, + Flags usage = SessionUsageBit::Reflection | SessionUsageBit::Transpile); SpvcSession(SpvcSession&) = delete; @@ -90,12 +97,19 @@ namespace MobileGL { spvc_result ParseMetaData(); private: + Flags usage; + + // SPIRV-Cross state (used when Transpile flag is set) spvc_context context = nullptr; spvc_parsed_ir ir = nullptr; spvc_compiler compiler = nullptr; spvc_compiler_options compiler_options = nullptr; spvc_resources resources = nullptr; + // SPIRV-Reflect state (used when only Reflection flag is set) + SpvReflectShaderModule reflectModule = {}; + bool reflectModuleValid = false; + SpvcMetadata metadata; }; } // namespace ShaderTranspiler