[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:
2026-07-11 00:39:23 -04:00
parent bebe534bad
commit 22ac8a8c10
6 changed files with 79 additions and 10 deletions
@@ -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