-
Notifications
You must be signed in to change notification settings - Fork 126
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 high CPU usage when window is occluded on macOS Native #1003
Fix high CPU usage when window is occluded on macOS Native #1003
Conversation
|
||
// When window is not visible - it doesn't make sense to redraw fast to avoid battery drain. | ||
if (isWindowOccluded) { | ||
withTimeoutOrNull(300) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I copied this 300 millisecond value from the JVM code to keep it consistent. However, I don't understand the reason for this delay, why not just suspend indefinitely (on native & jvm)? I can remove this timeout if needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After some testing it seems all works fine without the 300ms timeout. CPU usage is reduced even more, on both native and JVM. Added a screenrecording with results for native app in the PR. I could not find any specific reason this timeout was added in git blame, so I removed it. If there's something I am missing please let me know.
What still stands out to me is that CPU and memory usage are both much lower for native vs JVM on my M1 Max macbook, with the same FPS result (100fps, my refresh rate). This is the also the case for my own much heavier app.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rendering each 300 ms was added before windowOcclusionStateChannel.receive()
is introduced, to avoid full CPU loading, because a window in occluded state isn't synchronised with the display refresh rate (we had 1000 FPS).
After windowOcclusionStateChannel.receive()
was added, the 300 ms just wasn't removed.
Everything will work as before if an app rendering logic doesn't contain any side effects besides just changing visuals. A hypothetical side effect in Compose could be some action in response to a state change inside a window's composition:
- send a system notification
- show a modal dialog
- send analytics
It is not correct to write such actions inside a window composition, because they don't affect window's visuals. It is fine if they are called outside of window composition (in LaunchedEffect
, or in the application
composition).
Because it is incorrect, we can do this change.
But we should add Release notes in the PR description:
## Release Notes (general)
### Breaking changes - Desktop
- rendering on macOs is no longer executed if a window is occluded. Some applications can stop working correctly if they contain non window-related side effects inside rendering logic.
## Release Notes (Compose)
### Breaking changes - Desktop
- rendering on macOs is no longer executed if a window is occluded. Some applications can stop working correctly if they contain non window-related side effects inside composition/layout/draw
@m-sasha, could you review the JVM change as well? It looks fine by me, but if you have doubts, we can reduce the scope of the PR, by keeping only the macos native changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@igordmn thanks for the explanation! I added the release notes to the description.
@igordmn it would be great if this could be reviewed. This PR improves CPU usage for native macOS but also JVM macOS. |
Seems that the change @igordmn mentioned is the only one here for JVM. I think it's a bit risky, but I'm not opposed to it. One thing we should do though, is to run all Compose tests with this change, before merging. @Thomas-Vos Could you do that? |
Just FYI, I noticed a strange (existing) behavior in my app. Sometimes if I run it and immediately switch to a different window before the app's window is shown, it will never get shown. I haven't investigated it, though, so it may or may not be related. |
806b7a8
to
d435e84
Compare
@m-sasha rebased and I ran the tests on latest compose-multiplatform-core in :compose only for macosArm64, some tests were already failing (19) even without this PR. After including Skiko local build I also got 19 failed tests. Not sure what to do about this. I could not find any information on how to set up the compose-multiplatform repo with a local build of compose-multiplatform-core and skiko. So unfortunately I was not able to test that repo. If it's too risky I can take out the change and put it in a separate PR. Then at least the macOS Native changes could get merged. |
@Thomas-Vos Let me run the tests myself first then. |
If we are not sure and it requires tests, let's just revert the jvm change and add Then there will be no obstacles for merging, the change will be simple. |
I think that's best. I'm not 100% convinced that occluded windows should not recompose anyway. |
This reverts commit d435e84.
@igordmn reverted the change, thanks for the review. |
Thanks! Also removed Release Notes. |
My app is used a lot in the background, and I noticed high CPU usage in the native macOS version. With this PR, CPU usage is reduced when window is invisible by disabling drawing. This was already implemented for the JVM, and that behaviour is now copied to native macOS. So the code is similar to JVM parts in:
The draw function is now suspend, which required moving some other functions around. I tried to keep the implementation similar to the JVM version.
Testing
Tested using
./gradlew runNative
insamples/SkiaMultiplatformSample
. Now the CPU usage is reduced a lot when moving another (non transparent) app over the window.Old behaviour
Screen.Recording.2024-12-09.at.17.23.28.mov
New behaviour with 300ms timeout
See reduction in CPU usage when window is invisible
Screen.Recording.2024-12-09.at.17.26.17.mov
New behaviour without 300ms timeout
Screen.Recording.2024-12-11.at.23.27.20.mov