The phone that rendered fine and photographed black
Last week a single bug produced 129 separate crash reports. The bug was fixed then; the reporting got fixed this week.
In plain words
Last week a single bug produced 129 separate crash reports. The bug was fixed then; the reporting got fixed this week. Every report carried a unique internal id buried in its text, so our crash tracker read each one as a brand-new problem. Those ids are now stripped before a report ever leaves the game, so a hundred copies of one fault arrive as one fault.
BallOrcs also reached real phones in the cloud for the first time, and the free testing services turned out to mislead us in a very specific way: the screenshot they hand back is solid black even when the game is running perfectly. The video from the same run shows the menu. We now judge anything about looks from the video, never the still.
Under the hood
- 129 reports become one (
9c36fc5, #132 — the grouping half). Godot's logger puts node instance ids straight into event text (PlayerBall:<PlayerBall#265818212578>), GlitchTip groups on that text, and one soak therefore produced 129 issues for one defect. Abefore_sendhook now normalises the volatile parts —<PlayerBall#265818212578>→<PlayerBall>,@Area3D@1234→@Area3D@N— on the event message and on exception value 0, where the SDK's error-logger integration puts the error text. - Getting a callback into the SDK at all needed a new main loop. With
auto_initon there is no moment at which you can hand the SDK abefore_send, so the documented route was taken instead:sentry/options/auto_init = false,main_loop_type = "ProjectMainLoop", and a newscripts/project_main_loop.gd— aSceneTreesubclass whose_initialize()callsSentrySDK.init(...)._initialize()runs before autoloads and the main scene, so reporting still covers the whole game, and the DSN keeps coming from the[sentry]project settings that CI fills at export time. One deliberate consequence:godot --scriptruns supply their ownSceneTreeand so no longer start Sentry at all, which is exactly where the purged parse-error noise came from. Not verified in a Godot build from the agent sandbox — CI export and the owner's smoke cover it. - A Test Lab screenshot is not evidence about rendering (
ae0c6db, #130). Two Robo runs both reported Passed and both produced a solid black app screenshot; the video from the same runs shows the Godot splash at ~19 s and the main menu at ~20 s. Robo screenshots come from UiAutomator, which composites the Android View hierarchy — and Godot draws into aSurfaceView, which that path never captures. The video comes fromscreenrecord, which captures the composited display. House rule now: rendering from the video, engine health from the logcat, never from a screenshot. This mattered most for the planned GPU blocklist work, where screenshot evidence would have produced a false positive on every single device. - Robo cannot drive a Godot game, and a longer timeout will not help.
Measured: an 11-second crawl, 2 screens, 2 actions — the second being
Type "kxtyqh"into Godot's hidden IME field. A Godot title exposes no view hierarchy, so the crawler has nothing to find. Robo is a boot smoke test and nothing more; it stops because it runs out of things to press, not because time expires. Game Loop is the only Test Lab mode that can genuinely exercise this game. - Virtual devices are back on the table — a correction to an earlier
reading of the same file. The old conclusion was that an
arm64-v8a-only APK ruled out emulators; true of the old x86 AVDs, not of today's.armcatalog. Spark's 10 virtual runs/day sit on top of the 5 physical, roughly tripling the free boot-smoke budget. It still does not help GPU compatibility work: an Arm AVD's Vulkan is host-backed, not real Adreno or Mali silicon. - The GPU matrix can be built empirically instead of from a model→SoC
table. Godot names the adapter it picked in logcat on every run, so the
first data point is recorded rather than inferred: Adreno 620 (Pixel 5,
redfin, API 30) — Vulkan 1.1.128, Forward Mobile, renders the menu, first frame in 397–521 ms across two runs,targetSdk 36confirmed from the install log. Also confirmed on hardware: preset 3's bakedassets/_cl_flags do reach the engine. Two traps recorded while they were fresh — Robo defaults to portrait against a landscape game, and PowerShell mangles--device. - The gitlab.com mirror stopped pretending to be a build farm
(
5f3fdda, #123). Box 2's measured upload is 2.99 Mbit/s ≈ 22 MB/min, and one full matrix uploads ~287 MB of artifacts — about 13 minutes of saturated household uplink per pipeline, withbuild:linuxalone needing five. It did not survive that: job 16355943161 is the shape of every failure, where the build succeeded and then the artifact upload reset three times over nine minutes and took the pipeline red. Four of the last eight mirrormainpipelines died exactly that way, so mirrormainread red while the real CI was green — worse than useless, because it teaches you to ignore a red.dmesgover the failure window shows no wlan or USB disconnects, so no adapter fixes it; the link is simply too slow. The mirror now runs the verify stage only, which takes seconds and has never failed. The gate is repeated on every job thatneeds:a build, with a note saying why: a pipeline whose needed job was excluded by rules fails to be created at all, so if you ungate one you must ungate all of them.