[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
+24
View File
@@ -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