Repository navigation
Resize the renderer when a minimized window is restored on Windows - #15
Closed
chris-addison wants to merge 37 commits into
Closed
chris-addison wants to merge 37 commits into
chris-addison wants to merge 37 commits into
Conversation
core-foundation = "=0.10.0" in the workspace dependencies forces every downstream crate that depends on gpui to resolve exactly 0.10.0. Because 0.10.0 and 0.10.1 are semver-compatible, cargo picks one version per compatible range, so a consumer that needs 0.10.1 (or pins any other 0.10.x) fails to resolve instead of getting a second copy. A caret range keeps the same major/minor floor and lets the consumer's lockfile choose the patch. This repo's own Cargo.lock is unchanged and still resolves 0.10.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ws 0.61 - image: disable default features and enable only the formats gpui's ImageFormat maps (bmp, gif, ico, jpeg, png, pnm, tiff, webp) plus rayon. The default set pulled the ravif/rav1e AVIF encoder (~28 crates) that nothing calls, and the exr/dds/hdr/qoi/tga/ff decoders. - bindgen: drop gpui's macOS build-dependency, which build.rs never used, and gate gpui_media's to macOS, the only target its build.rs generates bindings for. - postage: optional behind test-support (and a dev-dependency). Only app/test_context.rs uses it, and that module is test-support-only. - windows-capture 1.5 -> 2.0.1, which moves it to windows 0.62, so the windows-061 alias goes and gpui, gpui_wgpu and gpui_windows use the workspace windows crate. Frame::timestamp() now returns a Result and as_raw_texture() is safe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- wgpu 29 -> 30.0.1 and naga 29 -> 30.0.1. API changes: surfaces take a color_space (SurfaceColorSpace::Auto, wgpu's pre-30 behavior), adapter requests take apply_limit_buckets (false), present() moved to Queue::present, metal texture_from_raw takes a drop callback (None) and create_texture_from_hal takes the initial state (UNINITIALIZED, what 29 hard-coded). - accesskit 0.24 -> 0.25.1, with accesskit_consumer 0.39.1, accesskit_macos 0.27.1, accesskit_unix 0.24.0 and accesskit_windows 0.35.1, so every platform adapter shares one consumer. Tree is renamed TreeInfo. - wgsl-rs: drop the workspace-wide validation feature, which only backs macro-generated #[cfg(test)] tests but linked wgsl-rs's own naga 29 into every consumer's build. gpui_render enables it as a dev-dependency, and its subpixel test validates with the workspace naga directly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- rand 0.9 -> 0.10.3 (workspace and gpui_sum_tree). The extension methods moved from Rng to RngExt, so three imports change. - getrandom 0.3 -> 0.4.3 for gpui's wasm entropy, matching what rand 0.10, tempfile and uuid already resolve. - flume 0.11 -> 0.12, which drops nanorand and flume's spin 0.9 use. - which 6 -> 8 in gpui_ce_util and gpui_zed_util, which drops rustix 0.38, linux-raw-sys 0.4, home and winsafe. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`CreateTargetForHwnd(hwnd, true)` puts gpui's visual tree on top of every child window of the gpui window. gpui creates no child HWNDs of its own, so this only bites an embedder that parents a native child, such as a wry / WebView2 webview built with `build_as_child`: gpui's opaque frame covers the child completely, while the child still receives mouse input and keeps running. A video embed therefore plays audio with no picture. `topmost: false` composes gpui's tree behind its children, which matches macOS, where a child `NSView` draws above gpui's Metal layer. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
wgpu 30 made `BufferSlice::get_mapped_range` return a `Result`. Upstream's offscreen readback in `gpui_wgpu`'s `test-support` path (`headless.rs`) still treated it as infallible, which only shows with `test-support` enabled. Map the error the way the surrounding readback steps already do.
`render-to-image` (HUB-7425) compiles `Window::render_to_image` outside `test-support`, and `gpui_macos`'s window forwards it to whichever renderer is active. Upstream's macOS WGPU renderer (gpui-ce#287) only provides `render_to_image` under `test-support`, because WGPU's offscreen readback needs `gpui_wgpu`'s `test-support`. So `wgpu` plus `render-to-image` failed to compile. Widen the renderer's existing "needs test-support" stub to cover `render-to-image`, so that combination builds and reports the gap at runtime rather than pulling in test-support's frame scheduling, which `render-to-image` exists to avoid.
Lets an element publish the URL it links to as the AccessKit `url` property, which is what a web page exposes for an `<a href>`. Like the other `aria_*` passthroughs it only describes the element: opening the URL stays with the element’s own click handler.
Mutter creates its window (and GNOME Shell matches it to a .desktop entry) as soon as it handles xdg_surface.get_toplevel. The app_id was only sent after the wgpu renderer was created, which flushes the connection and takes 100+ ms, so for that window GNOME showed the app as a separate generic "gear" icon in the dock that vanished once the app_id arrived. Set the title and app_id right after get_toplevel so they land in the same flush. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since the Parley migration, line breaking lives in the platform text system, and TestTextSystem lays every string out as one line: no soft wrapping and no break at a newline. Before, gpui wrapped lines itself on top of any text system, so headless tests could check that text wraps, clamps, or hit-tests across rows. They can't any more. This keeps the existing one-line layout, unchanged, for text that has no newline and fits its wrap width. Otherwise each hard line starts a row, and rows wrap greedily at whitespace with the same fixed glyph advances. A word wider than the wrap width overflows its row, matching Parley's default overflow-wrap, and line_clamp wraps only the first n - 1 rows, matching how the Parley backend clamps. Inline layout without embedded boxes gets the same rows. Caret, hit-testing, selection and vertical movement work per row. One elements::text test compared the measured text size with the root's bounds, which only held while the text could not wrap. It now checks the height and that the width fits.
Parley’s line breaker hangs a space that overflows a row and then breaks, so a space that ends the paragraph leaves an empty row behind, and that row counts in the layout height. A block’s max-content width comes from Layout::width(), which leaves trailing whitespace out, so laying the text out again at exactly that width always wraps it: a box holding "Visit our " measured one row taller than the text it paints. CSS hangs that whitespace instead. After line breaking, a paragraph whose last rows hold only whitespace after a soft break is now broken again: the earlier rows as before, and the last one with no width limit, still aligned within the wrap width. That covers several trailing spaces, whitespace-only text and clamped layouts, and the empty row Parley also leaves after an oversized inline box that ends a paragraph. A forced break (U+2028) still starts its own row, and widths don’t change, since Layout::width() already left the hanging whitespace out.
The previous commit hangs a paragraph’s closing whitespace on its last row instead of wrapping it onto an empty one. A block still measured its width with Layout::width(), which leaves trailing whitespace out, so a fragment ending in a space now sat flush against the next one: in a sentence built from fragments, "Visit our " followed by a link read "Visit ourdownload archive". A text element’s width (LineLayout::width) already keeps that space. A block’s width now counts the whitespace that ends its paragraph when it fits in the width the paragraph was given, so a block laid out again at its own measured width keeps both its row and its space. When it doesn’t fit, it still hangs and stops counting. Whitespace before a forced break and the spaces a soft wrap hangs still don’t count. The element test now builds that sentence as a baseline-aligned flex row and checks both its row and its space, and a unit test pins when the space counts.
`render-to-image` (HUB-7425) compiles `Window::render_to_image` outside `test-support`, but no Linux window implemented it, so the trait’s default answered “not implemented for this platform”. Linux windows draw through the WGPU renderer, which already has an offscreen readback for visual tests: it renders the scene into a texture, copies that to a buffer and maps it. That readback was compiled only under `gpui_wgpu`’s `test-support`. Give `gpui_wgpu` a `render-to-image` feature that compiles the readback alone (`WgpuHeadlessRenderer` stays test-only), forward it from `gpui_linux` and `gpui_platform`, and let the X11 and Wayland windows call it. The readback now refuses while a window recovers a lost device, as the DirectX one does, rather than panicking on the torn-down GPU resources.
The offscreen readback copies 8-bit rows and only swizzles `Bgra8Unorm`, which held while it served the headless renderer alone. A window’s surface can pick another format when it offers neither 8-bit UNORM one, so refuse those with an error rather than returning swapped channels or a blank image. Test the device-lost half of the guard too: the first test only covered missing GPU resources, so dropping the `device_lost()` check kept it green. The refusal now names both states, and the macOS `wgpu` stub’s comment no longer claims the readback needs `test-support`.
TestAppContext::build hardcodes its platform: the stub TestTextSystem, which paints no glyphs, no headless renderer, an empty asset registry, and windows whose scale factor is the constant 2. A test can open a window and read its scene, but not render it to pixels the way an app draws it. HeadlessAppContext can, but it isn’t a TestAppContext, so code written against TestAppContext and VisualTestContext can’t use it. TestAppContext::with_platform takes a text system, an asset source, a headless renderer factory and a scale factor. TestPlatform keeps the scale factor and hands it to each TestWindow, which reports it from scale_factor(), so Window::render_to_image returns an image of the window’s size times that factor. build, and with it #[gpui::test], keeps the stub text system, no renderer, no assets and a scale of 2. gpui_platform::current_headless_renderer now returns WGPU’s headless renderer on Linux, FreeBSD and Windows under test-support, through an optional gpui_wgpu dependency that Linux already builds for its windows. Under bench-support alone it still returns None there. Unit tests drive the new constructor with a recording renderer. A rendering test in gpui_platform renders IBM Plex through Parley with the platform’s headless renderer at two scales, and CI runs it on all three platforms.
DirectWrite answers fontique's script fallback with a single face, mapped from one sample character with no locale. On an English system that pick can be a Japanese or Korean UI font, so a Simplified-only character such as 简 has no candidate and shapes as .notdef. `ParleyTextSystem::with_script_fallback_families` resolves the system's own pick for a script first, then appends the installed families it is given. The Windows text system uses it to add the Chinese, Japanese and Korean UI faces, plus SimSun-ExtB, after DirectWrite's Han pick. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since upstream gpui-ce#232 (8ffa9a7), build.rs checks the target at run time, so every host compiles it, and it names bindgen unconditionally. 3edff57 (“Trim unused dependencies”) moved bindgen under a macOS-only build-dependencies table, so on a Linux or Windows host the build script fails with E0433. That reddens clippy, msrv, test-linux and test-windows on every PR into this branch. Back to upstream’s plain [build-dependencies], with a comment saying why it stays unconditional.
WgpuDeviceRequirements gains an experimental_features token that create_device passes to request_device when the application opted in and the merged required features include an EXPERIMENTAL_* one (for example EXPERIMENTAL_RAY_QUERY). The default keeps experimental features disabled, so existing callers create the same device as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e#287) - gpui_windows: opt-in `wgpu` feature swaps the DirectX renderer for a thin WindowsWgpuRenderer presenting to the HWND via wgpu surface; DirectX stays the default and remains compiled (direct_write shares its internals) - gpui_wgpu: new_for_raw_window_handle / recover_raw_window_handle for the Win32 HWND path (instance carries a synthesized Windows display handle), custom-gpu texture compositing in surfaces/windows.rs, cfg gates for WgpuContextHandle::from_window / WgpuRenderTarget::surface - gpui: SurfaceSource::Texture + gpu_context_info available on Windows behind gpui/custom-gpu - gpui_platform: windows-wgpu feature - custom_gpu example: enable on Windows; give the surface element an explicit size (a 0x0 layout bounds made the core drop the surface primitive)
Renders a backdrop quad plus an application WGPU texture through the full frame path (draw_surfaces -> custom-gpu arm) with WgpuHeadlessRenderer and asserts the composited pixels. Also exposes WgpuHeadlessRenderer::gpu_context for tests that pre-fill application textures.
- WindowsWgpuRenderer flags skipped frames via needs_redraw; WindowsWindow propagates it into force_render_pending so the vsync pump schedules another frame — a lost surface or deferred recovery no longer leaves the window blank (Copilot review finding on gpui-ce#307) - delegate new_for_raw_window_handle / recover_raw_window_handle to new_for_target / recover_target via NativeSurfaceTarget::Window, sharing the upstream construction/recovery flow; synthesize the Windows display handle the DXGI instance requires - drop leftover debug prints from the surface paint path and the empty conditional in Scene (review findings) - custom_gpu example: document the windows-wgpu run command - rebase onto main (includes gpui-ce#287 macOS wgpu renderer): cfg gates now cover macOS and Windows side by side
Recovery clears the WGPU atlas, so sprite batches in a pre-recovery scene reference tile ids that no longer exist and can panic in atlas lookup. Return after every recovery attempt (success or failure) and let the forced frame — requested via needs_redraw — rebuild the scene, matching the macOS WGPU renderer's recovery flow.
With the wgpu feature, the platform still created DirectXDevices at startup and the vsync thread polled them for device loss. The wgpu renderer never draws with them; it owns its device and recovers from losing it on the next draw. The spare D3D11 device sat on the GPU, and if recreating it after a loss failed, the vsync thread panicked an app whose rendering was fine. The vsync thread now runs without devices (it only needs DwmFlush) and skips the device-loss check when there are none. Window creation stops carrying DirectXDevices under the feature, and the window's WM_GPUI_GPU_DEVICE_LOST handler, which nothing sends any more, goes with them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
render_to_image bailed with "not implemented". It now renders through gpui_wgpu's offscreen path, as the macOS wgpu renderer does, and the test-support feature turns on gpui_wgpu's test-support to provide it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Requirements only take effect when the shared device is created, at the first window. A later set_gpu_requirements was silently ignored; it now logs a warning saying to call it before opening a window. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- with the wgpu feature the text system is ParleyTextSystem with the DX11-free swash rasterizer (SwashGlyphRasterizer), and no DirectX devices are created; the vsync thread then only paces frames - the vsync device-lost watch and handle_gpu_device_lost stay DirectX-renderer-only - begin_vsync_thread mutability fix after the cfg split
The fork's render-to-image feature compiles Window::render_to_image without test-support (the Hub's e2e screenshots). With the wgpu feature the window's renderer is the WGPU one, whose render_to_image was gated on test-support alone, so a render-to-image build without test-support did not compile. render-to-image now forwards to gpui_wgpu's render-to-image and enables the WGPU renderer's readback. Also joins a log message that a lost line continuation had split by a run of spaces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two test literals predate ContentMask's fade_out field (gpui-ce#298), and the custom_gpu example predates wgpu 30's optional vertex buffer layouts, so cargo test -p gpui_ce_wgpu stopped compiling. Both now fill the new shapes with their defaults. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WgpuHeadlessRenderer::gpu_context_info returns the WgpuContextHandle a window's renderer would hand a custom-GPU control through WgpuContextHandle::from_window. A headless window has no platform renderer to ask, so a test that drives a texture-surface control headlessly can now give it the same device the frame is composited on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…_PREF WgpuHeadlessRenderer::new_with_requirements passes the features and limits an application would hand a window's renderer, so a headless capture of a custom-GPU control runs on a device that can do what the window's can. The headless adapter request still prefers low power, and now honours WGPU_POWER_PREF, so a dual-GPU machine can measure the discrete GPU the window would get. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wgpu 30's create_texture_from_hal takes the wrapped resource's initial state. The fork's wgpu 30 upgrade passed it on macOS but missed the Windows capture path behind wgpu-surfaces, which nothing built until the Windows WGPU renderer turned it on. It passes UNINITIALIZED, what wgpu 29 always assumed, as the macOS path does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`WgpuDeviceRequirements::preferred_backend` names the native backend to try first when gpui creates a window's (or a headless renderer's) device; the platform's other backends follow in their usual order, so a machine without it still gets a device. An application that wants Vulkan on Windows, where ray queries on Direct3D 12 also need DXC, asks for it here. `None` (the default) keeps the platform's order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`WindowOptions::windows_renderer` (`WindowsRenderer::DirectX`, the default, or `WindowsRenderer::Wgpu`) picks each Windows window's renderer. The `wgpu` feature now compiles the WGPU renderer in beside Direct3D 11 instead of replacing it for every window, so an application can draw one window with WGPU (and lend its device to a custom GPU control) while the rest keep Direct3D 11 and DirectComposition. Without the feature a `Wgpu` window logs a warning and draws with DirectX. Text stays DirectWrite under both renderers: the platform keeps the DirectWrite text system (its glyphs are rasterized on the CPU, so they upload to either atlas), and a WGPU window applies DirectWrite's gamma and contrast, so its text matches a DirectX window's. The DirectX devices and the vsync thread's device-lost watch are back for `wgpu` builds; a WGPU window ignores the DirectX device-lost message and recovers its own device. The custom_gpu example opens its window with `WindowsRenderer::Wgpu`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restoring a window from the taskbar skipped the renderer resize, so a window restored at a different size than it was minimized at (a display, scale or resolution change while minimized, or restoring to maximized) kept its swap chain at the old size. Layout used the new size, so the right and bottom of the window were cut off until the next manual resize. Always resize: both renderers return early when the size is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chris-addison
force-pushed
the
unity/HUB-8360-restore-resizes-renderer
branch
from
October 17, 2026 21:13
3759e0d to
87792a4
Compare
…ting inline_reflow.rs still called assert_document_contract, which the upstream refactor (gpui-ce#338) renamed to assert_document_structure, so every build step failed. surfaces/windows.rs needed cargo fmt. Neither is part of the restore fix; both were already broken on unity/hub-consolidated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chris-addison
force-pushed
the
unity/hub-consolidated
branch
from
October 17, 2026 21:29
2e8f5ed to
8bd155e
Compare
…ologies/gpui-ce into unity/HUB-8360-restore-resizes-renderer # Conflicts: # crates/gpui_wgpu/src/wgpu_renderer/surfaces/windows.rs # crates/gpui_windows/src/wgpu_renderer.rs
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.
On Windows, a window restored from the taskbar at a different size than it was minimized at drew only part of its client area. Found in the native Unity Hub (HUB-8360, Hub 4.0 beta feedback): after restoring, the right side was cut off, including the close button, and old pixels showed in the gap.
WM_SIZEafterSIZE_MINIMIZEDrestored the frame callback but skipped the renderer resize. The comment said the swap chain would be recreated throughupdate_drawable_size_even_if_unchanged, which does not exist. Layout took the new size, but the swap chain kept the old one until the next manual resize. This happens when the display scale or resolution changes while the window is minimized, a monitor sleeps, a laptop docks or undocks, or the window restores to maximized.WM_SIZEthat is not a minimize resizes the renderer. Both renderers return early when the size is unchanged, so a plain restore costs nothing.handle_size_changeloses itsshould_resize_rendererflag, since every caller now passestrue.Upstream gpui-ce
mainand zed have the same code, and neither has a fix. This also covers a minimized window shown again throughWindow::set_visible(true)(gpui-ce#239), which restores it through the same branch.Testing
platform::tests::restoring_a_minimized_window_resizes_the_rendereropens a real window with a frame callback, minimizes it, grows its restore rectangle while it is minimized, restores it, and checks that the renderer's size matches the client rectangle. Run on a Windows desktop session:gpui_ce_windowslib suite passes (24 tests,test-support,render-to-image).It needs a desktop session, like the existing
directx_renderer::tests. In session 0 (for example over SSH) DXGI refuses the swap chain with0x887A0022.A second commit fixes CI that was already broken on
unity/hub-consolidated:inline_reflow.rsstill calledassert_document_contract, which gpui-ce#338 renamed toassert_document_structure, so every build step failed, andgpui_wgpu'ssurfaces/windows.rsneededcargo fmt. Workspace clippy and thegpui_ce_parleylib tests pass locally.This branch is
unity/hub-consolidated, the Hub's current pin, plus these two commits.🤖 Generated with Claude Code