[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
+8
View File
@@ -493,6 +493,14 @@ namespace MobileGL::MG_State {
return m_renderState.GetColorMask();
}
void GLContext::SetColorMaskIndexed(Uint index, BoolVec4 mask) {
m_renderState.SetColorMaskIndexed(index, mask);
}
BoolVec4 GLContext::GetColorMaskIndexed(Uint index) const {
return m_renderState.GetColorMaskIndexed(index);
}
void GLContext::SetClearColor(FloatVec4 color) {
m_renderState.SetClearColor(color);
}
+2
View File
@@ -164,6 +164,8 @@ namespace MobileGL {
const StencilFaceState& GetStencilState(StencilFace face) const;
void SetColorMask(BoolVec4 mask);
BoolVec4 GetColorMask() const;
void SetColorMaskIndexed(Uint index, BoolVec4 mask);
BoolVec4 GetColorMaskIndexed(Uint index) const;
void SetClearColor(FloatVec4 color);
const FloatVec4& GetClearColor() const;
void SetClearDepth(Float depth);
@@ -26,7 +26,12 @@ namespace MobileGL {
}
} // namespace
RenderState::RenderState() {}
RenderState::RenderState() {
// The color writemask defaults to all-true for every draw buffer.
for (auto& mask : m_parameters.ColorMasks) {
mask = BoolVec4(true, true, true, true);
}
}
Uint RenderState::GetVersion() const {
return m_version;
@@ -437,14 +442,30 @@ namespace MobileGL {
// -------------------- Color Mask --------------------
void RenderState::SetColorMask(BoolVec4 mask) {
if (m_parameters.ColorMask == mask) return;
m_parameters.ColorMask = mask;
++m_version;
// glColorMask broadcasts the same mask to every draw buffer.
Bool changed = false;
for (auto& slot : m_parameters.ColorMasks) {
if (!(slot == mask)) {
slot = mask;
changed = true;
}
}
if (changed) ++m_version;
}
BoolVec4 RenderState::GetColorMask() const {
return m_parameters.ColorMask;
// Non-indexed query reports draw buffer 0.
return m_parameters.ColorMasks[0];
}
void RenderState::SetColorMaskIndexed(Uint index, BoolVec4 mask) {
if (m_parameters.ColorMasks[index] == mask) return;
m_parameters.ColorMasks[index] = mask;
++m_version;
}
BoolVec4 RenderState::GetColorMaskIndexed(Uint index) const {
return m_parameters.ColorMasks[index];
}
// -------------------- Clear State --------------------
@@ -230,8 +230,9 @@ namespace MobileGL {
DepthTestFunc DepthFunc = DepthTestFunc::Less;
Bool DepthMask = true;
// Color Mask
BoolVec4 ColorMask = BoolVec4(true, true, true, true);
// Color Mask. Per-draw-buffer state (glColorMaski); glColorMask broadcasts to all buffers.
// Every entry is initialized to all-true in RenderState's constructor.
Array<BoolVec4, MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS> ColorMasks;
// Clear State
FloatVec4 ClearColor = FloatVec4(0.0f, 0.0f, 0.0f, 1.0f);
@@ -358,9 +359,13 @@ namespace MobileGL {
StencilOperation depthPass);
const StencilFaceState& GetStencilState(StencilFace face) const;
// Color Mask
// Color Mask. SetColorMask broadcasts to every draw buffer and GetColorMask returns
// draw buffer 0; the indexed forms address a single draw buffer (glColorMaski). The
// caller is responsible for validating index against MAX_DRAW_BUFFERS.
void SetColorMask(BoolVec4 mask);
BoolVec4 GetColorMask() const;
void SetColorMaskIndexed(Uint index, BoolVec4 mask);
BoolVec4 GetColorMaskIndexed(Uint index) const;
// Clear State
void SetClearColor(FloatVec4 color);