Skip to content

Android Embedder Migration to Embedder API - #24

Open
mboetger wants to merge 43 commits into
flutter:mainfrom
mboetger:rfc-410-android-embedder-c-api
Open

mboetger wants to merge 43 commits into
flutter:mainfrom
mboetger:rfc-410-android-embedder-c-api

Conversation

@mboetger

@mboetger mboetger commented Sep 22, 2026 •

Copy link
Copy Markdown

Android Embedder Migration RFC

Tracking issue: flutter/flutter#193309

Pre-launch Checklist

  • I read [RFC 000.0001: Taxonomy] and followed the file and path naming and metadata standards.
  • I read [RFC 000.0002: Process] and confirmed this proposal meets the threshold for a full RFC.
  • I read and agree to the [Code of Conduct].
  • I signed the [CLA].
  • I have linked an issue from flutter/flutter with the label design doc.
  • All existing and new tests are passing.
  • I have enabled "Allow edits from maintainers" on this PR so the bot can automatically assign an RFC number (or I will run dart run bin/assign_rfc_number.dart locally when instructed).

@mboetger mboetger changed the title Android Embedder Migration Android Embedder Migration to Embedder API Sep 24, 2026
@mboetger
mboetger requested a review from gaaclarke September 24, 2026 21:22
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated

**What the Android callback does.** It runs the checks Android runs today in [`GetActualRenderingAPIForImpeller`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.cc#L79-L120): known emulators, Huawei devices (`ro.com.google.clientidbase`), MediaTek devices with `ro.vendor.build.version.sdk < 32`, and the `ro.product.board` deny-list ([`IsKnownBadSOC`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.cc#L68-L77)). If one matches, it returns `kOpenGL` without loading Vulkan. Otherwise it loads `libvulkan.so`, creates the `VkInstance`, `VkDevice` and `VkQueue`, and returns `kVulkan`. `AndroidContextDynamicImpeller` does the same work on the raster thread today, so the UI thread never waits for it, and nothing has to start earlier on a `FlutterLoader` executor. A process-wide singleton (`VulkanDeviceOwner`) holds the device, so the callbacks of later [`FlutterEngine`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java#L90) instances return the same handles without probing again.

**When the engine rejects a configuration.** Android falls back to OpenGL ES today when Vulkan context creation fails or the driver is on Impeller's known-bad list ([`AndroidContextVKImpeller`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/android_context_vk_impeller.cc#L65-L69)). In the embedder path, [`impeller::ContextVK::Create`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc#L176-L188) still checks the embedder's device, but a failure leaves the engine without a renderer. For a `kDeferred` configuration the engine keeps Android's fallback:

@loic-sharma loic-sharma Oct 20, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of having this negotiation back-and-forth between the embedder and engine, could we move all checks to the embedder? Android would be fully responsible for picking between Vulkan and OpenGL. The engine would error if Android picks Vulkan but a Vulkan context cannot be created.

I think this model would let us have a simplified deferred renderer config:

Simplified 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;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved the checks to the Embedder and highlighted that code be moved out of Impeller.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, I don't think we want the engine to error out if Android picks Vulkan but then fails to create a context. We want it to fall back to openGLES in that case, right? Vulkan context creation can fail for reasons that are hard to predict ahead of time, like a bad driver update or something.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we want it to fall back.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So here's the problem. If we move ALL the checks into flutter/common and have the embedder do this - then some embedders won't get the benefit of the Vulkan capability checks. If we only move some of the checks to embedder we still need a way of handling the fall back to OpenGL (and another embedder API). WDYT @loic-sharma

Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated

@gaaclarke gaaclarke left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't make it through the whole doc yet. I have to put a pin in it for now and come back so here's some general feedback to go with my notes so far.

I think there is a lot of awesome work that went into this. This scares the hell out of me. I'm glad there is a section that lays out the implementation plan since that is my biggest concern, "how do we implement this huge change carefully and iteratively". I'm looking forward to that. Also I'm glad you've included a testing section since we are by virtue of expanding embedder.h, signing ourselves up for maintaining a new API, which should be tested.

I do find this document hard to navigate. I don't have a good hierarchy of it in my head (maybe this is the failings of my markdown preview). Maybe it's big enough there should be something like a TOC at the beginning?

I think leading with diagrams would be helpful. I often found myself reading code in prose form, trying to follow along and then finding the diagram and not liking that I had just wasted time trying to juggle the picture in my mind. I don't really find "code in prose" much help. Pseudocode or sequence diagrams are better.

When writing newspaper articles what they try to do is lead with the important information first so that the most important information is conveyed first to accommodate someone being able to abort reading a section and still coming away with a general understanding of the section. The concept should be communicated first and each line should provide further details. I think that sort of ordering can help make this easier to digest. Someone shouldn't have to read a whole section to get the gist.

The diagrams are following an ad hoc language. I'd find it more helpful if you use standardized diagrams (like UML class, UML sequence).

I have a lot of meetings tomorrow but I'll try to come back to this ASAP.

(note to self left off at Alternative Considered: Splitting Startup into Separate Shell and Isolate Launch APIs)

Similarly, [`SurfaceChanged`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc#L285-L292) triggers [`NotifyChanged`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/platform_view_android.cc#L236-L248) to resize the onscreen surface, and [`SurfaceDestroyed`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/platform_view_android_jni_impl.cc#L294-L296) triggers [`NotifyDestroyed`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/platform_view_android.cc#L221-L234) to tear down the raster surface context synchronously.

```mermaid
flowchart TD

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This chart would be more clear if it used standardized iconography like a UML class diagram.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly it made it less readable to me.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense that you'd author it the way that was most comfortable for you. Being less readable is likely a familiarity thing though that will adjust over time. It's nice to have a standard instead of coming into each doc and trying to decipher a new design language =)

Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
sequenceDiagram
autonumber
participant UI as "Android UI Thread"
participant Emb as "embedder.cc & PlatformViewEmbedder"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: It would be helpful if standard sequence diagram iconography was used here too (UML sequence diagram). In particular participants not being associated with instances is a bit of a twist, but abstract participants for threads is clear.

Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
} FlutterEngineSpawnInfo;

FLUTTER_EXPORT
FlutterEngineResult FlutterEngineSpawn(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have misgivings about making "Spawn" a public API. It's not very friendly. What we've done previously is made FlutterEngineGroup the public API to this concept, which uses spawn under the covers. I think that's a better approach.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THe android embedder does provide FlutterEngineGroup as a public API - I guess we could match the same API and just forward to the embedder API. Will take a look

Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
| **`AndroidHardwareBuffer` and `AndroidVulkanExternalTexture`** | [`//flutter/common/graphics`](https://github.com/flutter/flutter/tree/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/common/graphics) ([`texture.h`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/common/graphics/texture.h), [`gl_context_switch.h`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/common/graphics/gl_context_switch.h)) | - [`FlutterVulkanExternalTexture`](#external-textures-zero-copy-vulkan-and-opengl-es) (`ycbcr_conversion_info`), [`FlutterVulkanYcbcrConversionInfo`](#external-textures-zero-copy-vulkan-and-opengl-es), and [`FlutterVulkanTextureFrameCallback`](#external-textures-zero-copy-vulkan-and-opengl-es)<br>- [`FlutterOpenGLExternalTexture`](#external-textures-zero-copy-vulkan-and-opengl-es) (`uv_transform`) and [`FlutterOpenGLTextureFrameCallback`](#external-textures-zero-copy-vulkan-and-opengl-es)<br>- [`FlutterEngineMarkExternalTextureFrameAvailable`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L3262-L3264) | Eliminates subclassing of internal C++ `flutter::Texture`. Supports zero-copy `AHardwareBuffer` import into `VkImage` (Vulkan) and `EGLImageKHR` (OpenGL ES) as well as UV transformation matrices for `SurfaceTexture` camera and video streams. |
| **`AndroidSemanticsMapper` and `AndroidWindowMetricsMapper`** | [`//flutter/flow`](https://github.com/flutter/flutter/tree/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/flow) ([`flow/semantics/semantics_node.h`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/lib/ui/semantics/semantics_node.h))<br>[`//flutter/lib/ui/window/viewport_metrics.h`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/lib/ui/window/viewport_metrics.h) | - [`FlutterSemanticsNode2`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L1730-L1783) in-place extension (`FlutterSemanticsRole`, `hit_test_child_count`)<br>- `FlutterWindowMetricsEvent` in-place extension (`FlutterDisplayFeature`, `FlutterEdgeInsets`, `FlutterDisplayCornerRadii`) | Delivers complete accessibility semantics metadata to Java `AccessibilityBridge` and foldable/edge-to-edge metrics to the engine without coupling to internal Flow or `lib/ui` headers. |
| **[`APKAssetProvider`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/apk_asset_provider.h#L26)** | [`//flutter/assets`](https://github.com/flutter/flutter/tree/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/assets) ([`asset_resolver.h`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/assets/asset_resolver.h)) | - `FlutterProjectArgs.custom_asset_resolvers`<br>- `FlutterCustomAssetResolver` & `FlutterAssetMapping` | Decouples APK asset loading from internal `flutter::AssetResolver` C++ virtual tables while preserving zero-copy NDK `AAssetManager` memory mapping across hot restarts and split APK installs. |
| **`AndroidVsyncWaiter`** | [`//flutter/shell/common`](https://github.com/flutter/flutter/tree/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/common) ([`vsync_waiter.h`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/common/vsync_w

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to maintain an alternate benchmark for legacy? Can't we just watch the existing benchmark as we start to transition?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's behind a flag - so we need to be able to test before switching the flag on by default.

@mboetger

mboetger commented Oct 21, 2026 •

Copy link
Copy Markdown
Author

@gaaclarke - I took a massive hatchet to the context provided on how things work and why and left only what was necessary. I think this makes the doc more manageable. PTAL.

Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md Outdated
> - **Embedder API additions (`embedder.h`)**: Adds `kDeferred` (`FlutterDeferredRendererConfig`) and `FlutterRendererConfigResolveCallback` to [`FlutterRendererConfig`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder.h#L1055).
> - **Behavioral changes**:
> - [`PlatformViewEmbedder`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/platform_view_embedder.h#L36), [`EmbedderSurfaceVulkanImpeller`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder_surface_vulkan_impeller.h), and [`EmbedderSurfaceGLImpeller`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.h) move [`impeller::Context`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/impeller/renderer/context.h#L70) creation from their UI-thread constructors into `PlatformViewEmbedder::SetupImpellerContext()` on the raster thread, and `PlatformViewEmbedder::NotifyCreated()` queues surface creation on the FIFO raster task runner instead of blocking the UI thread on a latch.
> - `AndroidSurfaceControl` performs all Android Vulkan eligibility checks inside `resolve_callback` before creating a `VkDevice` and returns either `kVulkan` or `kOpenGL` in a single call. To make those checks available without depending on `//flutter/impeller`, the Android GPU blocklist in [`DriverInfoVK::IsKnownBadDriver()`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/impeller/renderer/backend/vulkan/driver_info_vk.cc#L379-L431) and the required Android Vulkan capability checks in [`CapabilitiesVK`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.cc#L98-L529) are moved into `AndroidSurfaceControl` alongside the existing Android system-property and SoC checks from [`android_context_dynamic_impeller.cc`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.cc#L23-L119).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is AndroidSurfaceControl the best place for the bad driver checks when considering other embedders?

The checks look mostly android-focused, but some of the checks refer to hardware like PowerVR and Adreno, which can exist on non-Android embedded systems. It would be unfortunate if a new custom embedder had to start from scratch or duplicate the checks because we moved them to an android embedder specific location.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've gone back and forth a couple times with @loic-sharma and @gaaclarke about this - it simplifies the Embedder API such that we don't need to add a "Driver Check" API. But I agree other embedders will lose out. Perhaps moving it to the common library makes more sense.


**What the Android callback does.** It runs the checks Android runs today in [`GetActualRenderingAPIForImpeller`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.cc#L79-L120): known emulators, Huawei devices (`ro.com.google.clientidbase`), MediaTek devices with `ro.vendor.build.version.sdk < 32`, and the `ro.product.board` deny-list ([`IsKnownBadSOC`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.cc#L68-L77)). If one matches, it returns `kOpenGL` without loading Vulkan. Otherwise it loads `libvulkan.so`, creates the `VkInstance`, `VkDevice` and `VkQueue`, and returns `kVulkan`. `AndroidContextDynamicImpeller` does the same work on the raster thread today, so the UI thread never waits for it, and nothing has to start earlier on a `FlutterLoader` executor. A process-wide singleton (`VulkanDeviceOwner`) holds the device, so the callbacks of later [`FlutterEngine`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java#L90) instances return the same handles without probing again.

**When the engine rejects a configuration.** Android falls back to OpenGL ES today when Vulkan context creation fails or the driver is on Impeller's known-bad list ([`AndroidContextVKImpeller`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/android_context_vk_impeller.cc#L65-L69)). In the embedder path, [`impeller::ContextVK::Create`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/impeller/renderer/backend/vulkan/context_vk.cc#L176-L188) still checks the embedder's device, but a failure leaves the engine without a renderer. For a `kDeferred` configuration the engine keeps Android's fallback:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, I don't think we want the engine to error out if Android picks Vulkan but then fails to create a context. We want it to fall back to openGLES in that case, right? Vulkan context creation can fail for reasons that are hard to predict ahead of time, like a bad driver update or something.

/// 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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we conflating a caching policy with information about topology here? It's weird to me that a field called is_root_layer would ever be false for the root layer. Do we need a different channel to communicate whether a backingstore should be cached or not? Maybe an is_cacheable field on FlutterBackingStore, or swapchain info

Comment thread rfc/410.0000-flutter-android-embedder-c-api-migration.md
/// When true, the engine coalesces all non-root Flutter slices composited
/// above platform views into a single top-most overlay `FlutterBackingStore`
/// per view per frame, clipping out the bounding rectangles of any higher-Z
/// platform views using `DlClipOp::kDifference`.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DlClipOp is engine internals. I assume any higher-Z platform views would have FlutterPlatformViewMutations?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This happens in the engine internals - embedder_external_view_embedder.cc.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but the embedder API is externally-facing. The embedder API's docs should prefer the external names for concepts over the internal names.

/// render threads (`FlutterCustomTaskRunners.platform_task_runner` and
/// `render_task_runner` must be NULL so the engine binds `fml::MessageLoop`
/// to the calling platform thread and creates the raster thread).
bool merge_raster_thread_on_platform_views;

@loic-sharma loic-sharma Oct 22, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should make this an engine flag instead, similar other engine flags like --enable-hcpp-and-surface-control.

This would let us remove raster thread merging from the embedder API if/when we remove HC or we update HC to use latches instead of thread merging.

///
/// Requires engine-managed message loop task queues for the platform and
/// render threads (`FlutterCustomTaskRunners.platform_task_runner` and
/// `render_task_runner` must be NULL so the engine binds `fml::MessageLoop`

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe to opt-in to merged threads you need to pass a non-NULL FlutterCustomTaskRunners.platform_task_runner (which matches the ui_task_runner). If so, can we remove this requirement?

@knopp

knopp commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Is raster thread merging absolutely necessary? This is one of the most convoluted parts of the engine and it's only used by the android embedder right now. "traditionally" platform views were mutated in preroll, so the thread merging was in a way necessary, but the embedder api records mutations so we only need to synchronize platform and raster thread at a single spot (schedule mutation on platform thread and latch the raster thread). Both macOS and iOS embedders do this. Does this not work for android embedder? I'd very much like to not expose dynamic thread merging in the embedder API.

@gaaclarke gaaclarke left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Matt, the doc is looking way better layout in the presentation. Here are a few questions that came to my mind during it:

1) What about SDFs?

I recommend we try to ignore this as long as possible. Ideally in the future, everyone just uses the SDF rendering. If a customer asks for it we can consider engine flags.

2) What about vulkan validation layers?

What does the user story look like for the embedder that wants to enable vulkan validation layers? Do we care to support that?

3) What about kMergeAfterLaunch?

How do we want to surface its in the embedder, if at all? Right now flutter engine groups don't work with it. So we need to probably opt out of it, or allow it to be selectable if we want to expose flutter engine group apis.

> - [`PlatformViewEmbedder`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/platform_view_embedder.h#L36), [`EmbedderSurfaceVulkanImpeller`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder_surface_vulkan_impeller.h), and [`EmbedderSurfaceGLImpeller`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/embedder/embedder_surface_gl_impeller.h) move [`impeller::Context`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/impeller/renderer/context.h#L70) creation from their UI-thread constructors into `PlatformViewEmbedder::SetupImpellerContext()` on the raster thread.
> - `PlatformViewEmbedder::NotifyCreated()` queues surface creation on the FIFO raster task runner instead of blocking the UI thread on a latch.
> - `AndroidSurfaceControl` performs all Android Vulkan eligibility checks inside `resolve_callback` before creating a `VkDevice` to return `kVulkan` or `kOpenGL` in a single call.
> - The `VkPhysicalDeviceProperties` GPU parser and known-bad-driver check in [`DriverInfoVK::IsKnownBadDriver()`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/impeller/renderer/backend/vulkan/driver_info_vk.cc#L379-L431) move into [`//flutter/common`](https://github.com/flutter/flutter/tree/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/common) (so `//flutter/impeller`, `AndroidSurfaceControl`, and other embedders share a single implementation), while the required Android Vulkan capability checks in [`CapabilitiesVK`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/impeller/renderer/backend/vulkan/capabilities_vk.cc#L98-L529) move into `AndroidSurfaceControl` alongside the Android system-property and SoC checks from [`android_context_dynamic_impeller.cc`](https://github.com/flutter/flutter/blob/4762dec4fc9072cd9269814ef36364f35a9a66dc/engine/src/flutter/shell/platform/android/android_context_dynamic_impeller.cc#L23-L119).

@loic-sharma loic-sharma Oct 22, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we put this in //flutter/shell/platform/common? That's where we generally put code that's shared across embedders, instead of //flutter/common.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

rfc-ready RFC is ready for review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants