Skip to content

[linux] Add an Impeller Vulkan backend for the GTK embedder - #189584

Open
sero583 wants to merge 6 commits into
flutter:masterfrom
sero583:impeller-vulkan-linux
Open

sero583 wants to merge 6 commits into
flutter:masterfrom
sero583:impeller-vulkan-linux

Conversation

@sero583

@sero583 sero583 commented Jul 16, 2026 •

Copy link
Copy Markdown
Contributor

Adds an opt-in Vulkan rendering path to the Linux GTK embedder: Impeller renders with its existing Vulkan backend through a KHR swapchain, presented into a synchronized wl_subsurface on Wayland or the toplevel window on X11. This is the Linux half of the desktop Impeller Vulkan effort (#181711); the Windows half is presented through DirectComposition in a separate PR because knopp's review of #183382 asked Windows off the KHR-swapchain path, while KHR presentation remains the right fit on Linux where the Mesa/driver WSI is the native presentation mechanism.

Activation uses the engine switch mechanism, matching the Windows backend and in line with the review guidance to gate the backend behind flags rather than public API: --enable-impeller together with --impeller-backend=vulkan. Without the switches, or when no capable Vulkan device exists, nothing changes: OpenGL remains the default and the fallback, and the existing software renderer stays opt-in through the FLUTTER_LINUX_RENDERER environment variable. When Vulkan is active, EGL is not initialized at all.

How it works

  • FlVulkanManager owns the VkInstance, VkDevice, VkQueue, and the presentation surface. On Wayland it creates a child wl_surface, makes it a subsurface of the toplevel positioned at the content area (below the header bar), sets an empty input region so events pass through to GTK, and marks it opaque. On X11 it presents to the toplevel window directly.
  • Impeller manages the KHR swapchain end to end (SurfaceContextVK); the engine renders into the acquired swapchain images and presents through the Vulkan queue.
  • FlEngine selects the renderer from the engine switches and skips EGL / OpenGL manager creation when Vulkan is chosen, so a Vulkan-only session does not load libepoxy.
  • FlViewRendererVulkan is a new FlViewRenderer subclass alongside FlViewRendererOpenGL and FlViewRendererSoftware. FlView instantiates it for the Vulkan renderer type. Unlike the OpenGL/software renderers it does not composite layers (Impeller owns presentation through the swapchain); it creates the FlVulkanManager on realize, keeps the subsurface positioned, and drives parent-surface commits (see below).

Synchronized subsurface (Wayland)

The Vulkan subsurface runs in synchronized mode (wl_subsurface_set_sync) so its buffer is applied atomically with the parent surface's commit. This keeps the Vulkan content and the window geometry in lockstep during resizes, avoiding the tearing a desynchronized subsurface produces when the parent resizes before the child presents its new-size buffer. Because a synchronized subsurface only becomes visible when the parent commits, and GTK does not redraw the toplevel on its own while only the Vulkan content changes, FlViewRendererVulkan keeps a frame-clock tick callback registered; it queues a draw each frame, and the renderer's draw handler paints the background (committing the parent), which flushes the subsurface's latest frame. The tick callback is removed on dispose.

Embedder plumbing

The Vulkan renderer config gains an optional surface field (FlutterVulkanSurfaceHandle). When the embedder supplies a surface, Impeller drives the swapchain and the get_next_image / present_image callbacks are optional; when it does not, the existing embedder-managed image callbacks are used unchanged. The field is struct_size compatible, so existing embedders are unaffected. The legacy vulkan/ support library gains the Xlib and Wayland surface procs and the Linux native surface wrapper.

Startup fixes on real hardware

@ales-drnz built this branch on NVIDIA + KDE Plasma Wayland and found the backend did not start there at all. His fixes are in this PR as his own commits:

  • realize_cb called fl_opengl_manager_clear_current unconditionally, but there is no FlOpenGLManager when the renderer is Vulkan, so every launch hit a SIGSEGV before the first frame.
  • The first frame was announced only from the GTK draw handler, which cannot run before the runner shows the window, while the runner shows the window on that announcement. The Vulkan path now announces from FlutterEngineSetNextFrameCallback through a new optional FlRenderable.notify_first_frame, which fires once the frame is in the swapchain regardless of window visibility.
  • wl_subsurface.set_position is double-buffered state of the parent surface, so the position is applied by committing the parent instead of the child. That also removes a commit issued on the child from the platform thread while the WSI commits the same surface from the raster thread.
  • A stray executable bit on vulkan_native_surface_linux.{cc,h}.

Not in this PR

  • Any public rendering-backend introspection API. The Windows and Linux PoC in [Impeller] Add Vulkan rendering backend for Linux and Windows desktops #183382 exposed getters (FlRendererType, fl_engine_get_rendering_backend); those are removed per the review guidance, and selection is internal via switches only.
  • flutter_tools changes: activation is manual until the backend matures.
  • Fractional scale support for the subsurface. On a fractionally scaled display the subsurface is drawn too large. The fix needs wp_viewport, which means vendoring a Wayland protocol into the engine, and there is no third-party location for those files today (build_overrides/wayland.gni sets wayland_gn_dir = ""). Deferred until it is clear where they belong; wl_surface_set_buffer_scale is not an alternative here, because the swapchain starts at a fallback size rather than at the widget allocation times the scale.

Testing

New unit tests in flutter_linux_unittests (hardware-dependent cases skip cleanly without Vulkan):

  • FlVulkanManagerTest (6): availability, create-or-null without a window, shutdown/wait-idle, deferred-deletion null safety, memory properties, surface release.

  • VulkanNativeSurfaceLinuxTest (7): X11 and Wayland surface validity and extension-name checks, zero/negative dimension rejection.

  • FlEngineTest.RendererTypeVulkan plus the existing software/OpenGL/default/unknown selection tests: the Vulkan backend is selected only through the switches and otherwise falls back to OpenGL. RendererTypeVulkanEnvVarFallsBackToOpenGL covers the migration path: FLUTTER_LINUX_RENDERER=vulkan is not a selector, warns with a pointer to the switches, and falls back to OpenGL.

  • FlViewTest.NotifyFirstFrameEmitsFirstFrameSignal: the first-frame signal reaches the runner through FlRenderable without the widget being realized, and is not re-emitted for later frames.

The startup fixes were found and verified on NVIDIA + KDE Plasma Wayland; the full flutter_linux_unittests suite passes with them.

Manual verification on AMD Radeon RX 6750 XT: the backend was exercised on a real Wayland compositor (WSLg, which exposes VK_KHR_wayland_surface over both the D3D12 translation layer and llvmpipe). The switch selection picks Vulkan, the KHR swapchain initializes on the subsurface, and the app renders continuously. The synchronized-subsurface tick driver kept the parent committing every frame on this WSLg setup, but that does not hold on real desktop hardware: on NVIDIA + KDE at 144Hz the parent was measured at 48 commits/s (#189584 (comment)), the GDK frame clock does not run at display rate there. Frame pacing needs a proper vsync source, that is a follow up (see the comment thread).

Part of #181711. This PR has no build-time dependency on the Impeller hardening PR (#189580) and can be reviewed in parallel; landing the hardening PR first is recommended because its KHR swapchain robustness fixes (fence re-signal on failed acquire, zero-extent minimized-window guard) directly improve the runtime behavior of the swapchain path this PR enables. Design doc: https://flutter.dev/go/impeller-backend-desktop

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance.

Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the gemini-code-assist bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.

Selects the Vulkan backend via engine switches, presents through a KHR
swapchain into a synchronized wl_subsurface (Wayland) or the toplevel
window (X11), and integrates with the FlViewRenderer architecture via a
new FlViewRendererVulkan. Removes the public rendering-backend getter.
@github-actions github-actions Bot added engine flutter/engine related. See also e: labels. platform-linux Building on or for Linux specifically a: desktop Running on desktop team-linux Owned by the Linux platform team labels Jul 16, 2026
@ales-drnz

ales-drnz commented Aug 6, 2026 •

Copy link
Copy Markdown

I built this branch to test frame pacing on top of it. Five findings on NVIDIA + KDE Wayland, all in the Vulkan path only (the OpenGL path is unaffected), plus where the pacing ended up.

Environment: RTX 4070 SUPER (proprietary driver), KDE Plasma 6.7.4 Wayland, GTK 3.24.52, branch at d45d937, host_profile, enabled with --enable-impeller --impeller-backend=vulkan.

1. SIGSEGV on realize

Every launch, before any frame:

Stack trace of thread 207818:
#0  fl_opengl_manager_clear_current (libflutter_linux_gtk.so + 0x555278)
#5  gtk_widget_realize (libgtk-3.so.0 + 0x3bc0f7)
#13 g_application_activate (libgio-2.0.so.0 + 0xf259c)

realize_cb in fl_view.cc calls the OpenGL manager unconditionally, but fl_engine.cc only creates it when the renderer isn't Vulkan, so it's a null dereference whenever the Vulkan backend is selected.

   // Flutter engine will need to make the context current from raster thread
-  // during initialization.
-  fl_opengl_manager_clear_current(fl_engine_get_opengl_manager(self->engine));
+  // during initialization. The Vulkan renderer has no FlOpenGLManager - see
+  // where it is created in fl_engine.cc.
+  if (fl_engine_get_renderer_type(self->engine) != kVulkan) {
+    fl_opengl_manager_clear_current(fl_engine_get_opengl_manager(self->engine));
+  }

With this the backend starts and reports Using the Impeller rendering backend (VulkanSDF). [KHR swapchain mode].

2. The window never appears

After the crash fix the process runs and Impeller renders, but nothing is ever shown. Verified by screenshot with only this instance running: no window on screen at all, and visible=0 mapped=0 five seconds in. The same binary on the OpenGL backend shows its window normally.

The dependency is circular:

runner shows the window on the "first-frame" signal
   ↑
"first-frame" is emitted by fl_view_renderer_notify_frame()
   ↑
in fl_view_renderer_vulkan it is called only from the draw handler
   ↑
the draw handler only runs once the window is shown

fl_view_renderer_opengl avoids this by calling notify_frame from redraw_cb, scheduled by g_idle_add in present_layers, which doesn't depend on the window being visible. The Vulkan renderer moved the call into fl_view_renderer_vulkan_draw, which closes the loop. The embedder API already has the right hook for this: FlutterEngineSetNextFrameCallback fires once the first frame is actually in the swapchain, regardless of window visibility.

3. The parent surface commits at 48Hz

To get past #2 I added a temporary g_timeout_add in the runner that calls gtk_widget_show(toplevel) after 2s, ignoring first-frame. With the window up, a counter in fl_view_renderer_vulkan_draw alongside one in the tick callback gives:

vulkan draws=48.00/s  ticks=48.00/s  refresh_interval=6944us complete=7/8

Two things worth separating there.

The good half: refresh_interval=6944us with complete=7/8 means GDK learns the real 144Hz rate on this branch, which it never does on the OpenGL path (16667us, complete=0/8 - the cairo background paint is what keeps GDK's frame accounting alive). That is a real property of this design and the reason it is the only branch where the 60Hz cap can move at all.

The other half: ticks == draws, 1:1. The draw handler isn't skipping anything, the toplevel's frame clock is itself running at 48Hz. 1/48 = 20.83ms is exactly 3 × 6.944ms, which points at the _gdk_frame_clock_freeze() in on_frame_clock_after_paint (gdkwindow-wayland.c:729): the clock stays frozen until the wl_surface_frame comes back, and that round trip costs three vblanks.

This is a direct counter-measurement to the PR description, which says the synchronized-subsurface tick driver was confirmed to keep the parent committing every frame, verified on WSLg/llvmpipe. On a 144Hz display with a real GPU it commits 48 times a second - below the 60 the OpenGL path manages.

args.vsync_callback is also still unset, so the engine is separately paced by VsyncWaiterFallback's hardcoded 60Hz timer, but that is the smaller of the two problems here.

4. set_position commits the wrong surface

fl_vulkan_manager_set_subsurface_position commits the child surface to apply the new position. But wl_subsurface.set_position is double-buffered state of the parent: "The scheduled coordinates will take effect whenever the state of the parent surface is applied." The child commit is a no-op for this purpose. The position currently applies only when something else commits the parent - on this branch, the tick-callback redraw. So it works, but at the tick rate and by coincidence.

5. Fractional scaling sizes the subsurface wrong

On a fractionally scaled display (KDE at 1.5x) the subsurface shows up 1.5x too large, overflowing the window. The buffer for a 1280x720 logical view is 1920x1080 pixels, and without a viewport the compositor displays it as 1920x1080 in surface-local units. Binding wp_viewporter and setting wp_viewport.set_destination to the widget allocation fixes it. The destination is double-buffered on the child surface, so it applies with Impeller's own commits - no extra commit from the platform thread needed.

One ordering trap here: GTK allocates before it realizes, so the first size_allocate runs before the Vulkan manager exists. The destination also has to be declared at realize, or a window that is never resized never gets one.

Where the pacing ended up

For context, the full 60Hz root cause is written up on #183703 - why the OpenGL path can't be fixed by a vsync waiter alone, and a reproducer with no Flutter in it.

A vsync waiter driven by wl_surface_frame on the child surface, with the subsurface switched to set_desync, reaches the full display rate. On this setup: 143.9-144.1 produced, ~137-144 presented. "Presented" is counted at the last link, as the WSI's own commits on the child surface via WAYLAND_DEBUG - per the methodology in #183703, not a production-side counter. Three details carry it:

  • Arm the callback before returning the baton. A frame request only binds to a commit made after it, and that commit comes from the present the baton triggers. Arming after is always one commit late - that is where my earlier 32/20fps numbers came from.
  • Guard with a timeout before the surface has content. The compositor has nothing to answer yet, and the engine won't commit until a baton comes back.
  • Pace off the child surface. Paced off the toplevel, production rises but presentation doesn't move.

wp_presentation_feedback is still the right source for timestamps; frame callbacks are enough for pacing. One warning: the vsync callback must be registered only for the Vulkan backend. Wired unconditionally, the GL path produces 89-97fps while its toplevel still attaches at ~57/s - the production/presentation trap from #183703, measured.

Working branch

All of the above is running on top of the PR, pushed here:
https://github.com/ales-drnz/flutter/tree/impeller-vulkan-linux - three commits on top of d45d937, full diff. It has the fixes for 1, 2, 4 and 5, and the frame-callback vsync waiter (with tests) gated to Vulkan. Verified: Wayland Vulkan 144/144, OpenGL and software unchanged at their baseline, X11 starts and runs at 60 (the separate GDK/X11 cap from #183703 applies there), flutter_linux_unittests green.

Known limitations: content lags the window by up to ~130 physical px for a few frames during compositor-driven resizes (inherent to a desynchronized subsurface), only tested on KWin + NVIDIA so far, and the vendored viewporter protocol file will need license-check handling.

@sero583 happy to send any subset as a PR against your branch, or as patches - whatever is easiest to pick through. Given knopp's note on #49757 that there is team effort underway on Wayland subsurfaces, I'll defer on where the pacing piece should ultimately land.

@sero583

sero583 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor Author

@ales-drnz Sorry for the late reply.

This is great work, thank you for testing on real hardware. My verification was WSLg/llvmpipe only and that clearly hid a lot. 1 and 2 mean the backend did not even start on a real desktop, better to know that now than after review.

Please send 1, 2, 4 and 5 as a PR against impeller-vulkan-linux on my fork, that is easiest for me to pick through and your authorship stays on the commits. I looked through your branch, please leave the FLUTTER_LINUX_RENDERER selector out for now, that one I want to think about separately. The viewporter header license note you mentioned we can sort out in the PR.

On the pacing piece I agree with your conclusion, lets keep that separate until the subsurface direction from #49757 is clear. Your 48Hz measurement contradicts my PR description, I will correct that part. And the frame-callback waiter results look very promising, when the team direction is clear that deserves to be its own PR from you.

realize_cb calls the OpenGL manager unconditionally, but fl_engine only
creates it when the renderer is not Vulkan, so selecting the Vulkan
backend dereferenced a null FlOpenGLManager on every launch:

  #0  fl_opengl_manager_clear_current
  #1  realize_cb
  flutter#7  gtk_widget_realize
The runner shows the window on the "first-frame" signal, which the
Vulkan renderer only emitted from its draw handler - and the draw
handler cannot run before the window is shown, so the window never
appeared.

Announce it from FlutterEngineSetNextFrameCallback instead: it fires on
the raster thread once the first frame is actually in the swapchain,
regardless of window visibility. Routed to the view through a new
FlRenderable.notify_first_frame so backends that present outside the
GTK draw cycle can signal it. Tested on an unrealized view, matching
how the runner receives it.
wl_subsurface.set_position is double-buffered state of the parent
surface - "the scheduled coordinates will take effect whenever the
state of the parent surface is applied" - so committing the child was a
no-op for it. The position only applied when the tick-callback redraw
happened to commit the parent.
Mode change only; the engine's pre-push formatting hook trips on it.
@sero583

sero583 commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Taking this out of draft.

@ales-drnz was kind enough to test it on NVIDIA + KDE Plasma Wayland, and it turned out the backend did not start there at all: a null dereference on realize, and a first frame that was announced from the GTK draw handler, which cannot run before the runner shows the window. His four fixes are in the PR as his own commits, and the full flutter_linux_unittests suite passes with them.

There are two decisions I would rather not guess at, so if a maintainer could weigh in I would appreciate it:

  1. Fractional scaling needs wp_viewport, which means vendoring a Wayland protocol into the engine. There is no third party location for that today, build_overrides/wayland.gni sets wayland_gn_dir = "", and I did not want to invent one. Where would you like those files to live? wl_surface_set_buffer_scale is unfortunately not an alternative, the swapchain starts at a fallback size rather than at the widget allocation times the scale.

  2. Frame pacing. The tick driver in this PR keeps the parent committing, but on a 144Hz display it lands at 48 commits/s because the GDK frame clock does not run at display rate. A vsync waiter driven by wl_surface_frame on the child surface reaches the full rate, measured on that setup. Given the team subsurface work in Desktop app frame rate isn't synced to display #49757, is that something worth pursuing here, or would you rather it waited for that?

Neither of them blocks review of what is in the PR now. Happy to take this in whatever direction suits the team.

@sero583
sero583 marked this pull request as ready for review August 18, 2026 14:25
@sero583
sero583 requested review from a team and loic-sharma as code owners August 18, 2026 14:25

@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 adds Vulkan rendering support with Impeller on Linux, introducing FlVulkanManager and FlViewRendererVulkan to manage Vulkan resources and handle frame presentation. Feedback on the implementation identifies critical issues in FlVulkanManager, including undefined behavior from unconstructed C++ members in the GObject initialization, compilation failures from calling non-existent Wayland destructors and non-standard template destructor syntax, and a potential memory retention issue in embedder.cc where capturing a weak pointer instead of a strong pointer is recommended to avoid delaying the destruction of SurfaceContextVK.

Comment thread engine/src/flutter/shell/platform/linux/fl_vulkan_manager.cc Outdated
Comment on lines +783 to +790
if (self->wl_subcompositor_iface != nullptr) {
wl_subcompositor_destroy(self->wl_subcompositor_iface);
self->wl_subcompositor_iface = nullptr;
}
if (self->wl_compositor_iface != nullptr) {
wl_compositor_destroy(self->wl_compositor_iface);
self->wl_compositor_iface = nullptr;
}

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.

high

The Wayland interfaces wl_compositor and wl_subcompositor do not have destructor requests defined in the Wayland XML protocol, meaning wl_compositor_destroy and wl_subcompositor_destroy do not exist and calling them will cause compilation errors. Use wl_proxy_destroy to properly release these client-side proxies.

  if (self->wl_subcompositor_iface != nullptr) {
    wl_proxy_destroy(reinterpret_cast<struct wl_proxy*>(self->wl_subcompositor_iface));
    self->wl_subcompositor_iface = nullptr;
  }
  if (self->wl_compositor_iface != nullptr) {
    wl_proxy_destroy(reinterpret_cast<struct wl_proxy*>(self->wl_compositor_iface));
    self->wl_compositor_iface = nullptr;
  }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I do not think this one is right, and the reasoning is inverted: the absence of a destructor request is exactly why wl_compositor_destroy exists in this form.

wayland-scanner emits a <interface>_destroy inline for every interface that has no destructor request, and that inline is a plain wl_proxy_destroy wrapper (scanner.c#L1227-L1237):

	if (!has_destroy && strcmp(interface->name, "wl_display") != 0) {
		printf("/** @ingroup iface_%s */\n", interface->name);
		printf("static inline void\n"
		       "%s_destroy(struct %s *%s)\n"
		       "{\n"
		       "\twl_proxy_destroy("
		       "(struct wl_proxy *) %s);\n"
		       "}\n\n",
		       interface->name, interface->name, interface->name,
		       interface->name);
	}

So in the sysroot this builds against, build/linux/debian_bullseye_amd64-sysroot/usr/include/wayland-client-protocol.h (generated by wayland-scanner 1.18.0), lines 1198-1202 are:

/** @ingroup iface_wl_compositor */
static inline void
wl_compositor_destroy(struct wl_compositor *wl_compositor)
{
	wl_proxy_destroy((struct wl_proxy *) wl_compositor);
}

with wl_subcompositor_destroy at line 5546 in the same shape. That is the identical wl_proxy_destroy call the suggestion proposes, so the current code already does exactly that, and it compiles and links here. Leaving it as is.

Comment thread engine/src/flutter/shell/platform/embedder/embedder.cc Outdated
Comment thread engine/src/flutter/shell/platform/linux/fl_vulkan_manager.cc
Comment thread engine/src/flutter/shell/platform/linux/fl_vulkan_manager.cc Outdated
…ze capture

Review feedback on the Vulkan GTK backend:

- FlVulkanManager holds a RefPtr and several vectors, but GObject hands
  out zeroed raw memory and never runs their constructors. They are
  constructed in place in _init now, pairing with the explicit
  destruction that was already in _finalize. Both lists use the
  decltype form so they stay symmetric and do not depend on the
  injected class name.
- The embedder surface-size updater captured the SurfaceContextVK
  shared_ptr strongly. The updater is owned by the engine and outlives
  the surface, so that kept the Vulkan context and its resources alive
  until engine teardown. It captures a weak_ptr and locks it now.
return;
}

FlVulkanManager* vulkan_manager = fl_vulkan_manager_new(window);

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.

This could be g_autoptr(FlVulkanManager) to avoid the g_object_unref later.

@robert-ancell

Copy link
Copy Markdown
Contributor

Thanks for working on this.

I've done an initial review, first thoughts:

  • fl_engine_set_vulkan_manager is not going to work with multiple views - each view will replace the existing manager with a new one. The Vulkan manager should be able to be created once like the OpenGL one is.
  • This was developed before the subsurface code landed, I think this can integrated into fl_view_renderer_subsurface.cc.
  • Being a big change, any way to split out parts of this into smaller PRs always helps :)

@sero583

sero583 commented Sep 22, 2026

Copy link
Copy Markdown
Contributor Author

@robert-ancell Thanks as well. Currently I don't have that much time, but I'll probably get it done by the weekend or something (same counts for my other pending PRs). What would be a good plan on splitting this into smaller PRs? I was thinking of one feature having one branch, even if that feature is a bit larger.

@ales-drnz

Copy link
Copy Markdown

@robert-ancell thanks for the review. @sero583, before you spend the weekend on the split I wanted to share what I found, because the first point goes deeper than where the manager is created.

This PR gives the engine a single VkSurfaceKHR through the new FlutterVulkanRendererConfig.surface field and turns the compositor off for Vulkan, so a second view has nowhere to render wherever the manager lives. Rendering each view on its own needs the compositor path, where the engine calls present_view_callback per view, and that path was never implemented for Vulkan with Impeller (kFlutterBackingStoreTypeVulkan only logs "Unimplemented"). knopp also asked in #183382 for no Impeller-owned swapchain and a synchronized subsurface.

So I rebuilt it on the compositor and on FlViewRendererSubsurface, split the way Robert suggested:

  1. [embedder] Support Vulkan backing stores with Impeller lets Impeller render into a VkImage from the embedder, as the compositor path already does for OpenGL and Metal. The public API only gains documentation.
  2. [Linux] Move OpenGL presentation out of FlViewRendererSubsurface puts the GL-specific code behind a small FlSubsurfacePresenter interface and doesn't change behavior. Robert, this touches your code, so I'd like to know if you're happy with how it's split.
  3. [Linux] Add a Vulkan renderer creates FlVulkanManager once per engine and shares it between views like FlOpenGLManager. Each view copies its layer into a swapchain on its own subsurface. It's enabled with the --impeller-backend=vulkan engine switch, as loic-sharma asked for in [Impeller] Add Vulkan rendering backend for Linux and Windows desktops #183382.

While testing I also found a synchronization bug in Impeller's Vulkan render passes that the validation layers report on every frame, which I'll send separately.

On NVIDIA with KWin at 144 Hz the Vulkan path keeps up with OpenGL on master and the validation layers stay clean, including while resizing and with two windows. I haven't been able to try Mesa.

Sero, the device selection, the features Impeller expects and the Dozen handling all come from your work, and you're a co-author of the renderer commit. The three commits are on https://github.com/ales-drnz/flutter/tree/linux-vulkan-subsurface, on top of current master, so you can fetch them into this PR or split them however you like. Your branch is based on July's master, before FlViewRendererSubsurface landed, so it will need updating to master either way. I'm happy to help with that.

@robert-ancell

Copy link
Copy Markdown
Contributor

@robert-ancell Thanks as well. Currently I don't have that much time, but I'll probably get it done by the weekend or something (same counts for my other pending PRs). What would be a good plan on splitting this into smaller PRs? I was thinking of one feature having one branch, even if that feature is a bit larger.

Really anything that can land in separate chunks. For example, if there are any assumptions in the current code that will not work with Vulkan then we can land fixes for those. You could start a minimal Vulkan manager that doesn't do any rendering now, and then build on that. This PR shows where we're going, and we can build towards that. This PR can then merge/rebase and will shrink over time.

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

Labels

a: desktop Running on desktop engine flutter/engine related. See also e: labels. platform-linux Building on or for Linux specifically team-linux Owned by the Linux platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants