Repository navigation
Conversation
…/1pFgFYzAKrIFEQQfSz6NbOvKXG6K3oksPQqhKSQZ7t68 TAG=agy CONV=0c48c67f-eb59-40f8-9424-1a74c8da8e46
…ate BUILD.gn line links
| - [`//flutter/shell/platform/embedder:embedder_headers`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/BUILD.gn#L210) and [`//flutter/shell/platform/embedder:embedder_as_internal_library`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/BUILD.gn#L206) (with `defines = [ "FLUTTER_ENGINE_NO_PROTOTYPES" ]` enforcing dynamic `FlutterEngineProcTable` resolution) | ||
| - [`//flutter/fml`](https://github.com/flutter/flutter/tree/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/fml) | ||
| - [`//flutter/common`](https://github.com/flutter/flutter/tree/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/common) | ||
| - [`//flutter/assets`](https://github.com/flutter/flutter/tree/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/assets) |
There was a problem hiding this comment.
Just curious, why is this needed? I would think we could remove this dependency as well. I believe desktop platforms do not depend on this library. Not a big deal either ways, asking for my understanding.
There was a problem hiding this comment.
It's not actually needed - I just added it since it was considered optional - will make it more clear.
| - **Texture Layer Hybrid Composition (TLHC) and Virtual Display (VD) via External Textures**: Both [`configureForTextureLayerComposition`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L652) and [`configureForVirtualDisplay`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L596) allocate a `PlatformViewRenderTarget` from `TextureRegistry` (`SurfaceProducer` / `AHardwareBuffer` or `SurfaceTexture`) in Java and return its texture ID to the Dart framework `Texture` widget. At the native engine boundary, both TLHC and VD operate strictly through the C-API external texture callbacks (`FlutterEngineRegisterExternalTexture`, `FlutterEngineMarkExternalTextureFrameAvailable`, `FlutterVulkanExternalTextureFrameCallback`, `FlutterHardwareBufferExternalTextureFrameCallback`, and `FlutterOpenGLTexture`) without creating `FlutterPlatformView` layers. | ||
| - **Hybrid Composition (HC) and Hybrid Composition++ (HCPP) via `FlutterCompositor`**: The engine's internal [`EmbedderExternalViewEmbedder`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder_external_view_embedder.h) implements `flutter::ExternalViewEmbedder` behind the public [`FlutterCompositor`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L2117) interface (`create_backing_store_callback`, `collect_backing_store_callback`, and `present_layers_callback` / `present_view_callback`). `AndroidCompositor` and `AndroidPlatformViewsController` drive both HC and HCPP over `FlutterCompositor` without including internal `//flutter/flow` or `fml::RasterThreadMerger` headers: | ||
|
|
||
| 1. **Backing Store Allocation (`create_backing_store_callback`)**: For the primary Flutter layer (`platform_views_count == 0`), `AndroidCompositor::CreateBackingStore` returns the main surface framebuffer (`GetFBO()`). When a Hybrid Composition platform view splits the scene and Flutter renders UI layers above the platform view, subsequent `CreateBackingStore` calls within the same frame allocate offscreen framebuffers (`AcquireOffscreenFBO`). |
There was a problem hiding this comment.
For the primary Flutter layer (
platform_views_count == 0),AndroidCompositor::CreateBackingStorereturns the main surface framebuffer (GetFBO()).
Does the prototype already do this? I'd be curious how this works. This seems like a good optimization, but AFAICT FlutterBackingStoreConfig does not provide enough information in the create_backing_store_callback to determine if you can render to the main surface framebuffer directly.
There was a problem hiding this comment.
I did try doing this - but ended up adding an is_overlay to the FlutterBackingStoreConfig to get things to actually work, so this is no longer true - you're right that there wasn't enough info.
I'll rewrite step 1 to propose an is_root_layer field on FlutterBackingStoreConfig (false unless the layer is the bottom one with no platform view beneath it), require avoid_backing_store_cache = true, and fall back to an offscreen store plus a blit when the field is missing. I'd imagine the iOS RFC needs the same info - wdyt @cbracken ?
There was a problem hiding this comment.
Here's the similar conversation on the iOS RFC: #27 (comment)
FYI here's a concern I bubbled up on the iOS design:
One thing that's not clear to me: how will resizing work with the drawable-root approach? Today, embedders that use
FlutterCompositorresize duringFlutterCompositor.present_view_callback. I imagine with the drawable-root approach, the embedder will instead need to resize when the engine requests the root layer's backing store? Assuming that's correct, this might require migrating existing embedders that useFlutterCompositor.
If Android uses the main surface framebuffer as the backing store, we'll need to resize step to happen when the engine requests the backing store, instead of when the engine presents (which is what all embedders that use FlutterCompositor do today).
| 2. **Raster-Before-Acquire Ordering and Synchronous Platform-Thread Latch**: | ||
| In the legacy architecture, `AndroidExternalViewEmbedder` merged the raster thread onto the platform thread via `fml::RasterThreadMerger` only because it called JNI view hierarchy mutations inline during `SubmitFlutterView`. In `AndroidCompositor::PresentLayers`: | ||
| - On the raster thread, `surface_manager_->Present()` and `surface_manager_->BlitAndSwapOverlaySurface(...)` first complete `eglSwapBuffers` / `vkQueuePresentKHR` into the root `FlutterImageView` and `PlatformOverlayView` `ImageReader` `ANativeWindow` queues for Frame $N$. | ||
| - `AndroidCompositor` packages Frame $N$'s platform view geometry (`offset`, `size`), value-copied `AndroidMutatorsStack`, and overlay rectangles into a single atomic platform-thread frame transaction (`onBeginFrame` $\rightarrow$ `onDisplayPlatformView` $\rightarrow$ `onDisplayOverlaySurface` $\rightarrow$ `onEndFrame`). |
There was a problem hiding this comment.
... single atomic platform-thread frame transaction...
Could you expand on this? I don't follow this bit. Is this the synchronization_fence_fd mechanism described below, or something else?
There was a problem hiding this comment.
Something else, and "transaction" was the wrong word next to SurfaceControl.Transaction. synchronization_fence_fd only exists on the HCPP path (API 34+, Vulkan); HC never uses it. What I meant: after the raster thread swaps frame N into the ImageReaders, it posts one platform-thread task that runs onBeginFrame, every onDisplayPlatformView and onDisplayOverlaySurface, and onEndFrame, so no Android traversal can draw a half-applied frame; today's HCPP embedder already does this with a single PostTask (external_view_embedder_2.cc). For HC the raster thread also waits for that task, because onEndFrame calls acquireLatestImage() (PlatformViewsController.java), which would otherwise show frame N+1's pixels with frame N's view positions; the legacy SubmitFlutterView got this ordering for free by rendering and making the JNI calls on the merged platform thread. I'll rewrite the section in those terms, drop "atomic", and describe the two steps that still need a synchronous platform-thread hop mid-frame: creating an overlay surface, and the first HC frame's switch to FlutterImageView.
| double max_value; | ||
| int32_t traversal_parent; | ||
| FlutterTransformation hit_test_transform; | ||
| ``` |
There was a problem hiding this comment.
These LGTM, but we should get these new members reviewed by @chunhtai and @hannah-hyj
There was a problem hiding this comment.
Added SemanticRole and tried to align more with what Android currently uses. Dropped traversal_parent - since I guess that's just web. PTAL
| FlutterRect clip_rect; | ||
| FlutterRoundedRect clip_rounded_rect; | ||
| FlutterTransformation transformation; | ||
| const FlutterPath* clip_path; |
There was a problem hiding this comment.
Does this need to be a pointer?
| 3. **Pre-run buffering is self-contained in `FlutterEmbedderNative`**: Queueing platform messages, initial route, viewport metrics, and external texture registrations in `FlutterEmbedderNative` between [`attachToNative()`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java#L433-L442) and [`runBundleAndSnapshotFromLibrary()`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java#L1072-L1089)—and flushing them immediately after [`FlutterEngineRunInitialized`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L3006-L3007)—requires no new [`embedder.h`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h) entry points or [`RuntimeController`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/runtime/runtime_controller.h#L52) lifecycle flags. | ||
| 4. **`defer_surface_creation` already moves [`SetupImpellerContext()`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/common/platform_view.h#L1040) off the UI thread**: Because [`runBundleAndSnapshotFromLibrary()`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java#L1072-L1089) executes during `Activity.onCreate` / `onStart` well before WindowManager dispatches `SurfaceHolder.Callback.surfaceCreated`, skipping the implicit [`NotifyCreated()`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/common/platform_view.cc#L64-L85) call inside [`FlutterEngineRunInitialized`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L3006-L3007) already gives the raster thread the entire window between [`runBundleAndSnapshotFromLibrary()`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java#L1072-L1089) and `surfaceCreated()` to complete [`SetupImpellerContext()`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/common/platform_view.h#L1040) asynchronously. | ||
|
|
||
| | Dimension | Proposed: Two-Phase Init ([`FlutterEngineInitialize`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L2970-L2975) + [`FlutterEngineRunInitialized`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L3006-L3007) + `defer_surface_creation`) | Alternative: Split Shell & Isolate Launch (`FlutterEngineLaunchShell` + `FlutterEngineLaunchEngine`) | |
There was a problem hiding this comment.
A potential alternative might be adding a new renderer type that is a callback that produces a renderer config.
Strawman API...
typedef enum {
...
/// The renderer is chosen lazily by the embedder after the engine
/// has been initialized. See |FlutterDeferredRendererConfig|.
kDeferred,
} FlutterRendererType;
struct FlutterRendererConfig {
FlutterRendererType type;
union {
FlutterOpenGLRendererConfig open_gl;
FlutterSoftwareRendererConfig software;
FlutterMetalRendererConfig metal;
FlutterVulkanRendererConfig vulkan;
FlutterDeferredRendererConfig deferred;
};
};
/// Callback invoked by the engine to resolve a deferred renderer
/// configuration into a concrete one.
///
/// @param[in] user_data The `user_data` passed to
/// `FlutterEngineInitialize`
/// or `FlutterEngineRun`.
/// @param[out] config_out Engine-owned storage. The embedder must set
/// `config_out->type` to `kOpenGL`, `kSoftware`,
/// `kMetal`, or `kVulkan` and populate the
/// corresponding union member. `kDeferred` is not
/// allowed. All memory referenced by the
/// configuration must remain valid until
/// `FlutterEngineDeinitilaize` or
/// `FlutterEngineShutdown` returns.
///
/// @return On error, `false`.
typedef bool (*FlutterRendererConfigResolveCallback)(
void* /* user_data */,
FlutterRendererConfig* /* config_out */);
typedef struct {
size_t struct_size;
/// Invoked exactly once by the engine to obtain the concrete renderer
/// configuration. Required.
///
/// The callback is invoked once on the raster thread.
/// Platform thread work that requires a GPU context blocks until this
/// callback returns.
FlutterRendererConfigResolveCallback resolve_callback;
} FlutterDeferredRendererConfig;This approach should remove the need for FlutterEngineLaunchShell, FlutterEngineNotifyCreated, FlutterEngineNotifyDestroyed. I suspect we might be able to remove defer_surface_creation by deferring PlatformView::NotifyCreated until the deferred renderer config is resolved.
Opus's feedback:
- External texture registration would need to be updated since that depends on the renderer type.
- Today,
FlutterEngineInitializevalidates the renderer config at startup. This would now happen in a background thread.
| /// An embedder may return the view's on-screen render target (for example, | ||
| /// the next swapchain image or default framebuffer) when this is true. The | ||
| /// engine's backing store cache ignores this field, so such an embedder must | ||
| /// set `FlutterCompositor.avoid_backing_store_cache` to true. |
There was a problem hiding this comment.
I imagine the engine should never cache the default framebuffer / next swapchain image? Should we make a prerequisite for this being true that avoid_backing_store_cache is true? Otherwise, the engine would never be able to cache the root layer's backing store.
| 1. **Explicit Hybrid Composition ([`initExpensiveAndroidView`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/packages/flutter/lib/src/services/platform_views.dart#L194-L210))**: The plugin requests [`createForPlatformViewLayer`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L168-L177), routing directly to [`PlatformViewsController.configureForHybridComposition`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L581-L585). | ||
| 2. **Embedded Child `SurfaceView` or `TextureView` ([`VIEW_TYPES_REQUIRE_NON_TLHC`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L68))**: When a plugin uses [`initAndroidView`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/packages/flutter/lib/src/services/platform_views.dart#L134-L152) ([`createForTextureLayer`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L194-L247)) and the embedded Android `View` hierarchy contains a `SurfaceView` or `TextureView` child whose out-of-band buffer queue bypasses `ViewParent.onDescendantInvalidated()`, [`PlatformViewsController.createForTextureLayer`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L194-L247) detects `!supportsTextureLayerMode` and branches by `request.displayMode`: | ||
| - **[`TEXTURE_WITH_VIRTUAL_FALLBACK`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformViewCreationRequest.java#L24) (Virtual Display / VD)**: Calls [`configureForVirtualDisplay`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L596-L638), hosting the view and its child `SurfaceView` inside an offscreen `VirtualDisplay` backed by a [`TextureRegistry`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/view/TextureRegistry.java#L19) render target. Frame synchronization is handled entirely on the GPU/raster thread as a standard C-API external texture ([`FlutterEngineRegisterExternalTexture`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L3227-L3229)). | ||
| - **[`TEXTURE_WITH_HYBRID_FALLBACK`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformViewCreationRequest.java#L28) (Hybrid Composition / HC)**: Calls [`configureForHybridComposition`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L581-L585) and returns [`NON_TEXTURE_FALLBACK`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/PlatformViewsChannel.java#L316) (`-2`), instructing the Dart framework to composite the view via a [`PlatformViewLayer`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/flow/layers/platform_view_layer.h#L13) ([`kFlutterLayerContentTypePlatformView`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L2175)). |
There was a problem hiding this comment.
This section seems unnecessarily dense. Could we simplify it?
| - `AndroidCompositor` captures frame N's platform view geometry (`offset`, `size`), value-copied `AndroidMutatorsStack`, and overlay rectangles by value and posts a single task (`PostTask`) to the platform thread that executes [`onBeginFrame`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L1311-L1319), every [`onDisplayPlatformView`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L1252-L1289), every [`onDisplayOverlaySurface`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L1291-L1309), and [`onEndFrame`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L1321-L1347) in order. Because the Android UI `Looper` runs one message at a time and `ViewRootImpl` schedules view traversals in a separate `Choreographer.CALLBACK_TRAVERSAL` callback, no Android view traversal can run mid-task and observe a partially applied frame (the same single-`PostTask` pattern used by [`AndroidExternalViewEmbedder2::SubmitFlutterView`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/external_view_embedder/external_view_embedder_2.cc#L89-L229)). This is purely a CPU-side `Looper` ordering mechanism; it does not use `synchronization_fence_fd`, which exists only on the HCPP `SurfaceControl.Transaction` path. | ||
| - When `platform_views_count > 0` and HCPP is not active (`!IsHcppEnabled()`), if `PresentLayers` was called on the raster thread (`!platform_runner->RunsTasksOnCurrentThread()`), the raster thread waits on a frame-completion latch ([`fml::AutoResetWaitableEvent`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/fml/synchronization/waitable_event.h#L28)) until the platform thread finishes [`onEndFrame()`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L1321-L1347) (polling a surface-attached flag that the platform thread clears before calling `FlutterEngineNotifyDestroyed` so teardown never deadlocks). Waiting on this latch is required because [`onEndFrame()`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L1321-L1347) calls `ImageReader.acquireLatestImage()`, which drops older queued images; without the wait, the raster thread could queue frame N+1 before the platform thread runs frame N's task, pairing frame N+1's pixels with frame N's [`FlutterMutatorView`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/mutatorsstack/FlutterMutatorView.java#L32) geometry. |
There was a problem hiding this comment.
This seems like a major shift in how HC platform views work.
From my understanding, the Android embedder checks at startup whether it can use HC or HCPP. If HC is picked, we merge the raster thread whenever there are platform views. Did we consider an alternative where the embedder API has an opt-in flag to merge the raster thread if there are platform views, and the Android embedder turns on this flag if it detects the current device does not support HCPP?
If we do stick with this approach, this seems like something we complete in phase 1.
| kFlutterPlatformViewMutationTypeClipRect, | ||
| kFlutterPlatformViewMutationTypeClipRoundedRect, | ||
| kFlutterPlatformViewMutationTypeTransformation, | ||
| kFlutterPlatformViewMutationTypeClipPath, |
There was a problem hiding this comment.
One concern is that an embedder might break if they receive a new mutation type they don't know how to handle. We could add a kFlutterPlatformViewMutationCapabilityClipPath capability, and if the embedder didn't opt-in, fallback to a clip rect mutation. Am I being too paranoid though? I lean towards skipping this and keeping it simple.
cc @cbracken for thoughts.
| kFlutterSemanticsRoleMain = 30, | ||
| kFlutterSemanticsRoleNavigation = 31, | ||
| kFlutterSemanticsRoleRegion = 32, | ||
| kFlutterSemanticsRoleSearch = 33, |
There was a problem hiding this comment.
Looks like this one doesn't exist:
|
|
||
| /// File descriptor representing a sync_fence / ASyncFence. | ||
| /// When presenting on Android API 34+ via SurfaceControl.Transaction, | ||
| /// the engine passes this fence to the embedder. |
There was a problem hiding this comment.
I assume this isn't used with the OpenGL renderer?
| Because split APK assets on Android are loaded via `AAssetManager` or memory-mapped (`mmap`) from disk, buffer lifetime must be explicitly managed: | ||
|
|
||
| 1. When Dart requests a library, [`FlutterRequestDartDeferredLibraryCallback`](#dart-deferred-library-loading) runs asynchronously (`void` return type), so the embedder can initiate download or extraction without blocking the engine thread. | ||
| 2. When loading completes, `FlutterDartDeferredLibrary.destruction_callback` provides the unmap trigger. In the proposed [`embedder.cc`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.cc) implementation, the engine captures this callback in a [`DeferredLibraryLifetime`](#deferred-library-memory-lifetime-management) handle held by [`fml::NonOwnedMapping`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/fml/mapping.h#L115). When the Dart VM finishes loading and drops its references to both data and instruction buffers, the destructor triggers `destruction_callback(user_data)`, which permits the Android embedder to safely `munmap()` or close the underlying [`AAsset`](https://developer.android.com/ndk/reference/group/asset). |
There was a problem hiding this comment.
When the Dart VM finishes loading and drops its references to both data and instruction buffers, ...
It's not clear to me this is correct. The Dart embedder API does not specify the lifetime of these buffers: https://github.com/dart-lang/sdk/blob/7b69ded4350b8f3b02883ccdb34ac4d48c43e71d/runtime/include/dart_api.h#L3607-L3618
The Flutter engine appears to attempt to keep these buffers alive while it is alive: https://github.com/flutter/flutter/blob/d60340594eb2ab394bd1198c2f950d066dd679c6/engine/src/flutter/runtime/dart_isolate.cc#L571
I suspect the embedder will need to keep the buffers in memory until the engine is shutdown.
| Input events processed in [`AndroidTouchProcessor.java`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java#L372-L445) provide axis and device metadata. The C-API embedder populates [`FlutterPointerEvent`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L1362-L1409) with: | ||
|
|
||
| - Device kind: [`kFlutterPointerDeviceKindTouch`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L1321), [`kFlutterPointerDeviceKindMouse`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L1320), [`kFlutterPointerDeviceKindStylus`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L1322), or [`kFlutterPointerDeviceKindTrackpad`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L1323). | ||
| - Axis values: `tilt`, `orientation`, `pressure`, and secondary scroll axes. |
There was a problem hiding this comment.
It looks like tilt and orientation need to be added to the embedder API:
|
|
||
| ### Pointer Input Events, Viewport Metrics, and System Configuration | ||
|
|
||
| #### Pointer Input Events |
There was a problem hiding this comment.
It looks like we'll need to add FlutterPointerEvent.embedder_id to the embedder API:
See:
- https://github.com/flutter/flutter/blob/1bc0d733e022a685ef50ff6abbbf0dbd534c9c44/engine/src/flutter/shell/platform/android/io/flutter/embedding/android/AndroidTouchProcessor.java#L372
- https://github.com/flutter/flutter/blob/1bc0d733e022a685ef50ff6abbbf0dbd534c9c44/engine/src/flutter/shell/platform/embedder/embedder.cc#L2895-L2896
- https://github.com/flutter/flutter/blob/1bc0d733e022a685ef50ff6abbbf0dbd534c9c44/engine/src/flutter/shell/platform/android/io/flutter/plugin/platform/PlatformViewsController.java#L732-L734
| int entrypoint_argc; | ||
| void* user_data; | ||
| const FlutterProjectArgs* project_args; | ||
| const FlutterRendererConfig* renderer_config; |
There was a problem hiding this comment.
It's weird that some of the members on FlutterProjectArgs are repeated on FlutterEngineSpawnInfo. For example, the entrypoint arguments are on both structs.
Is it possible to remove the project args and renderer config? Can the new engine reuse the parent engine's configuration? This would let us avoid weird things like attempting to use different rendering configs.
| #### Synchronization Fence Lifecycle and Ownership | ||
|
|
||
| 1. **GPU Fence Export**: When Impeller submits render passes for a backing store, it exports the GPU completion semaphore or fence into a POSIX `sync_fence` file descriptor using the [`VK_KHR_external_fence_fd`](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_external_fence_fd.html) extension (`vkGetFenceFdKHR`). | ||
| 2. **Transfer to Embedder**: The engine populates `FlutterBackingStorePresentInfo.synchronization_fence_fd` and delivers the layers to [`present_view_callback`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L2321). Ownership of the file descriptor transfers to the embedder. |
There was a problem hiding this comment.
From my understanding, the reason the embedder API's Skia Vulkan renderer config didn't need this is because the engine performed the host sync before invoking the present callback:
Can/should we reuse this approach for HCPP?
If we do decide to keep this fence, I suspect we should make this an opt-in feature. Perhaps something like FlutterVulkanRendererConfig.enable_manual_present_host_sync.
| size_t struct_size; | ||
| const char* entrypoint; | ||
| const char* library_uri; | ||
| const char* initial_route; |
There was a problem hiding this comment.
Should we add the initial route to FlutterProjectArgs instead? It looks like you can use set the initial route without multi-engine: https://docs.flutter.dev/add-to-app/android/add-flutter-fragment#run-flutter-with-a-specified-initial-route
val flutterFragment = FlutterFragment.withNewEngine()
.initialRoute("myInitialRoute/")
.build()EDIT: Never mind this suggestion. Android/iOS set the initial engine's initial route by sending a message immediately at startup (link). This is gross, but we can fix that later.
| FlutterEngineResult FlutterEngineGetCallbackInformation( | ||
| int64_t handle, | ||
| FlutterDartCallbackInformation* info_out); | ||
| ``` |
There was a problem hiding this comment.
I would add more information to this section. I wasn't familiar with the callback cache and how one uses it. Consider linking to this blog post: https://flutter.dev/blog/executing-dart-in-the-background-with-flutter-plugins-and-geofencing
| void* user_data; | ||
| FlutterImageGeneratorGetInfoCallback get_info; | ||
| FlutterImageGeneratorGetPixelsCallback get_pixels; | ||
| VoidCallback destruction_callback; |
There was a problem hiding this comment.
Could we document which threads each of these callbacks are invoked from?
| typedef enum { | ||
| kFlutterImageGeneratorColorTypeRGBA8888, | ||
| kFlutterImageGeneratorColorTypeBGRA8888, | ||
| } FlutterImageGeneratorColorType; |
There was a problem hiding this comment.
Could you expand how you picked the name "image generator" instead of "image decoder"? The latter seems more common.
| size_t struct_size; | ||
| void* user_data; | ||
| FlutterImageGeneratorGetInfoCallback get_info; | ||
| FlutterImageGeneratorGetPixelsCallback get_pixels; |
There was a problem hiding this comment.
Could you explain how an image with multiple fames (eg. GIFs) is decoded? Does the engine call get_pixels for each frame or something? Or does the engine create a generator per frame?
| uint32_t width; | ||
| uint32_t height; | ||
| FlutterImageGeneratorColorType color_type; | ||
| FlutterImageGeneratorAlphaType alpha_type; |
There was a problem hiding this comment.
Should we add a FlutterImageGeneratorColorSpace enum that today only has a kFlutterImageGeneratorColorSpaceSRGB value? This would make it easier for us to add wide gamut support in the future.
| typedef struct { | ||
| size_t struct_size; | ||
| FlutterImageGeneratorFactoryCallback create_generator; | ||
| int32_t priority; |
There was a problem hiding this comment.
Could you add docs on this? I assume the engine picks these in descending order, 0 is the engine's built-in priority, -1 means prefer engine built-in implementation, etc?
Android Embedder Migration RFC
Tracking issue: flutter/flutter#193309
Pre-launch Checklist
design doc.dart run bin/assign_rfc_number.dartlocally when instructed).