[Fix] (GL): answer one for the name-length program queries of a program linked from SPIR-V

This commit is contained in:
2026-10-12 08:32:44 -04:00
parent b4da0e1a87
commit 45f5bae8b3
4 changed files with 66 additions and 4 deletions
@@ -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
@@ -9,6 +9,7 @@
#include "ProgramObject.h"
#include "ProgramLinkTask.h"
#include "ProgramSpirvTask.h"
#include <algorithm>
#include <atomic>
#include <cstring>
#include <MG_Util/Async/ShaderCompilePool.h>
@@ -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<ShaderObject>& shader) { return shader->HasSpirvBinary(); });
if (addDefaultFSIfMissingForRenderingPipelineProgram) {
AddDefaultFragmentShaderIfMissing();
@@ -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.
+48
View File
@@ -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<GLint>(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