diff --git a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp index ebb8a998..0fa8df53 100644 --- a/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp +++ b/MobileGL/MG_Backend/DirectGLES/DirectGLES.cpp @@ -702,11 +702,47 @@ namespace MobileGL::MG_Backend::DirectGLES { } } - { // 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())); + { // Color mask. Uniform masks use the non-indexed glColorMask (works everywhere); divergent + // per-draw-buffer masks use the indexed glColorMaski when draw_buffers_indexed is + // available, otherwise fall back to broadcasting draw buffer 0. Mirrors the blend block. + using FBO = MG_State::GLState::FramebufferObject; + const auto& targetMasks = parameters.ColorMasks; + const auto& syncedMasks = g_syncedRenderStateParameters.ColorMasks; + + Bool anyDirty = false; + Bool allSame = true; + for (Uint i = 0; i < FBO::MAX_DRAW_BUFFERS; ++i) { + if (targetMasks[i] != syncedMasks[i]) anyDirty = true; + if (i > 0 && targetMasks[i] != targetMasks[0]) allSame = false; + } + + if (anyDirty) { + if (allSame || !g_GLESCapabilities.SupportsIndexedColorMask) { + const BoolVec4& m = targetMasks[0]; + g_GLESFuncs.glColorMask(ToGLBoolean(m.x()), ToGLBoolean(m.y()), ToGLBoolean(m.z()), + ToGLBoolean(m.w())); + } else { + const auto colorMaskiFn = g_GLESFuncs.glColorMaski ? g_GLESFuncs.glColorMaski + : g_GLESFuncs.glColorMaskiEXT ? g_GLESFuncs.glColorMaskiEXT + : g_GLESFuncs.glColorMaskiOES; + for (Uint i = 0; i < FBO::MAX_DRAW_BUFFERS; ++i) { + if (targetMasks[i] != syncedMasks[i]) { + const BoolVec4& m = targetMasks[i]; + colorMaskiFn(i, ToGLBoolean(m.x()), ToGLBoolean(m.y()), ToGLBoolean(m.z()), + ToGLBoolean(m.w())); + } + } + } + } + } + + { // Polygon mode. GLES core has no glPolygonMode; use NV/ANGLE_polygon_mode when present. + // Without the extension the mode stays FILL and non-FILL requests are dropped. + if (parameters.PolygonModeFront != g_syncedRenderStateParameters.PolygonModeFront && + g_GLESCapabilities.SupportsPolygonMode) { + const auto polygonModeFn = + g_GLESFuncs.glPolygonModeNV ? g_GLESFuncs.glPolygonModeNV : g_GLESFuncs.glPolygonModeANGLE; + polygonModeFn(GL_FRONT_AND_BACK, parameters.PolygonModeFront); } } diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp index df09f75d..e7c2197f 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp @@ -82,6 +82,13 @@ namespace MobileGL::MG_Util::BackendLoader { } \ } while (0); +// Optional (extension-provided) entry points: a null pointer is expected on drivers that lack the +// extension, so absence is not an error. The call site null-checks before use. +#define INIT_GLES_FUNC_OPTIONAL(name) \ + do { \ + funcs.name = (MG_External::GLES::name##_PTR)procAddress(#name); \ + } while (0); + { INIT_GLES_FUNC(glActiveTexture) INIT_GLES_FUNC(glAttachShader) @@ -454,6 +461,10 @@ namespace MobileGL::MG_Util::BackendLoader { INIT_GLES_FUNC(glGetQueryObjectui64vEXT) INIT_GLES_FUNC(glBindFragDataLocationEXT) INIT_GLES_FUNC(glMapBufferOES) + INIT_GLES_FUNC_OPTIONAL(glPolygonModeNV) + INIT_GLES_FUNC_OPTIONAL(glPolygonModeANGLE) + INIT_GLES_FUNC_OPTIONAL(glColorMaskiEXT) + INIT_GLES_FUNC_OPTIONAL(glColorMaskiOES) INIT_GLES_FUNC(glMultiDrawArraysIndirectEXT) INIT_GLES_FUNC(glMultiDrawElementsIndirectEXT) INIT_GLES_FUNC(glMultiDrawElementsBaseVertexEXT) @@ -734,6 +745,16 @@ namespace MobileGL::MG_Util::BackendLoader { } } + // Detect optional raster/color-mask entry points by whether they loaded. glColorMaski is GLES + // 3.2 core (no extension string), so pointer presence is the reliable signal for all of these. + caps.SupportsPolygonMode = + glesFuncs.glPolygonModeNV != nullptr || glesFuncs.glPolygonModeANGLE != nullptr; + caps.SupportsIndexedColorMask = glesFuncs.glColorMaski != nullptr || + glesFuncs.glColorMaskiEXT != nullptr || + glesFuncs.glColorMaskiOES != nullptr; + MGLOG_I(" glPolygonMode (NV/ANGLE): %s", caps.SupportsPolygonMode ? "yes" : "no"); + MGLOG_I(" indexed glColorMaski: %s", caps.SupportsIndexedColorMask ? "yes" : "no"); + MGLOG_I("OpenGL ES capabilities:"); glesFuncs.glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &caps.UniformBufferOffsetAlignment); MGLOG_I(" GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: %d", caps.UniformBufferOffsetAlignment); diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h index c86e4418..cc173951 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h @@ -615,6 +615,11 @@ namespace MobileGL { GL_FUNC_TYPEDEF(void, glGetQueryObjectui64vEXT, GLuint id, GLenum pname, GLuint64* params) GL_FUNC_TYPEDEF(void, glBindFragDataLocationEXT, GLuint program, GLuint colorNumber, const GLchar* name) GL_FUNC_TYPEDEF(void*, glMapBufferOES, GLenum target, GLenum access) + // Extension aliases for glPolygonMode (GLES core has none) and indexed glColorMaski. + GL_FUNC_TYPEDEF(void, glPolygonModeNV, GLenum face, GLenum mode) + GL_FUNC_TYPEDEF(void, glPolygonModeANGLE, GLenum face, GLenum mode) + GL_FUNC_TYPEDEF(void, glColorMaskiEXT, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) + GL_FUNC_TYPEDEF(void, glColorMaskiOES, GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a) GL_FUNC_TYPEDEF(void, glMultiDrawArraysIndirectEXT, GLenum mode, const void* indirect, GLsizei drawcount, GLsizei stride) @@ -1006,6 +1011,10 @@ namespace MobileGL { GL_FUNC_DECL(glGetQueryObjectui64vEXT) GL_FUNC_DECL(glBindFragDataLocationEXT) GL_FUNC_DECL(glMapBufferOES) + GL_FUNC_DECL(glPolygonModeNV) + GL_FUNC_DECL(glPolygonModeANGLE) + GL_FUNC_DECL(glColorMaskiEXT) + GL_FUNC_DECL(glColorMaskiOES) GL_FUNC_DECL(glMultiDrawArraysIndirectEXT) GL_FUNC_DECL(glMultiDrawElementsIndirectEXT) @@ -1025,6 +1034,12 @@ namespace MobileGL { Bool SupportsBaseInstance = false; // GL_EXT_disjoint_timer_query is present in the extension string. Bool SupportsDisjointTimerQuery = false; + // glPolygonModeNV/ANGLE loaded (GL_NV_polygon_mode / GL_ANGLE_polygon_mode). GLES core + // has no glPolygonMode, so without this the mode stays FILL. + Bool SupportsPolygonMode = false; + // An indexed glColorMaski entry point loaded (GLES 3.2 core, or GL_OES/EXT_draw_buffers_indexed). + // Without it, only the non-indexed glColorMask (draw buffer 0 broadcast) is available. + Bool SupportsIndexedColorMask = false; // GL_RENDERER contains "ANGLE". Bool IsAngleRenderer = false; // GL_RENDERER contains both "ANGLE" and "llvmpipe".