From 7d31a6fcd7a72f84ea5b1a8616d1796adb2023f7 Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Fri, 10 Jul 2026 01:08:06 +0000 Subject: [PATCH] [Refactor] (MG_Config): centralize env-var and driver-feature reads MG_Config::FeaturesTable snapshots every MOBILEGL_* toggle once in ConfigLoader::Init with a single truthy rule (non-empty, not '0', not 'false' case-insensitively), replacing 13 scattered std::getenv sites that used four different parsing conventions. Renderer-derived bits (IsAngleRenderer/IsAngleLlvmpipeRenderer/AvoidSamplerMipmapMinFilter) move into GLESCapabilities, set once in FillInGLESCapabilities, so hot paths (glMemoryBarrier ANGLE flush, sampler min-filter sync) stop doing per-call string scans. MOBILEGL_PRESENT_DUMP_CALL/_CURRENT_CALL stay live getenv (the retrace harness mutates them at runtime) and MOBILEGL_LOG_FILE_PATH stays in Log.cpp (log init precedes config init); both are documented in Config.h. Known semantic unification: MOBILEGL_DISABLE_SUBGROUP previously required exactly 'true' and MOBILEGL_PRESENT_STATS exactly '1'; both now follow the shared rule (CI's 0/1 values parse identically). Also bumps CoreVersion to 26.07. Co-Authored-By: Claude Fable 5 --- MobileGL/Config.h | 45 ++++++++++++++++++- MobileGL/ConfigLoader.cpp | 42 +++++++++++++++++ MobileGL/Init.cpp | 3 +- MobileGL/MG_Backend/DirectGLES/Managers.cpp | 9 ++-- MobileGL/MG_Backend/DirectGLES/Utils.cpp | 2 +- .../MG_Util/BackendLoaders/Vulkan/Loader.cpp | 9 +--- MobileGL/MG_Util/Debug/Log.cpp | 2 + 7 files changed, 97 insertions(+), 15 deletions(-) diff --git a/MobileGL/Config.h b/MobileGL/Config.h index bbb83faf..55d4cf71 100644 --- a/MobileGL/Config.h +++ b/MobileGL/Config.h @@ -14,9 +14,52 @@ namespace MobileGL::MG_Config { inline const String ProjectName = "MobileGL"; inline const String CoreName = "MobileGL Core"; inline const String CoreVendor = "MobileGL-Dev (BZLZHH, Swung0x48, Tungsten)"; - inline const Version CoreVersion = {26, 6, 0, "-dev", VersionType::Development}; + inline const Version CoreVersion = {26, 7, 0, "-dev", VersionType::Development}; inline const VersionStringFormatAttrib DefaultVersionStringFormatAttrib = {2, 2, 0, true, true}; inline const Uint64 CacheVersion = 0; extern BackendType ActiveBackendType; + + // 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" + // (case-insensitive). + // + // Env variables intentionally NOT mirrored here (kept as live std::getenv at their + // call sites): + // - MOBILEGL_PRESENT_DUMP_CALL / MOBILEGL_PRESENT_CURRENT_CALL: the retrace harness + // mutates them at runtime via setenv, so a one-shot snapshot would go stale. + // - DISPLAY: X11 session variable, not MobileGL configuration. + // - MOBILEGL_LOG_FILE_PATH: log-file init runs before MG_ConfigLoader::Init + // (see MG_Util/Debug/Log.cpp). + struct FeaturesTable { + // MOBILEGL_DISABLE_TIMERQUERY: do not advertise or use GPU timer queries. + Bool DisableTimerQuery = false; + // MOBILEGL_RETRACE_USE_ANGLE: load ANGLE EGL/GLES libraries for retrace runs. + Bool RetraceUseAngle = false; + // MOBILEGL_RETRACE_ANGLE_DIR: directory searched first for the ANGLE libraries. + String RetraceAngleDir; + // MOBILEGL_DISABLE_SUBGROUP: force-disable Vulkan shader subgroup support. + Bool DisableSubgroup = false; + // MOBILEGL_VULKAN_R11G11B10F_FALLBACK: use fallback format for R11G11B10F on Vulkan. + Bool VulkanR11G11B10FFallback = false; + // MOBILEGL_GLES_PRESENT_STATS: log present pixel statistics (DirectGLES backend). + Bool GlesPresentStats = false; + // MOBILEGL_ANGLE_LLVMPIPE_AVOID_SAMPLER_MIPMAP_MIN_FILTER: avoid mipmap min + // filters in samplers on ANGLE/llvmpipe renderers. + Bool AvoidAngleLlvmpipeSamplerMipmapMinFilter = false; + // MOBILEGL_DESCRIPTOR_STATS: log descriptor binding statistics (DirectVulkan). + Bool DescriptorStats = false; + // MOBILEGL_TEXTURE_UPLOAD_STATS: log texture upload statistics (DirectVulkan). + Bool TextureUploadStats = false; + // MOBILEGL_VERTEX_INPUT_STATS: log vertex input statistics (DirectVulkan). + Bool VertexInputStats = false; + // MOBILEGL_PRESENT_DUMP_PATH: directory for present frame dumps (DirectVulkan). + String PresentDumpPath; + // MOBILEGL_PRESENT_STATS: log present pixel statistics (DirectVulkan backend). + Bool PresentStats = false; + // MOBILEGL_TRACE_SKIP_AUTODESTROY: skip teardown in the ELF destructor (Init.cpp). + Bool TraceSkipAutodestroy = false; + }; + extern FeaturesTable Features; } // namespace MobileGL::MG_Config diff --git a/MobileGL/ConfigLoader.cpp b/MobileGL/ConfigLoader.cpp index bbfc542c..3b29fc79 100644 --- a/MobileGL/ConfigLoader.cpp +++ b/MobileGL/ConfigLoader.cpp @@ -12,6 +12,12 @@ extern char** environ; #endif +namespace MobileGL::MG_Config { + // Zero/default-initialized at static-init time (all fields have constexpr-friendly + // defaults), so it is safe to read even if MG_ConfigLoader::Init has not run yet. + FeaturesTable Features; +} // namespace MobileGL::MG_Config + namespace MobileGL::MG_ConfigLoader { static UniquePtr> acceptedEnvVariablesMap; @@ -60,6 +66,41 @@ namespace MobileGL::MG_ConfigLoader { } } + // Unified truthy rule for boolean feature env variables: set, non-empty, not "0", + // and not "false" (case-insensitive). + static Bool IsTruthyValue(const String& value) { + if (value.empty() || value == "0") { + return false; + } + String lowered = value; + std::transform(lowered.begin(), lowered.end(), lowered.begin(), + [](unsigned char c) { return static_cast(std::tolower(c)); }); + return lowered != "false"; + } + + inline Bool QueryEnvFlag(const String& key) { + auto it = acceptedEnvVariablesMap->find(key); + return it != acceptedEnvVariablesMap->end() && IsTruthyValue(it->second); + } + + inline void InitFeatures() { + auto& features = MG_Config::Features; + features.DisableTimerQuery = QueryEnvFlag("MOBILEGL_DISABLE_TIMERQUERY"); + features.RetraceUseAngle = QueryEnvFlag("MOBILEGL_RETRACE_USE_ANGLE"); + QueryEnvVariable("MOBILEGL_RETRACE_ANGLE_DIR", features.RetraceAngleDir, ""); + features.DisableSubgroup = QueryEnvFlag("MOBILEGL_DISABLE_SUBGROUP"); + features.VulkanR11G11B10FFallback = QueryEnvFlag("MOBILEGL_VULKAN_R11G11B10F_FALLBACK"); + features.GlesPresentStats = QueryEnvFlag("MOBILEGL_GLES_PRESENT_STATS"); + features.AvoidAngleLlvmpipeSamplerMipmapMinFilter = + QueryEnvFlag("MOBILEGL_ANGLE_LLVMPIPE_AVOID_SAMPLER_MIPMAP_MIN_FILTER"); + features.DescriptorStats = QueryEnvFlag("MOBILEGL_DESCRIPTOR_STATS"); + features.TextureUploadStats = QueryEnvFlag("MOBILEGL_TEXTURE_UPLOAD_STATS"); + features.VertexInputStats = QueryEnvFlag("MOBILEGL_VERTEX_INPUT_STATS"); + QueryEnvVariable("MOBILEGL_PRESENT_DUMP_PATH", features.PresentDumpPath, ""); + features.PresentStats = QueryEnvFlag("MOBILEGL_PRESENT_STATS"); + features.TraceSkipAutodestroy = QueryEnvFlag("MOBILEGL_TRACE_SKIP_AUTODESTROY"); + } + inline void InitBackendType() { String backendTypeStr; QueryEnvVariable("MOBILEGL_BACKEND_TYPE", backendTypeStr, "DirectGLES"); @@ -81,6 +122,7 @@ namespace MobileGL::MG_ConfigLoader { InitializeAcceptedEnvVariables(); InitBackendType(); + InitFeatures(); // Destroy the map since we won't need it anymore acceptedEnvVariablesMap.reset(); diff --git a/MobileGL/Init.cpp b/MobileGL/Init.cpp index d61b5a82..8a2d560d 100644 --- a/MobileGL/Init.cpp +++ b/MobileGL/Init.cpp @@ -13,7 +13,6 @@ #include #include #include -#include namespace MobileGL { namespace { @@ -75,7 +74,7 @@ namespace MobileGL { } __attribute__((destructor)) static void AutoDestroy() { - if (std::getenv("MOBILEGL_TRACE_SKIP_AUTODESTROY") != nullptr) { + if (MG_Config::Features.TraceSkipAutodestroy) { return; } #if defined(__APPLE__) diff --git a/MobileGL/MG_Backend/DirectGLES/Managers.cpp b/MobileGL/MG_Backend/DirectGLES/Managers.cpp index 50e0fea1..09538372 100644 --- a/MobileGL/MG_Backend/DirectGLES/Managers.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Managers.cpp @@ -41,13 +41,14 @@ namespace MobileGL::MG_Backend::DirectGLES { constexpr const char* ZERO_BASED_INSTANCE_ID_NAME = "mg_ZeroBasedInstanceID"; static Bool IsAngleLlvmpipeRenderer() { - return g_GLESCapabilities.GLESRendererString.find("ANGLE") != String::npos && - g_GLESCapabilities.GLESRendererString.find("llvmpipe") != String::npos; + return g_GLESCapabilities.IsAngleLlvmpipeRenderer; } static Bool ShouldAvoidSamplerMipmapMinFilterOnAngleLlvmpipe() { - const char* value = std::getenv("MOBILEGL_ANGLE_LLVMPIPE_AVOID_SAMPLER_MIPMAP_MIN_FILTER"); - return value != nullptr && std::strcmp(value, "1") == 0 && IsAngleLlvmpipeRenderer(); + // IsAngleLlvmpipeRenderer combined with the + // MOBILEGL_ANGLE_LLVMPIPE_AVOID_SAMPLER_MIPMAP_MIN_FILTER feature toggle, + // both resolved in FillInGLESCapabilities. + return g_GLESCapabilities.AvoidSamplerMipmapMinFilter; } static GLenum ResolveBackendMinFilter(const SamplerParameters& samplerParams, diff --git a/MobileGL/MG_Backend/DirectGLES/Utils.cpp b/MobileGL/MG_Backend/DirectGLES/Utils.cpp index cfb13778..4b28e477 100644 --- a/MobileGL/MG_Backend/DirectGLES/Utils.cpp +++ b/MobileGL/MG_Backend/DirectGLES/Utils.cpp @@ -23,7 +23,7 @@ namespace MobileGL::MG_Backend::DirectGLES { namespace { Flags GetForcedPixelFormatNormalizeOptions() { Flags options; - if (g_GLESCapabilities.GLESRendererString.find("ANGLE") != String::npos) { + if (g_GLESCapabilities.IsAngleRenderer) { options |= PixelFormatNormalizeOptionBit::NoRgb16; options |= PixelFormatNormalizeOptionBit::NoSnorm16; options |= PixelFormatNormalizeOptionBit::NoSnorm8; diff --git a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp index dd333671..1323087c 100644 --- a/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp +++ b/MobileGL/MG_Util/BackendLoaders/Vulkan/Loader.cpp @@ -8,8 +8,7 @@ #include "Loader.h" -#include -#include +#include namespace MobileGL::MG_Util::BackendLoader { namespace { @@ -61,11 +60,7 @@ namespace MobileGL::MG_Util::BackendLoader { } Bool IsShaderSubgroupForcedDisabled() { - const char* value = std::getenv("MOBILEGL_DISABLE_SUBGROUP"); - if (!value) { - return false; - } - return std::strcmp(value, "true") == 0 || std::strcmp(value, "TRUE") == 0; + return MG_Config::Features.DisableSubgroup; } } // namespace diff --git a/MobileGL/MG_Util/Debug/Log.cpp b/MobileGL/MG_Util/Debug/Log.cpp index 815144ce..c55f0fb5 100644 --- a/MobileGL/MG_Util/Debug/Log.cpp +++ b/MobileGL/MG_Util/Debug/Log.cpp @@ -63,6 +63,8 @@ namespace MobileGL { void InitFile() { #if MOBILEGL_LOG_ENABLE_FILE if (!s_logFile) { + // MOBILEGL_LOG_FILE_PATH must stay a raw getenv (not MG_Config::Features): + // InitFile() runs before MG_ConfigLoader::Init() in MobileGL::Initialize(). const char* logPath = std::getenv("MOBILEGL_LOG_FILE_PATH"); if (!logPath || !*logPath) { logPath = MOBILEGL_LOG_FILE_PATH;