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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user