From 45f5bae8b3ce2c2fad132999febf67b135ab11fd Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Mon, 12 Oct 2026 08:02:43 -0400 Subject: [PATCH] [Fix] (GL): answer one for the name-length program queries of a program linked from SPIR-V --- .../MG_Impl/GLImpl/Program/GL_Program.cpp | 12 +++-- .../GLState/ProgramState/ProgramObject.cpp | 3 ++ .../GLState/ProgramState/ProgramObject.h | 7 +++ MobileGL/MG_Test/Program/ProgramTest.cpp | 48 +++++++++++++++++++ 4 files changed, 66 insertions(+), 4 deletions(-) diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp index da6ca51d..6d8b9476 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp @@ -893,8 +893,11 @@ namespace MobileGL::MG_Impl::GLImpl { *params = programObject->GetActiveAttributesCount(); MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params); break; + // GL 4.6 core 7.13: "If no name reflection information is available, one is returned" - + // the case for every program linked from SPIR-V (see ProgramObject::GetLinkedFromSpirv), + // for this and the three other name-length queries below. case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH: - *params = programObject->GetActiveAttributesMaxLength() + 1; + *params = programObject->GetLinkedFromSpirv() ? 1 : programObject->GetActiveAttributesMaxLength() + 1; MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params); break; case GL_ACTIVE_UNIFORMS: @@ -902,7 +905,7 @@ namespace MobileGL::MG_Impl::GLImpl { MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params); break; case GL_ACTIVE_UNIFORM_MAX_LENGTH: - *params = programObject->GetUniformMaxLength() + 1; + *params = programObject->GetLinkedFromSpirv() ? 1 : programObject->GetUniformMaxLength() + 1; MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params); break; case GL_ACTIVE_UNIFORM_BLOCKS: // GL >= 3.1 @@ -912,7 +915,8 @@ namespace MobileGL::MG_Impl::GLImpl { MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params); break; case GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH: // ditto. - *params = programObject->GetActiveUniformBlocksMaxNameLength() + 1; + *params = + programObject->GetLinkedFromSpirv() ? 1 : programObject->GetActiveUniformBlocksMaxNameLength() + 1; MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params); break; case GL_TRANSFORM_FEEDBACK_VARYINGS: @@ -924,7 +928,7 @@ namespace MobileGL::MG_Impl::GLImpl { MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params); break; case GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH: - *params = programObject->GetTransformFeedbackVaryingMaxLength(); + *params = programObject->GetLinkedFromSpirv() ? 1 : programObject->GetTransformFeedbackVaryingMaxLength(); MGLOG_D("%s: %s = %d", __func__, MG_Util::ConvertGLEnumToString(pname).c_str(), *params); break; case GL_COMPUTE_WORK_GROUP_SIZE: { // GL >= 4.3 diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp index 07fc5f11..040a3705 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.cpp @@ -9,6 +9,7 @@ #include "ProgramObject.h" #include "ProgramLinkTask.h" #include "ProgramSpirvTask.h" +#include #include #include #include @@ -587,6 +588,8 @@ namespace MobileGL::MG_State::GLState { RemoveShader(detachedShader); } m_detachedShaders.clear(); + m_linkedFromSpirv = std::any_of(m_shaders.begin(), m_shaders.end(), + [](const SharedPtr& shader) { return shader->HasSpirvBinary(); }); if (addDefaultFSIfMissingForRenderingPipelineProgram) { AddDefaultFragmentShaderIfMissing(); diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h index 29c69837..e5fce84b 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h @@ -913,6 +913,11 @@ namespace MobileGL::MG_State::GLState { // assert. The live flag stays available as GetSeparable() for glGetProgramiv's sibling // state and for the next link to latch. Bool GetLinkedSeparable() const { return m_linkedSeparable; } + // Whether the last link consumed SPIR-V modules (ARB_gl_spirv; mixing them with GLSL + // shaders fails the link). Such a program carries no name reflection - OpName is + // optional debug information, and the names the translation invents are not the + // application's - so GL 4.6 core 7.13's name-length queries answer one for it. + Bool GetLinkedFromSpirv() const { return m_linkedFromSpirv; } void SetSeparable(Bool separable) { m_separable = separable; // ---- arming the uniform-write tracking latch ---- @@ -1438,6 +1443,8 @@ namespace MobileGL::MG_State::GLState { // not a result the worker computes - and because a FAILED link still latches it, exactly // as a successful one does. Bool m_linkedSeparable = false; + // See GetLinkedFromSpirv; latched by Link() alongside m_linkedSeparable. + Bool m_linkedFromSpirv = false; // Monotone "this program may ever be a pipeline stage" latch; see SetSeparable for why // it is a latch and not just m_separable. Outside LinkArtifacts on purpose: a relink // clears the write SET, but a program that was separable is still separable after it. diff --git a/MobileGL/MG_Test/Program/ProgramTest.cpp b/MobileGL/MG_Test/Program/ProgramTest.cpp index 8332d5f8..3d49528c 100644 --- a/MobileGL/MG_Test/Program/ProgramTest.cpp +++ b/MobileGL/MG_Test/Program/ProgramTest.cpp @@ -5077,6 +5077,54 @@ TEST_F(ProgramTest, ShaderSourceClearsTheSpecializedLatch) { DrainProgramTestErrors(); } +// GL 4.6 core 7.13: a program with no name reflection - every program linked from SPIR-V - answers +// the four name-length queries with one (KHR-GL46.gl_spirv.spirv_modules_state_queries_test), while +// the same interface linked from GLSL keeps reporting its real longest name. +TEST_F(ProgramTest, ASpirvProgramReportsNoNameReflection) { + DrainProgramTestErrors(); + + const auto linkWith = [](GLuint shader) { + const GLuint program = CreateProgram(); + AttachShader(program, shader); + LinkProgram(program); + GLint linked = GL_FALSE; + GetProgramiv(program, GL_LINK_STATUS, &linked); + EXPECT_EQ(linked, GL_TRUE); + return program; + }; + + const GLuint spirvShader = CreateShader(GL_VERTEX_SHADER); + ShaderBinary(1, &spirvShader, GL_SHADER_BINARY_FORMAT_SPIR_V, kVertexModule, sizeof(kVertexModule)); + SpecializeShader(spirvShader, "main", 0, nullptr, nullptr); + ASSERT_EQ(GetError(), GL_NO_ERROR); + const GLuint spirvProgram = linkWith(spirvShader); + + const GLuint glslShader = CreateShader(GL_VERTEX_SHADER); + const char* source = "#version 450 core\nlayout(location = 0) in vec2 aPos;\n" + "void main() { gl_Position = vec4(aPos, 0.0, 1.0); }\n"; + ShaderSource(glslShader, 1, &source, nullptr); + CompileShader(glslShader); + const GLuint glslProgram = linkWith(glslShader); + ASSERT_EQ(GetError(), GL_NO_ERROR); + + for (const GLenum pname : {GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, GL_ACTIVE_UNIFORM_MAX_LENGTH, + GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH}) { + GLint length = -1; + GetProgramiv(spirvProgram, pname, &length); + EXPECT_EQ(GetError(), GL_NO_ERROR); + EXPECT_EQ(length, 1) << "pname 0x" << std::hex << pname; + } + GLint glslLength = -1; + GetProgramiv(glslProgram, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &glslLength); + EXPECT_EQ(glslLength, static_cast(sizeof("aPos"))); + + DeleteProgram(spirvProgram); + DeleteProgram(glslProgram); + DeleteShader(spirvShader); + DeleteShader(glslShader); + DrainProgramTestErrors(); +} + // A shader that came from glShaderBinary has never had glShaderSource called on it, so GL 4.6 // core 7.1 makes its source the empty string - including AFTER glSpecializeShader, when the // object internally holds the GLSL the module was translated into. That text is MobileGL's, not