Repository navigation
Add a deferred platform-view-created path to the shell and use it on Android - #196342
Conversation
…Android On Android, surfaceCreated and surfaceChanged block the main thread until the raster thread has created the swapchain and set up the rasterizer. This happens inside the first view traversal, so the activity's first draw and Dart's first frame both wait behind it (about 100 ms on a Galaxy A16 and a Moto G05). shell/common: - Adds PlatformView::NotifyCreatedDeferred and PlatformView::Delegate::OnPlatformViewCreatedDeferred. The platform passes a surface factory; Shell runs it on the raster thread and requests a frame without waiting. - The default delegate implementation is synchronous, and no other platform calls the new path. - Rasterizer::Setup can now run while the platform thread disables or reads the thread merger, so that access is locked, a merger created while disabled starts disabled, and a spawned shell resolves its parent's merger lazily. Android: - PlatformViewAndroid::NotifyCreated and NotifyChanged post to the raster thread and return. A generation counter skips tasks from a surface that has since been destroyed or replaced. - NotifyDestroyed stays synchronous, and a failed surface creation is handled as before. Fixes flutter#196180
Add deterministic unit tests in shell_unittests and android_shell_holder_unittests covering non-blocking deferred surface creation, attempt-and-validate surface setup semantics (nullptr and invalid Surface rejection without calling Setup or CreateResourceContext, and setup continuation when SetNativeWindow fails yet CreateRenderingSurface returns a valid Surface), IO resource-context ordering after SetupImpellerContext, immediate UI frame scheduling whose draw orders after Rasterizer::Setup, in-flight NotifyDestroyed ordering and thread-merger disable safety, Shell destruction with pending deferred creation, Shell::Spawn thread-merger sharing when parent setup is deferred, and superseded surface_generation_ skipping.
Removes the synchronous default implementation, which ran the surface factory on the calling thread and was only there so test delegates did not need an override. Shell is the only production delegate; the test delegates now declare the method like the rest of the interface.
There was a problem hiding this comment.
Code Review
This pull request introduces deferred platform view creation to prevent blocking the platform thread during GPU context or swapchain initialization. It adds asynchronous rendering surface construction via a SurfaceFactory callable on the raster task runner, updates PlatformViewAndroid to utilize this deferred path with generation tracking, and adds thread-safety guards around the Rasterizer thread merger. Feedback suggests ensuring that all writes to raster_thread_merger_ (including in Teardown) are properly guarded by the new mutex, and defensively checking the validity of surface_factory before invocation to prevent potential crashes.
…e merged NotifyChanged used RunNowOrPostTask on the raster task runner. While the raster queue is merged onto the platform thread (hybrid composition), that runs the resize inline, ahead of a surface setup or an earlier resize still waiting in the raster queue. Post the resize unconditionally so it stays in FIFO order. Since the task can now be queued while the queues are merged, and Shell::~Shell() does not drain a merged raster queue, the task no longer captures `this`. It shares the surface generation counter instead, and the destructor bumps the counter so a task that outlives the platform view is a no-op. Adds AndroidShellHolder.NotifyChangedStaysBehindQueuedSetupWhenThreadsMerge.
|
I found one ordering of operations bugs in the HC merged threads case, pushed a fix, but the description is: While a Hybrid Composition platform view is on screen, raster work runs on the platform thread, so the previous use of |
... and also make
PlatformViewAndroid::NotifyChangednon-blocking at the same time.On Android,
surfaceCreatedandsurfaceChangedcurrently block the main thread until the raster thread has finished any queuedSetupImpellerContext()work, created the swapchain, and set up the rasterizer. Because this runs inside the first view traversal, both theActivity's first native draw and Dart's first frame wait behind it (~80–110 ms on mid/low-tier Android devices with Impeller Vulkan, e.g. ~70 ms faster non-debuggable cold-start time-to-first-frame on a Moto G05 once unblocked).Per Android's
SurfaceHolder.Callbackcontract, onlysurfaceDestroyedis required to block until the render thread stops accessing theSurface;surfaceCreatedandsurfaceChangedcan forward surface work asynchronously whilefml::RefPtr<AndroidNativeWindow>(ANativeWindow_fromSurface) keeps the native window reference alive until teardown (see #196180 for more details)Changes
shell/common:PlatformView::NotifyCreatedDeferredandPlatformView::Delegate::OnPlatformViewCreatedDeferred, used only by Android in this PR. The platform passes aSurfaceFactorycallback;Shellposts surface creation +Rasterizer::Setupdirectly to the raster task runner (preserving FIFO ordering with subsequent resize/teardown tasks on the raster queue), postsCreateResourceContextto the IO task runner once a validSurfaceis created, and schedules a UI frame without waiting.PlatformView::NotifyCreated, which is unchanged.Rasterizer::Setupcan now run concurrently with platform-thread calls that disable or read the raster thread merger, protects merger access withraster_thread_merger_mutex_, starts a lazily created merger disabled ifDisableThreadMergerIfNeeded()was called while setup was in flight, and resolves a spawned shell's parent merger lazily on the raster thread.Android (
shell/platform/android):PlatformViewAndroid::NotifyCreatedandPlatformViewAndroid::NotifyChangedto post to the raster thread and return immediately. An atomic generation counter (surface_generation_) skips queued setup/resize tasks from a surface that has since been destroyed or replaced.NotifyDestroyed(andNotifySurfaceWindowChangedforFlutterImageViewresize) synchronous, and preserves existing attempt-and-validate failure handling when surface creation fails.Perfetto cold-start traces on Moto G05 (Impeller Vulkan): before vs. after.
Fixes #196180, and is part of flutter/rfc#24
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.