Repository navigation
Conversation
A window that enabled composition presents through PlatformWindow::draw while its tree holds only the base and overlay surfaces, and hands the platform only the base surface, so macOS, Windows and Wayland release the overlay's view or visual, drawable layer and textures. Adding a native, external or extra GPUI surface brings them back and splits the present again; the scene's split markers are recorded in both modes, so a retained view keeps its surface across the switch. Composition stayed on for the window's whole life after its first surface: every frame copied and sorted the scene once per layer and presented an empty overlay drawable, and the overlay held its textures, about 1.3 ms of process CPU per frame and 50 MB of IOSurface on a 1600x1000 window of 5000 retained primitives.
…goes set_composition_order destroys the overlay window when no native or external surface remains in the order. Only draw_composed hid it, and a window without a native surface now presents with draw, so an overlay window mapped above the last native surface would have stayed on screen with its last content. Not built or run: this host has no Linux target, so the change is checked by reading only.
huacnlee
reviewed
Oct 26, 2026
| state.enabled && state.splits() | ||
| }; | ||
| if !splits { | ||
| window.platform_window.draw(&window.rendered_frame.scene); |
Member
There was a problem hiding this comment.
[P2] Preserve base/overlay ordering in the plain presentation path
A root canvas can paint a red quad through with_composition_surface(overlay, ...), then an overlapping opaque blue quad on the restored base surface: composed rendering keeps red above blue, but this raw-scene path paints blue last. Consequently, removing the last extra surface changes the visible stacking of the remaining content. Preserve composition ordering when using one drawable and test overlapping interleaved base/overlay paints; reconstructing split markers alone does not exercise this branch.
Reviewed by ChatGPT on behalf of @huacnlee.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fast: Present plainly while a composing window holds no extra surface
Problem
Once a window enables composition it stays composed for its life, even after its last native, external or extra GPUI surface is removed. With only the default base and overlay surfaces left, every frame still copies and sorts the scene once per layer (
composed_scene_layers,ComposedScene::layer_scene,replay_balanced,finish) and presents an empty overlay drawable, and the platform keeps the overlay's view,CAMetalLayerand path textures.Measured on macOS, a hidden 1600x1000 window, about 5000 retained primitives, 600 frames after a native surface was added and then removed, release build, base and branch alternated, CPU time:
With a surface still held, base and branch are equal. After removal the branch matches a window that never composed.
Change
fast/composition.rs:WindowCompositionState::splits()is true when the tree holds a surface beyond the two defaults.presenttakes the plainPlatformWindow::drawpath unless composition is enabled andsplits()holds.platform_order(split out ofsynchronize_platform_order) leaves the overlay out of what the platform is handed whilesplits()is false, so the platform releases what draws it; adding a surface hands it back.enabledorsplits(), so a retained view keeps painting onto its surface across a switch.gpui_linuxX11:set_composition_orderhides the overlay window when no native or external surface remains (second commit; see below).docs/upstream-sync.mdlists the behaviour with the other composition differences. No public API changes; no hook outsidefast/.Per platform (by reading; macOS also measured above)
set_composition_orderremoves any GPUI view not in the list, so the overlay view, renderer, layer and textures go.set_orderkeeps only the overlays in the list, dropping their swap chain, visual and resources.set_composition_orderdestroys the subsurface and renderer of a GPUI surface that left the list.draw_composedhid it. With plain presents that would leave it mapped, so the second commit hides it inset_composition_order. Its renderer is kept as a spare, as before, so X11 releases no memory here.Costs and risks
presentand the composition tests nearby; the two will conflict textually and need a rebase of whichever lands second.Tests (
cargo test -p gpui --lib --features test-support composition, 18 pass)retained_views_keep_their_split_as_surfaces_come_and_go: three add/remove cycles; the platform order is the base alone while unsplit and base, surface, overlay while split; every operation stays on its surface and the retained view is built once. Leaving the overlay in the platform order, or counting one extra surface insplits(), fails it. The plain/composed choice inpresentitself is not observable on the test platform and is covered by the measurement.script/check-upstream: 0 violations.Reproduce
No headless run shows the saving: the test platform returns
unsupportedforenable_window_composition, so agpui_perfscenario cannot compose, and the cost is mostly the platform's drawable and textures. The table comes from a temporary harness (a hidden window, a native surface added and removed, 600 frames, process and main-thread CPU time plus IOSurface and footprint per frame) that is not part of this PR. The unit test above is the regression guard.Scope
Composition is a gpui-fast feature (zed#62379 is not merged upstream), so this belongs here under CONTRIBUTING's "needs that are clearly reasonable and pressing": a window that has dropped its last native surface pays for composition on every frame. #54 (
gpui_perf --composition) measured this cost and was closed by its author without a recorded reason; this PR removes it.Commits
fast: Present plainly while a composing window holds no extra surfacegpui_linux: Hide the X11 overlay window when the last native surface goes