mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Fix] (DirectGLES): repair the emulation-guard mask restore and scissor the resolve fallback's staging blit correctly
Final audit round over the DirectGLES scratch/shadow mechanisms; three verified defects fixed: - ~ScopedEmulationDrawState restored the APPLICATION's per-buffer colour masks, not what SyncRenderState actually pushed: a widened attachment's alpha-off doctoring (g_syncedColorMaskAlphaWidenMask) was dropped while the memo still claimed it applied, so the next sync early-outed and draws wrote fragment alpha into the widened buffer - breaking the stored-alpha==1.0 invariant the widen discipline exists to protect. The restore now re-applies the doctoring. - The same restore loop gated on the core glColorMaski name only, while the sync push falls back to glColorMaskiEXT/OES: EXT/OES-only devices were left holding buffer 0's mask broadcast across every draw buffer with the shadow recording the divergent set (never repaired). The restore now uses the same three-way pointer fallback. - ResolveThenBlit ran its resolve-into-scratch staging blit under the application's scissor: a box not covering the scratch-origin rect clipped the resolve silently (no GL error), and the second blit then copied stale scratch renderbuffer texels into the destination. The staging blit now runs scissor-off (shadow-tracked, like ScopedScissorDisable); the caller-visible blit keeps its native scissor semantics.
This commit is contained in:
@@ -3966,8 +3966,19 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
Bool resolved = g_GLESFuncs.glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
|
Bool resolved = g_GLESFuncs.glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
DrainBlitErrors();
|
DrainBlitErrors();
|
||||||
|
// A blit is scissored like a draw (the replicate path's guard documents the
|
||||||
|
// same rule): the application's box would clip this resolve into the
|
||||||
|
// scratch, and the second blit would then copy never-written scratch texels
|
||||||
|
// into the destination - silently, since scissor clipping raises no GL
|
||||||
|
// error. Disable for the staging blit only; the caller-visible blit below
|
||||||
|
// keeps the blit's native scissor semantics. Tracked via the render-state
|
||||||
|
// shadow, exactly like ScopedScissorDisable.
|
||||||
|
const Bool scissorWasEnabled =
|
||||||
|
(RenderStateImpl::g_syncedRenderStateParameters.ScissorTestEnabledMask & 1u) != 0;
|
||||||
|
if (scissorWasEnabled) g_GLESFuncs.glDisable(GL_SCISSOR_TEST);
|
||||||
g_GLESFuncs.glBlitFramebuffer(left, bottom, right, top, 0, 0, width, height, GL_COLOR_BUFFER_BIT,
|
g_GLESFuncs.glBlitFramebuffer(left, bottom, right, top, 0, 0, width, height, GL_COLOR_BUFFER_BIT,
|
||||||
GL_NEAREST);
|
GL_NEAREST);
|
||||||
|
if (scissorWasEnabled) g_GLESFuncs.glEnable(GL_SCISSOR_TEST);
|
||||||
resolved = g_GLESFuncs.glGetError() == GL_NO_ERROR;
|
resolved = g_GLESFuncs.glGetError() == GL_NO_ERROR;
|
||||||
}
|
}
|
||||||
if (resolved) {
|
if (resolved) {
|
||||||
@@ -4113,13 +4124,25 @@ namespace MobileGL::MG_Backend::DirectGLES {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// The per-draw-buffer colour masks are not covered by the non-indexed
|
// The per-draw-buffer colour masks are not covered by the non-indexed
|
||||||
// glColorMask above.
|
// glColorMask above. Restore what the SYNC actually pushed, not the raw
|
||||||
for (Uint index = 0; index < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; ++index) {
|
// application masks: a widened attachment's alpha write is forced off by
|
||||||
const BoolVec4& colorMask = RenderStateImpl::g_syncedRenderStateParameters.ColorMasks[index];
|
// SyncRenderState and memoized in g_syncedColorMaskAlphaWidenMask, and the
|
||||||
if (g_GLESFuncs.glColorMaski) {
|
// next sync early-outs on an unchanged version - restoring the undoctored
|
||||||
g_GLESFuncs.glColorMaski(index, colorMask.x() ? GL_TRUE : GL_FALSE,
|
// mask here would leave alpha writes enabled on the widened buffer with
|
||||||
colorMask.y() ? GL_TRUE : GL_FALSE, colorMask.z() ? GL_TRUE : GL_FALSE,
|
// nothing left to repair it. Same three-way pointer fallback as
|
||||||
colorMask.w() ? GL_TRUE : GL_FALSE);
|
// SyncRenderState's push: gating on the core name alone left EXT/OES-only
|
||||||
|
// devices holding buffer 0's mask broadcast across every buffer.
|
||||||
|
const auto colorMaskiFn = g_GLESFuncs.glColorMaski ? g_GLESFuncs.glColorMaski
|
||||||
|
: g_GLESFuncs.glColorMaskiEXT ? g_GLESFuncs.glColorMaskiEXT
|
||||||
|
: g_GLESFuncs.glColorMaskiOES;
|
||||||
|
if (colorMaskiFn) {
|
||||||
|
for (Uint index = 0; index < MG_State::GLState::FramebufferObject::MAX_DRAW_BUFFERS; ++index) {
|
||||||
|
BoolVec4 colorMask = RenderStateImpl::g_syncedRenderStateParameters.ColorMasks[index];
|
||||||
|
if (index < 32 && (RenderStateImpl::g_syncedColorMaskAlphaWidenMask & (1u << index)) != 0) {
|
||||||
|
colorMask.w() = false;
|
||||||
|
}
|
||||||
|
colorMaskiFn(index, colorMask.x() ? GL_TRUE : GL_FALSE, colorMask.y() ? GL_TRUE : GL_FALSE,
|
||||||
|
colorMask.z() ? GL_TRUE : GL_FALSE, colorMask.w() ? GL_TRUE : GL_FALSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_pausedTransformFeedback && g_GLESFuncs.glResumeTransformFeedback) {
|
if (m_pausedTransformFeedback && g_GLESFuncs.glResumeTransformFeedback) {
|
||||||
|
|||||||
Reference in New Issue
Block a user