Skip to content

fix: (darwin) share CVMetalTextureCache to stop per-texture IOSurface leak (#178) - #202

Merged
nmfisher merged 1 commit into
developfrom
fix/darwin-metal-cache-leak
Jul 30, 2026
Merged

fix: (darwin) share CVMetalTextureCache to stop per-texture IOSurface leak (#178)#202
nmfisher merged 1 commit into
developfrom
fix/darwin-metal-cache-leak

Conversation

@nmfisher

@nmfisher nmfisher commented Jul 30, 2026

Copy link
Copy Markdown
Owner

This PR fixes the IOSurface leak (#178).

Root cause was MetalTextureWrapper.allocate() creating a fresh CVMetalTextureCache per texture. Releasing that cache does not synchronously free the IOSurfaces it has cached, so every CVMetalTextureCacheCreateTextureFromImage stranded one IOSurface.

Fix is to use one process-wide cache (created lazily, keyed off the system default device), flushed on teardown with kCVMetalTextureCacheMaximumTextureAgeKey: 0. by sharing one process-wide CVMetalTextureCache instead of creating one per MetalTextureWrapper.

…ace leak (#178)

MetalTextureWrapper.allocate() created a fresh CVMetalTextureCache per
texture. Releasing that cache does not synchronously free the IOSurfaces it
has cached, so every CVMetalTextureCacheCreateTextureFromImage stranded
~one IOSurface — roughly one screen-sized surface per mount/unmount.

Fix: use one process-wide CVMetalTextureCache (created lazily, keyed off the
system default device) instead of one per wrapper, and flush it on teardown
with kCVMetalTextureCacheMaximumTextureAgeKey: 0. Verified in a standalone
reproducer; see docs/DARWIN_IOSURFACE_LEAK_ROOT_CAUSE.md.

The darwin texture teardown is otherwise correct (create/destroy of the
descriptor, Flutter adapter, and Filament render target are all balanced);
the remaining phys_footprint movement is bounded Metal/Filament allocator
churn that plateaus, not a leak. No texture pool is required — the pool on
the wip branch only masked the async reclamation by reusing one texture.

Accordingly rewrite darwin_texture_leak_test.dart to assert bounded (not
linear) growth: the steady-state per-session phys_footprint delta over the
trailing sessions must stay under one BGRA surface. The original per-cache
leak (~one surface/session) trips this; the fix's churn plateau passes with
margin. Verified the test catches the regression: steady-state 1.87 MB/session
without the fix (FAIL) vs 0.60 MB/session with it (PASS); one surface = 1.69 MB.

Co-Authored-By: Claude <noreply@anthropic.com>
@nmfisher nmfisher changed the title fix(darwin): share one CVMetalTextureCache to stop per-texture IOSurface leak (#178) fix: (darwin) share CVMetalTextureCache to stop per-texture IOSurface leak (#178) Jul 30, 2026
@nmfisher
nmfisher merged commit 63a61e8 into develop Jul 30, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant