Repository navigation
fix(🍏): hold GPU work while the app is in the background - #4373
Open
wcandillon wants to merge 2 commits into
Open
wcandillon wants to merge 2 commits into
wcandillon wants to merge 2 commits into
Conversation
… when it is iOS refuses GPU work from an app in the background, and Dawn 157 turns the refused Metal command buffer into a device loss (#4290): every canvas froze until the device was replaced, and everything made on it had to be made again. A Dawn patch (scripts/dawn-patches/metal-suspend-submissions-in-background, to ship with the next react-native-webgpu-dawn) adds a device toggle under which Dawn holds the commit of command buffers while the app is in the background, from UIApplication's own notifications: the work is encoded, committed when the app comes back, and presents meanwhile are dropped. On entering the background the pending work is committed and waited on to be scheduled, as Apple asks. Nothing is lost, reordered or refused, so Dawn's bookkeeping, Graphite's completion tracking and JS promises stay right, which neither ignoring the refusal (Dawn PR 91, a glyph atlas page refused would stay garbage) nor rejecting the submit (Graphite ignores the result) could offer. Dawn commits from inside WaitAny too, so only a gate inside Dawn sees every commit. Every device Skia makes requests the toggle; a Dawn without it drops the name. The secondary device enables ImplicitDeviceSynchronization: each device has a lock of its own, and the hold is only atomic against an in-flight submit through it. Held frames keep what they use alive until the return, so a producer that keeps drawing in the background (a video player under background audio) should stop: Skia.gpu.foreground says whether the app is in the foreground and active, synchronously on every runtime, from UIApplicationWillResignActive to UIApplicationDidBecomeActive (the host activity's resume and pause on Android, the document's visibility on the web). Documented in webgpu.md. Verification: Dawn white-box tests for the hold (held work completes once resumed, pending work is committed on suspend, a waiter thread wakes on resume, MapAsync resolves once resumed, repeated calls and a destroyed device) pass on macOS with and without implicit device synchronization, and a regression subset of the queue and buffer tests passes; the iOS-only observer compiles against the iPhoneOS SDK. The touched headers and RNSkApplePlatformContext.mm pass a clang syntax check for the iOS simulator; tsc, eslint, cpplint and clang-format are clean; the Gpu unit tests pass. Not run on a device: the simulator does not enforce the rule, and the toggle does nothing until react-native-webgpu-dawn ships the patch.
wcandillon
marked this pull request as ready for review
October 30, 2026 12:44
This branch has not been deployed
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.
iOS refuses GPU work from an app in the background, and Dawn 157 turns the refused Metal command buffer into a device loss (#4290): every canvas froze until the device was replaced, and everything made on it had to be made again.
A Dawn patch (scripts/dawn-patches/metal-suspend-submissions-in-background, to ship with the next react-native-webgpu-dawn) adds a device toggle under which Dawn holds the commit of command buffers while the app is in the background, from UIApplication's own notifications: the work is encoded, committed when the app comes back, and presents meanwhile are dropped. On entering the background the pending work is committed and waited on to be scheduled, as Apple asks. Nothing is lost, reordered or refused, so Dawn's bookkeeping, Graphite's completion tracking and JS promises stay right, which neither ignoring the refusal (Dawn PR 91, a glyph atlas page refused would stay garbage) nor rejecting the submit (Graphite ignores the result) could offer. Dawn commits from inside WaitAny too, so only a gate inside Dawn sees every commit. Every device Skia makes requests the toggle; a Dawn without it drops the name. The secondary device enables ImplicitDeviceSynchronization: each device has a lock of its own, and the hold is only atomic against an in-flight submit through it.
Held frames keep what they use alive until the return, so a producer that keeps drawing in the background (a video player under background audio) should stop: Skia.gpu.foreground says whether the app is in the foreground and active, synchronously on every runtime, from
UIApplicationWillResignActive to UIApplicationDidBecomeActive (the host activity's resume and pause on Android, the document's visibility on the web). Documented in webgpu.md.
Verification: Dawn white-box tests for the hold (held work completes once resumed, pending work is committed on suspend, a waiter thread wakes on resume, MapAsync resolves once resumed, repeated calls and a destroyed device) pass on macOS with and without implicit device synchronization, and a regression subset of the queue and buffer tests passes; the iOS-only observer compiles against the iPhoneOS SDK. The touched headers and RNSkApplePlatformContext.mm pass a clang syntax check for the iOS simulator; tsc, eslint, cpplint and clang-format are clean; the Gpu unit tests pass. Not run on a device: the simulator does not enforce the rule, and the toggle does nothing until react-native-webgpu-dawn ships the patch.