Repository navigation
Android (Adreno 830): changing aspect ratio or resolution crashes the game #72
Description
What happens
Hi, all! Big fan of the project so far. Just putting this up front since everything I've done trying to fix this issue has been through Opus 5.5/Sol 5.6, and everything in the hidden section below is fully generated. I definitely didn't figure any of this out myself, so any technical bits up here are in quotes to show they come from Claude, not me.
TL;DR: The app crashes for me on Android if I change the aspect ratio or resolution options. The changed code implemented in the attached .patch and described below fixes the issue for me.
My first fix (with GPT) just "skipped copying the old depth buffer when the phone couldn't blit it, and threw the old contents away." That stopped the crash and has been working fine for me, but Claude pointed out it only really works when the game "clears that buffer right afterwards. If a resize ever lands mid-frame, you could get a frame of depth glitches."
The patch below is the more proper version according to Claude, which I implemented tonight: "Instead of skipping the copy, it does the copy with a small draw, so the depth (and stencil) contents carry over to the new size. Devices that can already blit keep using the blit, so desktop shouldn't change at all."
depth-resize-without-blits.patch
It's a .patch file instead of a PR since I didn't want to go through forking and committing, and it applies cleanly with git apply on current main (9ca2a76). I've tested it on my Fold 7, and Claude also ran it through "the renderer smoke test." Generated details are below if you want them. Happy to provide any logs if needed or test other changes on Android.
Technical details: cause, fix, and testing (generated by Claude)
What happens
On a Galaxy Z Fold 7 (Snapdragon 8 Elite, Adreno 830), changing the aspect ratio or the internal resolution while playing aborts the game. Reproduces on current main (9ca2a76, v0.2.7).
Cause
After a scale or aspect change, rescale() in runtime/src/gfx/vulkan/surfaces.cpp makes a replacement image and copies the old contents into it with resample(), which uses vkCmdBlitImage. resample() checks BLIT_SRC/BLIT_DST and throws Vulkan device cannot blit this surface format when they're missing. The exception isn't caught on the render thread, so the process aborts.
Vulkan doesn't require blit support for any depth/stencil format (BLIT_DST is optional for all of them), and Adreno doesn't report it for some. Desktop drivers and MoltenVK happen to support it, which is why this only shows up on Android. Logging on the Fold 7 shows the formats affected there:
VK_FORMAT_D32_SFLOAT(126): hit on the first aspect changeVK_FORMAT_D16_UNORM(124): hit on the first resolution change
The scaled-copy branch of copy_surface_impl() (GX2CopySurface) has the same check and would abort the same way for a scaled depth copy.
Fix (attached patch)
When a depth format can't be blitted, the scaled copy is drawn instead:
- A fullscreen triangle per layer. Each pixel
texelFetches the source texel aVK_FILTER_NEARESTblit would pick (the spec's(x+0.5)·src/dstmapping) and writes it withgl_FragDepth. - Stencil (D32F+S8) is copied one bit per pass: the depth pass zeroes stencil, then 8 passes each write one bit with
discardwhere the source bit is clear. NoVK_EXT_shader_stencil_exportneeded. - Used by both
resample()(sorescale()andread_guest_texels()) and the scaled GX2CopySurface path. - Devices that can blit keep the blit, so desktop and macOS behavior is unchanged. The first time a format takes the draw path, one log line names it.
WWHD_VK_DEPTH_COPY=drawforces the draw path on any device, for testing.- Adds
vkCmdSetStencilWriteMask(Vulkan 1.0 core) toloader.h.
Old depth contents survive the resize, so a rescale landing mid-frame doesn't leave a frame depth-testing against garbage. Skipping the copy for unblittable depth formats would avoid the crash too, but only works when the game clears the buffer right after.
Testing
Renderer smoke test (Linux, lavapipe, WWHD_VK_VALIDATION=1): new depth_copy_check(), with the draw path forced:
- D16, D32F and D32F+S8, 2-layer arrays, scaled up (40×24 → 64×37), down (→ 17×11) and same size. Every depth and stencil texel is compared against the nearest-filter mapping.
- A partial region from part of the source (
uMax/vMax< 1). Pixels outside the region keep their earlier clear values. - The real resize path: a screen-shaped D32F+S8 target through a 21:9 aspect change (64×36 → 84×36), then 2× resolution (→ 168×72), then back, checked after each step.
- Full smoke test PASS, zero validation messages. The check fails as expected when the depth lookup is shifted one texel or a stencil bit pass is skipped.
On the Fold 7: aspect ratio Match window (1.11), 16:10, 21:9, 32:9, 16:9, then internal resolution 1.5×, 2×, 3×, 1×, all switched live with no crash.
Steps to reproduce
-
Run the latest version of the game compiled for Android.
-
Load the game and attempt to change either the resolution or aspect ratio. The app immediately crashes.
Save state (link), or why you can't add one
Not sure where to find this on Android, but the issue is very easily reproducible.
Version
main (9ca2a76, v0.2.7).
Platform
Android
Hardware
Galaxy Fold 7, Snapdragon 8 Elite, Adreno 830
Renderer
Vulkan
Logs
Can get some runs in on main and get logs for the crashes if needed.
Activity
Vulkan: draw scaled depth/stencil copies where the device cannot blit…
Vulkan: scaled copies without blits never abort; plain copy at the sa…
lschaupp commentedon Oct 8, 2026
Hi @Blivii, thanks a lot for this, and for being upfront about how it was made. The analysis was spot on: Vulkan doesn't require blit support for depth formats, Adreno doesn't offer it for D16/D32F, and the exception wasn't caught on the render thread.
We took your patch as the base (it's in unchanged, with you as co-author) and added a follow-up commit:
WWHD_VK_DEPTH_COPY=drawswitch stays, plus=noneto test that last resort. Both are documented in docs/vulkan.md, and the smoke test checks the fallback as well.On desktop/MoltenVK we forced the draw path and switched resolution and aspect ratio live several times mid-game with validation on. No crashes, no new validation messages, and the pictures match the blit path. Your smoke checks pass texel for texel.
This will be in the next release (v0.2.8). Could you try it on your Fold 7, cycling through the aspect ratios and 1.5×/2×/3×/1× as before? A log would be great too: you should see one line per depth format saying it's drawn rather than blitted. Thanks again!
lschaupp commentedon Oct 8, 2026
@Blivii this is now in v0.2.8 (and on
main), with you credited in the release notes. For Android, build v0.2.8 the same way you built your patched version (Android (build it yourself)).I'm closing this as fixed, since your own patch already worked on the Fold 7 and the release keeps that path. If v0.2.8 still crashes when you change the aspect ratio or resolution, please comment here with a log and we'll reopen it right away. Thanks again!
Original project fixes: interpolation, Medli, quick doors, shader lin…