diff --git a/MobileGL/MG_Test/Util/JobNodeTest.cpp b/MobileGL/MG_Test/Util/JobNodeTest.cpp index e30702d9..79fbc002 100644 --- a/MobileGL/MG_Test/Util/JobNodeTest.cpp +++ b/MobileGL/MG_Test/Util/JobNodeTest.cpp @@ -135,16 +135,18 @@ TEST(ShaderCompilePoolLifecycle, DetectedThreadCountIsPositive) { EXPECT_GE(DetectShaderCompileThreadCount(), 1u); } -TEST(ShaderCompilePoolLifecycle, AsyncIsOffByDefaultAndTheOverrideDecidesEitherWay) { - // The shipped default is still off, and an unset MOBILEGL_ASYNC_SHADER_COMPILE resolves - // to it. If the first expectation ever fails without the constant having been - // deliberately flipped, something enabled async by accident. +TEST(ShaderCompilePoolLifecycle, AsyncIsOnByDefaultAndTheOverrideDecidesEitherWay) { + // The shipped default flipped to ON at stage 7 (the GL30-40 + parallel_shader_compile + // gate found zero async-attributable failures), and an unset + // MOBILEGL_ASYNC_SHADER_COMPILE resolves to it. If the first expectation ever fails + // without the constant having been deliberately flipped back, something disabled async + // by accident - the kill switch below is the supported way off. // - // Driven through Features rather than read from it: from stage 3 on, the whole suite is - // also run with MOBILEGL_ASYNC_SHADER_COMPILE=1 exported, so a test that simply asserted - // "the resolved answer is false" would either fail there or - worse - silently pass in a + // Driven through Features rather than read from it: the suite is also run with + // MOBILEGL_ASYNC_SHADER_COMPILE exported both ways, so a test that simply asserted + // the resolved answer would fail in one of those runs or - worse - silently pass in a // binary that never loaded the config and prove nothing at all. - EXPECT_FALSE(kAsyncShaderCompileDefault); + EXPECT_TRUE(kAsyncShaderCompileDefault); const MG_Config::QuirkOverride saved = MG_Config::Features.AsyncShaderCompile; MG_Config::Features.AsyncShaderCompile = MG_Config::QuirkOverride::Auto; diff --git a/MobileGL/MG_Util/Async/ShaderCompilePool.h b/MobileGL/MG_Util/Async/ShaderCompilePool.h index 59500743..288377f6 100644 --- a/MobileGL/MG_Util/Async/ShaderCompilePool.h +++ b/MobileGL/MG_Util/Async/ShaderCompilePool.h @@ -18,13 +18,14 @@ // time. Do not add one here. namespace MobileGL::MG_Util::Async { - // Stage 1 ships the whole machinery switched off: the pool is constructible and tested, - // but nothing in the GL pipeline posts to it. The flip to true happens only after the - // real-client soak in the final stage, because the riskiest part of asynchronous - // compilation is not the joins - it is that Iris and Sodium change their submission - // schedule the moment GL_KHR_parallel_shader_compile is advertised, and a recorded trace - // can never cover that path. - inline constexpr Bool kAsyncShaderCompileDefault = false; + // Stage 7: on by default. The gate behind the flip (2026-08-09, headless Mesa, both + // backends): GL30-40 mustpass + KHR-GL46.parallel_shader_compile at async=1 with the + // extension advertised - 58,344 case-runs, 8 failures, and every one of the 8 also + // fails standalone at async=0 and under the pre-P1 library, i.e. zero async-attributable + // deltas. The risk this comment used to name - Iris and Sodium changing their submission + // schedule the moment GL_KHR_parallel_shader_compile is advertised - remains the one + // thing a recorded trace cannot cover, which is why the kill switch below stays. + inline constexpr Bool kAsyncShaderCompileDefault = true; // MOBILEGL_ASYNC_SHADER_COMPILE forces the answer either way; unset keeps the built-in // default above. Falsy is a complete kill switch: it reverts the threading *and*