mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-09 12:48:32 +09:00
[Feat] (MG_Impl/GLImpl, MG_State): implement glBindFragDataLocationIndexed
Bind a fragment output to both a color number and a color index (0 or 1 for dual-source blending), and report the bound index back through glGetFragDataIndex. - ProgramObject now tracks a per-output color index alongside the location: SetExplicitFragmentOutIndex stores it, it is snapshotted into the linked map at link time (like the location map), and GetFragmentDataIndex returns it (0 by default) for an active output. - glBindFragDataLocation becomes glBindFragDataLocationIndexed with index 0, matching the GL definition, so it also resets a previously-bound index to 0. - Validation: index must be 0 or 1 (GL_INVALID_VALUE); colorNumber is bounded by GL_MAX_DRAW_BUFFERS for index 0 and GL_MAX_DUAL_SOURCE_DRAW_BUFFERS (reported as 1) for index 1 (GL_INVALID_VALUE); a gl_ name is GL_INVALID_OPERATION. - glGetFragDataIndex now returns the real bound index instead of a hardcoded 0. The index is tracked for reflection but is not yet plumbed into dual-source blend rendering, and shader-side layout(index=) qualifiers are not reflected -- both documented at the call sites. Tests: index round-trip through a re-link (bind 1 -> GetFragDataIndex == 1; glBindFragDataLocation resets to 0), plus the validation error table; mutation-verified end to end. ProgramTest 24/24.
This commit is contained in:
@@ -866,7 +866,7 @@ DECLARE_GL_FUNCTION_HEAD(void, MultiDrawElementsBaseVertex, GLenum mode, const G
|
||||
DECLARE_GL_FUNCTION_HEAD(void, ProvokingVertex, GLenum mode) DECLARE_GL_FUNCTION_END_NO_RETURN(void, ProvokingVertex, mode)
|
||||
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_HEAD(void, BindFragDataLocationIndexed, GLuint program, GLuint colorNumber, GLuint index, const GLchar* name) DECLARE_GL_FUNCTION_END_NO_RETURN(void, BindFragDataLocationIndexed, program, colorNumber, index, 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)
|
||||
|
||||
@@ -1483,7 +1483,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
length ? *length : 0);
|
||||
}
|
||||
|
||||
void BindFragDataLocation_State(GLuint program, GLuint colorNumber, const char* name) {
|
||||
void BindFragDataLocationIndexed_State(GLuint program, GLuint colorNumber, GLuint index, const char* name) {
|
||||
auto& programObject = TryToGetProgramObject(program);
|
||||
// TryToGetProgramObject already recorded the error for a bad handle (GL_INVALID_VALUE for an
|
||||
// unknown name, GL_INVALID_OPERATION for a non-program object); do not record a second one.
|
||||
@@ -1494,12 +1494,23 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "name cannot be null."));
|
||||
return;
|
||||
}
|
||||
// index selects the single (0) or dual-source (1) color; it must be 0 or 1.
|
||||
if (index > 1) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__, "index must be 0 or 1."));
|
||||
return;
|
||||
}
|
||||
const auto& dynamicParameters = MG_Backend::pActiveBackendObject->GetDynamicParameters();
|
||||
if (colorNumber >= static_cast<GLuint>(dynamicParameters.MaxDrawBuffers)) {
|
||||
// colorNumber is bounded by GL_MAX_DRAW_BUFFERS for index 0, and by
|
||||
// GL_MAX_DUAL_SOURCE_DRAW_BUFFERS (which MobileGL reports as 1) for index 1.
|
||||
const GLuint colorNumberLimit =
|
||||
(index == 0) ? static_cast<GLuint>(dynamicParameters.MaxDrawBuffers) : 1u;
|
||||
if (colorNumber >= colorNumberLimit) {
|
||||
MG_State::pGLContext->RecordError(
|
||||
ErrorCode::InvalidValue,
|
||||
MakeUnique<GenericErrorInfo>("MG_Impl/GLImpl", __func__,
|
||||
"colorNumber is greater than or equal to GL_MAX_DRAW_BUFFERS."));
|
||||
"colorNumber exceeds the applicable draw-buffer limit."));
|
||||
return;
|
||||
}
|
||||
if (strncmp(name, "gl_", 3) == 0) {
|
||||
@@ -1510,8 +1521,14 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
return;
|
||||
}
|
||||
|
||||
MGLOG_D("%s: loc %02d = \"%s\"", __func__, colorNumber, name);
|
||||
MGLOG_D("%s: loc %02d index %u = \"%s\"", __func__, colorNumber, index, name);
|
||||
programObject->SetExplicitFragmentOutLocation(colorNumber, name);
|
||||
programObject->SetExplicitFragmentOutIndex(index, name);
|
||||
}
|
||||
|
||||
// glBindFragDataLocation is glBindFragDataLocationIndexed with color index 0.
|
||||
void BindFragDataLocation_State(GLuint program, GLuint colorNumber, const char* name) {
|
||||
BindFragDataLocationIndexed_State(program, colorNumber, 0, name);
|
||||
}
|
||||
|
||||
GLint GetFragDataLocation_State(GLuint program, const char* name) {
|
||||
@@ -1549,11 +1566,10 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
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;
|
||||
// Returns the color index bound by glBindFragDataLocationIndexed (0 by default), or -1 if name
|
||||
// is not an active user-defined output. Note: the index is tracked for reflection but is not
|
||||
// yet plumbed into dual-source blend rendering, and shader-side layout(index=) is not reflected.
|
||||
return programObject->GetFragmentDataIndex(name);
|
||||
}
|
||||
|
||||
void ValidateProgram_State(GLuint program) {
|
||||
@@ -1990,6 +2006,10 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
BindFragDataLocation_State(program, colorNumber, name);
|
||||
}
|
||||
|
||||
void BindFragDataLocationIndexed(GLuint program, GLuint colorNumber, GLuint index, const char* name) {
|
||||
BindFragDataLocationIndexed_State(program, colorNumber, index, name);
|
||||
}
|
||||
|
||||
GLint GetFragDataLocation(GLuint program, const char* name) {
|
||||
return GetFragDataLocation_State(program, name);
|
||||
}
|
||||
|
||||
@@ -125,6 +125,7 @@ namespace MobileGL::MG_Impl::GLImpl {
|
||||
void GetActiveUniformBlockName(GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei* length,
|
||||
GLchar* uniformBlockName);
|
||||
void BindFragDataLocation(GLuint program, GLuint colorNumber, const char* name);
|
||||
void BindFragDataLocationIndexed(GLuint program, GLuint colorNumber, GLuint index, 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);
|
||||
|
||||
@@ -291,6 +291,7 @@ namespace MobileGL::MG_State::GLState {
|
||||
m_linkStatus = true;
|
||||
m_program = result.value();
|
||||
m_linkedFragDataLocation = m_explicitFragDataLocation;
|
||||
m_linkedFragDataIndex = m_explicitFragDataIndex;
|
||||
MGLOG_D("ProgramObject %u: LinkProgram succeeded, TProgram ptr %p", m_externalIndex, m_program.get());
|
||||
} else {
|
||||
m_infoLog = result.error().log;
|
||||
@@ -692,6 +693,12 @@ namespace MobileGL::MG_State::GLState {
|
||||
m_externalIndex, name, index);
|
||||
}
|
||||
|
||||
void ProgramObject::SetExplicitFragmentOutIndex(Uint colorIndex, const char* name) {
|
||||
m_explicitFragDataIndex[name] = colorIndex;
|
||||
MGLOG_D("ProgramObject %u: SetExplicitFragmentOutIndex - stored color index for '%s' -> %u", m_externalIndex,
|
||||
name, colorIndex);
|
||||
}
|
||||
|
||||
Bool ProgramObject::ValidateFragmentOutputLocations() {
|
||||
if (!m_program) return false;
|
||||
|
||||
@@ -746,4 +753,13 @@ namespace MobileGL::MG_State::GLState {
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
Int ProgramObject::GetFragmentDataIndex(const char* name) {
|
||||
// Only an active user-defined fragment output has an index; reuse the location lookup to test
|
||||
// that. The color index defaults to 0 unless glBindFragDataLocationIndexed bound it to 1.
|
||||
// (Shader-side layout(index = ...) qualifiers are not reflected here, only API bindings.)
|
||||
if (GetFragmentDataLocation(name) < 0) return -1;
|
||||
const auto it = m_linkedFragDataIndex.find(name);
|
||||
return it != m_linkedFragDataIndex.end() ? static_cast<Int>(it->second) : 0;
|
||||
}
|
||||
} // namespace MobileGL::MG_State::GLState
|
||||
|
||||
@@ -26,10 +26,14 @@ namespace MobileGL::MG_State::GLState {
|
||||
|
||||
void SetExplicitVertexInLocation(Uint index, const char* name);
|
||||
void SetExplicitFragmentOutLocation(Uint index, const char* name);
|
||||
// Dual-source blend color index (glBindFragDataLocationIndexed). Takes effect on next link.
|
||||
void SetExplicitFragmentOutIndex(Uint colorIndex, const char* name);
|
||||
void SetMaxFragmentOutputColorNumber(Int maxDrawBuffers) {
|
||||
m_maxFragmentOutputColorNumber = maxDrawBuffers;
|
||||
}
|
||||
Int GetFragmentDataLocation(const char* name);
|
||||
// Bound color index for an active fragment output (0 by default), or -1 if name is not one.
|
||||
Int GetFragmentDataIndex(const char* name);
|
||||
|
||||
Vector<SharedPtr<ShaderObject>>& GetAttachedShaders();
|
||||
const Vector<SharedPtr<ShaderObject>>& GetAttachedShaders() const;
|
||||
@@ -361,6 +365,10 @@ namespace MobileGL::MG_State::GLState {
|
||||
// FragData (Frag out)
|
||||
UnorderedMap<String, Uint> m_explicitFragDataLocation;
|
||||
UnorderedMap<String, Uint> m_linkedFragDataLocation;
|
||||
// Dual-source blend color index per output name (glBindFragDataLocationIndexed); snapshotted
|
||||
// into the linked map at link time, like the location maps above.
|
||||
UnorderedMap<String, Uint> m_explicitFragDataIndex;
|
||||
UnorderedMap<String, Uint> m_linkedFragDataIndex;
|
||||
Int m_maxFragmentOutputColorNumber = 8;
|
||||
|
||||
// Uniforms
|
||||
|
||||
@@ -1389,6 +1389,30 @@ TEST_F(ProgramTest, CompileAndLinkWithExplicitFragmentOut) {
|
||||
// }
|
||||
ASSERT_TRUE(pSrcfragOut != nullptr) << "Not found expected string in generated shader.\n(Searching for \"" << needle
|
||||
<< "\")";
|
||||
|
||||
// glBindFragDataLocationIndexed round-trips the color index through a re-link. index 1 requires
|
||||
// colorNumber 0 (GL_MAX_DUAL_SOURCE_DRAW_BUFFERS is 1).
|
||||
BindFragDataLocationIndexed(program, 0, 1, "fragColor");
|
||||
LinkProgram(program);
|
||||
GetProgramiv(program, GL_LINK_STATUS, &linkStatus);
|
||||
ASSERT_EQ(linkStatus, GL_TRUE);
|
||||
EXPECT_EQ(GetFragDataIndex(program, "fragColor"), 1);
|
||||
EXPECT_EQ(GetFragDataIndex(program, "notAnActiveOutput"), -1);
|
||||
|
||||
// glBindFragDataLocation is equivalent to index 0 and resets it.
|
||||
BindFragDataLocation(program, 0, "fragColor");
|
||||
LinkProgram(program);
|
||||
EXPECT_EQ(GetFragDataIndex(program, "fragColor"), 0);
|
||||
|
||||
// Validation: index > 1 and a too-large colorNumber for index 1 are GL_INVALID_VALUE; a gl_ name is
|
||||
// GL_INVALID_OPERATION.
|
||||
BindFragDataLocationIndexed(program, 0, 2, "fragColor");
|
||||
EXPECT_EQ(GetError(), GL_INVALID_VALUE);
|
||||
BindFragDataLocationIndexed(program, 1, 1, "fragColor"); // colorNumber 1 invalid for index 1
|
||||
EXPECT_EQ(GetError(), GL_INVALID_VALUE);
|
||||
BindFragDataLocationIndexed(program, 0, 0, "gl_FragColor");
|
||||
EXPECT_EQ(GetError(), GL_INVALID_OPERATION);
|
||||
EXPECT_EQ(GetError(), GL_NO_ERROR);
|
||||
}
|
||||
|
||||
const char* vs_sampler_as_varname = R"(#version 330
|
||||
|
||||
Reference in New Issue
Block a user