gpui: Enable per-pixel, GPU composited transparency on Windows (#26645)

Move the SetLayeredWindowAttributes call to immediately after window
construction, and initialize it with per-pixel transparency settings, no
color key and no global blending. The render pipeline will perform alpha
blending during compositing.

Cleaned up the DWM acrylic API calls some, to explicitly set to the
three appropriate modes depending on opaque, transparent or blurred
settings. The API internally hides versioning concerns from the caller.

Set the window class background color to black, this prevents a
flashbang on slow startup, e.g. debug builds on a heavily loaded system.

The outcome is that the window no longer receives paint demands for
underlying window updates, while also having per-pixel transparency -
opaque theme elements are now correctly opaque. The transparency
settings are now portable across windows and macOS having mostly similar
outcomes (modulo palette differences). Small fonts may still appear to
be alpha blended - this seems to be in the glyph atlas, their pixels are
not actually opaque. Larger fonts (or higher DPIs) don't suffer this and
are as opaque as expected. Layering the window atop one that is
rendering at 120fps, the editor window can drop to its 8fps idle state,
while still being composited with 120fps alpha blend in the background,
in both blur and transparent modes.

Updates #20400

Release Notes:

- Improved transparency on Windows to be more efficient, support fully
opaque elements and more closely match other platforms.
This commit is contained in:
James Tucker 2025-04-10 14:27:19 -07:00 committed by GitHub
parent 384868e597
commit 94b75f3ad9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -390,10 +390,10 @@ impl WindowsWindow {
.unwrap_or(""),
);
let (dwexstyle, mut dwstyle) = if params.kind == WindowKind::PopUp {
(WS_EX_TOOLWINDOW, WINDOW_STYLE(0x0))
(WS_EX_TOOLWINDOW | WS_EX_LAYERED, WINDOW_STYLE(0x0))
} else {
(
WS_EX_APPWINDOW,
WS_EX_APPWINDOW | WS_EX_LAYERED,
WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
)
};
@ -459,6 +459,14 @@ impl WindowsWindow {
state: WindowOpenState::Windowed,
});
}
// The render pipeline will perform compositing on the GPU when the
// swapchain is configured correctly (see downstream of
// update_transparency).
// The following configuration is a one-time setup to ensure that the
// window is going to be composited with per-pixel alpha, but the render
// pipeline is responsible for effectively calling UpdateLayeredWindow
// at the appropriate time.
unsafe { SetLayeredWindowAttributes(hwnd, COLORREF(0), 255, LWA_ALPHA)? };
Ok(Self(state_ptr))
}
@ -703,41 +711,20 @@ impl PlatformWindow for WindowsWindow {
window_state
.renderer
.update_transparency(background_appearance != WindowBackgroundAppearance::Opaque);
let mut version = unsafe { std::mem::zeroed() };
let status = unsafe { windows::Wdk::System::SystemServices::RtlGetVersion(&mut version) };
if status.is_ok() {
if background_appearance == WindowBackgroundAppearance::Blurred {
if version.dwBuildNumber >= 17763 {
set_window_composition_attribute(window_state.hwnd, Some((0, 0, 0, 10)), 4);
}
} else {
if version.dwBuildNumber >= 17763 {
match background_appearance {
WindowBackgroundAppearance::Opaque => {
// ACCENT_DISABLED
set_window_composition_attribute(window_state.hwnd, None, 0);
}
WindowBackgroundAppearance::Transparent => {
// Use ACCENT_ENABLE_TRANSPARENTGRADIENT for transparent background
set_window_composition_attribute(window_state.hwnd, None, 2);
}
//Transparent effect might cause some flickering and performance issues due `WS_EX_COMPOSITED` is enabled
//if `WS_EX_COMPOSITED` is removed the window instance won't initiate
if background_appearance == WindowBackgroundAppearance::Transparent {
unsafe {
let current_style = GetWindowLongW(window_state.hwnd, GWL_EXSTYLE);
SetWindowLongW(
window_state.hwnd,
GWL_EXSTYLE,
current_style | WS_EX_LAYERED.0 as i32 | WS_EX_COMPOSITED.0 as i32,
);
SetLayeredWindowAttributes(window_state.hwnd, COLORREF(0), 225, LWA_ALPHA)
.inspect_err(|e| log::error!("Unable to set window to transparent: {e}"))
.ok();
};
} else {
unsafe {
let current_style = GetWindowLongW(window_state.hwnd, GWL_EXSTYLE);
SetWindowLongW(
window_state.hwnd,
GWL_EXSTYLE,
current_style & !WS_EX_LAYERED.0 as i32 & !WS_EX_COMPOSITED.0 as i32,
);
}
WindowBackgroundAppearance::Blurred => {
// Enable acrylic blur
// ACCENT_ENABLE_ACRYLICBLURBEHIND
set_window_composition_attribute(window_state.hwnd, Some((0, 0, 0, 0)), 4);
}
}
}
@ -1084,6 +1071,7 @@ fn register_wnd_class(icon_handle: HICON) -> PCWSTR {
lpszClassName: PCWSTR(CLASS_NAME.as_ptr()),
style: CS_HREDRAW | CS_VREDRAW,
hInstance: get_module_handle().into(),
hbrBackground: unsafe { CreateSolidBrush(COLORREF(0x00000000)) },
..Default::default()
};
unsafe { RegisterClassW(&wc) };
@ -1241,6 +1229,12 @@ fn retrieve_window_placement(
}
fn set_window_composition_attribute(hwnd: HWND, color: Option<Color>, state: u32) {
let mut version = unsafe { std::mem::zeroed() };
let status = unsafe { windows::Wdk::System::SystemServices::RtlGetVersion(&mut version) };
if !status.is_ok() || version.dwBuildNumber < 17763 {
return;
}
unsafe {
type SetWindowCompositionAttributeType =
unsafe extern "system" fn(HWND, *mut WINDOWCOMPOSITIONATTRIBDATA) -> BOOL;