From 1a74432c48bc8b287f3b355322989a5f3211f67f Mon Sep 17 00:00:00 2001 From: megboyzz Date: Sat, 19 Sep 2026 17:34:23 +0300 Subject: [PATCH] Add an opt-in glViewport probe; settles task #52 (0.8 render scale, not a bug) A Xiaomi 14 capture showed viewport 2136x960 against a 2670x1200 surface and raised the question of whether the engine was feeding the game a wrong size - the shape of task #39, where the UI projection used 2000x1000 instead of the real 2400x1080. Logging each DISTINCT viewport with its bound framebuffer and the real EGL surface size answers it. Pixel 6a: VIEWPORT [0,0 2400x1080] fb=0 | EGL surface 2400x1080 <- screen, exact VIEWPORT [0,0 1920x864] fb=3 | EGL surface 2400x1080 <- an FBO VIEWPORT [0,0 512x512] fb=1 | EGL surface 2400x1080 <- another FBO The default framebuffer matches the surface exactly; the smaller viewport is an offscreen target. And the ratio is decisive: Pixel 6a 1920/2400 = 0.80 864/1080 = 0.80 Xiaomi 14 2136/2670 = 0.80 960/1200 = 0.80 Exactly 0.8 on both devices and both axes - the game's own render scale, drawing the 3D scene into a reduced FBO and upscaling while the HUD stays at full resolution. Nothing is broken; the engine reproduces it faithfully. The probe stays in the tree behind kLogViewportChanges (default off, per the standing rule that diagnostics are opt-in), because "which framebuffer was bound" is exactly the context whose absence made the original observation ambiguous. Co-Authored-By: Claude --- mpcore/src/main/cpp/emu/gles_shim.cpp | 36 ++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/mpcore/src/main/cpp/emu/gles_shim.cpp b/mpcore/src/main/cpp/emu/gles_shim.cpp index 13de449..bc884cd 100644 --- a/mpcore/src/main/cpp/emu/gles_shim.cpp +++ b/mpcore/src/main/cpp/emu/gles_shim.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,10 @@ uint32_t GuestCallerLR(GuestEngine& eng) { // eglSwapBuffers - see Shim_glClear's own comment for why presenting a // back buffer that received none of them is what made the screen cycle // between stale, black and real frames. +// Task #52 probe switch - see Shim_glViewport. Opt-in per this +// project's standing rule that diagnostics default to off. +constexpr bool kLogViewportChanges = false; + std::atomic g_drawsSinceSwap{0}; // Shadowed GL state (2026-09-19). These exist so the per-draw code paths @@ -1722,7 +1727,36 @@ uint32_t Shim_glVertexAttribPointer(GuestEngine& eng, uint32_t r0, uint32_t r1, } uint32_t Shim_glViewport(GuestEngine& eng, uint32_t r0, uint32_t r1, uint32_t r2, uint32_t r3, uint32_t sp) { - glViewport((GLint)eng.ReadIncomingArg(0, r0, r1, r2, r3, sp), (GLint)eng.ReadIncomingArg(1, r0, r1, r2, r3, sp), (GLsizei)eng.ReadIncomingArg(2, r0, r1, r2, r3, sp), (GLsizei)eng.ReadIncomingArg(3, r0, r1, r2, r3, sp)); + GLint vx = (GLint)eng.ReadIncomingArg(0, r0, r1, r2, r3, sp); + GLint vy = (GLint)eng.ReadIncomingArg(1, r0, r1, r2, r3, sp); + GLsizei vw = (GLsizei)eng.ReadIncomingArg(2, r0, r1, r2, r3, sp); + GLsizei vh = (GLsizei)eng.ReadIncomingArg(3, r0, r1, r2, r3, sp); + // Task #52 probe (2026-09-19, temporary). A Xiaomi 14 capture showed the + // game rendering into viewport=[0,0,2136,960] while the surface was + // 2670x1200 - i.e. a reduced internal resolution being upscaled. Whether + // that is the game's own choice or a wrong size we fed it is unknown, and + // the project already has form here: task #39 was exactly this shape (the + // UI projection used 2000x1000 instead of the real 2400x1080 because a + // JNI float argument was arriving as zero). Logging each DISTINCT + // viewport against the real EGL surface size, so the comparison is a fact + // rather than an inference. + if (kLogViewportChanges) { + static std::mutex mu; + static std::set> seen; + std::lock_guard lock(mu); + if (seen.insert({vx, vy, vw, vh}).second && seen.size() <= 24) { + EGLint sw = -1, sh = -1; + EGLDisplay dpy = eglGetCurrentDisplay(); + EGLSurface surf = eglGetCurrentSurface(EGL_DRAW); + eglQuerySurface(dpy, surf, EGL_WIDTH, &sw); + eglQuerySurface(dpy, surf, EGL_HEIGHT, &sh); + GLint fb = g_shadowBoundFramebuffer.load(std::memory_order_relaxed); + Log("gles_shim: VIEWPORT [%d,%d %dx%d] fb=%d | EGL surface %dx%d%s", + vx, vy, vw, vh, fb, sw, sh, + (fb == 0 && (vw != sw || vh != sh)) ? " <<< SMALLER THAN THE SCREEN >>>" : ""); + } + } + glViewport(vx, vy, vw, vh); return 0; }