Repository navigation
fix(🤖): stop a TextureView canvas from freezing the UI thread on Android 11 - #543
YevheniiKotyrlo wants to merge 2 commits into
Conversation
52c1106 to
4bf3876
Compare
Device measurement — before / after
This PR — no freeze: the window draws 986–1004 frames in each window. #542 alone leaves Android 11 as
Release builds of the example opening Reanimated's UI-thread screen on its default |
A canvas drawn from Reanimated's UI thread presents inside the main thread's Choreographer frame, ahead of the window's draw. A TextureView takes its next buffer only when the window draws, and its buffer queue replaces a buffer it has not taken yet, so the next present replaced every other frame and the window showed half of them. A frame presented on the main thread to a TextureView surface now waits for a present the view posts to the main looper, behind the draw the window has pending, as react-native-skia's FrameScheduler does. The next frame boundary presents a frame still waiting first, a detach drops it with its surface, and frames presented on other threads, or to a SurfaceView or HardwareBufferView, are presented as before. Android 11 keeps presenting at once: its Vulkan loader gives a TextureView's buffer queue a 4.3 s dequeue timeout, and a present held behind the draw stretched those waits into 14 s freezes and an ANR on the Android 11 emulator. The weak handle the hardware buffer view's waker kept on its view is shared by both calls from native, the canvas docs say when a UI-thread frame reaches a TextureView, and the e2e suite presents frames from a worklet on a TextureView canvas on the tests screen.
…oid 11 Android 11's Vulkan loader gives a TextureView's queue a 4.29 s dequeue timeout where it means none, and a canvas presenting on the UI thread then waits there for a buffer only that thread's next draw gives back. On Android 11 a TextureView canvas acquires only once its window has drawn the frame presented last, rendering into the offscreen texture meanwhile, so a frame presented on the UI thread waits behind the window's pending draw there too. The TextureViewPresent spec runs on the UI thread as well.
eef933c to
e067360
Compare
Problem
On Android 11, a canvas backed by a
TextureView(the default backing view) freezes the UI thread for 4.3 s at a time when it renders from the UI thread. Android 11's Vulkan loader passes the swapchain's dequeue timeout as anintwhere the window reads anint64_t(swapchain.cpp,Surface.cpp), so theTextureView's buffer queue waits up to 4.29 s for a free buffer instead of failing at once. A frame acquired before the window has drawn the previous one waits for that draw, which only the UI thread can run. react-native-skia'sTextureViewcanvas has the same freeze; wcandillon/react-native-skia#4251 fixes it there.Solution
On Android 11, a
TextureViewcanvas acquires its next texture only once the window has drawn the frame presented last. A frame that comes sooner renders into the offscreen texture and is copied onto the surface as soon as the window has drawn, so the latest frame always shows and nothing waits:WebGPUView.acquiresAfterDrawdecides it (aTextureViewon Android 11) and passes it with the surface.SurfaceInfocounts the presents and the presents the window has drawn;getCurrentTexture()defers a frame while the two differ, and the deferred frame is copied onto the surface with the attach blit's copy.WebGPUTextureView.onSurfaceTextureUpdatedreads the present count while the window draws and posts it behind that draw, so a draw counts only the presents it took.WebGPUView.presentsBehindPendingDraw).Every other release and backing view is unchanged. The canvas docs mention the Android 11 copy.
Verification
TextureViewPresentnow presents from the UI thread and from the JS thread, and fails when the last frame does not show or when a frame stalls the thread for 2 s. On the Android 11 emulator the UI-thread case fails with fix(🤖): show every frame a worklet presents on a TextureView canvas #542 alone, in 2 of 2 runs (a 4339 ms stall, and a 13 s run whose readback is then aborted), and passes with this change in 2 of 2; on the Android 15 emulator both cases pass either way.TextureView, two 30 s windows on the Android 11 emulator: eight 4.3 s freezes in each window onmainand with fix(🤖): show every frame a worklet presents on a TextureView canvas #542 alone, none with this change (the window drew 43–46 frames per window onmain, 43–44 with fix(🤖): show every frame a worklet presents on a TextureView canvas #542 alone and 986–1004 with this change) (measurement). Other releases are unchanged: only aTextureViewon Android 11 acquires after the draw.main's 220 and the twoTextureViewPresentcases; the two failures, the YUV cases ofExternalTextureColorSpaceandNativeBufferWrapper, fail onmainthere too.mainc080934epasses 222 of 222. On an iPad Pro 12.9-inch (4th generation, iOS 27.0.1), built and run as CI's iOS job does on a simulator (Debug build, Metro withCI=true), it passed 223 of 223 before the rebase onto feat(🤖): name the Android backing view explicitly #574 (main8f664fca: 221 of 221).yarn lint·yarn tsc· the package and web-template builds ·yarn test:plugin·yarn check-dawn·yarn build:docs· cpplint · clang-format on the changed lines · the example's iOS build for the simulator SDK, oneef933cf, whose iOS-compiled files this head keeps.Base
Stacked on #542, which posts a present made on the UI thread behind the window's pending draw everywhere but Android 11; this change brings it to Android 11 with the acquire gate. Both are on
mainc080934e.