diff --git a/MobileGL/ConfigLoader.cpp b/MobileGL/ConfigLoader.cpp index a5fea0ed..1018c448 100644 --- a/MobileGL/ConfigLoader.cpp +++ b/MobileGL/ConfigLoader.cpp @@ -458,6 +458,15 @@ namespace MobileGL::MG_ConfigLoader { inline void InitTransport() { String value; QueryEnvVariable("MOBILEGL_TRANSPORT", value, "monolith"); +#if defined(__ANDROID__) + // As debug.mobilegl.backend above: a developer picks the transport of an app-hosted + // library (a launcher's game process) without rebuilding it or its launcher. + char property[PROP_VALUE_MAX] = {}; + if (__system_property_get("debug.mobilegl.transport", property) > 0) { + MGLOG_I("Config: debug.mobilegl.transport=%s overrides MOBILEGL_TRANSPORT=%s", property, value.c_str()); + value = property; + } +#endif String lowered = value; std::transform(lowered.begin(), lowered.end(), lowered.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); diff --git a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp index 63eb4e6a..45375ee4 100644 --- a/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp +++ b/MobileGL/MG_Impl/EGLImpl/EGLImpl.cpp @@ -46,12 +46,64 @@ #include #endif #include +#include #include #include #include +#if defined(__ANDROID__) +#include +#endif +#if defined(__unix__) || defined(__linux__) || defined(__ANDROID__) +#include +#endif namespace MobileGL::MG_Impl::EGLImpl { namespace { + // MOBILEGL_FPS_LOG=1 (on Android also the property debug.mobilegl.fps_log=1): one INFO line + // about every two seconds with the frames this process presented, their mean frame time and + // the presenting thread's CPU per frame. A frame rate for an app-hosted game that shows none + // of its own (a launcher's Minecraft with the debug overlay closed, which is itself a cost). + // Off, a swap pays one load and branch. + Bool FrameRateLogEnabled() { + static const Bool enabled = [] { + const char* value = std::getenv("MOBILEGL_FPS_LOG"); +#if defined(__ANDROID__) + char property[PROP_VALUE_MAX] = {}; + if (value == nullptr && __system_property_get("debug.mobilegl.fps_log", property) > 0) value = property; +#endif + return value != nullptr && value[0] == '1'; + }(); + return enabled; + } + + void NoteSwapForFrameRateLog() { + if (!FrameRateLogEnabled()) return; + using Clock = std::chrono::steady_clock; + static Clock::time_point windowStart = Clock::now(); + static Uint32 frames = 0; + static Double cpuStartMs = -1.0; + const auto threadCpuMs = []() -> Double { +#if defined(__unix__) || defined(__linux__) || defined(__ANDROID__) + struct timespec now {}; + if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now) == 0) + return static_cast(now.tv_sec) * 1000.0 + static_cast(now.tv_nsec) / 1e6; +#endif + return -1.0; + }; + if (cpuStartMs < 0.0) cpuStartMs = threadCpuMs(); + ++frames; + const auto now = Clock::now(); + const Double elapsedMs = std::chrono::duration(now - windowStart).count(); + if (elapsedMs < 2000.0) return; + const Double cpuNow = threadCpuMs(); + MGLOG_I("MobileGL fps: %.1f over %u frames (%.2f ms/frame, presenting thread CPU %.2f ms/frame)", + 1000.0 * frames / elapsedMs, frames, elapsedMs / frames, + cpuNow >= 0.0 && cpuStartMs >= 0.0 ? (cpuNow - cpuStartMs) / frames : -1.0); + windowStart = now; + frames = 0; + cpuStartMs = cpuNow; + } + // SetDispatcherCurrentApiHook. std::atomic g_dispatcherCurrentApi{nullptr}; using EGLStateContext = MG_State::EGLState::EGLContext; @@ -766,6 +818,7 @@ namespace MobileGL::MG_Impl::EGLImpl { // A resize made between frames takes effect for the next one. ApplyWaylandResize(state, dpy, draw); #endif + NoteSwapForFrameRateLog(); return EGL_TRUE; }