From 4aa70fda65e9bb33e15a4968904092c6ce656e30 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Fri, 15 Aug 2025 15:49:31 +0800 Subject: [PATCH] [Refactor] (MG_State/Program): refractor uniform reflection using glslang reflection API --- .../GLState/ProgramState/ProgramObject.cpp | 145 ++++++++++-------- .../GLState/ProgramState/ProgramObject.h | 17 +- .../GLState/ProgramState/ShaderObject.cpp | 72 ++++----- .../GLState/ProgramState/ShaderObject.h | 2 +- MobileGL/MG_Test/Program/ProgramTest.cpp | 3 - 5 files changed, 132 insertions(+), 107 deletions(-) diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp index c1a6eaed..71e3f767 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp @@ -47,13 +47,11 @@ namespace MobileGL { } else { m_linkStatus = false; m_infoLog = result.error().log; - - const std::string e = std::format("Shader link failed: \nerrc: {}\nmsg: {}\n", result.error().errc, - result.error().log); - THROW_EXCEPTION(e); } // PostLink(); + + DoReflection(); } void ProgramObject::MarkAsDeleted() { @@ -64,69 +62,96 @@ 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 - if (loc != 4095 || m_uniforms.find(name) == m_uniforms.end()) { - 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; - } - } + void ProgramObject::DoReflection() { + if (!m_program->buildReflection()) { + m_linkStatus = false; + m_infoLog = "Build reflection failed."; + return; } - // 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; - } + auto uniformCount = m_program->getNumUniformVariables(); + for (int i = 0; i < uniformCount; i++) { + auto& uniform = m_program->getUniform(i); + auto location = uniform.layoutLocation(); + m_maxUniformLocation = std::max(m_maxUniformLocation, location); + m_uniformLocations[uniform.name] = location; } - 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; - } + m_uniformNames.resize(m_maxUniformLocation + 1); + m_uniformTypes.resize(m_maxUniformLocation + 1); + m_uniformOffsets.resize(m_maxUniformLocation + 1); - // 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; - } - } - } - - m_uniformNames.resize(m_uniformOffsets.size()); - for (auto& [name, loc] : m_uniforms) { - m_uniformNames[loc] = name; - m_uniformNameMaxLength = std::max(m_uniformNameMaxLength, (Int)name.length()); + for (int i = 0; i < uniformCount; i++) { + auto& uniform = m_program->getUniform(i); + auto location = uniform.layoutLocation(); + m_uniformNames[location] = uniform.name; + m_uniformTypes[location] = uniform.glDefineType; } } - void ProgramObject::PostLink() { + // 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 + // if (loc != 4095 || m_uniforms.find(name) == m_uniforms.end()) { + // 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; + // } + // } + // } + // + // m_uniformNames.resize(m_uniformOffsets.size()); + // for (auto& [name, loc] : m_uniforms) { + // m_uniformNames[loc] = name; + // m_uniformNameMaxLength = std::max(m_uniformNameMaxLength, (Int)name.length()); + // } + // } + // + // void ProgramObject::PostLink() { // if (m_programBinary.empty()) { // assert(false); // return; @@ -156,7 +181,7 @@ namespace MobileGL { // auto location = m_uniforms[name]; // m_uniformTypes[location] = gltype; // } - } + // } } // 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 1133460f..dbdf2957 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h @@ -19,8 +19,8 @@ namespace MobileGL { Int GetUniformMaxLength() const { return m_uniformNameMaxLength; } Uint GetUniformCount() { return m_uniformOffsets.size(); } Int GetUniformLocation(const String& name) { - const auto it = m_uniforms.find(name); - return (it == m_uniforms.end()) ? -1 : it->second; + const auto it = m_uniformLocations.find(name); + return (it == m_uniformLocations.end()) ? -1 : it->second; } GLenum GetUniformType(Uint index) const { return m_uniformTypes[index]; @@ -30,8 +30,9 @@ namespace MobileGL { return m_uniformNames[index]; } private: - void PreLink(); - void PostLink(); + void DoReflection(); + // void PreLink(); + // void PostLink(); const Uint m_id = 0; Vector> m_shaders; @@ -41,16 +42,16 @@ namespace MobileGL { // Uniforms MG_Util::ShaderTranspiler::SpvcMetadata m_metadata; - 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; + UnorderedMap m_uniformLocations; Vector m_uniformNames; Vector m_uniformTypes; + // Need to be reflected after linking of SPIR-V binary + Vector m_uniformOffsets; Vector m_uboScratch; Int m_uniformNameMaxLength = 0; + Uint m_maxUniformLocation = 0; String m_infoLog; Bool m_deleteStatus = false; diff --git a/MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp b/MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp index d90f40e9..ec795d3a 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp +++ b/MobileGL/MG_State/GLState/ProgramState/ShaderObject.cpp @@ -15,15 +15,19 @@ namespace MobileGL { } void ShaderObject::Compile() { - if (!DoReflection()) { - return; - } + // if (!DoReflection()) { + // return; + // } using namespace MG_Util::ShaderTranspiler; + + // Compile for OpenGL here, so that we can do validation and link + // like a real OpenGL driver at linking stage + // Will compile for other backends later. ShaderAttrib attrib{ .shaderType = GetGLShaderTypeByMGLShaderStage(m_stage), .sourceStr = m_source, - .flags = 0 + .flags = ShaderCompileBits::CompileForOpenGL }; auto result = ShaderCompiler::CompileShader(attrib); @@ -33,9 +37,6 @@ namespace MobileGL { } else { m_compileStatus = false; m_infoLog = result.error().log; - - const std::string e = std::format("Shader compilation failed: \nerrc: {}\nmsg: {}\n", - result.error().errc, result.error().log); } } @@ -43,35 +44,36 @@ namespace MobileGL { m_deleteStatus = true; } - bool ShaderObject::DoReflection() { - using namespace MG_Util::ShaderTranspiler; - ShaderAttrib attrib{ - .shaderType = GetGLShaderTypeByMGLShaderStage(m_stage), - .sourceStr = m_source, - .flags = ShaderCompileBits::CompileForOpenGL - }; + // bool ShaderObject::DoReflection() { + // using namespace MG_Util::ShaderTranspiler; + // ShaderAttrib attrib{ + // .shaderType = GetGLShaderTypeByMGLShaderStage(m_stage), + // .sourceStr = m_source, + // .flags = ShaderCompileBits::CompileForOpenGL + // }; + // + // auto result = ShaderCompiler::CompileShader(attrib); + // if (!result) { + // m_compileStatus = false; + // m_infoLog = result.error().log; + // + // const std::string e = std::format("Shader compilation failed: \nerrc: {}\nmsg: {}\n", + // result.error().errc, result.error().log); + // return false; + // } + // + // auto pShader = result.value(); + // auto root = pShader->getIntermediate()->getTreeRoot(); + // UniformTraverser traverser; + // root->traverse(&traverser); + // auto& symbols = traverser.GetCollectedSymbols(); + // for (const auto& symbol : symbols) { + // m_uniforms[symbol->getName().c_str()] = symbol->getQualifier().layoutLocation; + // } + // + // return true; + // } - auto result = ShaderCompiler::CompileShader(attrib); - if (!result) { - m_compileStatus = false; - m_infoLog = result.error().log; - - const std::string e = std::format("Shader compilation failed: \nerrc: {}\nmsg: {}\n", - result.error().errc, result.error().log); - return false; - } - - auto pShader = result.value(); - auto root = pShader->getIntermediate()->getTreeRoot(); - UniformTraverser traverser; - root->traverse(&traverser); - auto& symbols = traverser.GetCollectedSymbols(); - for (const auto& symbol : symbols) { - m_uniforms[symbol->getName().c_str()] = symbol->getQualifier().layoutLocation; - } - - return true; - } } // namespace GLState } // namespace MG_State } // namespace MobileGL \ No newline at end of file diff --git a/MobileGL/MG_State/GLState/ProgramState/ShaderObject.h b/MobileGL/MG_State/GLState/ProgramState/ShaderObject.h index bdbf74e4..9ed7ebb8 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ShaderObject.h +++ b/MobileGL/MG_State/GLState/ProgramState/ShaderObject.h @@ -69,7 +69,7 @@ namespace MobileGL { const String& GetInfoLog() const { return m_infoLog; } const UnorderedMap& GetUniformLocations() const { return m_uniforms; } private: - bool DoReflection(); + // bool DoReflection(); const Uint m_id = 0; const ShaderStage m_stage; String m_source; diff --git a/MobileGL/MG_Test/Program/ProgramTest.cpp b/MobileGL/MG_Test/Program/ProgramTest.cpp index fba40d91..3504e45a 100644 --- a/MobileGL/MG_Test/Program/ProgramTest.cpp +++ b/MobileGL/MG_Test/Program/ProgramTest.cpp @@ -113,9 +113,6 @@ TEST_F(ProgramTest, CompileAndLink) { LinkProgram(program); printf("Program linked.\n"); - // FIXME: fix these later, refactoring uniform location reflection stuff - FAIL() << "GetUniformLocation not implemented yet!"; - EXPECT_EQ(GetUniformLocation(program, "ProjMat"), 0); EXPECT_EQ(GetUniformLocation(program, "Gray"), 1); EXPECT_EQ(GetUniformLocation(program, "Saturation"), 6);