From fb73bca3e94a7261dcc80e81e6f1f0e104bee9ce Mon Sep 17 00:00:00 2001 From: Swung0x48 Date: Sat, 17 Oct 2026 11:44:23 -0400 Subject: [PATCH] [Test] (MG_Test/Program, MG_Util/ShaderTranspiler, MobileGL): probe the prewarm by its log line - test hooks stay out of production code: ShaderCompiler::BuiltinsPrewarmed() existed only for ShaderPrewarmTest and is gone - Initialize() logs at INFO whether it prewarmed the built-in tables or skipped them for MOBILEGL_DISABLE_SHADER_PREWARM; the test reads that line from a private log file - still red with the knob's gate removed and with Destroy's latch reset removed; passes on the split and monolith builds --- MobileGL/Init.cpp | 8 +- .../MG_Test/Program/ShaderPrewarmTest.cpp | 93 +++++++++++++++---- .../ShaderTranspiler/ShaderCompiler.cpp | 5 +- .../MG_Util/ShaderTranspiler/ShaderCompiler.h | 2 - 4 files changed, 84 insertions(+), 24 deletions(-) diff --git a/MobileGL/Init.cpp b/MobileGL/Init.cpp index 474bd742..d39e5eaa 100644 --- a/MobileGL/Init.cpp +++ b/MobileGL/Init.cpp @@ -206,8 +206,12 @@ namespace MobileGL { // flag here rather than inside PrewarmBuiltins so ShaderCompiler keeps no dependency // on the async subsystem (ProgramUtilTest compiles that file without it). // MOBILEGL_DISABLE_SHADER_PREWARM skips it for processes too short-lived to repay it. - if (MG_Util::Async::AsyncShaderCompileEnabled() && !MG_Config::Features.DisableShaderPrewarm) { - MG_Util::ShaderTranspiler::ShaderCompiler::PrewarmBuiltins(); + if (MG_Util::Async::AsyncShaderCompileEnabled()) { + if (MG_Config::Features.DisableShaderPrewarm) { + MGLOG_I("glslang built-in table prewarm skipped: MOBILEGL_DISABLE_SHADER_PREWARM is set"); + } else { + MG_Util::ShaderTranspiler::ShaderCompiler::PrewarmBuiltins(); + } } MGLOG_D("glslang initialized"); g_isInitialized = true; diff --git a/MobileGL/MG_Test/Program/ShaderPrewarmTest.cpp b/MobileGL/MG_Test/Program/ShaderPrewarmTest.cpp index 6ed10daf..c82d46e4 100644 --- a/MobileGL/MG_Test/Program/ShaderPrewarmTest.cpp +++ b/MobileGL/MG_Test/Program/ShaderPrewarmTest.cpp @@ -7,14 +7,18 @@ // End of Source File Header // MOBILEGL_DISABLE_SHADER_PREWARM. Initialize() builds glslang's built-in tables ahead of the -// first compile unless the variable is truthy; the CI test lanes set it, so these cases set or -// clear it themselves. MG_ConfigLoader::Init() rereads the environment on every Initialize(), -// so the variable, not the feature block, is the input. Its own binary: every case calls -// Destroy(), and ShaderCompilePool's stop is a one-way latch (see AsyncTeardownTest). +// first compile unless the variable is truthy, and logs which way it went; these cases read that +// line back. The CI test lanes set the variable, so every case sets or clears it itself: +// MG_ConfigLoader::Init() rereads the environment on every Initialize(). The log file is +// truncated by each Initialize() and closed by Destroy(), so each case reads it after Destroy(). +// Its own binary: every case calls Destroy(), and ShaderCompilePool's stop is a one-way latch. #include #include +#include +#include +#include #include #include "Config.h" @@ -22,15 +26,22 @@ #include "Init.h" #include "MG_Impl/GLImpl/Getter/GL_Getter.h" #include "MG_Impl/GLImpl/Program/GL_Program.h" -#include "MG_Util/ShaderTranspiler/ShaderCompiler.h" +#include "MG_Util/Debug/Log.h" + +#if defined(_WIN32) +#include +#else +#include +#endif using namespace MobileGL; using namespace MobileGL::MG_Impl::GLImpl; -using MobileGL::MG_Util::ShaderTranspiler::ShaderCompiler; namespace { constexpr const char* kPrewarmKnob = "MOBILEGL_DISABLE_SHADER_PREWARM"; constexpr const char* kAsyncKnob = "MOBILEGL_ASYNC_SHADER_COMPILE"; + constexpr const char* kPrewarmedLine = "prewarmed the glslang built-in tables"; + constexpr const char* kSkippedLine = "built-in table prewarm skipped"; // Sets (or, with nullptr, clears) one environment variable for a scope. class ScopedEnv { @@ -62,6 +73,54 @@ namespace { bool m_had = false; }; + int ProcessId() { +#if defined(_WIN32) + return _getpid(); +#else + return static_cast(getpid()); +#endif + } + + // Points the library's log at a private file for the case; Log() reads what the client role + // wrote, by the library's own naming rule. + class PrivateLog { + public: + PrivateLog() + : m_base((std::filesystem::temp_directory_path() / + ("mobilegl-shaderprewarm-test-" + std::to_string(ProcessId()) + ".log")) + .string()), + m_env("MOBILEGL_LOG_FILE_PATH", m_base.c_str()) {} + ~PrivateLog() { + std::error_code ignored; + std::filesystem::remove(Path(), ignored); + std::filesystem::remove(m_base, ignored); + } + PrivateLog(const PrivateLog&) = delete; + PrivateLog& operator=(const PrivateLog&) = delete; + + std::string Path() const { + return MG_Util::Debug::RoleLogPath(m_base.c_str(), MG_Util::Debug::LogRole::Client); + } + + std::string Log() const { + std::ifstream in(Path()); + std::stringstream text; + text << in.rdbuf(); + return text.str(); + } + + private: + std::string m_base; + ScopedEnv m_env; + }; + + int Count(const std::string& text, const char* line) { + int count = 0; + for (auto at = text.find(line); at != std::string::npos; at = text.find(line, at + 1)) + ++count; + return count; + } + GLint CompileStatusOf(const GLenum type, const char* source) { const GLuint shader = CreateShader(type); ShaderSource(shader, 1, &source, nullptr); @@ -77,35 +136,37 @@ namespace { } // namespace TEST(ShaderPrewarm, InitializePrewarmsByDefaultAndAgainAfterDestroy) { + const PrivateLog log; const ScopedEnv knob(kPrewarmKnob, nullptr); const ScopedEnv async(kAsyncKnob, "1"); MobileGL::Initialize(); - EXPECT_FALSE(MG_Config::Features.DisableShaderPrewarm); - EXPECT_TRUE(ShaderCompiler::BuiltinsPrewarmed()); MobileGL::Destroy(); - // Destroy finalizes glslang, which frees the tables the latch stands for. - EXPECT_FALSE(ShaderCompiler::BuiltinsPrewarmed()); + EXPECT_EQ(Count(log.Log(), kPrewarmedLine), 1) << log.Log(); + // Destroy finalizes glslang, which frees the tables, so the next Initialize warms again. MobileGL::Initialize(); - EXPECT_TRUE(ShaderCompiler::BuiltinsPrewarmed()); MobileGL::Destroy(); + EXPECT_EQ(Count(log.Log(), kPrewarmedLine), 1) << log.Log(); } TEST(ShaderPrewarm, ATruthyKnobSkipsThePrewarmAndCompilesStillWork) { + const PrivateLog log; const ScopedEnv knob(kPrewarmKnob, "1"); const ScopedEnv async(kAsyncKnob, "1"); MobileGL::Initialize(); - EXPECT_TRUE(MG_Config::Features.DisableShaderPrewarm); - EXPECT_FALSE(ShaderCompiler::BuiltinsPrewarmed()); EXPECT_EQ(CompileStatusOf(GL_FRAGMENT_SHADER, kFragment460), GL_TRUE); - EXPECT_FALSE(ShaderCompiler::BuiltinsPrewarmed()); MobileGL::Destroy(); + const std::string text = log.Log(); + EXPECT_EQ(Count(text, kPrewarmedLine), 0) << text; + EXPECT_EQ(Count(text, kSkippedLine), 1) << text; } TEST(ShaderPrewarm, AFalsyKnobKeepsThePrewarm) { + const PrivateLog log; const ScopedEnv knob(kPrewarmKnob, "0"); const ScopedEnv async(kAsyncKnob, "1"); MobileGL::Initialize(); - EXPECT_FALSE(MG_Config::Features.DisableShaderPrewarm); - EXPECT_TRUE(ShaderCompiler::BuiltinsPrewarmed()); MobileGL::Destroy(); + const std::string text = log.Log(); + EXPECT_EQ(Count(text, kPrewarmedLine), 1) << text; + EXPECT_EQ(Count(text, kSkippedLine), 0) << text; } diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp index 788c1731..18460c76 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.cpp @@ -456,10 +456,6 @@ namespace MobileGL { g_builtinsPrewarmed = false; } - Bool ShaderCompiler::BuiltinsPrewarmed() { - return g_builtinsPrewarmed; - } - void ShaderCompiler::PrewarmBuiltins() { if (g_builtinsPrewarmed) return; g_builtinsPrewarmed = true; @@ -491,6 +487,7 @@ namespace MobileGL { // The parses above left this thread's glslang allocator pointing at the last // TShader's pool, and that TShader is about to be destroyed with it. glslang::SetThreadPoolAllocator(nullptr); + MGLOG_I("ShaderCompiler: prewarmed the glslang built-in tables for #version 460 and 330 core"); } namespace { diff --git a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h index 627f3145..b730b959 100644 --- a/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h +++ b/MobileGL/MG_Util/ShaderTranspiler/ShaderCompiler.h @@ -533,8 +533,6 @@ namespace MobileGL { // it, the second eglInitialize of a process comes back up unwarmed and with // no way left to warm it. static void ResetPrewarmLatch(); - // Whether PrewarmBuiltins has run since the last ResetPrewarmLatch. - static Bool BuiltinsPrewarmed(); // Validation is an explicit immutable option of each compiler operation. The // program-link task snapshots MOBILEGL_ENABLE_SPIRV_VALIDATION before it can run