Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix memory leak on macOS #3056

Merged
merged 1 commit into from
Oct 13, 2022
Merged

Conversation

xiaopengli89
Copy link
Contributor

Checklist

  • Run cargo clippy.

Description
Fix remaining leaks for metal backend.

@jinleili
Copy link
Contributor

jinleili commented Oct 5, 2022

From Apple's autoreleasepool doc:

... Therefore you typically do not have to create an autorelease pool block yourself, or even see the code that is used to create one. There are, however, three occasions when you might use your own autorelease pool blocks:

  • If you are writing a program that is not based on a UI framework, such as a command-line tool.
  • If you write a loop that creates many temporary objects.
    You may use an autorelease pool block inside the loop to dispose of those objects before the next iteration. Using an autorelease pool block in the loop helps to reduce the maximum memory footprint of the application.
  • If you spawn a secondary thread.
    You must create your own autorelease pool block as soon as the thread begins executing; otherwise, your application will leak objects.

I have also checked several official Metal demos, all Metal instructions put into @autoreleasepool is not necessary. Also, no resulting memory leaks were observed on Intel and M1 Macs.

@xiaopengli89
Copy link
Contributor Author

If the user does not interact with the program for a long time, leakage will occur, such as a video playback software on mac, but the user only operates remotely through the mobile phone, related #2092.

@xiaopengli89
Copy link
Contributor Author

Here is the memory leak diagnosed with Xcode:
20221007-205949

@jinleili
Copy link
Contributor

jinleili commented Oct 7, 2022

Indeed, after the window is completely occluded, the occupied memory will suddenly increase to a higher level, and then it can be observed that it will slowly decrease, and then there will be repeats, but it will always be below that highest value.

I also tested Apple's official metal demo and didn't observe this phenomenon.

This phenomenon is not like a memory leak caused by the wgpu itself, but a combination of wgpu + winit, and my personal projects that do not use winit have not observed this problem.

@jinleili
Copy link
Contributor

jinleili commented Oct 8, 2022

I'm sorry, but after testing your PR, the memory growth phenomenon is still the same as described above.

截屏2022-10-08 12 09 25

@xiaopengli89
Copy link
Contributor Author

The problem with the occluded window should be related to this issue #1783.

@jinleili
Copy link
Contributor

jinleili commented Oct 8, 2022

Thanks for the link, I didn't realize this issue has been discussed for so long. Basically I agree with the statement on this beby issue:

It was concluded that this is impractical to solve entirely at the wgpu level, and that there are two primary workarounds for this issue:

  1. Detecting window occlusion using winit's WindowEvent::Occluded and avoiding any rendering at all when the window is occluded.
  2. Driving rendering through CVDisplayLink (macOS) / CADisplayLink (iOS) frame callbacks.

@xiaopengli89
Copy link
Contributor Author

In my scenario, it was not caused by the concluded window, here is the memory growth of the wgpu process:
20221008-165548

@jinleili
Copy link
Contributor

jinleili commented Oct 8, 2022

WebKit's metal-cpp doc mentions a way to debug autoreleasepool:

Use the Environment Variable OBJC_DEBUG_MISSING_POOLS=YES to print a runtime warning when an autoreleased object is leaked because no enclosing AutoreleasePool is available for its thread.

It doesn't seem to eliminate the missing autoreleasepool warning. And since autoreleasepool isn't zero-overhead, maybe we can dig a little deeper into the cause of the problem to make the metal backend perform better.
截屏2022-10-08 17 31 58

@xiaopengli89
Copy link
Contributor Author

I tested this pr with OBJC_DEBUG_MISSING_POOLS=YES and it reduces the warnings from 101 to 45, maybe there are other leaks, also outside of wgpu.

@cwfitzgerald
Copy link
Member

I'm quite unfamiliar with all this autorelease pool stuff, so I'll defer to general consensus if this is something we need. Less warnings sounds generally good though.

@kpreid
Copy link
Contributor

kpreid commented Oct 12, 2022

2¢ from someone who once wrote a lot of Objective-C:

If adding an autorelease pool reduces leaks, then there definitely should be an autorelease pool added. There might be a more ideal scope for the pool — wider to catch more things, or narrower to reduce the maximum accumulation of not-yet-autoreleased objects — but there should definitely be a pool.

The only way adding an autorelease pool can cause harm (besides the cost of constructing it) is if code tries to autorelease an object pointer inside the pool scope and then use the pointer after the pool scope ends. You can think of the required discipline as vaguely like a Rust lifetime: autoreleaseing an object pointer is sort of like converting Rc<T> into &'a T, and the end of the pool's scope is dropping the Rc<T>, thus invalidating the “reference” you were using.

Skimming, I don't see any risks of that in this PR, because the pools all end at the same time the function scope ends (and if the WGPU objects being returned were containing non-retained references to ObjC objects, then that'd already be unsound).

Copy link
Member

@cwfitzgerald cwfitzgerald left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool then, lets land this!

@cwfitzgerald cwfitzgerald merged commit 9272cc6 into gfx-rs:master Oct 13, 2022
@kpreid
Copy link
Contributor

kpreid commented Nov 2, 2022

FWIW, I just tested my application with wgpu from git master vs stable 0.14.0, and there seems to be no longer a casually visible memory leak (stable showed a constantly growing size in Activity Monitor, around 0.01 MB/s). So, this change seems to have made a substantial improvement.

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.

4 participants