[Feat] (MG_Impl/GLImpl, MG_State, MG_Backend): implement glColorMaski

Promote the color writemask to per-draw-buffer state and implement the
indexed glColorMaski entry point (previously a stub), plus its read-back
through glGetBooleani_v.

- RenderState: replace the single BoolVec4 ColorMask with an array of
  MAX_DRAW_BUFFERS masks, all initialized to true. SetColorMask now
  broadcasts to every draw buffer (glColorMask semantics); GetColorMask
  returns draw buffer 0. Add indexed set/get accessors + GLContext
  wrappers.
- glColorMaski sets only the addressed draw buffer; out-of-range index
  raises GL_INVALID_VALUE (buf is a GLuint, so no GL_INVALID_ENUM path),
  mirroring the indexed blend entry points' MAX_DRAW_BUFFERS bound.
- glGetBooleani_v(GL_COLOR_WRITEMASK, i) reports draw buffer i's four
  booleans; the non-indexed glGetBooleanv still reports draw buffer 0.
- Fix GLboolean coercion in the color-mask path: any nonzero value
  enables the component (was == GL_TRUE, which wrongly rejected e.g. 2).
- DirectGLES sync reads ColorMasks[0] (GLES core has only non-indexed
  glColorMask).

Tests: ColorMaskIndexedStoresAndReadsBack covers the per-buffer vs
broadcast semantics, buffer-0 read-back, out-of-range INVALID_VALUE, and
the GLboolean coercion (mutation-verified: == GL_TRUE fails it). Full
SanityTest sweep green (30/30).
This commit is contained in:
2026-07-10 21:30:57 -04:00
parent 95876d9d8c
commit 5e8106114f
9 changed files with 145 additions and 14 deletions
@@ -702,9 +702,9 @@ namespace MobileGL::MG_Backend::DirectGLES {
}
}
{ // Color mask
if (parameters.ColorMask != g_syncedRenderStateParameters.ColorMask) {
const BoolVec4& colorMask = parameters.ColorMask;
{ // Color mask. GLES core has only the non-indexed glColorMask, so sync draw buffer 0.
if (parameters.ColorMasks[0] != g_syncedRenderStateParameters.ColorMasks[0]) {
const BoolVec4& colorMask = parameters.ColorMasks[0];
g_GLESFuncs.glColorMask(ToGLBoolean(colorMask.x()), ToGLBoolean(colorMask.y()),
ToGLBoolean(colorMask.z()), ToGLBoolean(colorMask.w()));
}