From 4c1e0856c92399597e9eec68a69e6257d73d0545 Mon Sep 17 00:00:00 2001 From: Laura Hermanns Date: Fri, 21 Jun 2024 11:21:02 -0400 Subject: [PATCH] [GL] Mapping of name to active GL uniform location. The location returned by glGetUniformLocation() must be used for gl*Uniform*() functions only, not as index for glGetActiveUniform(). This change builds a full map that maps a name to its active GL uniform type and array size. --- .../OpenGL/RenderState/GLPipelineState.cpp | 95 ++++++++++++++----- .../OpenGL/RenderState/GLPipelineState.h | 22 ++++- 2 files changed, 94 insertions(+), 23 deletions(-) diff --git a/sources/Renderer/OpenGL/RenderState/GLPipelineState.cpp b/sources/Renderer/OpenGL/RenderState/GLPipelineState.cpp index 51819cf545..29630e220a 100644 --- a/sources/Renderer/OpenGL/RenderState/GLPipelineState.cpp +++ b/sources/Renderer/OpenGL/RenderState/GLPipelineState.cpp @@ -122,10 +122,59 @@ void GLPipelineState::BuildUniformMap(GLShader::Permutation permutation, const s { const GLuint program = shaderPipelines_[permutation]->GetID(); + /* Build name-to-index map of all active uniforms, since glGetUniformLocation() does *not* map to the active uniform index */ + GLNameToUniformMap nameToUniformMap; + BuildNameToActiveUniformMap(program, nameToUniformMap); + /* Build uniform locations from input descriptors */ uniformMap_.resize(uniforms.size()); for_range(i, uniforms.size()) - BuildUniformLocation(program, uniformMap_[i], uniforms[i]); + BuildUniformLocation(program, uniformMap_[i], uniforms[i], nameToUniformMap); + } +} + +/* +Returns the name of an active GL uniform as an identifier, i.e. removing any subscripts. +Arrays of uniforms are reflected with a subscript for the first entry, +e.g. "uniform inputTextures[2];" yields the active uniform name "inputTextures[0]". +This functions removes the subscript, effecitvely returning "inputTextures" for this example. +*/ +static std::string GetActiveUniformNameAsIdent(const GLchar* uniformName) +{ + std::string ident{ uniformName }; + const std::size_t subscriptStartPos = ident.find('['); + if (subscriptStartPos != std::string::npos) + return ident.substr(0, subscriptStartPos); + return ident; +} + +void GLPipelineState::BuildNameToActiveUniformMap(GLuint program, GLNameToUniformMap& outNameToUniformMap) +{ + /* Determine number of active GL uniforms */ + GLint numActiveUniforms = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &numActiveUniforms); + if (numActiveUniforms <= 0) + return; + + /* Determine buffer size for largest GL uniform name */ + GLint maxUniformNameLength = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformNameLength); + if (maxUniformNameLength <= 0) + return; + + /* Reserve memory to iterate over all active GL uniforms */ + std::vector uniformName; + uniformName.resize(maxUniformNameLength, '\0'); + + outNameToUniformMap.reserve(static_cast(numActiveUniforms)); + + /* Build name-to-index map for all active GL uniforms */ + for_range(i, static_cast(numActiveUniforms)) + { + GLActiveUniform uniform = {}; + glGetActiveUniform(program, i, static_cast(uniformName.size()), nullptr, &uniform.size, &uniform.type, &uniformName[0]); + const std::string uniformIdent = GetActiveUniformNameAsIdent(uniformName.data()); + outNameToUniformMap[uniformIdent] = uniform; } } @@ -184,31 +233,33 @@ static GLuint GetUniformWordSize(GLenum type) } } -void GLPipelineState::BuildUniformLocation(GLuint program, GLUniformLocation& outUniform, const UniformDescriptor& inUniform) +void GLPipelineState::BuildUniformLocation( + GLuint program, + GLUniformLocation& outUniform, + const UniformDescriptor& inUniform, + const GLNameToUniformMap& nameToUniformMap) { + /* Initialize output with invalid uniform location */ + outUniform.type = UniformType::Undefined; + outUniform.location = -1; + outUniform.count = 0; + outUniform.wordSize = 0; + /* Find uniform location by name in shader pipeline */ GLint location = glGetUniformLocation(program, inUniform.name.c_str()); if (location == -1) - { - /* Write invalid uniform location */ - outUniform.type = UniformType::Undefined; - outUniform.location = -1; - outUniform.count = 0; - outUniform.wordSize = 0; - } - else - { - /* Determine type of uniform */ - GLenum type = 0; - GLint size = 0; - glGetActiveUniform(program, static_cast(location), 0, nullptr, &size, &type, nullptr); - - /* Write output uniform */ - outUniform.type = GLTypes::UnmapUniformType(type); - outUniform.location = location; - outUniform.count = size; - outUniform.wordSize = GetUniformWordSize(type); - } + return; + + /* Determine type of uniform */ + auto it = nameToUniformMap.find(inUniform.name); + if (it == nameToUniformMap.end()) + return; + + /* Write output uniform */ + outUniform.type = GLTypes::UnmapUniformType(it->second.type); + outUniform.location = location; + outUniform.count = it->second.size; + outUniform.wordSize = GetUniformWordSize(it->second.type); } diff --git a/sources/Renderer/OpenGL/RenderState/GLPipelineState.h b/sources/Renderer/OpenGL/RenderState/GLPipelineState.h index 317d5491a8..6eb3d2959d 100644 --- a/sources/Renderer/OpenGL/RenderState/GLPipelineState.h +++ b/sources/Renderer/OpenGL/RenderState/GLPipelineState.h @@ -18,6 +18,7 @@ #include #include #include +#include namespace LLGL @@ -95,13 +96,32 @@ class GLPipelineState : public PipelineState return report_; } + private: + + struct GLActiveUniform + { + GLint size; + GLenum type; + }; + + // Maps a name to its active GL uniform. + using GLNameToUniformMap = std::unordered_map; + private: // Builds the index-to-uniform map. void BuildUniformMap(GLShader::Permutation permutation, const std::vector& uniforms); + // Builds the container that maps a name to the index of its active GL uniform. + void BuildNameToActiveUniformMap(GLuint program, GLNameToUniformMap& outNameToUniformMap); + // Builds the specified uniform location. - void BuildUniformLocation(GLuint program, GLUniformLocation& outUniform, const UniformDescriptor& inUniform); + void BuildUniformLocation( + GLuint program, + GLUniformLocation& outUniform, + const UniformDescriptor& inUniform, + const GLNameToUniformMap& nameToUniformMap + ); private: