diff --git a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp index 343e4399..07a429cc 100644 --- a/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp +++ b/MobileGL/MG_Impl/GLImpl/Exporting/Definitions.cpp @@ -867,7 +867,7 @@ DECLARE_GL_FUNCTION_HEAD(void, ProvokingVertex, GLenum mode) DECLARE_GL_FUNCTION DECLARE_GL_FUNCTION_HEAD(void, TexImage2DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexImage2DMultisample, target, samples, internalformat, width, height, fixedsamplelocations) DECLARE_GL_FUNCTION_HEAD(void, TexImage3DMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations) DECLARE_GL_FUNCTION_END_NO_RETURN(void, TexImage3DMultisample, target, samples, internalformat, width, height, depth, fixedsamplelocations) DECLARE_GL_FUNCTION_STUB_HEAD(void, BindFragDataLocationIndexed, GLuint program, GLuint colorNumber, GLuint index, const GLchar* name) DECLARE_GL_FUNCTION_STUB_END_NO_RETURN(void, BindFragDataLocationIndexed, program, colorNumber, index, name) -DECLARE_GL_FUNCTION_STUB_HEAD(GLint, GetFragDataIndex, GLuint program, const GLchar* name) DECLARE_GL_FUNCTION_STUB_END(GLint, GetFragDataIndex, program, name) +DECLARE_GL_FUNCTION_HEAD(GLint, GetFragDataIndex, GLuint program, const GLchar* name) DECLARE_GL_FUNCTION_END(GLint, GetFragDataIndex, program, name) DECLARE_GL_FUNCTION_HEAD(void, QueryCounter, GLuint id, GLenum target) DECLARE_GL_FUNCTION_END_NO_RETURN(void, QueryCounter, id, target) DECLARE_GL_FUNCTION_HEAD(void, GetQueryObjecti64v, GLuint id, GLenum pname, GLint64* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetQueryObjecti64v, id, pname, params) DECLARE_GL_FUNCTION_HEAD(void, GetQueryObjectui64v, GLuint id, GLenum pname, GLuint64* params) DECLARE_GL_FUNCTION_END_NO_RETURN(void, GetQueryObjectui64v, id, pname, params) diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp index ff2dc65a..9fb0fcf5 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.cpp @@ -1543,6 +1543,35 @@ namespace MobileGL::MG_Impl::GLImpl { return programObject->GetFragmentDataLocation(name); } + GLint GetFragDataIndex_State(GLuint program, const char* name) { + auto& programObject = TryToGetProgramObject(program); + if (programObject == nullptr) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + std::to_string(program) + " is not the name of a program object.")); + return -1; + } + if (name == nullptr) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidValue, + MakeUnique("MG_Impl/GLImpl", __func__, "name cannot be null.")); + return -1; + } + if (!programObject->GetLinkStatus()) { + MG_State::pGLContext->RecordError( + ErrorCode::InvalidOperation, + MakeUnique("MG_Impl/GLImpl", __func__, + std::to_string(program) + " has not been linked successfully.")); + return -1; + } + // A name that is not an active user-defined fragment output (including gl_ built-ins) has no + // index. Every bound output uses color index 0: MobileGL does not yet track dual-source + // (index 1) bindings -- glBindFragDataLocationIndexed and the layout(index = 1) qualifier are + // not supported -- so this is exact for every program that does not use dual-source blending. + return programObject->GetFragmentDataLocation(name) < 0 ? -1 : 0; + } + void ValidateProgram_State(GLuint program) { // THROW_UNIMPL_EXCEPTION; } @@ -1981,6 +2010,10 @@ namespace MobileGL::MG_Impl::GLImpl { return GetFragDataLocation_State(program, name); } + GLint GetFragDataIndex(GLuint program, const char* name) { + return GetFragDataIndex_State(program, name); + } + void GetProgramInterfaceiv(GLuint program, GLenum programInterface, GLenum pname, GLint* params) { auto& programObject = TryToGetLinkedProgramForInterfaceQuery(program, __func__); if (!programObject) return; diff --git a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h index 5d7794e3..9e4f9ebe 100644 --- a/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h +++ b/MobileGL/MG_Impl/GLImpl/Program/GL_Program.h @@ -126,6 +126,7 @@ namespace MobileGL::MG_Impl::GLImpl { GLchar* uniformBlockName); void BindFragDataLocation(GLuint program, GLuint colorNumber, const char* name); GLint GetFragDataLocation(GLuint program, const char* name); + GLint GetFragDataIndex(GLuint program, const char* name); void GetProgramInterfaceiv(GLuint program, GLenum programInterface, GLenum pname, GLint* params); GLuint GetProgramResourceIndex(GLuint program, GLenum programInterface, const GLchar* name); void GetProgramResourceName(GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, diff --git a/MobileGL/MG_Test/Program/ProgramTest.cpp b/MobileGL/MG_Test/Program/ProgramTest.cpp index 81ac07c6..8efc4950 100644 --- a/MobileGL/MG_Test/Program/ProgramTest.cpp +++ b/MobileGL/MG_Test/Program/ProgramTest.cpp @@ -1360,6 +1360,11 @@ TEST_F(ProgramTest, CompileAndLinkWithExplicitFragmentOut) { GLint fragColorLoc = GetFragDataLocation(program, "fragColor"); ASSERT_EQ(fragColorLoc, 7); + // glGetFragDataIndex: a valid user output uses color index 0 (dual-source index 1 is not tracked); + // a name that is not an active output returns -1. Neither records a GL error. + EXPECT_EQ(GetFragDataIndex(program, "fragColor"), 0); + EXPECT_EQ(GetFragDataIndex(program, "notAnActiveOutput"), -1); + auto programObject = MG_State::pGLContext->GetCurrentProgram(); auto& spirvs = programObject->GetGeneratedSpirv(); auto& fragSpirv = spirvs[programObject->GetShaderIndexByStage(ShaderStage::Fragment)]; @@ -1554,6 +1559,16 @@ void main() { fragColor = apply_fog(color, sphericalVertexDistance, cylindricalVertexDistance, FogEnvironmentalStart, FogEnvironmentalEnd, FogRenderDistanceStart, FogRenderDistanceEnd, FogColor); })"; +TEST_F(ProgramTest, GetFragDataIndexRejectsInvalidProgram) { + // A handle that was never generated is rejected and returns -1. Like glGetFragDataLocation, this + // routes through the shared program-name check, which records GL_INVALID_VALUE for an unknown name. + EXPECT_EQ(GetFragDataIndex(999999u, "fragColor"), -1); + EXPECT_EQ(GetError(), GL_INVALID_VALUE); + // The name check and the entry point each queue an error for an unknown handle; drain the rest so + // no stale error leaks into a later test (the fixture does not reset the error queue). + while (GetError() != GL_NO_ERROR) {} +} + TEST_F(ProgramTest, CompileShaderWithSamplerAsVarName) { char infoLog[1024] = "";