From 2dcc15bb0eeb9a394f8c813815cacddbfbd20c5a Mon Sep 17 00:00:00 2001 From: BZLZHH Date: Tue, 4 Aug 2026 10:27:23 -0400 Subject: [PATCH] [Feat] (MG_Impl, DirectGLES): advertise GL_ARB_get_program_binary with no binary format glProgramParameteri is not core before GL 4.1, so in the 4.0 context the CTS runs it only exists through GL_ARB_get_program_binary or GL_ARB_separate_shader_objects. MobileGL advertised neither, so dEQP's loader left the entry point null - and KHR-GL40.api.coverage, which registers glProgramParameteri from GL 3.2 upwards, called straight through the null pointer and took the process down. GL_NUM_PROGRAM_BINARY_FORMATS was already 0, and the extension explicitly allows an implementation to support no binary format at all; that is the honest state of things here, since a MobileGL program is a glslang link plus a per-backend translation with no serialised form. So the extension is advertised for what it really provides: glProgramParameteri stores GL_PROGRAM_BINARY_RETRIEVABLE_HINT (reported back by glGetProgramiv alongside a GL_PROGRAM_BINARY_LENGTH of zero), glGetProgramBinary is the INVALID_OPERATION the spec requires when that length is zero, and glProgramBinary rejects every format with INVALID_ENUM and leaves the program's LINK_STATUS false. Applications that ask for a binary get the documented "no formats" answer and fall back, which is what they already had to do - only now they can ask. --- .../DirectGLES/BackendObject_DirectGLES.cpp | 6 +- .../MG_Impl/GLImpl/Exporting/Definitions.cpp | 6 +- .../MG_Impl/GLImpl/Program/GL_Program.cpp | 66 +++++++++++++++++++ MobileGL/MG_Impl/GLImpl/Program/GL_Program.h | 3 + .../GLState/ProgramState/ProgramObject.h | 12 ++++ 5 files changed, 89 insertions(+), 4 deletions(-) diff --git a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp index 4116fa66..6e8ef1a2 100644 --- a/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/BackendObject_DirectGLES.cpp @@ -910,7 +910,11 @@ namespace MobileGL::MG_Backend::DirectGLES { // picks a whole different shader for draw_buffers without // explicit_attrib_location. DirectVulkan advertises both. E_GL_ARB_explicit_attrib_location, E_GL_ARB_texture_multisample, - E_GL_ARB_shader_image_size}; + E_GL_ARB_shader_image_size, + // Advertised with GL_NUM_PROGRAM_BINARY_FORMATS = 0, which the + // extension explicitly permits. It is also the only thing that + // exposes glProgramParameteri before GL 4.1. + E_GL_ARB_get_program_binary}; // Only advertised when the device driver actually has usable timer queries // (GL_EXT_disjoint_timer_query plus its entry points) and the // MOBILEGL_DISABLE_TIMERQUERY escape hatch is off. diff --git a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp index 86eb9fcc..7024976a 100644 --- a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp +++ b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp @@ -298,9 +298,9 @@ DECLARE_GL_FUNCTION_HEAD(void, GenTransformFeedbacks, GLsizei n, GLuint* ids) DE DECLARE_GL_FUNCTION_HEAD(GLboolean, IsTransformFeedback, GLuint id) DECLARE_GL_FUNCTION_END(GLboolean, IsTransformFeedback, id) DECLARE_GL_FUNCTION_HEAD(void, PauseTransformFeedback) DECLARE_GL_FUNCTION_END_NO_RETURN(void, PauseTransformFeedback) DECLARE_GL_FUNCTION_HEAD(void, ResumeTransformFeedback) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ResumeTransformFeedback) -DECLARE_GL_FUNCTION_STUB_HEAD(void, GetProgramBinary, GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, void* binary) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, GetProgramBinary, program, bufSize, length, binaryFormat, binary) -DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramBinary, GLuint program, GLenum binaryFormat, const void* binary, GLsizei length) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramBinary, program, binaryFormat, binary, length) -DECLARE_GL_FUNCTION_STUB_HEAD(void, ProgramParameteri, GLuint program, GLenum pname, GLint value) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, ProgramParameteri, program, pname, value) +DECLARE_GL_FUNCTION_HEAD(void, GetProgramBinary, GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, void* binary) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetProgramBinary, program, bufSize, length, binaryFormat, binary) +DECLARE_GL_FUNCTION_HEAD(void, ProgramBinary, GLuint program, GLenum binaryFormat, const void* binary, GLsizei length) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramBinary, program, binaryFormat, binary, length) +DECLARE_GL_FUNCTION_HEAD(void, ProgramParameteri, GLuint program, GLenum pname, GLint value) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProgramParameteri, program, pname, value) DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateFramebuffer, GLenum target, GLsizei numAttachments, const GLenum* attachments) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateFramebuffer, target, numAttachments, attachments) DECLARE_GL_FUNCTION_STUB_HEAD(void, InvalidateSubFramebuffer, GLenum target, GLsizei numAttachments, const GLenum* attachments, GLint x, GLint y, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, InvalidateSubFramebuffer, target, numAttachments, attachments, x, y, width, height) DECLARE_GL_FUNCTION_HEAD(void, TexStorage2D, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexStorage2D, target, levels, internalformat, width, height) diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp index 98bc303e..16049656 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp @@ -648,6 +648,13 @@ namespace MobileGL::MG_Impl::GLImpl { } case GL_PROGRAM_BINARY_LENGTH: + // No program binary format is exposed, so a program never has a retrievable + // binary and its length is zero (ARB_get_program_binary). + *params = 0; + break; + case GL_PROGRAM_BINARY_RETRIEVABLE_HINT: + *params = programObject->GetBinaryRetrievableHint() ? GL_TRUE : GL_FALSE; + break; case GL_GEOMETRY_VERTICES_OUT: case GL_GEOMETRY_INPUT_TYPE: @@ -2258,6 +2265,65 @@ namespace MobileGL::MG_Impl::GLImpl { ValidateProgram_State(program); } + // ARB_get_program_binary with no supported binary format (GL_NUM_PROGRAM_BINARY_FORMATS + // is 0, which the extension explicitly allows). The three entry points below are what an + // application - and dEQP's function loader - reach through the extension; without it + // glProgramParameteri is not exposed in a 4.0 context at all. + void ProgramParameteri(GLuint program, GLenum pname, GLint value) { + auto& programObject = TryToGetProgramObject(program); + if (!programObject) return; + if (pname != GL_PROGRAM_BINARY_RETRIEVABLE_HINT) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", __func__, "pname is not an accepted value.")); + return; + } + if (value != GL_TRUE && value != GL_FALSE) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "value must be GL_TRUE or GL_FALSE.")); + return; + } + programObject->SetBinaryRetrievableHint(value == GL_TRUE); + } + + void GetProgramBinary(GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, void* binary) { + (void)binaryFormat; + (void)binary; + auto& programObject = TryToGetProgramObject(program); + if (!programObject) return; + if (bufSize < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "bufSize must be non-negative.")); + return; + } + if (length) *length = 0; + // GL_PROGRAM_BINARY_LENGTH is always zero here, which the spec makes an error to ask for. + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, "The program has no retrievable binary.")); + } + + void ProgramBinary(GLuint program, GLenum binaryFormat, const void* binary, GLsizei length) { + (void)binaryFormat; + (void)binary; + auto& programObject = TryToGetProgramObject(program); + if (!programObject) return; + if (length < 0) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "length must be non-negative.")); + return; + } + // No format is supported, so every binary is rejected - and the program's link status + // has to read FALSE afterwards. + programObject->MarkLinkFailedByProgramBinary(); + MG_State::pGLContext->RecordError( + ErrorCode::InvalidEnum, + MakeUnique("MG_Impl/GLImpl", __func__, "binaryFormat is not a supported format.")); + } + void TransformFeedbackVaryings(GLuint program, GLsizei count, const GLchar* const* varyings, GLenum bufferMode) { auto& programObject = TryToGetProgramObject(program); if (!programObject) return; diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h index ff5375d4..2cf45640 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h @@ -138,6 +138,9 @@ namespace MobileGL::MG_Impl::GLImpl { GLint GetProgramResourceLocationIndex(GLuint program, GLenum programInterface, const GLchar* name); void ShaderStorageBlockBinding(GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); void ValidateProgram(GLuint program); + void ProgramParameteri(GLuint program, GLenum pname, GLint value); + void GetProgramBinary(GLuint program, GLsizei bufSize, GLsizei* length, GLenum* binaryFormat, void* binary); + void ProgramBinary(GLuint program, GLenum binaryFormat, const void* binary, GLsizei length); void TransformFeedbackVaryings(GLuint program, GLsizei count, const GLchar* const* varyings, GLenum bufferMode); void GetTransformFeedbackVarying(GLuint program, GLuint index, GLsizei bufSize, GLsizei* length, GLsizei* size, GLenum* type, GLchar* name); diff --git a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h index 582b5a83..4c5c1d0a 100644 --- a/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h +++ b/MobileGL/MG_State/GLState/ProgramState/ProgramObject.h @@ -377,6 +377,17 @@ namespace MobileGL::MG_State::GLState { Bool GetDeleteStatus() const { return m_deleteStatus; } Bool GetLinkStatus() const { return m_linkStatus; } + // GL_PROGRAM_BINARY_RETRIEVABLE_HINT. MobileGL exposes no program binary format + // (GL_NUM_PROGRAM_BINARY_FORMATS is 0), so the hint is pure state - which is all + // ARB_get_program_binary requires of it. + Bool GetBinaryRetrievableHint() const { return m_binaryRetrievableHint; } + void SetBinaryRetrievableHint(Bool hint) { m_binaryRetrievableHint = hint; } + // glProgramBinary always fails here (there is no format it could accept) and the + // spec then requires the program's LINK_STATUS to read FALSE. + void MarkLinkFailedByProgramBinary() { + ResetLinkArtifacts(); + m_infoLog = "No program binary format is supported."; + } Bool GetValidateStatus() const { return m_validateStatus; } Int GetActiveAtomicCounterCount() const { return m_program->getNumAtomicCounters(); } Int GetActiveAttributesCount() const { return m_program->getNumPipeInputs(); } @@ -593,6 +604,7 @@ namespace MobileGL::MG_State::GLState { String m_infoLog; Bool m_deleteStatus = false; Bool m_linkStatus = false; + Bool m_binaryRetrievableHint = false; Bool m_validateStatus = true; Uint32 m_backendStateVersion = 0;