mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-07 01:53:24 +09:00
[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
This commit is contained in:
+6
-2
@@ -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;
|
||||
|
||||
@@ -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 <gtest/gtest.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#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 <process.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#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<int>(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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user