diff --git a/MobileGL/Config.h b/MobileGL/Config.h index 5784571f..ff5c7ce1 100644 --- a/MobileGL/Config.h +++ b/MobileGL/Config.h @@ -29,6 +29,16 @@ namespace MobileGL::MG_Config { ForceOff, }; + // Preferred DirectVulkan dispatch tier for the glMultiDraw* families. A preference, + // never a demand: the renderer clamps it to what the device supports at device + // creation, falling down the chain ext -> indirect -> unroll with one log line. + enum class MultiDrawMode : Uint8 { + Auto = 0, // unset: best supported tier + Ext, // VK_EXT_multi_draw: one vkCmdDrawMultiEXT / vkCmdDrawMultiIndexedEXT + Indirect, // multiDrawIndirect feature: one vkCmdDraw*Indirect over a transient command array + Unroll, // one vkCmdDraw* per sub-draw + }; + // 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" @@ -91,6 +101,10 @@ namespace MobileGL::MG_Config { // feature off. It is enabled by default to match GL's defined out-of-range fetch // behavior; this escape hatch exists to measure or dodge its GPU cost on a device. Bool DisableRobustBufferAccess = false; + // MOBILEGL_MAGMA_MULTIDRAW_MODE: preferred DirectVulkan multi-draw dispatch tier + // ("ext" | "indirect" | "unroll", see MultiDrawMode). Clamped to device support; + // unset picks the best supported tier. + MultiDrawMode MagmaMultiDrawMode = MultiDrawMode::Auto; }; extern FeaturesTable Features; } // namespace MobileGL::MG_Config diff --git a/MobileGL/ConfigLoader.cpp b/MobileGL/ConfigLoader.cpp index 5b98d637..6cce0eb7 100644 --- a/MobileGL/ConfigLoader.cpp +++ b/MobileGL/ConfigLoader.cpp @@ -97,6 +97,25 @@ namespace MobileGL::MG_ConfigLoader { : MG_Config::QuirkOverride::ForceOff; } + // Multi-draw mode is a named-value preference: unset keeps Auto (best supported tier), + // a recognized name selects that tier as the ceiling, anything else warns and keeps Auto. + inline MG_Config::MultiDrawMode QueryEnvMultiDrawMode(const String& key) { + auto it = acceptedEnvVariablesMap->find(key); + if (it == acceptedEnvVariablesMap->end()) { + return MG_Config::MultiDrawMode::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::MultiDrawMode::Ext; + if (lowered == "indirect") return MG_Config::MultiDrawMode::Indirect; + if (lowered == "unroll") return MG_Config::MultiDrawMode::Unroll; + if (lowered.empty() || lowered == "auto") return MG_Config::MultiDrawMode::Auto; + MGLOG_W("Config: Ignoring invalid env variable %s='%s'; expected ext|indirect|unroll|auto, using auto", + key.c_str(), it->second.c_str()); + return MG_Config::MultiDrawMode::Auto; + } + inline Uint32 QueryEnvUint32(const String& key, Uint32 defaultValue, Uint32 minValue, Uint32 maxValue) { auto it = acceptedEnvVariablesMap->find(key); if (it == acceptedEnvVariablesMap->end()) { @@ -138,6 +157,7 @@ namespace MobileGL::MG_ConfigLoader { features.MagmaDisableBlendedDepthWriteQuirk = QueryEnvQuirkOverride("MOBILEGL_MAGMA_DISABLE_BLENDED_DEPTH_WRITE"); features.DisableRobustBufferAccess = QueryEnvFlag("MOBILEGL_DISABLE_ROBUST_BUFFER_ACCESS"); + features.MagmaMultiDrawMode = QueryEnvMultiDrawMode("MOBILEGL_MAGMA_MULTIDRAW_MODE"); } inline void InitBackendType() { diff --git a/MobileGL/MG_Util/SelfTest/DriverPost.cpp b/MobileGL/MG_Util/SelfTest/DriverPost.cpp index a99ad1df..2b7bcc19 100644 --- a/MobileGL/MG_Util/SelfTest/DriverPost.cpp +++ b/MobileGL/MG_Util/SelfTest/DriverPost.cpp @@ -1426,8 +1426,9 @@ namespace MobileGL::MG_Util::SelfTest { if (features.multiDrawIndirect == VK_TRUE) { builder.Pass("multiDrawIndirect", "indirect multi-draw batches run as single native commands"); } else { - builder.Warn("multiDrawIndirect", - "unsupported; indirect multi-draw batches fall back to one draw per command"); + builder.Info("multiDrawIndirect", + "unsupported; multi-draw batches fall back to one draw per command (tier " + "\"indirect\" of the multi-draw dispatch is unavailable)"); } if (features.drawIndirectFirstInstance == VK_TRUE) { builder.Pass("drawIndirectFirstInstance", "indirect commands may carry a non-zero firstInstance"); @@ -1435,6 +1436,42 @@ namespace MobileGL::MG_Util::SelfTest { builder.Warn("drawIndirectFirstInstance", "unsupported; indirect commands with a non-zero baseInstance cannot run natively"); } + // Multi-draw dispatch tiers (ext -> indirect -> unroll). INFO on the missing + // pieces: every tier has a fallback, nothing is lost, only batched into more + // commands. The renderer resolves the same chain at device creation, clamped + // by MOBILEGL_MAGMA_MULTIDRAW_MODE. + { + Bool multiDrawExtUsable = false; + if (HasVkExtension(deviceExtensions, VK_EXT_MULTI_DRAW_EXTENSION_NAME) && + vkGetPhysicalDeviceFeatures2Fn != nullptr) { + VkPhysicalDeviceMultiDrawFeaturesEXT multiDrawFeatures{}; + multiDrawFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTI_DRAW_FEATURES_EXT; + VkPhysicalDeviceFeatures2 features2{}; + features2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; + features2.pNext = &multiDrawFeatures; + vkGetPhysicalDeviceFeatures2Fn(physicalDevice, &features2); + multiDrawExtUsable = multiDrawFeatures.multiDraw == VK_TRUE; + } + if (multiDrawExtUsable) { + builder.Pass("VK_EXT_multi_draw", + "supported; a glMultiDraw* batch runs as one vkCmdDrawMulti(Indexed)EXT"); + } else { + builder.Info("VK_EXT_multi_draw", + "unsupported; glMultiDraw* batches use the indirect or unrolled tier"); + } + const char* resolvedTier = multiDrawExtUsable ? "ext" + : features.multiDrawIndirect == VK_TRUE ? "indirect" + : "unroll"; + String tierDetail = format("default tier \"{}\" (chain: ext -> indirect -> unroll)", resolvedTier); + const MG_Config::MultiDrawMode multiDrawMode = MG_Config::Features.MagmaMultiDrawMode; + if (multiDrawMode != MG_Config::MultiDrawMode::Auto) { + tierDetail += format("; MOBILEGL_MAGMA_MULTIDRAW_MODE={} caps it (clamped to device support)", + multiDrawMode == MG_Config::MultiDrawMode::Ext ? "ext" + : multiDrawMode == MG_Config::MultiDrawMode::Indirect ? "indirect" + : "unroll"); + } + builder.Info("Multi-draw dispatch tier", tierDetail); + } if (features.vertexPipelineStoresAndAtomics == VK_TRUE) { builder.Pass("vertexPipelineStoresAndAtomics", "supported by driver (not currently enabled by the DirectVulkan backend)");