From df444655e3a484075720c9815eb79f3d8fc6cd07 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Wed, 13 Aug 2025 23:14:22 +0800 Subject: [PATCH] [Feat] (MG_State/Program): retrieve/generate layout location from/for program, and retrieve generated ubo size and all the offsets of members --- MobileGL/Includes.h | 1 + .../MG_Impl/GLImpl/Program/GL_Program.cpp | 3 +- .../GLState/ProgramState/ProgramObject.cpp | 80 +++++++++++++++++++ .../GLState/ProgramState/ProgramObject.h | 11 +++ .../GLState/ProgramState/ShaderObject.h | 3 +- MobileGL/MG_Util/Miscellany/IndexGenerator.h | 5 +- .../MG_Util/ShaderTranspiler/SpvcSession.cpp | 1 + .../MG_Util/ShaderTranspiler/SpvcSession.h | 1 + 8 files changed, 102 insertions(+), 3 deletions(-) diff --git a/MobileGL/Includes.h b/MobileGL/Includes.h index 41de2941..d3703e4e 100644 --- a/MobileGL/Includes.h +++ b/MobileGL/Includes.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp index 8b2cde1e..32fd7d57 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp @@ -265,7 +265,7 @@ namespace MobileGL { std::string src; for (GLsizei i = 0; i < count; i++) { src += - (length[i] <= 0) ? string[i] : std::string(string[i], length[i]); + (length == nullptr || length[i] <= 0) ? string[i] : std::string(string[i], length[i]); } shaderObject->SetShaderSource(Move(src)); } @@ -377,6 +377,7 @@ namespace MobileGL { GLuint CreateProgram(void) { return CreateProgram_State(); } + GLuint CreateShader(GLenum type) { return CreateShader_State(type); } diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp index 11d66352..78f4117d 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp @@ -30,6 +30,8 @@ namespace MobileGL { } void ProgramObject::Link() { + PreLink(); + Vector shaderTypes(m_shaders.size()); Vector> shaders(m_shaders.size()); for (SizeT i = 0; i < m_shaders.size(); i++) { @@ -54,6 +56,8 @@ namespace MobileGL { result.error().log); THROW_EXCEPTION(e); } + + PostLink(); } void ProgramObject::MarkAsDeleted() { @@ -64,6 +68,82 @@ namespace MobileGL { return m_shaders; } + void ProgramObject::PreLink() { + m_uniforms.clear(); + m_uniformOffsets.clear(); + + for (const auto& shader : m_shaders) { + for (const auto& [name, loc] : shader->GetUniformLocations()) { + // collect all the names to map + m_uniforms[name] = loc; + + // set a flag for those who have an explicit location + if (loc != 4095) { + if (loc >= m_uniformOffsets.size()) { + m_uniformOffsets.reserve(std::bit_ceil(loc + 1)); + m_uniformOffsets.resize(loc + 1, 0); + } + assert(m_uniformOffsets[loc] == 0); + m_uniformOffsets[loc] = 1; + } + } + } + + // Let's find a location for those who doesn't have one yet + Uint nextLocation = 0; + + // Find first empty location + for (SizeT i = 0; i < m_uniformOffsets.size(); i++) { + if (m_uniformOffsets[i] == 0) { + nextLocation = i; + break; + } + } + + for (auto& [name, loc] : m_uniforms) { + if (loc == 4095) { + // check if we drained all the holes already + if (nextLocation >= m_uniformOffsets.size()) { + loc = nextLocation; + m_uniformOffsets.push_back(1); + nextLocation++; + continue; + } + + // assign an empty location + loc = nextLocation; + m_uniformOffsets[loc] = 1; + + // Find next empty location + for (nextLocation++; nextLocation < m_uniformOffsets.size(); nextLocation++) { + if (m_uniformOffsets[nextLocation] == 0) + break; + } + } + } + } + + void ProgramObject::PostLink() { + if (m_programBinary.empty()) { + assert(false); + return; + } + MG_Util::ShaderTranspiler::SpvcSession session(m_programBinary[0]); + const char* src = nullptr; // we dont care the source atm + auto result = session.Compile(&src); + if (result != SPVC_SUCCESS) { + assert(false); + return; + } + auto& metadata = session.GetMetadata(); + auto& uniformOffsets = metadata.plainUniformOffsetsInUBO; + for (const auto& [name, offset] : uniformOffsets) { + assert(m_uniforms.find(name) != m_uniforms.end()); + assert(m_uniforms[name] < m_uniformOffsets.size()); + m_uniformOffsets[m_uniforms[name]] = offset; + } + m_uboScratch.resize(metadata.uboSize); + } } // namespace GLState } // namespace MG_State } // namespace MobileGL \ No newline at end of file diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h index 1a4d95e0..94f29e50 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h @@ -16,10 +16,21 @@ namespace MobileGL { Vector>& GetAttachedShaders(); const String& GetInfoLog() const { return m_infoLog; } private: + void PreLink(); + void PostLink(); + const Uint m_id = 0; Vector> m_shaders; // basically this contains SPIR-V in binary format Vector> m_programBinary; + + // Uniforms + UnorderedMap m_uniforms; + // 0 or 1 for if the location is explicitly specified at PreLink stage, + // offsets into global ubo for PostLink + Vector m_uniformOffsets; + Vector m_uboScratch; + String m_infoLog; Bool m_deleteStatus = false; Bool m_linkStatus = true; diff --git a/MobileGL/MG_State/GLState/ProgramState/ShaderObject.h b/MobileGL/MG_State/GLState/ProgramState/ShaderObject.h index 60a0285d..d4366345 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ShaderObject.h +++ b/MobileGL/MG_State/GLState/ProgramState/ShaderObject.h @@ -67,13 +67,14 @@ namespace MobileGL { const String& GetShaderSource() const { return m_source; } SharedPtr GetCompiledShader() const { return m_shader; } const String& GetInfoLog() const { return m_infoLog; } + const UnorderedMap GetUniformLocations() const { return m_uniforms; } private: bool DoReflection(); const Uint m_id = 0; const ShaderStage m_stage; String m_source; SharedPtr m_shader; - UnorderedMap m_uniforms; + UnorderedMap m_uniforms; String m_infoLog; Bool m_deleteStatus = false; diff --git a/MobileGL/MG_Util/Miscellany/IndexGenerator.h b/MobileGL/MG_Util/Miscellany/IndexGenerator.h index 5a6b4032..2fff1e99 100644 --- a/MobileGL/MG_Util/Miscellany/IndexGenerator.h +++ b/MobileGL/MG_Util/Miscellany/IndexGenerator.h @@ -5,10 +5,13 @@ namespace MobileGL { template class IndexGenerator { public: - explicit IndexGenerator(SizeT initial_capacity = 1024, IndexType first_index = 0) : next_index_(first_index) { + explicit IndexGenerator(SizeT initial_capacity = 1024, IndexType first_index = 1) { const SizeT words_needed = (initial_capacity + 63) / 64; is_valid_.resize(words_needed, ~0ull); freed_indices_.reserve(initial_capacity); + std::vector valuesBeforeFirst(first_index); + std::iota(valuesBeforeFirst.begin(), valuesBeforeFirst.end(), 0); + Generate(valuesBeforeFirst.size(), valuesBeforeFirst.data()); } void Generate(SizeT n, IndexType* indices) { diff --git a/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp b/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp index adcfca11..831d9879 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.cpp @@ -84,6 +84,7 @@ namespace MobileGL { 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.uboSize); 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); diff --git a/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h b/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h index 6c1442bd..39f786c6 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h +++ b/MobileGL/MG_Util/ShaderTranspiler/SpvcSession.h @@ -17,6 +17,7 @@ namespace MobileGL { namespace ShaderTranspiler { struct SpvcMetadata { UnorderedMap plainUniformOffsetsInUBO; + SizeT uboSize = 0; }; class SpvcSession {