diff --git a/MobileGL/Config.h b/MobileGL/Config.h index ff5c7ce1..4595528b 100644 --- a/MobileGL/Config.h +++ b/MobileGL/Config.h @@ -39,6 +39,23 @@ namespace MobileGL::MG_Config { Unroll, // one vkCmdDraw* per sub-draw }; + // Preferred DirectGLES emulation tier for glMultiDrawElements(BaseVertex). GLES has no + // such entry point in core, so every tier below is an emulation; they differ only in + // which driver capability they lean on and how many driver calls a batch costs. Like + // the Magma knob this is a preference, clamped at resolution time to what the ES + // driver actually supports, with one log line when it falls back. + enum class GLESMultiDrawMode : Uint8 { + Auto = 0, // unset: best supported tier + Ext, // one glMultiDrawElementsBaseVertexEXT + MultiIndirect, // one glMultiDrawElementsIndirectEXT over a scratch command buffer + Indirect, // one glDrawElementsIndirect per sub-draw over that same buffer + BaseVertex, // one glDrawElementsBaseVertex per sub-draw + DrawElements, // baseVertex folded into a scratch index buffer on the CPU, then plain + // glDrawElements per sub-draw (for drivers with no base-vertex draw at all) + Compute, // a compute shader flattens every sub-draw into one rebased index buffer, + // drawn by a single glDrawElements + }; + // Feature toggles parsed once from environment variables in MG_ConfigLoader::Init() // (ConfigLoader.cpp), before the accepted-env map is destroyed. All Bool fields share // one truthy rule: the variable is set, non-empty, not "0", and not "false" @@ -105,6 +122,11 @@ namespace MobileGL::MG_Config { // ("ext" | "indirect" | "unroll", see MultiDrawMode). Clamped to device support; // unset picks the best supported tier. MultiDrawMode MagmaMultiDrawMode = MultiDrawMode::Auto; + // MOBILEGL_ESPRYT_MULTIDRAW_MODE: preferred DirectGLES glMultiDrawElements emulation + // tier ("ext" | "multiindirect" | "indirect" | "basevertex" | "drawelements" | + // "compute", see GLESMultiDrawMode). Clamped to driver support; unset picks the best + // supported tier, which never includes "compute" - see the note on its resolution. + GLESMultiDrawMode EsprytMultiDrawMode = GLESMultiDrawMode::Auto; }; extern FeaturesTable Features; } // namespace MobileGL::MG_Config diff --git a/MobileGL/ConfigLoader.cpp b/MobileGL/ConfigLoader.cpp index 6cce0eb7..8ed0b58c 100644 --- a/MobileGL/ConfigLoader.cpp +++ b/MobileGL/ConfigLoader.cpp @@ -116,6 +116,28 @@ namespace MobileGL::MG_ConfigLoader { return MG_Config::MultiDrawMode::Auto; } + // Same contract as QueryEnvMultiDrawMode, over the DirectGLES tier names. + inline MG_Config::GLESMultiDrawMode QueryEnvGLESMultiDrawMode(const String& key) { + auto it = acceptedEnvVariablesMap->find(key); + if (it == acceptedEnvVariablesMap->end()) { + return MG_Config::GLESMultiDrawMode::Auto; + } + String lowered = it->second; + std::transform(lowered.begin(), lowered.end(), lowered.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + if (lowered == "ext") return MG_Config::GLESMultiDrawMode::Ext; + if (lowered == "multiindirect") return MG_Config::GLESMultiDrawMode::MultiIndirect; + if (lowered == "indirect") return MG_Config::GLESMultiDrawMode::Indirect; + if (lowered == "basevertex") return MG_Config::GLESMultiDrawMode::BaseVertex; + if (lowered == "drawelements") return MG_Config::GLESMultiDrawMode::DrawElements; + if (lowered == "compute") return MG_Config::GLESMultiDrawMode::Compute; + if (lowered.empty() || lowered == "auto") return MG_Config::GLESMultiDrawMode::Auto; + MGLOG_W("Config: Ignoring invalid env variable %s='%s'; expected " + "ext|multiindirect|indirect|basevertex|drawelements|compute|auto, using auto", + key.c_str(), it->second.c_str()); + return MG_Config::GLESMultiDrawMode::Auto; + } + inline Uint32 QueryEnvUint32(const String& key, Uint32 defaultValue, Uint32 minValue, Uint32 maxValue) { auto it = acceptedEnvVariablesMap->find(key); if (it == acceptedEnvVariablesMap->end()) { @@ -158,6 +180,7 @@ namespace MobileGL::MG_ConfigLoader { QueryEnvQuirkOverride("MOBILEGL_MAGMA_DISABLE_BLENDED_DEPTH_WRITE"); features.DisableRobustBufferAccess = QueryEnvFlag("MOBILEGL_DISABLE_ROBUST_BUFFER_ACCESS"); features.MagmaMultiDrawMode = QueryEnvMultiDrawMode("MOBILEGL_MAGMA_MULTIDRAW_MODE"); + features.EsprytMultiDrawMode = QueryEnvGLESMultiDrawMode("MOBILEGL_ESPRYT_MULTIDRAW_MODE"); } inline void InitBackendType() { diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp index 68be1e5f..d841e2be 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp @@ -888,6 +888,18 @@ namespace MobileGL::MG_Util::BackendLoader { caps.SupportsMultiDrawElementsBaseVertex = hasDrawElementsBaseVertexExtension && hasMultiDrawArraysExtension && glesFuncs.glMultiDrawElementsBaseVertexEXT != nullptr; + // Core from ES 3.2 on, so an extension string is not required there; below 3.2 the + // extension is, and the pointer still has to have resolved either way. + const Bool esAtLeast32 = caps.GLESVersion.Major > 3 || + (caps.GLESVersion.Major == 3 && caps.GLESVersion.Minor >= 2); + const Bool esAtLeast31 = caps.GLESVersion.Major > 3 || + (caps.GLESVersion.Major == 3 && caps.GLESVersion.Minor >= 1); + caps.SupportsDrawElementsBaseVertex = (esAtLeast32 || hasDrawElementsBaseVertexExtension) && + glesFuncs.glDrawElementsBaseVertex != nullptr; + caps.SupportsComputeShader = esAtLeast31 && glesFuncs.glDispatchCompute != nullptr && + glesFuncs.glMemoryBarrier != nullptr && + glesFuncs.glCreateShader != nullptr && + glesFuncs.glCreateProgram != nullptr; caps.SupportsShaderMultisampleInterpolation = caps.SupportsShaderMultisampleInterpolation || caps.GLESVersion.Major > 3 || (caps.GLESVersion.Major == 3 && caps.GLESVersion.Minor >= 2); @@ -907,6 +919,9 @@ namespace MobileGL::MG_Util::BackendLoader { caps.SupportsMultiDrawIndirect ? "yes" : "no"); MGLOG_I(" multi-draw base vertex (EXT/OES_draw_elements_base_vertex + EXT_multi_draw_arrays): %s", caps.SupportsMultiDrawElementsBaseVertex ? "yes" : "no"); + MGLOG_I(" draw elements base vertex (ES 3.2 core or EXT/OES_draw_elements_base_vertex): %s", + caps.SupportsDrawElementsBaseVertex ? "yes" : "no"); + MGLOG_I(" compute shaders (ES 3.1 core): %s", caps.SupportsComputeShader ? "yes" : "no"); MGLOG_I("OpenGL ES capabilities:"); glesFuncs.glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &caps.UniformBufferOffsetAlignment); diff --git a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h index 37c5d647..e1897962 100644 --- a/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h +++ b/MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.h @@ -1084,6 +1084,15 @@ namespace MobileGL { // requires (EXT or OES draw_elements_base_vertex) AND GL_EXT_multi_draw_arrays // AND a resolved pointer; callers must gate on it, never on the pointer. Bool SupportsMultiDrawElementsBaseVertex = false; + // glDrawElementsBaseVertex is callable: ES 3.2 core, or EXT/OES_draw_elements_base_vertex + // before that, with the pointer resolved. This is the weaker sibling of the flag above - + // it does NOT need GL_EXT_multi_draw_arrays, only the single-draw entry point - and it + // decides whether a multi-draw batch can carry per-sub-draw base vertices at all or has + // to fold them into rewritten indices. + Bool SupportsDrawElementsBaseVertex = false; + // Compute shaders are usable: ES 3.1 core (there is no pre-3.1 extension in ES), with + // the dispatch and barrier entry points resolved. + Bool SupportsComputeShader = false; // GL_RENDERER contains "ANGLE". Bool IsAngleRenderer = false; // GL_RENDERER contains both "ANGLE" and "llvmpipe".