Repository navigation
Conversation
Contributor
Author
|
The Android check failed before it checked out the code: Docker Hub refused to pull the |
Sorry, something went wrong.
`new_texture` retained the texture `newTextureWithDescriptor` had already returned retained, and the `MTLSamplerDescriptor` `new` had, and `delete_texture` released only the texture, once. So no texture deleted on Metal was ever freed, render targets included, nor any texture's sampler descriptor or sampler state. The filter and wrap setters also dropped the sampler state they replaced, and `texture_set_min_filter` the descriptor it made. Each object now holds the one reference its constructor returns. `delete_texture` releases the texture, its sampler state and its sampler descriptor, and each setter releases the state it replaces and any descriptor it made for the new one. These releases wait until `commit_frame` has seen the frame's command buffer complete, since a draw queued earlier in the frame can still be encoded with a texture after it's deleted. macroquad does this whenever a font atlas grows: it deletes the old atlas texture mid-frame, and the text it already batched is only encoded at the end of the frame. Released at once, Metal was handed a freed texture. Verified on macOS: a program that makes a render target with depth and a texture, draws into the target and deletes all three every frame now holds steady, as long as the run loop drains an autorelease pool; without one, every frame's command buffer stays alive with the textures it drew with. With zombies enabled, drawing text, then growing its font's atlas in the same frame, runs clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: multica-agent <github@multica.ai>
benface
force-pushed
the
metal-release-deleted-textures
branch
from
October 18, 2026 18:04
5036e4e to
4c6f3a2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
On Metal, no texture is freed when it's deleted, render targets included:
new_textureretains the texturenewTextureWithDescriptor:already returned retained, and the sampler descriptornewalready returned retained.delete_texturereleases only the texture, once.texture_set_min_filter,texture_set_mag_filterandtexture_set_wrapdrop the sampler state they replace, andtexture_set_min_filterthe descriptor it makes.Change
delete_texturereleases the texture, its sampler state and its sampler descriptor, and each setter releases the state it replaces and any descriptor it made for the new one.commit_framehas seen the frame's command buffer complete. A draw queued earlier in the frame can still be encoded with a texture after it's deleted. macroquad does this whenever a font atlas grows: it deletes the old atlas texture mid-frame, and the text it already batched is only encoded at the end of the frame. Released at once, Metal was handed a freed texture: with zombie objects on, drawing text and then growing its font's atlas in the same frame sentretainto a deallocated texture. It now runs clean.Measurements
On iOS and tvOS, this is all it takes, since UIKit's run loop drains an autorelease pool each time round. On macOS, memory only stays flat with #676 as well. Its run loop never drains one, so every frame's command buffer stays alive, with every texture it drew with.
Physical footprint of a small program on macOS (Apple silicon, Metal):
A macroquad game (CHOMP) sees the same on macOS:
Verification
cargo checkpasses foraarch64-apple-darwin,aarch64-apple-iosandaarch64-apple-tvos.🤖 Generated with Claude Code