From 4be8544777deea7e5ab7a3cf19fb90a13dc80d67 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Mon, 2 Mar 2026 13:30:40 -0700 Subject: [PATCH] wGPU: Select more specifically (#50528) This uses the compositor hints if available to pick the best GPU. If none is available, it tries each GPU in turn, and the first that actually works is chosen Release Notes: - Linux: Select a more appropriate GPU --------- Co-authored-by: John Tur --- crates/gpui_linux/Cargo.toml | 1 + crates/gpui_linux/src/linux/platform.rs | 40 +++ crates/gpui_linux/src/linux/wayland/client.rs | 74 +++++- crates/gpui_linux/src/linux/wayland/window.rs | 7 +- crates/gpui_linux/src/linux/x11/client.rs | 36 ++- crates/gpui_linux/src/linux/x11/window.rs | 7 +- crates/gpui_wgpu/src/wgpu_context.rs | 237 ++++++++++++------ crates/gpui_wgpu/src/wgpu_renderer.rs | 21 +- crates/zed/src/main.rs | 2 +- 9 files changed, 339 insertions(+), 86 deletions(-) diff --git a/crates/gpui_linux/Cargo.toml b/crates/gpui_linux/Cargo.toml index 08c759125a7600f94867cff95035d0318f26305a..9078fa82c2884421c6cd11c6d3384645621b7e6f 100644 --- a/crates/gpui_linux/Cargo.toml +++ b/crates/gpui_linux/Cargo.toml @@ -121,6 +121,7 @@ x11rb = { version = "0.13.1", features = [ "cursor", "resource_manager", "sync", + "dri3", ], optional = true } # WARNING: If you change this, you must also publish a new version of zed-xim to crates.io xim = { git = "https://github.com/zed-industries/xim-rs.git", rev = "16f35a2c881b815a2b6cdfd6687988e84f8447d8", features = [ diff --git a/crates/gpui_linux/src/linux/platform.rs b/crates/gpui_linux/src/linux/platform.rs index ff79aa64b2f7cd61c3ab6a8b54e2e11b72614d0f..924303cc84b5c662847bdde96979239073adbe19 100644 --- a/crates/gpui_linux/src/linux/platform.rs +++ b/crates/gpui_linux/src/linux/platform.rs @@ -1038,6 +1038,46 @@ pub(super) fn capslock_from_xkb(keymap_state: &State) -> gpui::Capslock { gpui::Capslock { on } } +/// Resolve a Linux `dev_t` to PCI vendor/device IDs via sysfs, returning a +/// [`CompositorGpuHint`] that the GPU adapter selection code can use to +/// prioritize the compositor's rendering device. +#[cfg(any(feature = "wayland", feature = "x11"))] +pub(super) fn compositor_gpu_hint_from_dev_t(dev: u64) -> Option { + fn dev_major(dev: u64) -> u32 { + ((dev >> 8) & 0xfff) as u32 | (((dev >> 32) & !0xfff) as u32) + } + + fn dev_minor(dev: u64) -> u32 { + (dev & 0xff) as u32 | (((dev >> 12) & !0xff) as u32) + } + + fn read_sysfs_hex_id(path: &str) -> Option { + let content = std::fs::read_to_string(path).ok()?; + let trimmed = content.trim().strip_prefix("0x").unwrap_or(content.trim()); + u32::from_str_radix(trimmed, 16).ok() + } + + let major = dev_major(dev); + let minor = dev_minor(dev); + + let vendor_path = format!("/sys/dev/char/{major}:{minor}/device/vendor"); + let device_path = format!("/sys/dev/char/{major}:{minor}/device/device"); + + let vendor_id = read_sysfs_hex_id(&vendor_path)?; + let device_id = read_sysfs_hex_id(&device_path)?; + + log::info!( + "Compositor GPU hint: vendor={:#06x}, device={:#06x} (from dev {major}:{minor})", + vendor_id, + device_id, + ); + + Some(gpui_wgpu::CompositorGpuHint { + vendor_id, + device_id, + }) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/gpui_linux/src/linux/wayland/client.rs b/crates/gpui_linux/src/linux/wayland/client.rs index a810a00af642c3a252a9a144b884837f82eac7e7..b49e269a72459d52c13c21b8d1a474ab310dbffd 100644 --- a/crates/gpui_linux/src/linux/wayland/client.rs +++ b/crates/gpui_linux/src/linux/wayland/client.rs @@ -95,7 +95,10 @@ use gpui::{ ScrollDelta, ScrollWheelEvent, SharedString, Size, TaskTiming, TouchPhase, WindowParams, point, profiler, px, size, }; -use gpui_wgpu::WgpuContext; +use gpui_wgpu::{CompositorGpuHint, WgpuContext}; +use wayland_protocols::wp::linux_dmabuf::zv1::client::{ + zwp_linux_dmabuf_feedback_v1, zwp_linux_dmabuf_v1, +}; /// Used to convert evdev scancode to xkb scancode const MIN_KEYCODE: u32 = 8; @@ -202,6 +205,7 @@ pub(crate) struct WaylandClientState { serial_tracker: SerialTracker, globals: Globals, pub gpu_context: Option, + pub compositor_gpu: Option, wl_seat: wl_seat::WlSeat, // TODO: Multi seat support wl_pointer: Option, wl_keyboard: Option, @@ -515,6 +519,7 @@ impl WaylandClient { }) .unwrap(); + let compositor_gpu = detect_compositor_gpu(); let gpu_context = None; let seat = seat.unwrap(); @@ -571,6 +576,7 @@ impl WaylandClient { serial_tracker: SerialTracker::new(), globals, gpu_context, + compositor_gpu, wl_seat: seat, wl_pointer: None, wl_keyboard: None, @@ -715,10 +721,12 @@ impl LinuxClient for WaylandClient { let parent = state.keyboard_focused_window.clone(); let appearance = state.common.appearance; + let compositor_gpu = state.compositor_gpu.take(); let (window, surface_id) = WaylandWindow::new( handle, state.globals.clone(), &mut state.gpu_context, + compositor_gpu, WaylandClientStatePtr(Rc::downgrade(&self.0)), params, appearance, @@ -904,6 +912,70 @@ impl LinuxClient for WaylandClient { } } +struct DmabufProbeState { + device: Option, +} + +impl Dispatch for DmabufProbeState { + fn event( + _: &mut Self, + _: &wl_registry::WlRegistry, + _: wl_registry::Event, + _: &GlobalListContents, + _: &Connection, + _: &QueueHandle, + ) { + } +} + +impl Dispatch for DmabufProbeState { + fn event( + _: &mut Self, + _: &zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1, + _: zwp_linux_dmabuf_v1::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + } +} + +impl Dispatch for DmabufProbeState { + fn event( + state: &mut Self, + _: &zwp_linux_dmabuf_feedback_v1::ZwpLinuxDmabufFeedbackV1, + event: zwp_linux_dmabuf_feedback_v1::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + if let zwp_linux_dmabuf_feedback_v1::Event::MainDevice { device } = event { + if let Ok(bytes) = <[u8; 8]>::try_from(device.as_slice()) { + state.device = Some(u64::from_ne_bytes(bytes)); + } + } + } +} + +fn detect_compositor_gpu() -> Option { + let connection = Connection::connect_to_env().ok()?; + let (globals, mut event_queue) = registry_queue_init::(&connection).ok()?; + let queue_handle = event_queue.handle(); + + let dmabuf: zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1 = + globals.bind(&queue_handle, 4..=4, ()).ok()?; + let feedback = dmabuf.get_default_feedback(&queue_handle, ()); + + let mut state = DmabufProbeState { device: None }; + + event_queue.roundtrip(&mut state).ok()?; + + feedback.destroy(); + dmabuf.destroy(); + + crate::linux::compositor_gpu_hint_from_dev_t(state.device?) +} + impl Dispatch for WaylandClientStatePtr { fn event( this: &mut Self, diff --git a/crates/gpui_linux/src/linux/wayland/window.rs b/crates/gpui_linux/src/linux/wayland/window.rs index dd8e0b27c32ca9d15152028e686b065165a9e0c1..4c0dbae530ee254f5232eaead187b93d10b0b8e3 100644 --- a/crates/gpui_linux/src/linux/wayland/window.rs +++ b/crates/gpui_linux/src/linux/wayland/window.rs @@ -34,7 +34,7 @@ use gpui::{ WindowDecorations, WindowKind, WindowParams, layer_shell::LayerShellNotSupportedError, px, size, }; -use gpui_wgpu::{WgpuContext, WgpuRenderer, WgpuSurfaceConfig}; +use gpui_wgpu::{CompositorGpuHint, WgpuContext, WgpuRenderer, WgpuSurfaceConfig}; #[derive(Default)] pub(crate) struct Callbacks { @@ -318,6 +318,7 @@ impl WaylandWindowState { client: WaylandClientStatePtr, globals: Globals, gpu_context: &mut Option, + compositor_gpu: Option, options: WindowParams, parent: Option, ) -> anyhow::Result { @@ -338,7 +339,7 @@ impl WaylandWindowState { }, transparent: true, }; - WgpuRenderer::new(gpu_context, &raw_window, config)? + WgpuRenderer::new(gpu_context, &raw_window, config, compositor_gpu)? }; if let WaylandSurfaceState::Xdg(ref xdg_state) = surface_state { @@ -488,6 +489,7 @@ impl WaylandWindow { handle: AnyWindowHandle, globals: Globals, gpu_context: &mut Option, + compositor_gpu: Option, client: WaylandClientStatePtr, params: WindowParams, appearance: WindowAppearance, @@ -515,6 +517,7 @@ impl WaylandWindow { client, globals, gpu_context, + compositor_gpu, params, parent, )?)), diff --git a/crates/gpui_linux/src/linux/x11/client.rs b/crates/gpui_linux/src/linux/x11/client.rs index 7e3f67c9bf5fe3176f3badd9b33375ffdeb9dc19..3a970d9f72e1dc82215fc0d11297d222835df431 100644 --- a/crates/gpui_linux/src/linux/x11/client.rs +++ b/crates/gpui_linux/src/linux/x11/client.rs @@ -31,7 +31,7 @@ use x11rb::{ AtomEnum, ChangeWindowAttributesAux, ClientMessageData, ClientMessageEvent, ConnectionExt as _, EventMask, ModMask, Visibility, }, - protocol::{Event, randr, render, xinput, xkb, xproto}, + protocol::{Event, dri3, randr, render, xinput, xkb, xproto}, resource_manager::Database, wrapper::ConnectionExt as _, xcb_ffi::XCBConnection, @@ -64,7 +64,7 @@ use gpui::{ PlatformKeyboardLayout, PlatformWindow, Point, RequestFrameOptions, ScrollDelta, Size, TouchPhase, WindowParams, point, px, }; -use gpui_wgpu::WgpuContext; +use gpui_wgpu::{CompositorGpuHint, WgpuContext}; /// Value for DeviceId parameters which selects all devices. pub(crate) const XINPUT_ALL_DEVICES: xinput::DeviceId = 0; @@ -178,6 +178,7 @@ pub struct X11ClientState { pub(crate) current_count: usize, pub(crate) gpu_context: Option, + pub(crate) compositor_gpu: Option, pub(crate) scale_factor: f32, @@ -430,6 +431,9 @@ impl X11Client { let clipboard = Clipboard::new().context("Failed to initialize clipboard")?; + let screen = &xcb_connection.setup().roots[x_root_index]; + let compositor_gpu = detect_compositor_gpu(&xcb_connection, screen); + let xcb_connection = Rc::new(xcb_connection); let ximc = X11rbClient::init(Rc::clone(&xcb_connection), x_root_index, None).ok(); @@ -490,6 +494,7 @@ impl X11Client { last_location: Point::new(px(0.0), px(0.0)), current_count: 0, gpu_context: None, + compositor_gpu, scale_factor, xkb_context, @@ -1514,11 +1519,13 @@ impl LinuxClient for X11Client { let atoms = state.atoms; let scale_factor = state.scale_factor; let appearance = state.common.appearance; + let compositor_gpu = state.compositor_gpu.take(); let window = X11Window::new( handle, X11ClientStatePtr(Rc::downgrade(&self.0)), state.common.foreground_executor.clone(), &mut state.gpu_context, + compositor_gpu, params, &xcb_connection, client_side_decorations_supported, @@ -1976,7 +1983,30 @@ fn fp3232_to_f32(value: xinput::Fp3232) -> f32 { value.integral as f32 + value.frac as f32 / u32::MAX as f32 } -fn check_compositor_present(xcb_connection: &XCBConnection, root: u32) -> bool { +fn detect_compositor_gpu( + xcb_connection: &XCBConnection, + screen: &xproto::Screen, +) -> Option { + use std::os::fd::AsRawFd; + use std::os::unix::fs::MetadataExt; + + xcb_connection + .extension_information(dri3::X11_EXTENSION_NAME) + .ok()??; + + let reply = dri3::open(xcb_connection, screen.root, 0) + .ok()? + .reply() + .ok()?; + let fd = reply.device_fd; + + let path = format!("/proc/self/fd/{}", fd.as_raw_fd()); + let metadata = std::fs::metadata(&path).ok()?; + + crate::linux::compositor_gpu_hint_from_dev_t(metadata.rdev()) +} + +fn check_compositor_present(xcb_connection: &XCBConnection, root: xproto::Window) -> bool { // Method 1: Check for _NET_WM_CM_S{root} let atom_name = format!("_NET_WM_CM_S{}", root); let atom1 = get_reply( diff --git a/crates/gpui_linux/src/linux/x11/window.rs b/crates/gpui_linux/src/linux/x11/window.rs index 55da1d89947eb9a39937b9e70b05ab71aceb6525..f2199ac65e425a8daa04755115264231dd869837 100644 --- a/crates/gpui_linux/src/linux/x11/window.rs +++ b/crates/gpui_linux/src/linux/x11/window.rs @@ -9,7 +9,7 @@ use gpui::{ Tiling, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowControlArea, WindowDecorations, WindowKind, WindowParams, px, }; -use gpui_wgpu::{WgpuContext, WgpuRenderer, WgpuSurfaceConfig}; +use gpui_wgpu::{CompositorGpuHint, WgpuContext, WgpuRenderer, WgpuSurfaceConfig}; use collections::FxHashSet; use raw_window_handle as rwh; @@ -392,6 +392,7 @@ impl X11WindowState { client: X11ClientStatePtr, executor: ForegroundExecutor, gpu_context: &mut Option, + compositor_gpu: Option, params: WindowParams, xcb: &Rc, client_side_decorations_supported: bool, @@ -679,7 +680,7 @@ impl X11WindowState { // too transparent: false, }; - WgpuRenderer::new(gpu_context, &raw_window, config)? + WgpuRenderer::new(gpu_context, &raw_window, config, compositor_gpu)? }; // Set max window size hints based on the GPU's maximum texture dimension. @@ -803,6 +804,7 @@ impl X11Window { client: X11ClientStatePtr, executor: ForegroundExecutor, gpu_context: &mut Option, + compositor_gpu: Option, params: WindowParams, xcb: &Rc, client_side_decorations_supported: bool, @@ -819,6 +821,7 @@ impl X11Window { client, executor, gpu_context, + compositor_gpu, params, xcb, client_side_decorations_supported, diff --git a/crates/gpui_wgpu/src/wgpu_context.rs b/crates/gpui_wgpu/src/wgpu_context.rs index 84b7166f6e6b97a9dc7f16c76069872bae473161..b7883a6910261da8dc3f1df6414c5e38e1c46cd2 100644 --- a/crates/gpui_wgpu/src/wgpu_context.rs +++ b/crates/gpui_wgpu/src/wgpu_context.rs @@ -12,9 +12,19 @@ pub struct WgpuContext { dual_source_blending: bool, } +#[cfg(not(target_family = "wasm"))] +pub struct CompositorGpuHint { + pub vendor_id: u32, + pub device_id: u32, +} + impl WgpuContext { #[cfg(not(target_family = "wasm"))] - pub fn new(instance: wgpu::Instance, surface: &wgpu::Surface<'_>) -> anyhow::Result { + pub fn new( + instance: wgpu::Instance, + surface: &wgpu::Surface<'_>, + compositor_gpu: Option, + ) -> anyhow::Result { let device_id_filter = match std::env::var("ZED_DEVICE_ID") { Ok(val) => parse_pci_id(&val) .context("Failed to parse device ID from `ZED_DEVICE_ID` environment variable") @@ -27,24 +37,15 @@ impl WgpuContext { } }; - let adapter = pollster::block_on(Self::select_adapter( - &instance, - device_id_filter, - Some(surface), - ))?; - - let caps = surface.get_capabilities(&adapter); - if caps.formats.is_empty() { - let info = adapter.get_info(); - anyhow::bail!( - "No adapter compatible with the display surface could be found. \ - Best candidate {:?} (backend={:?}, device={:#06x}) reports no \ - supported surface formats.", - info.name, - info.backend, - info.device, - ); - } + // Select an adapter by actually testing surface configuration with the real device. + // This is the only reliable way to determine compatibility on hybrid GPU systems. + let (adapter, device, queue, dual_source_blending) = + pollster::block_on(Self::select_adapter_and_device( + &instance, + device_id_filter, + surface, + compositor_gpu.as_ref(), + ))?; log::info!( "Selected GPU adapter: {:?} ({:?})", @@ -52,9 +53,6 @@ impl WgpuContext { adapter.get_info().backend ); - let (device, queue, dual_source_blending) = - pollster::block_on(Self::create_device(&adapter))?; - Ok(Self { instance, adapter, @@ -158,70 +156,165 @@ impl WgpuContext { Ok(()) } + /// Select an adapter and create a device, testing that the surface can actually be configured. + /// This is the only reliable way to determine compatibility on hybrid GPU systems, where + /// adapters may report surface compatibility via get_capabilities() but fail when actually + /// configuring (e.g., NVIDIA reporting Vulkan Wayland support but failing because the + /// Wayland compositor runs on the Intel GPU). #[cfg(not(target_family = "wasm"))] - async fn select_adapter( + async fn select_adapter_and_device( instance: &wgpu::Instance, device_id_filter: Option, - compatible_surface: Option<&wgpu::Surface<'_>>, - ) -> anyhow::Result { + surface: &wgpu::Surface<'_>, + compositor_gpu: Option<&CompositorGpuHint>, + ) -> anyhow::Result<(wgpu::Adapter, wgpu::Device, wgpu::Queue, bool)> { + let mut adapters: Vec<_> = instance.enumerate_adapters(wgpu::Backends::all()).await; + + if adapters.is_empty() { + anyhow::bail!("No GPU adapters found"); + } + if let Some(device_id) = device_id_filter { - let adapters: Vec<_> = instance.enumerate_adapters(wgpu::Backends::all()).await; + log::info!("ZED_DEVICE_ID filter: {:#06x}", device_id); + } - if adapters.is_empty() { - anyhow::bail!("No GPU adapters found"); - } + // Sort adapters into a single priority order. Tiers (from highest to lowest): + // + // 1. ZED_DEVICE_ID match — explicit user override + // 2. Compositor GPU match — the GPU the display server is rendering on + // 3. Device type — WGPU HighPerformance order (Discrete > Integrated > + // Other > Virtual > Cpu). "Other" ranks above "Virtual" because + // backends like OpenGL may report real hardware as "Other". + // 4. Backend — prefer Vulkan/Metal/Dx12 over GL/etc. + adapters.sort_by_key(|adapter| { + let info = adapter.get_info(); + + // Backends like OpenGL report device=0 for all adapters, so + // device-based matching is only meaningful when non-zero. + let device_known = info.device != 0; + + let user_override: u8 = match device_id_filter { + Some(id) if device_known && info.device == id => 0, + _ => 1, + }; + + let compositor_match: u8 = match compositor_gpu { + Some(hint) + if device_known + && info.vendor == hint.vendor_id + && info.device == hint.device_id => + { + 0 + } + _ => 1, + }; + + let type_priority: u8 = match info.device_type { + wgpu::DeviceType::DiscreteGpu => 0, + wgpu::DeviceType::IntegratedGpu => 1, + wgpu::DeviceType::Other => 2, + wgpu::DeviceType::VirtualGpu => 3, + wgpu::DeviceType::Cpu => 4, + }; + + let backend_priority: u8 = match info.backend { + wgpu::Backend::Vulkan => 0, + wgpu::Backend::Metal => 0, + wgpu::Backend::Dx12 => 0, + _ => 1, + }; + + ( + user_override, + compositor_match, + type_priority, + backend_priority, + ) + }); - let mut non_matching_adapter_infos: Vec = Vec::new(); - - for adapter in adapters.into_iter() { - let info = adapter.get_info(); - if info.device == device_id { - if let Some(surface) = compatible_surface { - let caps = surface.get_capabilities(&adapter); - if caps.formats.is_empty() { - log::warn!( - "GPU matching ZED_DEVICE_ID={:#06x} ({}) is not compatible \ - with the display surface. Falling back to auto-selection.", - device_id, - info.name, - ); - break; - } - } + // Log all available adapters (in sorted order) + log::info!("Found {} GPU adapter(s):", adapters.len()); + for adapter in &adapters { + let info = adapter.get_info(); + log::info!( + " - {} (vendor={:#06x}, device={:#06x}, backend={:?}, type={:?})", + info.name, + info.vendor, + info.device, + info.backend, + info.device_type, + ); + } + + // Test each adapter by creating a device and configuring the surface + for adapter in adapters { + let info = adapter.get_info(); + log::info!("Testing adapter: {} ({:?})...", info.name, info.backend); + + match Self::try_adapter_with_surface(&adapter, surface).await { + Ok((device, queue, dual_source_blending)) => { log::info!( - "Found GPU matching ZED_DEVICE_ID={:#06x}: {}", - device_id, - info.name + "Selected GPU (passed configuration test): {} ({:?})", + info.name, + info.backend + ); + return Ok((adapter, device, queue, dual_source_blending)); + } + Err(e) => { + log::info!( + " Adapter {} ({:?}) failed: {}, trying next...", + info.name, + info.backend, + e ); - return Ok(adapter); - } else { - non_matching_adapter_infos.push(info); } } + } - log::warn!( - "No compatible GPU found matching ZED_DEVICE_ID={:#06x}. Available devices:", - device_id - ); + anyhow::bail!("No GPU adapter found that can configure the display surface") + } - for info in &non_matching_adapter_infos { - log::warn!( - " - {} (device_id={:#06x}, backend={})", - info.name, - info.device, - info.backend - ); - } + /// Try to use an adapter with a surface by creating a device and testing configuration. + /// Returns the device and queue if successful, allowing them to be reused. + #[cfg(not(target_family = "wasm"))] + async fn try_adapter_with_surface( + adapter: &wgpu::Adapter, + surface: &wgpu::Surface<'_>, + ) -> anyhow::Result<(wgpu::Device, wgpu::Queue, bool)> { + let caps = surface.get_capabilities(adapter); + if caps.formats.is_empty() { + anyhow::bail!("no compatible surface formats"); + } + if caps.alpha_modes.is_empty() { + anyhow::bail!("no compatible alpha modes"); } - instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::HighPerformance, - compatible_surface, - force_fallback_adapter: false, - }) - .await - .map_err(|e| anyhow::anyhow!("Failed to request GPU adapter: {e}")) + // Create the real device with full features + let (device, queue, dual_source_blending) = Self::create_device(adapter).await?; + + // Use an error scope to capture any validation errors during configure + let error_scope = device.push_error_scope(wgpu::ErrorFilter::Validation); + + let test_config = wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: caps.formats[0], + width: 64, + height: 64, + present_mode: wgpu::PresentMode::Fifo, + desired_maximum_frame_latency: 2, + alpha_mode: caps.alpha_modes[0], + view_formats: vec![], + }; + + surface.configure(&device, &test_config); + + // Check if there was a validation error + let error = error_scope.pop().await; + if let Some(e) = error { + anyhow::bail!("surface configuration failed: {e}"); + } + + Ok((device, queue, dual_source_blending)) } pub fn supports_dual_source_blending(&self) -> bool { diff --git a/crates/gpui_wgpu/src/wgpu_renderer.rs b/crates/gpui_wgpu/src/wgpu_renderer.rs index 6e4169e34b4706dbdcdfc88238c170ec484180be..bbecca198eb3ae46b739ab4c42267e7f04b0f7a9 100644 --- a/crates/gpui_wgpu/src/wgpu_renderer.rs +++ b/crates/gpui_wgpu/src/wgpu_renderer.rs @@ -1,3 +1,5 @@ +#[cfg(not(target_family = "wasm"))] +use crate::CompositorGpuHint; use crate::{WgpuAtlas, WgpuContext}; use bytemuck::{Pod, Zeroable}; use gpui::{ @@ -96,6 +98,7 @@ pub struct WgpuRenderer { queue: Arc, surface: wgpu::Surface<'static>, surface_config: wgpu::SurfaceConfiguration, + surface_configured: bool, pipelines: WgpuPipelines, bind_group_layouts: WgpuBindGroupLayouts, atlas: Arc, @@ -132,6 +135,7 @@ impl WgpuRenderer { gpu_context: &mut Option, window: &W, config: WgpuSurfaceConfig, + compositor_gpu: Option, ) -> anyhow::Result { let window_handle = window .window_handle() @@ -167,7 +171,7 @@ impl WgpuRenderer { context.check_compatible_with_surface(&surface)?; context } - None => gpu_context.insert(WgpuContext::new(instance, &surface)?), + None => gpu_context.insert(WgpuContext::new(instance, &surface, compositor_gpu)?), }; Self::new_with_surface(context, surface, config) @@ -186,7 +190,7 @@ impl WgpuRenderer { Self::new_with_surface(context, surface, config) } - pub fn new_with_surface( + fn new_with_surface( context: &WgpuContext, surface: wgpu::Surface<'static>, config: WgpuSurfaceConfig, @@ -266,6 +270,8 @@ impl WgpuRenderer { alpha_mode, view_formats: vec![], }; + // Configure the surface immediately. The adapter selection process already validated + // that this adapter can successfully configure this surface. surface.configure(&context.device, &surface_config); let queue = Arc::clone(&context.queue); @@ -366,6 +372,7 @@ impl WgpuRenderer { queue, surface, surface_config, + surface_configured: true, pipelines, bind_group_layouts, atlas, @@ -857,7 +864,9 @@ impl WgpuRenderer { self.surface_config.width = clamped_width.max(1); self.surface_config.height = clamped_height.max(1); - self.surface.configure(&self.device, &self.surface_config); + if self.surface_configured { + self.surface.configure(&self.device, &self.surface_config); + } // Invalidate intermediate textures - they will be lazily recreated // in draw() after we confirm the surface is healthy. This avoids @@ -908,7 +917,9 @@ impl WgpuRenderer { if new_alpha_mode != self.surface_config.alpha_mode { self.surface_config.alpha_mode = new_alpha_mode; - self.surface.configure(&self.device, &self.surface_config); + if self.surface_configured { + self.surface.configure(&self.device, &self.surface_config); + } self.pipelines = Self::create_pipelines( &self.device, &self.bind_group_layouts, @@ -955,7 +966,7 @@ impl WgpuRenderer { let frame = match self.surface.get_current_texture() { Ok(frame) => frame, Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => { - self.surface.configure(&self.device, &self.surface_config); + self.surface_configured = false; return; } Err(e) => { diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index cfa339afc08faeac8b050ef3d3abbe627b19dadf..0921c12c2f06cea32ccba0e0bc58553d2fa91ab2 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -276,7 +276,7 @@ fn main() { zlog::init(); - if stdout_is_a_pty() { + if true { zlog::init_output_stdout(); } else { let result = zlog::init_output_file(paths::log_file(), Some(paths::old_log_file()));