Skip to content

[Embedder] Impeller support for kFlutterBackingStoreTypeVulkan backing stores #192816

Closed as duplicate

Description

@jwinarske

Use case

Linux embedders that own KMS directly: no Wayland or X compositor, the embedder holds DRM master, renders into dma-bufs and scans them out itself. Automotive IVI, kiosk, signage.

Such an embedder uses the compositor API rather than the root surface for one reason: layers. present_layers hands it each layer separately, so Flutter UI and platform-view content can go to separate hardware planes, composited by the display controller instead of the GPU. That buys:

  • a camera or video feed reaching the panel without a GPU round trip, as YUV, on
    an overlay plane;
  • protected/secure planes for content that must not transit the GPU;
  • fill rate saved on every frame the display controller composites for free.

Vulkan rather than GL because that is where the drivers are going, because explicit sync lets the display engine wait on a dma-buf fence (IN_FENCE_FD) instead of blocking the raster thread, and because Vulkan-native platform-view producers can share images without a GL interop copy.

Today that combination does not work. kFlutterBackingStoreTypeVulkan has no Impeller path, so these embedders are Skia-only. With Skia being retired, the class has no forward path: the renderer it must move to cannot use the presentation API it depends on.

Current behavior

shell/platform/embedder/embedder.cc, CreateEmbedderRenderTarget:

case kFlutterBackingStoreTypeVulkan: {
  if (enable_impeller) {
    FML_LOG(ERROR) << "Unimplemented";
    break;
  } else {
    auto skia_surface = MakeSkSurfaceFromBackingStore(
        context, config, &backing_store.vulkan);
    render_target = MakeRenderTargetFromSkSurface(
        backing_store, std::move(skia_surface), collect_callback.Release());
    break;
  }
}

Metal calls MakeRenderTargetFromBackingStoreImpeller. OpenGL calls it for
kFlutterOpenGLTargetTypeFramebuffer. Vulkan has neither.

With --enable-impeller, every frame fails and nothing is presented:

[ERROR:embedder.cc(1519)] Unimplemented
[ERROR:embedder.cc(1532)] Could not create a surface from an embedder provided render target.

Quiet from the embedder's side: those come from the engine's logger, so an embedder with its own logging shows nothing wrong, keeps running, and leaves a blank display.

Related: #151670 covers the GL surface target type, a different case in the same switch.

Proposal

Implement MakeRenderTargetFromBackingStoreImpeller for FlutterVulkanBackingStore, matching the existing GL and Metal overloads, and call it from the kFlutterBackingStoreTypeVulkan case.

The wrapping already exists in the tree. GPUSurfaceVulkanImpeller::AcquireFrame does exactly this for the embedder's root surface image (shell/gpu/gpu_surface_vulkan_impeller.cc):

impeller::vk::Image vk_image = impeller::vk::Image(
    reinterpret_cast<VkImage>(flutter_image.image));
impeller::TextureDescriptor desc;          // kDevicePrivate, kRenderTarget
...createImageViewUnique(view_info);
auto wrapped = std::make_shared<WrappedTextureSourceVK>(
    vk_image, std::move(image_view), desc);
auto surface = impeller::SurfaceVK::WrapSwapchainImage(transients_, wrapped, ...);
impeller::RenderTarget render_target = surface->GetRenderTarget();

FlutterVulkanBackingStore carries the same two fields that path consumes: a VkImage handle and a VkFormat. Suggested shape is to factor that sequence out of AcquireFrame into a helper taking (image, format, size) returning an impeller::RenderTarget, then call it from both places. No new Impeller capability required — the difference between the paths is where the image comes from, not what is done with it.

Secondary: WSI requirement on an embedder-provided instance

CapabilitiesVK::GetEnabledInstanceExtensions requires VK_KHR_surface plus one platform WSI extension, and the device check requires VK_KHR_swapchain, even when the context is built around an embedder-provided image and never creates a VkSurfaceKHR or a swapchain.

A KMS-direct embedder has no window system to declare, and declares one anyway:
VK_KHR_portability_enumeration is the only entry on that list implying no window system, so that is what gets used — plainly not what it means. VK_KHR_display, the one extension that would suit a KMS embedder, is not on the list, and enabling it makes the loader take the display so the embedder's own drmSetMaster fails with EPERM.

Suggestion: skip the swapchain-related capability checks when the context will not own a swapchain. Can be filed separately if preferred.

Workaround, and what it costs

Offer no FlutterCompositor when Impeller is active and present through the root surface (get_next_image / present_image). Measured on V3D 4.2 (v3dv) with a vc4 display at 1280x1440:

renderer path fps RSS
Skia compositor, Vulkan backing stores 42.0 99.4 MB
Impeller root surface 59.7 93.2 MB

Impeller is faster and slightly smaller, so the motivation is not theoretical. But the root-surface path has no layer stack, so platform-view layers cannot reach a hardware plane — the reason the compositor path exists. It also requires declaring the three WSI extensions described above, none of which the embedder uses.

Downstream reference

toyota-connected/ivi-homescreen#582 — same analysis, the workaround as landed, and the measurements.

Flutter version

Engine 77e2e94772b6eb43759e34ed1ad7da4674e19cab.

Activity

  1. jwinarske commented on Sep 15, 2026

    @jwinarske
    ContributorAuthor

    Duplicate of #187525, which is the same gap (CreateEmbedderRenderTarget has no Impeller path for kFlutterBackingStoreTypeVulkan) and already triaged P2.

    Moved the downstream evidence there: measurements, the root-surface workaround and what it gives up, and a note that the wrapping already exists in GPUSurfaceVulkanImpeller::AcquireFrame. Tracking downstream at toyota-connected/ivi-homescreen#582.

  2. github-actions commented on Oct 1, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  3. locked as resolved and limited conversation to collaborators on Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    r: duplicateIssue is closed as a duplicate of an existing issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions