Skip to content

Use CAMetalLayer while preserving platform view compatibility on iOS - #192794

Open
MTtankkeo wants to merge 2 commits into
flutter:masterfrom
MTtankkeo:ca-metal-layer
Open

MTtankkeo wants to merge 2 commits into
flutter:masterfrom
MTtankkeo:ca-metal-layer

Conversation

@MTtankkeo

@MTtankkeo MTtankkeo commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Description

This PR removes an additional one-frame presentation delay on iOS while preserving Flutter's existing platform-view compatibility.

FlutterMetalLayer currently subclasses CALayer and emulates the CAMetalLayer interface. Flutter renders into IOSurface-backed Metal textures and presents each completed frame by assigning its IOSurface to CALayer.contents.

While this supports the Core Animation transaction used for platform-view composition, the CALayer.contents update introduces an additional display interval before the Flutter frame becomes visible.

This PR changes FlutterMetalLayer to subclass CAMetalLayer and uses a native CAMetalDrawable for final presentation, while retaining Flutter's IOSurface-backed rendering path.

The updated presentation flow is:

  1. Flutter renders into an IOSurface-backed texture as before.
  2. During flutterPrepareForPresent:, the layer acquires a native CAMetalDrawable.
  3. A GPU blit from the completed IOSurface-backed texture to the native drawable is encoded on the same Metal command buffer.
  4. The drawable is presented at the existing presentation point.

This allows the Flutter frame to use the native CAMetalLayer presentation path without giving up the IOSurface lifecycle required by the existing platform-view composition architecture.

Encoding the copy on the same command buffer preserves GPU ordering without requiring a CPU wait. The native drawable is also acquired during preparation rather than at the beginning of rendering, keeping its lifetime relatively short.

This change additionally removes the need for FlutterMetalLayer to:

  • duplicate the CAMetalLayer interface from a CALayer subclass;
  • override isKindOfClass: to behave as a CAMetalLayer;
  • update CALayer.contents asynchronously on the main thread; and
  • maintain a separate CADisplayLink workaround for ProMotion presentation.

If a resize causes the Flutter render target and native drawable to have different dimensions or pixel formats, the frame is not copied.

Platform-view compatibility

The recording below demonstrates that Flutter and UIKit content continue to be composed and updated together using the new presentation path.

video.mp4

It covers platform-view interaction, animation, creation, removal, recreation, and periods without continuous Flutter animation.

This recording is intended to demonstrate platform-view compatibility rather than measure presentation latency.

Wide-gamut compatibility

A real CAMetalLayer validates its pixel format against the capabilities of its Metal device. Extended-range formats are therefore enabled only when the device supports the required Apple GPU family.

The related tests have been updated to account for the default color-space behavior of a real CAMetalLayer and simulator GPUs that do not support extended-range formats.

Investigation

The latency measurements, reduction process, alternative presentation experiments, and physical-device testing that led to this implementation are documented in #191543.

This PR extracts only the final presentation-side change from that investigation. The unrelated pointer-delivery, VSync, autorelease-pool, and experimental presentation changes explored there are not included.

Related issues

Related to #110431 and #175790.

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant in-code documentation (doc comments with ///).
  • If this PR introduces a new feature or capability, I created and linked a website documentation issue or PR in [flutter/website] (or verified none is needed).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • I followed the [breaking change policy] and added [Data Driven Fixes] where supported.
  • All existing and new tests are passing.

@MTtankkeo
MTtankkeo requested a review from a team as a code owner September 15, 2026 06:30
@github-actions github-actions Bot added platform-ios iOS applications specifically engine flutter/engine related. See also e: labels. team-ios Owned by iOS platform team labels Sep 15, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request refactors FlutterMetalLayer to inherit directly from CAMetalLayer instead of CALayer, removing custom display link logic and introducing a blit copy from the IOSurface-backed Flutter texture to the native drawable. It also restricts wide gamut support to devices supporting MTLGPUFamilyApple3. Feedback highlights a potential thread-blocking issue when acquiring drawables during a resize, suggesting a size and format check beforehand, and a potential crash on iOS 12 due to the use of supportsFamily:, recommending a selector check.

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

Labels

engine flutter/engine related. See also e: labels. platform-ios iOS applications specifically team-ios Owned by iOS platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant