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 <noreply@anthropic.com>
This commit is contained in:
2026-09-19 17:34:23 +03:00
co-authored by Claude
parent edfa360c0a
commit 1a74432c48
+35 -1
View File
@@ -4,6 +4,7 @@
#include <cstring>
#include <vector>
#include <set>
#include <tuple>
#include <unordered_map>
#include <atomic>
#include <thread>
@@ -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<int> 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<std::tuple<GLint, GLint, GLsizei, GLsizei>> seen;
std::lock_guard<std::mutex> 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;
}