Hello Arm graphics team,
I have an independent native GLES reproduction on Samsung SM-A5160, Android 13, Mali-G76, driver OpenGL ES 3.2 v1.r38p1-01bet0-mbs2v41_0.abf8345db3d3997d6844dcadfe1a9041. It uses generated pixels and does not depend on Flutter, Skia or network images.
Interleaving retained 200 x 300 full-mip image uploads with two sequential 1536 x 3072 RGBA8 temporary texture create/copy/delete cycles per frame reaches about 2,098 MiB Graphics after 56-58 images. Images are uploaded every 24 frames. Fixed blur render targets are reused. The attached source includes the exact GL/EGL command ordering and a monitored runner.
Two fresh-process repetitions per control show:- No temporary-copy turnover, including repeated recent fence waits: about 125 MiB peak at 160 images.- Turnover plus full mips: about 2,097-2,099 MiB. CPU-generated mip levels also reproduce this.- Same CPU-mip program with mips disabled: about 168-171 MiB at 160 images.- Retain at most 20 images: about 871 MiB at 160 uploads.
Timing matters: preloading images, waiting before uploads, or finishing every frame prevents most growth. However, after growth has formed, idle/completion waits do not release the main retained amount. Deleting image textures releases memory in steps while their explicit fences are still alive. Same-context uploads and a no-explicit-fence variant also reproduce high retention. Full mip state behaves differently from a level-0-only texture with approximately equal logical bytes.
Additional controls falsify a timing-independent block-pinning model: at 48 images, preloading gives about 141-142 MiB, interleaving about 1,787-1,805 MiB, and finishing both contexts plus waiting one second before each upload about 136 MiB. A 32-image approximately equal-byte level-0-only control is about 118 MiB versus 1,237 MiB for full mips. After retention forms, a separate 60-second idle run remains around 1,675 MiB.
Two texture/fence deletion controls each release 46 large Mali virtual mappings while fences remain alive, with zero additional large-map removals at fence deletion. Maps are virtual, not physical residency. Frequent maps observation itself suppresses the growth, so later high-growth measurements use endpoints. We have not established a permanent leak, specification violation or physical allocator mechanism.
Questions for Arm:1. Is this known in r38p1, and is there a fix/reference for the OEM?2. What could associate small image-texture lifetime with resources from recently deleted large copy textures even after later completion waits?3. Which counters/captures are available on a retail non-rooted Samsung device to distinguish retained resource versions, oversized backing reuse and suballocation?4. Is bounded temporary-texture reuse an appropriate mitigation, and what correctness constraints should we verify?
The investigation began in Flutter/Skia. A diagnostic scratch-reuse change reduced large allocation churn and Graphics to about 150 MiB. The native reproduction above is independent. This is not a proposal to add per-frame glFinish in production.
Attachments below contain tested source/APKs, 42 native trials of measurements and two figures. Use the monitored Python runner in gles-reproducer/native-cpumip; it stops allocations around 2 GiB, with possible sampling overshoot. Test on a dedicated device.
gles-reproducer.ziparm-native-evidence.zip