From 298b9df589c2be910676b59cb8213f98a8396fed Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 18 Dec 2024 13:47:09 -0800 Subject: [PATCH] Switch to a single GPU context in Blade (#20853) Closes #17005 Release Notes: - Improved GPU context management: share a single context with multiple surfaces. ### High Level Blade got a proper support for Surface objects in https://github.com/kvark/blade/pull/203. That was mainly motivated by Zed needing to draw multiple windows. With the Surface API, Zed is now able to have the GPU context tied to the "Platform" instead of "Window". Practically speaking, this means: - architecture more sound - faster to open/close windows - less surprises, more robust ### Concerns 1. Zed has been using a temporary workaround for the platform bug on some Intel+Nvidia machines that makes us unable to present - https://github.com/kvark/blade/pull/144 . This workaround is no longer available with the new architecture. I'm looking for ideas on how to approach this better. - we are now picking up the change in https://github.com/kvark/blade/pull/210, which allows forcing a specific Device ID. This should allow Zed users to work around the issue. We could help them to automate it, too. 2. ~~Metal-rs dependency is switched to https://github.com/kvark/metal-rs/tree/blade, since upstream isn't responsive in merging changes that are required for Blade. Hopefully, temporary.~~ - ~~we can also hack around it by just transmuting the texture references, since we know those are unchanged in the branch. That would allow Blade to use it's own version of Metal, temporarily, if switching metal-rs in the workspace is a concern.~~ - merged my metal-rs changes and updated Zed to use the upstream github reference --------- Co-authored-by: Mikayla Maki Co-authored-by: Mikayla Maki Co-authored-by: Conrad Irwin --- Cargo.lock | 33 +--- Cargo.toml | 12 +- crates/gpui/Cargo.toml | 2 +- crates/gpui/src/platform/blade.rs | 6 + .../gpui/src/platform/blade/apple_compat.rs | 60 +++++++ crates/gpui/src/platform/blade/blade_atlas.rs | 2 +- .../gpui/src/platform/blade/blade_context.rs | 24 +++ .../gpui/src/platform/blade/blade_renderer.rs | 155 +++++++----------- crates/gpui/src/platform/linux/platform.rs | 89 +++++----- .../gpui/src/platform/linux/wayland/client.rs | 59 ++++--- .../gpui/src/platform/linux/wayland/window.rs | 74 ++++----- crates/gpui/src/platform/linux/x11/client.rs | 87 ++++++---- crates/gpui/src/platform/linux/x11/window.rs | 59 +++---- crates/gpui/src/platform/windows/platform.rs | 12 +- crates/gpui/src/platform/windows/window.rs | 45 ++--- crates/media/Cargo.toml | 2 +- 16 files changed, 380 insertions(+), 341 deletions(-) create mode 100644 crates/gpui/src/platform/blade/apple_compat.rs create mode 100644 crates/gpui/src/platform/blade/blade_context.rs diff --git a/Cargo.lock b/Cargo.lock index 45aa6431b6..d4e18f46fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1750,15 +1750,6 @@ dependencies = [ "bit-vec 0.6.3", ] -[[package]] -name = "bit-set" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0481a0e032742109b1133a095184ee93d88f3dc9e0d28a5d033dc77a073f44f" -dependencies = [ - "bit-vec 0.7.0", -] - [[package]] name = "bit-set" version = "0.8.0" @@ -1774,12 +1765,6 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" -[[package]] -name = "bit-vec" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22" - [[package]] name = "bit-vec" version = "0.8.0" @@ -1828,7 +1813,7 @@ dependencies = [ [[package]] name = "blade-graphics" version = "0.5.0" -source = "git+https://github.com/kvark/blade?rev=e142a3a5e678eb6a13e642ad8401b1f3aa38e969#e142a3a5e678eb6a13e642ad8401b1f3aa38e969" +source = "git+https://github.com/kvark/blade?rev=099555282605c7c4cca9e66a8f40148298347f80#099555282605c7c4cca9e66a8f40148298347f80" dependencies = [ "ash", "ash-window", @@ -1858,7 +1843,7 @@ dependencies = [ [[package]] name = "blade-macros" version = "0.3.0" -source = "git+https://github.com/kvark/blade?rev=e142a3a5e678eb6a13e642ad8401b1f3aa38e969#e142a3a5e678eb6a13e642ad8401b1f3aa38e969" +source = "git+https://github.com/kvark/blade?rev=099555282605c7c4cca9e66a8f40148298347f80#099555282605c7c4cca9e66a8f40148298347f80" dependencies = [ "proc-macro2", "quote", @@ -1868,7 +1853,7 @@ dependencies = [ [[package]] name = "blade-util" version = "0.1.0" -source = "git+https://github.com/kvark/blade?rev=e142a3a5e678eb6a13e642ad8401b1f3aa38e969#e142a3a5e678eb6a13e642ad8401b1f3aa38e969" +source = "git+https://github.com/kvark/blade?rev=099555282605c7c4cca9e66a8f40148298347f80#099555282605c7c4cca9e66a8f40148298347f80" dependencies = [ "blade-graphics", "bytemuck", @@ -7594,9 +7579,8 @@ dependencies = [ [[package]] name = "metal" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" +version = "0.30.0" +source = "git+https://github.com/gfx-rs/metal-rs?rev=ef768ff9d742ae6a0f4e83ddc8031264e7d460c4#ef768ff9d742ae6a0f4e83ddc8031264e7d460c4" dependencies = [ "bitflags 2.6.0", "block", @@ -7735,12 +7719,11 @@ checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03" [[package]] name = "naga" -version = "22.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd5a652b6faf21496f2cfd88fc49989c8db0825d1f6746b1a71a6ede24a63ad" +version = "23.0.0" +source = "git+https://github.com/gfx-rs/wgpu?rev=1a643291c2e8854ba7e4f5445a4388202731bfa1#1a643291c2e8854ba7e4f5445a4388202731bfa1" dependencies = [ "arrayvec", - "bit-set 0.6.0", + "bit-set 0.8.0", "bitflags 2.6.0", "cfg_aliases 0.1.1", "codespan-reporting", diff --git a/Cargo.toml b/Cargo.toml index 12eb379fff..a43e85ce65 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -355,9 +355,9 @@ async-watch = "0.3.1" async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] } base64 = "0.22" bitflags = "2.6.0" -blade-graphics = { git = "https://github.com/kvark/blade", rev = "e142a3a5e678eb6a13e642ad8401b1f3aa38e969" } -blade-macros = { git = "https://github.com/kvark/blade", rev = "e142a3a5e678eb6a13e642ad8401b1f3aa38e969" } -blade-util = { git = "https://github.com/kvark/blade", rev = "e142a3a5e678eb6a13e642ad8401b1f3aa38e969" } +blade-graphics = { git = "https://github.com/kvark/blade", rev = "099555282605c7c4cca9e66a8f40148298347f80" } +blade-macros = { git = "https://github.com/kvark/blade", rev = "099555282605c7c4cca9e66a8f40148298347f80" } +blade-util = { git = "https://github.com/kvark/blade", rev = "099555282605c7c4cca9e66a8f40148298347f80" } blake3 = "1.5.3" bytes = "1.0" cargo_metadata = "0.19" @@ -525,6 +525,12 @@ wasmtime-wasi = "24" which = "6.0.0" wit-component = "0.201" zstd = "0.11" +# Custom metal-rs is only needed for "macos-blade" feature of GPUI +#TODO: switch to crates once these are published: +# - https://github.com/gfx-rs/metal-rs/pull/335 +# - https://github.com/gfx-rs/metal-rs/pull/336 +# - https://github.com/gfx-rs/metal-rs/pull/337 +metal = { git = "https://github.com/gfx-rs/metal-rs", rev = "ef768ff9d742ae6a0f4e83ddc8031264e7d460c4" } [workspace.dependencies.async-stripe] git = "https://github.com/zed-industries/async-stripe" diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 347d70853a..aed1c25168 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -136,8 +136,8 @@ font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "40391b7" foreign-types = "0.5" log.workspace = true media.workspace = true -metal = "0.29" objc = "0.2" +metal.workspace = true [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))'.dependencies] pathfinder_geometry = "0.5" diff --git a/crates/gpui/src/platform/blade.rs b/crates/gpui/src/platform/blade.rs index 736c1888d8..9d966d8a4e 100644 --- a/crates/gpui/src/platform/blade.rs +++ b/crates/gpui/src/platform/blade.rs @@ -1,5 +1,11 @@ +#[cfg(target_os = "macos")] +mod apple_compat; mod blade_atlas; +mod blade_context; mod blade_renderer; +#[cfg(target_os = "macos")] +pub(crate) use apple_compat::*; pub(crate) use blade_atlas::*; +pub(crate) use blade_context::*; pub(crate) use blade_renderer::*; diff --git a/crates/gpui/src/platform/blade/apple_compat.rs b/crates/gpui/src/platform/blade/apple_compat.rs new file mode 100644 index 0000000000..b1baab8854 --- /dev/null +++ b/crates/gpui/src/platform/blade/apple_compat.rs @@ -0,0 +1,60 @@ +use super::{BladeContext, BladeRenderer, BladeSurfaceConfig}; +use blade_graphics as gpu; +use std::{ffi::c_void, ptr::NonNull}; + +#[derive(Clone)] +pub struct Context { + inner: BladeContext, +} +impl Default for Context { + fn default() -> Self { + Self { + inner: BladeContext::new().unwrap(), + } + } +} + +pub type Renderer = BladeRenderer; + +pub unsafe fn new_renderer( + context: Context, + _native_window: *mut c_void, + native_view: *mut c_void, + bounds: crate::Size, + transparent: bool, +) -> Renderer { + use raw_window_handle as rwh; + struct RawWindow { + view: *mut c_void, + } + + impl rwh::HasWindowHandle for RawWindow { + fn window_handle(&self) -> Result { + let view = NonNull::new(self.view).unwrap(); + let handle = rwh::AppKitWindowHandle::new(view); + Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) }) + } + } + impl rwh::HasDisplayHandle for RawWindow { + fn display_handle(&self) -> Result { + let handle = rwh::AppKitDisplayHandle::new(); + Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) }) + } + } + + BladeRenderer::new( + &context.inner, + &RawWindow { + view: native_view as *mut _, + }, + BladeSurfaceConfig { + size: gpu::Extent { + width: bounds.width as u32, + height: bounds.height as u32, + depth: 1, + }, + transparent, + }, + ) + .unwrap() +} diff --git a/crates/gpui/src/platform/blade/blade_atlas.rs b/crates/gpui/src/platform/blade/blade_atlas.rs index b876d5bb9b..9e666d71bf 100644 --- a/crates/gpui/src/platform/blade/blade_atlas.rs +++ b/crates/gpui/src/platform/blade/blade_atlas.rs @@ -268,7 +268,7 @@ impl BladeAtlasState { fn flush(&mut self, encoder: &mut gpu::CommandEncoder) { self.flush_initializations(encoder); - let mut transfers = encoder.transfer(); + let mut transfers = encoder.transfer("atlas"); for upload in self.uploads.drain(..) { let texture = &self.storage[upload.id]; transfers.copy_buffer_to_texture( diff --git a/crates/gpui/src/platform/blade/blade_context.rs b/crates/gpui/src/platform/blade/blade_context.rs new file mode 100644 index 0000000000..f03fff01e0 --- /dev/null +++ b/crates/gpui/src/platform/blade/blade_context.rs @@ -0,0 +1,24 @@ +use blade_graphics as gpu; +use std::sync::Arc; + +#[cfg_attr(target_os = "macos", derive(Clone))] +pub struct BladeContext { + pub(super) gpu: Arc, +} + +impl BladeContext { + pub fn new() -> anyhow::Result { + let gpu = Arc::new( + unsafe { + gpu::Context::init(gpu::ContextDesc { + presentation: true, + validation: false, + device_id: 0, //TODO: hook up to user settings + ..Default::default() + }) + } + .map_err(|e| anyhow::anyhow!("{:?}", e))?, + ); + Ok(Self { gpu }) + } +} diff --git a/crates/gpui/src/platform/blade/blade_renderer.rs b/crates/gpui/src/platform/blade/blade_renderer.rs index 92becf2342..55a53e8e9f 100644 --- a/crates/gpui/src/platform/blade/blade_renderer.rs +++ b/crates/gpui/src/platform/blade/blade_renderer.rs @@ -1,7 +1,7 @@ // Doing `if let` gives you nice scoping with passes/encoders #![allow(irrefutable_let_patterns)] -use super::{BladeAtlas, PATH_TEXTURE_FORMAT}; +use super::{BladeAtlas, BladeContext, PATH_TEXTURE_FORMAT}; use crate::{ AtlasTextureKind, AtlasTile, Background, Bounds, ContentMask, DevicePixels, GpuSpecs, MonochromeSprite, Path, PathId, PathVertex, PolychromeSprite, PrimitiveBatch, Quad, @@ -11,8 +11,6 @@ use bytemuck::{Pod, Zeroable}; use collections::HashMap; #[cfg(target_os = "macos")] use media::core_video::CVMetalTextureCache; -#[cfg(target_os = "macos")] -use std::{ffi::c_void, ptr::NonNull}; use blade_graphics as gpu; use blade_util::{BufferBelt, BufferBeltDescriptor}; @@ -20,66 +18,6 @@ use std::{mem, sync::Arc}; const MAX_FRAME_TIME_MS: u32 = 10000; -#[cfg(target_os = "macos")] -#[derive(Clone, Default)] -pub struct Context {} -#[cfg(target_os = "macos")] -pub type Renderer = BladeRenderer; - -#[cfg(target_os = "macos")] -pub unsafe fn new_renderer( - _context: self::Context, - _native_window: *mut c_void, - native_view: *mut c_void, - bounds: crate::Size, - transparent: bool, -) -> Renderer { - use raw_window_handle as rwh; - struct RawWindow { - view: *mut c_void, - } - - impl rwh::HasWindowHandle for RawWindow { - fn window_handle(&self) -> Result { - let view = NonNull::new(self.view).unwrap(); - let handle = rwh::AppKitWindowHandle::new(view); - Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) }) - } - } - impl rwh::HasDisplayHandle for RawWindow { - fn display_handle(&self) -> Result { - let handle = rwh::AppKitDisplayHandle::new(); - Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) }) - } - } - - let gpu = Arc::new( - gpu::Context::init_windowed( - &RawWindow { - view: native_view as *mut _, - }, - gpu::ContextDesc { - validation: cfg!(debug_assertions), - capture: false, - overlay: false, - }, - ) - .unwrap(), - ); - - BladeRenderer::new( - gpu, - BladeSurfaceConfig { - size: gpu::Extent { - width: bounds.width as u32, - height: bounds.height as u32, - depth: 1, - }, - transparent, - }, - ) -} - #[repr(C)] #[derive(Clone, Copy, Pod, Zeroable)] struct GlobalParams { @@ -354,10 +292,14 @@ pub struct BladeSurfaceConfig { pub transparent: bool, } +//Note: we could see some of these fields moved into `BladeContext` +// so that they are shared between windows. E.g. `pipelines`. +// But that is complicated by the fact that pipelines depend on +// the format and alpha mode. pub struct BladeRenderer { gpu: Arc, + surface: gpu::Surface, surface_config: gpu::SurfaceConfig, - alpha_mode: gpu::AlphaMode, command_encoder: gpu::CommandEncoder, last_sync_point: Option, pipelines: BladePipelines, @@ -370,7 +312,11 @@ pub struct BladeRenderer { } impl BladeRenderer { - pub fn new(gpu: Arc, config: BladeSurfaceConfig) -> Self { + pub fn new( + context: &BladeContext, + window: &I, + config: BladeSurfaceConfig, + ) -> anyhow::Result { let surface_config = gpu::SurfaceConfig { size: config.size, usage: gpu::TextureUsage::TARGET, @@ -379,20 +325,23 @@ impl BladeRenderer { allow_exclusive_full_screen: false, transparent: config.transparent, }; - let surface_info = gpu.resize(surface_config); + let surface = context + .gpu + .create_surface_configured(window, surface_config) + .unwrap(); - let command_encoder = gpu.create_command_encoder(gpu::CommandEncoderDesc { + let command_encoder = context.gpu.create_command_encoder(gpu::CommandEncoderDesc { name: "main", buffer_count: 2, }); - let pipelines = BladePipelines::new(&gpu, surface_info); + let pipelines = BladePipelines::new(&context.gpu, surface.info()); let instance_belt = BufferBelt::new(BufferBeltDescriptor { memory: gpu::Memory::Shared, min_chunk_size: 0x1000, alignment: 0x40, // Vulkan `minStorageBufferOffsetAlignment` on Intel Xe }); - let atlas = Arc::new(BladeAtlas::new(&gpu)); - let atlas_sampler = gpu.create_sampler(gpu::SamplerDesc { + let atlas = Arc::new(BladeAtlas::new(&context.gpu)); + let atlas_sampler = context.gpu.create_sampler(gpu::SamplerDesc { name: "atlas", mag_filter: gpu::FilterMode::Linear, min_filter: gpu::FilterMode::Linear, @@ -402,13 +351,13 @@ impl BladeRenderer { #[cfg(target_os = "macos")] let core_video_texture_cache = unsafe { use foreign_types::ForeignType as _; - CVMetalTextureCache::new(gpu.metal_device().as_ptr()).unwrap() + CVMetalTextureCache::new(context.gpu.metal_device().as_ptr()).unwrap() }; - Self { - gpu, + Ok(Self { + gpu: Arc::clone(&context.gpu), + surface, surface_config, - alpha_mode: surface_info.alpha, command_encoder, last_sync_point: None, pipelines, @@ -418,7 +367,7 @@ impl BladeRenderer { atlas_sampler, #[cfg(target_os = "macos")] core_video_texture_cache, - } + }) } fn wait_for_gpu(&mut self) { @@ -452,7 +401,8 @@ impl BladeRenderer { if always_resize || gpu_size != self.surface_config.size { self.wait_for_gpu(); self.surface_config.size = gpu_size; - self.gpu.resize(self.surface_config); + self.gpu + .reconfigure_surface(&mut self.surface, self.surface_config); } } @@ -460,10 +410,10 @@ impl BladeRenderer { if transparent != self.surface_config.transparent { self.wait_for_gpu(); self.surface_config.transparent = transparent; - let surface_info = self.gpu.resize(self.surface_config); + self.gpu + .reconfigure_surface(&mut self.surface, self.surface_config); self.pipelines.destroy(&self.gpu); - self.pipelines = BladePipelines::new(&self.gpu, surface_info); - self.alpha_mode = surface_info.alpha; + self.pipelines = BladePipelines::new(&self.gpu, self.surface.info()); } } @@ -490,13 +440,13 @@ impl BladeRenderer { #[cfg(target_os = "macos")] pub fn layer(&self) -> metal::MetalLayer { - self.gpu.metal_layer().unwrap() + self.surface.metal_layer() } #[cfg(target_os = "macos")] pub fn layer_ptr(&self) -> *mut metal::CAMetalLayer { use metal::foreign_types::ForeignType as _; - self.gpu.metal_layer().unwrap().as_ptr() + self.surface.metal_layer().as_ptr() } #[profiling::function] @@ -538,14 +488,17 @@ impl BladeRenderer { }; let vertex_buf = unsafe { self.instance_belt.alloc_typed(&vertices, &self.gpu) }; - let mut pass = self.command_encoder.render(gpu::RenderTargetSet { - colors: &[gpu::RenderTarget { - view: tex_info.raw_view, - init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack), - finish_op: gpu::FinishOp::Store, - }], - depth_stencil: None, - }); + let mut pass = self.command_encoder.render( + "paths", + gpu::RenderTargetSet { + colors: &[gpu::RenderTarget { + view: tex_info.raw_view, + init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack), + finish_op: gpu::FinishOp::Store, + }], + depth_stencil: None, + }, + ); let mut encoder = pass.with(&self.pipelines.path_rasterization); encoder.bind( @@ -566,6 +519,7 @@ impl BladeRenderer { self.instance_belt.destroy(&self.gpu); self.gpu.destroy_command_encoder(&mut self.command_encoder); self.pipelines.destroy(&self.gpu); + self.gpu.destroy_surface(&mut self.surface); } pub fn draw(&mut self, scene: &Scene) { @@ -575,7 +529,7 @@ impl BladeRenderer { let frame = { profiling::scope!("acquire frame"); - self.gpu.acquire_frame() + self.surface.acquire_frame() }; self.command_encoder.init_texture(frame.texture()); @@ -584,21 +538,24 @@ impl BladeRenderer { self.surface_config.size.width as f32, self.surface_config.size.height as f32, ], - premultiplied_alpha: match self.alpha_mode { + premultiplied_alpha: match self.surface.info().alpha { gpu::AlphaMode::Ignored | gpu::AlphaMode::PostMultiplied => 0, gpu::AlphaMode::PreMultiplied => 1, }, pad: 0, }; - if let mut pass = self.command_encoder.render(gpu::RenderTargetSet { - colors: &[gpu::RenderTarget { - view: frame.texture_view(), - init_op: gpu::InitOp::Clear(gpu::TextureColor::TransparentBlack), - finish_op: gpu::FinishOp::Store, - }], - depth_stencil: None, - }) { + if let mut pass = self.command_encoder.render( + "main", + gpu::RenderTargetSet { + colors: &[gpu::RenderTarget { + view: frame.texture_view(), + init_op: gpu::InitOp::Clear(gpu::TextureColor::TransparentBlack), + finish_op: gpu::FinishOp::Store, + }], + depth_stencil: None, + }, + ) { profiling::scope!("render pass"); for batch in scene.batches() { match batch { diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index d0c0f1768e..c5a39fddd8 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -1,52 +1,45 @@ -#![allow(unused)] - -use std::any::{type_name, Any}; -use std::cell::{self, RefCell}; -use std::env; -use std::ffi::OsString; -use std::fs::File; -use std::io::Read; -use std::ops::{Deref, DerefMut}; -use std::os::fd::{AsFd, AsRawFd, FromRawFd}; -use std::panic::{AssertUnwindSafe, Location}; -use std::rc::Weak; use std::{ + env, + panic::AssertUnwindSafe, path::{Path, PathBuf}, process::Command, rc::Rc, sync::Arc, +}; +#[cfg(any(feature = "wayland", feature = "x11"))] +use std::{ + ffi::OsString, + fs::File, + io::Read as _, + os::fd::{AsFd, AsRawFd, FromRawFd}, time::Duration, }; use anyhow::{anyhow, Context as _}; use async_task::Runnable; -use calloop::channel::Channel; -use calloop::{EventLoop, LoopHandle, LoopSignal}; -use flume::{Receiver, Sender}; +use calloop::{channel::Channel, LoopSignal}; use futures::{channel::oneshot, future::FutureExt}; -use parking_lot::Mutex; -use util::ResultExt; - +use util::ResultExt as _; #[cfg(any(feature = "wayland", feature = "x11"))] use xkbcommon::xkb::{self, Keycode, Keysym, State}; -use crate::platform::NoopTextSystem; use crate::{ px, Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, - ForegroundExecutor, Keymap, Keystroke, LinuxDispatcher, Menu, MenuItem, Modifiers, OwnedMenu, - PathPromptOptions, Pixels, Platform, PlatformDisplay, PlatformInputHandler, PlatformTextSystem, - PlatformWindow, Point, PromptLevel, Result, ScreenCaptureSource, SemanticVersion, SharedString, - Size, Task, WindowAppearance, WindowOptions, WindowParams, + ForegroundExecutor, Keymap, LinuxDispatcher, Menu, MenuItem, OwnedMenu, PathPromptOptions, + Pixels, Platform, PlatformDisplay, PlatformTextSystem, PlatformWindow, Point, Result, + ScreenCaptureSource, Task, WindowAppearance, WindowParams, }; - +#[cfg(any(feature = "wayland", feature = "x11"))] pub(crate) const SCROLL_LINES: f32 = 3.0; // Values match the defaults on GTK. // Taken from https://github.com/GNOME/gtk/blob/main/gtk/gtksettings.c#L320 +#[cfg(any(feature = "wayland", feature = "x11"))] pub(crate) const DOUBLE_CLICK_INTERVAL: Duration = Duration::from_millis(400); pub(crate) const DOUBLE_CLICK_DISTANCE: Pixels = px(5.0); pub(crate) const KEYRING_LABEL: &str = "zed-github-account"; +#[cfg(any(feature = "wayland", feature = "x11"))] const FILE_PICKER_PORTAL_MISSING: &str = "Couldn't open file picker due to missing xdg-desktop-portal implementation."; @@ -54,8 +47,9 @@ pub trait LinuxClient { fn compositor_name(&self) -> &'static str; fn with_common(&self, f: impl FnOnce(&mut LinuxCommon) -> R) -> R; fn displays(&self) -> Vec>; - fn primary_display(&self) -> Option>; + #[allow(unused)] fn display(&self, id: DisplayId) -> Option>; + fn primary_display(&self) -> Option>; fn open_window( &self, @@ -98,9 +92,9 @@ pub(crate) struct LinuxCommon { impl LinuxCommon { pub fn new(signal: LoopSignal) -> (Self, Channel) { let (main_sender, main_receiver) = calloop::channel::channel::(); + #[cfg(any(feature = "wayland", feature = "x11"))] let text_system = Arc::new(crate::CosmicTextSystem::new()); - #[cfg(not(any(feature = "wayland", feature = "x11")))] let text_system = Arc::new(crate::NoopTextSystem::new()); @@ -218,7 +212,7 @@ impl Platform for P { } } - fn activate(&self, ignoring_other_apps: bool) { + fn activate(&self, _ignoring_other_apps: bool) { log::info!("activate is not implemented on Linux, ignoring the call") } @@ -281,7 +275,7 @@ impl Platform for P { let (done_tx, done_rx) = oneshot::channel(); #[cfg(not(any(feature = "wayland", feature = "x11")))] - done_tx.send(Ok(None)); + let _ = (done_tx.send(Ok(None)), options); #[cfg(any(feature = "wayland", feature = "x11"))] self.foreground_executor() @@ -306,7 +300,7 @@ impl Platform for P { ashpd::Error::PortalNotFound(_) => anyhow!(FILE_PICKER_PORTAL_MISSING), err => err.into(), }; - done_tx.send(Err(result)); + let _ = done_tx.send(Err(result)); return; } }; @@ -322,7 +316,7 @@ impl Platform for P { Err(ashpd::Error::Response(_)) => Ok(None), Err(e) => Err(e.into()), }; - done_tx.send(result); + let _ = done_tx.send(result); }) .detach(); done_rx @@ -332,7 +326,7 @@ impl Platform for P { let (done_tx, done_rx) = oneshot::channel(); #[cfg(not(any(feature = "wayland", feature = "x11")))] - done_tx.send(Ok(None)); + let _ = (done_tx.send(Ok(None)), directory); #[cfg(any(feature = "wayland", feature = "x11"))] self.foreground_executor() @@ -356,7 +350,7 @@ impl Platform for P { } err => err.into(), }; - done_tx.send(Err(result)); + let _ = done_tx.send(Err(result)); return; } }; @@ -369,7 +363,7 @@ impl Platform for P { Err(ashpd::Error::Response(_)) => Ok(None), Err(e) => Err(e.into()), }; - done_tx.send(result); + let _ = done_tx.send(result); } }) .detach(); @@ -426,7 +420,7 @@ impl Platform for P { fn app_path(&self) -> Result { // get the path of the executable of the current process - let exe_path = std::env::current_exe()?; + let exe_path = env::current_exe()?; Ok(exe_path) } @@ -440,9 +434,9 @@ impl Platform for P { self.with_common(|common| Some(common.menus.clone())) } - fn set_dock_menu(&self, menu: Vec, keymap: &Keymap) {} + fn set_dock_menu(&self, _menu: Vec, _keymap: &Keymap) {} - fn path_for_auxiliary_executable(&self, name: &str) -> Result { + fn path_for_auxiliary_executable(&self, _name: &str) -> Result { Err(anyhow::Error::msg( "Platform::path_for_auxiliary_executable is not implemented yet", )) @@ -614,6 +608,7 @@ pub(super) fn reveal_path_internal( .detach(); } +#[allow(unused)] pub(super) fn is_within_click_distance(a: Point, b: Point) -> bool { let diff = a - b; diff.x.abs() <= DOUBLE_CLICK_DISTANCE && diff.y.abs() <= DOUBLE_CLICK_DISTANCE @@ -622,7 +617,7 @@ pub(super) fn is_within_click_distance(a: Point, b: Point) -> bo #[cfg(any(feature = "wayland", feature = "x11"))] pub(super) fn get_xkb_compose_state(cx: &xkb::Context) -> Option { let mut locales = Vec::default(); - if let Some(locale) = std::env::var_os("LC_CTYPE") { + if let Some(locale) = env::var_os("LC_CTYPE") { locales.push(locale); } locales.push(OsString::from("C")); @@ -650,6 +645,7 @@ pub(super) unsafe fn read_fd(mut fd: filedescriptor::FileDescriptor) -> Result String { // Based on cursor names from https://gitlab.gnome.org/GNOME/adwaita-icon-theme (GNOME) // and https://github.com/KDE/breeze (KDE). Both of them seem to be also derived from @@ -682,10 +678,12 @@ impl CursorStyle { } #[cfg(any(feature = "wayland", feature = "x11"))] -impl Keystroke { - pub(super) fn from_xkb(state: &State, modifiers: Modifiers, keycode: Keycode) -> Self { - let mut modifiers = modifiers; - +impl crate::Keystroke { + pub(super) fn from_xkb( + state: &State, + mut modifiers: crate::Modifiers, + keycode: Keycode, + ) -> Self { let key_utf32 = state.key_get_utf32(keycode); let key_utf8 = state.key_get_utf8(keycode); let key_sym = state.key_get_one_sym(keycode); @@ -759,7 +757,7 @@ impl Keystroke { let key_char = (key_utf32 >= 32 && key_utf32 != 127 && !key_utf8.is_empty()).then_some(key_utf8); - Keystroke { + Self { modifiers, key, key_char, @@ -776,7 +774,6 @@ impl Keystroke { Keysym::dead_acute => Some("´".to_owned()), Keysym::dead_circumflex => Some("^".to_owned()), Keysym::dead_tilde => Some("~".to_owned()), - Keysym::dead_perispomeni => Some("͂".to_owned()), Keysym::dead_macron => Some("¯".to_owned()), Keysym::dead_breve => Some("˘".to_owned()), Keysym::dead_abovedot => Some("˙".to_owned()), @@ -794,9 +791,7 @@ impl Keystroke { Keysym::dead_horn => Some("̛".to_owned()), Keysym::dead_stroke => Some("̶̶".to_owned()), Keysym::dead_abovecomma => Some("̓̓".to_owned()), - Keysym::dead_psili => Some("᾿".to_owned()), Keysym::dead_abovereversedcomma => Some("ʽ".to_owned()), - Keysym::dead_dasia => Some("῾".to_owned()), Keysym::dead_doublegrave => Some("̏".to_owned()), Keysym::dead_belowring => Some("˳".to_owned()), Keysym::dead_belowmacron => Some("̱".to_owned()), @@ -830,7 +825,7 @@ impl Keystroke { } #[cfg(any(feature = "wayland", feature = "x11"))] -impl Modifiers { +impl crate::Modifiers { pub(super) fn from_xkb(keymap_state: &State) -> Self { let shift = keymap_state.mod_name_is_active(xkb::MOD_NAME_SHIFT, xkb::STATE_MODS_EFFECTIVE); let alt = keymap_state.mod_name_is_active(xkb::MOD_NAME_ALT, xkb::STATE_MODS_EFFECTIVE); @@ -838,7 +833,7 @@ impl Modifiers { keymap_state.mod_name_is_active(xkb::MOD_NAME_CTRL, xkb::STATE_MODS_EFFECTIVE); let platform = keymap_state.mod_name_is_active(xkb::MOD_NAME_LOGO, xkb::STATE_MODS_EFFECTIVE); - Modifiers { + Self { shift, alt, control, diff --git a/crates/gpui/src/platform/linux/wayland/client.rs b/crates/gpui/src/platform/linux/wayland/client.rs index 2cafffa725..622f458754 100644 --- a/crates/gpui/src/platform/linux/wayland/client.rs +++ b/crates/gpui/src/platform/linux/wayland/client.rs @@ -1,12 +1,16 @@ -use std::cell::{RefCell, RefMut}; -use std::hash::Hash; -use std::os::fd::{AsRawFd, BorrowedFd}; -use std::path::PathBuf; -use std::rc::{Rc, Weak}; -use std::time::{Duration, Instant}; +use std::{ + cell::{RefCell, RefMut}, + hash::Hash, + os::fd::{AsRawFd, BorrowedFd}, + path::PathBuf, + rc::{Rc, Weak}, + time::{Duration, Instant}, +}; -use calloop::timer::{TimeoutAction, Timer}; -use calloop::{EventLoop, LoopHandle}; +use calloop::{ + timer::{TimeoutAction, Timer}, + EventLoop, LoopHandle, +}; use calloop_wayland_source::WaylandSource; use collections::HashMap; use filedescriptor::Pipe; @@ -64,30 +68,28 @@ use xkbcommon::xkb::{self, Keycode, KEYMAP_COMPILE_NO_FLAGS}; use super::display::WaylandDisplay; use super::window::{ImeInput, WaylandWindowStatePtr}; -use crate::platform::linux::wayland::clipboard::{ - Clipboard, DataOffer, FILE_LIST_MIME_TYPE, TEXT_MIME_TYPE, -}; -use crate::platform::linux::wayland::cursor::Cursor; -use crate::platform::linux::wayland::serial::{SerialKind, SerialTracker}; -use crate::platform::linux::wayland::window::WaylandWindow; -use crate::platform::linux::xdg_desktop_portal::{Event as XDPEvent, XDPEventSource}; -use crate::platform::linux::LinuxClient; + use crate::platform::linux::{ get_xkb_compose_state, is_within_click_distance, open_uri_internal, read_fd, reveal_path_internal, + wayland::{ + clipboard::{Clipboard, DataOffer, FILE_LIST_MIME_TYPE, TEXT_MIME_TYPE}, + cursor::Cursor, + serial::{SerialKind, SerialTracker}, + window::WaylandWindow, + }, + xdg_desktop_portal::{Event as XDPEvent, XDPEventSource}, + LinuxClient, }; -use crate::platform::PlatformWindow; +use crate::platform::{blade::BladeContext, PlatformWindow}; use crate::{ - point, px, size, Bounds, DevicePixels, FileDropEvent, ForegroundExecutor, MouseExitEvent, Size, - DOUBLE_CLICK_INTERVAL, SCROLL_LINES, + point, px, size, AnyWindowHandle, Bounds, CursorStyle, DevicePixels, DisplayId, FileDropEvent, + ForegroundExecutor, KeyDownEvent, KeyUpEvent, Keystroke, LinuxCommon, Modifiers, + ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseExitEvent, MouseMoveEvent, + MouseUpEvent, NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, ScaledPixels, + ScrollDelta, ScrollWheelEvent, Size, TouchPhase, WindowParams, DOUBLE_CLICK_INTERVAL, + SCROLL_LINES, }; -use crate::{ - AnyWindowHandle, CursorStyle, DisplayId, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, - ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, - NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, ScaledPixels, ScrollDelta, - ScrollWheelEvent, TouchPhase, -}; -use crate::{LinuxCommon, WindowParams}; /// Used to convert evdev scancode to xkb scancode const MIN_KEYCODE: u32 = 8; @@ -186,6 +188,7 @@ pub struct Output { pub(crate) struct WaylandClientState { serial_tracker: SerialTracker, globals: Globals, + gpu_context: BladeContext, wl_seat: wl_seat::WlSeat, // TODO: Multi seat support wl_pointer: Option, wl_keyboard: Option, @@ -459,6 +462,8 @@ impl WaylandClient { }) .unwrap(); + let gpu_context = BladeContext::new().expect("Unable to init GPU context"); + let seat = seat.unwrap(); let globals = Globals::new( globals, @@ -512,6 +517,7 @@ impl WaylandClient { let mut state = Rc::new(RefCell::new(WaylandClientState { serial_tracker: SerialTracker::new(), globals, + gpu_context, wl_seat: seat, wl_pointer: None, wl_keyboard: None, @@ -627,6 +633,7 @@ impl LinuxClient for WaylandClient { let (window, surface_id) = WaylandWindow::new( handle, state.globals.clone(), + &state.gpu_context, WaylandClientStatePtr(Rc::downgrade(&self.0)), params, state.common.appearance, diff --git a/crates/gpui/src/platform/linux/wayland/window.rs b/crates/gpui/src/platform/linux/wayland/window.rs index bb0485272b..bb1a687c32 100644 --- a/crates/gpui/src/platform/linux/wayland/window.rs +++ b/crates/gpui/src/platform/linux/wayland/window.rs @@ -1,8 +1,10 @@ -use std::cell::{Ref, RefCell, RefMut}; -use std::ffi::c_void; -use std::ptr::NonNull; -use std::rc::Rc; -use std::sync::Arc; +use std::{ + cell::{Ref, RefCell, RefMut}, + ffi::c_void, + ptr::NonNull, + rc::Rc, + sync::Arc, +}; use blade_graphics as gpu; use collections::HashMap; @@ -19,10 +21,11 @@ use wayland_protocols::xdg::shell::client::xdg_surface; use wayland_protocols::xdg::shell::client::xdg_toplevel::{self}; use wayland_protocols_plasma::blur::client::org_kde_kwin_blur; -use crate::platform::blade::{BladeRenderer, BladeSurfaceConfig}; -use crate::platform::linux::wayland::display::WaylandDisplay; -use crate::platform::linux::wayland::serial::SerialKind; -use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow}; +use crate::platform::{ + blade::{BladeContext, BladeRenderer, BladeSurfaceConfig}, + linux::wayland::{display::WaylandDisplay, serial::SerialKind}, + PlatformAtlas, PlatformInputHandler, PlatformWindow, +}; use crate::scene::Scene; use crate::{ px, size, AnyWindowHandle, Bounds, Decorations, Globals, GpuSpecs, Modifiers, Output, Pixels, @@ -123,37 +126,28 @@ impl WaylandWindowState { viewport: Option, client: WaylandClientStatePtr, globals: Globals, + gpu_context: &BladeContext, options: WindowParams, ) -> anyhow::Result { - let raw = RawWindow { - window: surface.id().as_ptr().cast::(), - display: surface - .backend() - .upgrade() - .unwrap() - .display_ptr() - .cast::(), - }; - let gpu = Arc::new( - unsafe { - gpu::Context::init_windowed( - &raw, - gpu::ContextDesc { - validation: false, - capture: false, - overlay: false, - }, - ) - } - .map_err(|e| anyhow::anyhow!("{:?}", e))?, - ); - let config = BladeSurfaceConfig { - size: gpu::Extent { - width: options.bounds.size.width.0 as u32, - height: options.bounds.size.height.0 as u32, - depth: 1, - }, - transparent: true, + let renderer = { + let raw_window = RawWindow { + window: surface.id().as_ptr().cast::(), + display: surface + .backend() + .upgrade() + .unwrap() + .display_ptr() + .cast::(), + }; + let config = BladeSurfaceConfig { + size: gpu::Extent { + width: options.bounds.size.width.0 as u32, + height: options.bounds.size.height.0 as u32, + depth: 1, + }, + transparent: true, + }; + BladeRenderer::new(gpu_context, &raw_window, config)? }; Ok(Self { @@ -168,7 +162,7 @@ impl WaylandWindowState { globals, outputs: HashMap::default(), display: None, - renderer: BladeRenderer::new(gpu, config), + renderer, bounds: options.bounds, scale: 1.0, input_handler: None, @@ -266,6 +260,7 @@ impl WaylandWindow { pub fn new( handle: AnyWindowHandle, globals: Globals, + gpu_context: &BladeContext, client: WaylandClientStatePtr, params: WindowParams, appearance: WindowAppearance, @@ -308,6 +303,7 @@ impl WaylandWindow { viewport, client, globals, + gpu_context, params, )?)), callbacks: Rc::new(RefCell::new(Callbacks::default())), diff --git a/crates/gpui/src/platform/linux/x11/client.rs b/crates/gpui/src/platform/linux/x11/client.rs index a0c9ab4794..dd6b022fa0 100644 --- a/crates/gpui/src/platform/linux/x11/client.rs +++ b/crates/gpui/src/platform/linux/x11/client.rs @@ -1,13 +1,17 @@ use core::str; -use std::cell::RefCell; -use std::collections::{BTreeMap, HashSet}; -use std::ops::Deref; -use std::path::PathBuf; -use std::rc::{Rc, Weak}; -use std::time::{Duration, Instant}; +use std::{ + cell::RefCell, + collections::{BTreeMap, HashSet}, + ops::Deref, + path::PathBuf, + rc::{Rc, Weak}, + time::{Duration, Instant}, +}; -use calloop::generic::{FdWrapper, Generic}; -use calloop::{EventLoop, LoopHandle, RegistrationToken}; +use calloop::{ + generic::{FdWrapper, Generic}, + EventLoop, LoopHandle, RegistrationToken, +}; use anyhow::Context as _; use collections::HashMap; @@ -15,44 +19,49 @@ use http_client::Url; use smallvec::SmallVec; use util::ResultExt; -use x11rb::connection::{Connection, RequestConnection}; -use x11rb::cursor; -use x11rb::errors::ConnectionError; -use x11rb::protocol::randr::ConnectionExt as _; -use x11rb::protocol::xinput::ConnectionExt; -use x11rb::protocol::xkb::ConnectionExt as _; -use x11rb::protocol::xproto::{ - AtomEnum, ChangeWindowAttributesAux, ClientMessageData, ClientMessageEvent, ConnectionExt as _, - EventMask, KeyPressEvent, +use x11rb::{ + connection::{Connection, RequestConnection}, + cursor, + errors::ConnectionError, + protocol::randr::ConnectionExt as _, + protocol::xinput::ConnectionExt, + protocol::xkb::ConnectionExt as _, + protocol::xproto::{ + AtomEnum, ChangeWindowAttributesAux, ClientMessageData, ClientMessageEvent, + ConnectionExt as _, EventMask, KeyPressEvent, + }, + protocol::{randr, render, xinput, xkb, xproto, Event}, + resource_manager::Database, + wrapper::ConnectionExt as _, + xcb_ffi::XCBConnection, }; -use x11rb::protocol::{randr, render, xinput, xkb, xproto, Event}; -use x11rb::resource_manager::Database; -use x11rb::wrapper::ConnectionExt as _; -use x11rb::xcb_ffi::XCBConnection; -use xim::{x11rb::X11rbClient, Client}; -use xim::{AttributeName, InputStyle}; +use xim::{x11rb::X11rbClient, AttributeName, Client, InputStyle}; use xkbc::x11::ffi::{XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION}; use xkbcommon::xkb::{self as xkbc, LayoutIndex, ModMask}; -use crate::platform::linux::LinuxClient; -use crate::platform::{LinuxCommon, PlatformWindow}; -use crate::{ - modifiers_from_xinput_info, point, px, AnyWindowHandle, Bounds, ClipboardItem, CursorStyle, - DisplayId, FileDropEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, Pixels, - Platform, PlatformDisplay, PlatformInput, Point, RequestFrameOptions, ScaledPixels, - ScrollDelta, Size, TouchPhase, WindowParams, X11Window, -}; - use super::{ button_or_scroll_from_event_detail, get_valuator_axis_index, modifiers_from_state, pressed_button_from_mask, ButtonOrScroll, ScrollDirection, }; use super::{X11Display, X11WindowStatePtr, XcbAtoms}; use super::{XimCallbackEvent, XimHandler}; -use crate::platform::linux::platform::{DOUBLE_CLICK_INTERVAL, SCROLL_LINES}; -use crate::platform::linux::xdg_desktop_portal::{Event as XDPEvent, XDPEventSource}; -use crate::platform::linux::{ - get_xkb_compose_state, is_within_click_distance, open_uri_internal, reveal_path_internal, + +use crate::platform::{ + blade::BladeContext, + linux::{ + get_xkb_compose_state, is_within_click_distance, open_uri_internal, + platform::{DOUBLE_CLICK_INTERVAL, SCROLL_LINES}, + reveal_path_internal, + xdg_desktop_portal::{Event as XDPEvent, XDPEventSource}, + LinuxClient, + }, + LinuxCommon, PlatformWindow, +}; +use crate::{ + modifiers_from_xinput_info, point, px, AnyWindowHandle, Bounds, ClipboardItem, CursorStyle, + DisplayId, FileDropEvent, Keystroke, Modifiers, ModifiersChangedEvent, MouseButton, Pixels, + Platform, PlatformDisplay, PlatformInput, Point, RequestFrameOptions, ScaledPixels, + ScrollDelta, Size, TouchPhase, WindowParams, X11Window, }; /// Value for DeviceId parameters which selects all devices. @@ -158,6 +167,8 @@ pub struct X11ClientState { pub(crate) last_location: Point, pub(crate) current_count: usize, + gpu_context: BladeContext, + pub(crate) scale_factor: f32, xkb_context: xkbc::Context, @@ -360,6 +371,8 @@ impl X11Client { let compose_state = get_xkb_compose_state(&xkb_context); let resource_database = x11rb::resource_manager::new_from_default(&xcb_connection).unwrap(); + let gpu_context = BladeContext::new().expect("Unable to init GPU context"); + let scale_factor = resource_database .get_value("Xft.dpi", "Xft.dpi") .ok() @@ -428,6 +441,7 @@ impl X11Client { last_mouse_button: None, last_location: Point::new(px(0.0), px(0.0)), current_count: 0, + gpu_context, scale_factor, xkb_context, @@ -1299,6 +1313,7 @@ impl LinuxClient for X11Client { handle, X11ClientStatePtr(Rc::downgrade(&self.0)), state.common.foreground_executor.clone(), + &state.gpu_context, params, &state.xcb_connection, state.client_side_decorations_supported, diff --git a/crates/gpui/src/platform/linux/x11/window.rs b/crates/gpui/src/platform/linux/x11/window.rs index 8fb378072f..9a685773a2 100644 --- a/crates/gpui/src/platform/linux/x11/window.rs +++ b/crates/gpui/src/platform/linux/x11/window.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Context}; +use crate::platform::blade::{BladeContext, BladeRenderer, BladeSurfaceConfig}; use crate::{ - platform::blade::{BladeRenderer, BladeSurfaceConfig}, px, size, AnyWindowHandle, Bounds, Decorations, DevicePixels, ForegroundExecutor, GpuSpecs, Modifiers, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PromptLevel, RequestFrameOptions, ResizeEdge, ScaledPixels, Scene, Size, @@ -247,7 +247,6 @@ pub struct X11WindowState { x_root_window: xproto::Window, pub(crate) counter_id: sync::Counter, pub(crate) last_sync_counter: Option, - _raw: RawWindow, bounds: Bounds, scale_factor: f32, renderer: BladeRenderer, @@ -358,6 +357,7 @@ impl X11WindowState { handle: AnyWindowHandle, client: X11ClientStatePtr, executor: ForegroundExecutor, + gpu_context: &BladeContext, params: WindowParams, xcb: &Rc, client_side_decorations_supported: bool, @@ -555,50 +555,39 @@ impl X11WindowState { xcb.flush().with_context(|| "X11 Flush failed.")?; - let raw = RawWindow { - connection: as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection(xcb) - as *mut _, - screen_id: x_screen_index, - window_id: x_window, - visual_id: visual.id, + let renderer = { + let raw_window = RawWindow { + connection: as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection( + xcb, + ) as *mut _, + screen_id: x_screen_index, + window_id: x_window, + visual_id: visual.id, + }; + let config = BladeSurfaceConfig { + // Note: this has to be done after the GPU init, or otherwise + // the sizes are immediately invalidated. + size: query_render_extent(xcb, x_window)?, + // We set it to transparent by default, even if we have client-side + // decorations, since those seem to work on X11 even without `true` here. + // If the window appearance changes, then the renderer will get updated + // too + transparent: false, + }; + BladeRenderer::new(gpu_context, &raw_window, config)? }; - let gpu = Arc::new( - unsafe { - gpu::Context::init_windowed( - &raw, - gpu::ContextDesc { - validation: false, - capture: false, - overlay: false, - }, - ) - } - .map_err(|e| anyhow!("{:?}", e))?, - ); - let config = BladeSurfaceConfig { - // Note: this has to be done after the GPU init, or otherwise - // the sizes are immediately invalidated. - size: query_render_extent(xcb, x_window)?, - // We set it to transparent by default, even if we have client-side - // decorations, since those seem to work on X11 even without `true` here. - // If the window appearance changes, then the renderer will get updated - // too - transparent: false, - }; check_reply(|| "X11 MapWindow failed.", xcb.map_window(x_window))?; - let display = Rc::new(X11Display::new(xcb, scale_factor, x_screen_index)?); Ok(Self { client, executor, display, - _raw: raw, x_root_window: visual_set.root, bounds: bounds.to_pixels(scale_factor), scale_factor, - renderer: BladeRenderer::new(gpu, config), + renderer, atoms: *atoms, input_handler: None, active: false, @@ -716,6 +705,7 @@ impl X11Window { handle: AnyWindowHandle, client: X11ClientStatePtr, executor: ForegroundExecutor, + gpu_context: &BladeContext, params: WindowParams, xcb: &Rc, client_side_decorations_supported: bool, @@ -730,6 +720,7 @@ impl X11Window { handle, client, executor, + gpu_context, params, xcb, client_side_decorations_supported, diff --git a/crates/gpui/src/platform/windows/platform.rs b/crates/gpui/src/platform/windows/platform.rs index 0c23a4ef7a..1da9dbc0cf 100644 --- a/crates/gpui/src/platform/windows/platform.rs +++ b/crates/gpui/src/platform/windows/platform.rs @@ -28,11 +28,12 @@ use windows::{ UI::ViewManagement::UISettings, }; -use crate::*; +use crate::{platform::blade::BladeContext, *}; pub(crate) struct WindowsPlatform { state: RefCell, raw_window_handles: RwLock>, + gpu_context: BladeContext, // The below members will never change throughout the entire lifecycle of the app. icon: HICON, main_receiver: flume::Receiver, @@ -94,12 +95,14 @@ impl WindowsPlatform { let icon = load_icon().unwrap_or_default(); let state = RefCell::new(WindowsPlatformState::new()); let raw_window_handles = RwLock::new(SmallVec::new()); + let gpu_context = BladeContext::new().expect("Unable to init GPU context"); let windows_version = WindowsVersion::new().expect("Error retrieve windows version"); let validation_number = rand::random::(); Self { state, raw_window_handles, + gpu_context, icon, main_receiver, dispatch_event, @@ -344,7 +347,12 @@ impl Platform for WindowsPlatform { handle: AnyWindowHandle, options: WindowParams, ) -> Result> { - let window = WindowsWindow::new(handle, options, self.generate_creation_info())?; + let window = WindowsWindow::new( + handle, + options, + self.generate_creation_info(), + &self.gpu_context, + )?; let handle = window.get_raw_handle(); self.raw_window_handles.write().push(handle); diff --git a/crates/gpui/src/platform/windows/window.rs b/crates/gpui/src/platform/windows/window.rs index be3deab631..cb8bde647d 100644 --- a/crates/gpui/src/platform/windows/window.rs +++ b/crates/gpui/src/platform/windows/window.rs @@ -27,7 +27,7 @@ use windows::{ }, }; -use crate::platform::blade::BladeRenderer; +use crate::platform::blade::{BladeContext, BladeRenderer}; use crate::*; pub(crate) struct WindowsWindow(pub Rc); @@ -78,6 +78,7 @@ impl WindowsWindowState { cs: &CREATESTRUCTW, current_cursor: HCURSOR, display: WindowsDisplay, + gpu_context: &BladeContext, ) -> Result { let scale_factor = { let monitor_dpi = unsafe { GetDpiForWindow(hwnd) } as f32; @@ -94,7 +95,7 @@ impl WindowsWindowState { }; let border_offset = WindowBorderOffset::default(); let is_minimized = None; - let renderer = windows_renderer::windows_renderer(hwnd, transparent)?; + let renderer = windows_renderer::init(gpu_context, hwnd, transparent)?; let callbacks = Callbacks::default(); let input_handler = None; let system_key_handled = false; @@ -227,6 +228,7 @@ impl WindowsWindowStatePtr { cs, context.current_cursor, context.display, + context.gpu_context, )?); Ok(Rc::new_cyclic(|this| Self { @@ -340,7 +342,7 @@ pub(crate) struct Callbacks { pub(crate) appearance_changed: Option>, } -struct WindowCreateContext { +struct WindowCreateContext<'a> { inner: Option>>, handle: AnyWindowHandle, hide_title_bar: bool, @@ -352,6 +354,7 @@ struct WindowCreateContext { windows_version: WindowsVersion, validation_number: usize, main_receiver: flume::Receiver, + gpu_context: &'a BladeContext, } impl WindowsWindow { @@ -359,6 +362,7 @@ impl WindowsWindow { handle: AnyWindowHandle, params: WindowParams, creation_info: WindowCreationInfo, + gpu_context: &BladeContext, ) -> Result { let WindowCreationInfo { icon, @@ -410,6 +414,7 @@ impl WindowsWindow { windows_version, validation_number, main_receiver, + gpu_context, }; let lpparam = Some(&context as *const _ as *const _); let creation_result = unsafe { @@ -1236,38 +1241,24 @@ fn set_window_composition_attribute(hwnd: HWND, color: Option, state: u32 } mod windows_renderer { - use std::{num::NonZeroIsize, sync::Arc}; - - use blade_graphics as gpu; + use crate::platform::blade::{BladeContext, BladeRenderer, BladeSurfaceConfig}; use raw_window_handle as rwh; + use std::num::NonZeroIsize; use windows::Win32::{Foundation::HWND, UI::WindowsAndMessaging::GWLP_HINSTANCE}; - use crate::{ - get_window_long, - platform::blade::{BladeRenderer, BladeSurfaceConfig}, - }; + use crate::get_window_long; - pub(super) fn windows_renderer(hwnd: HWND, transparent: bool) -> anyhow::Result { + pub(super) fn init( + context: &BladeContext, + hwnd: HWND, + transparent: bool, + ) -> anyhow::Result { let raw = RawWindow { hwnd }; - let gpu: Arc = Arc::new( - unsafe { - gpu::Context::init_windowed( - &raw, - gpu::ContextDesc { - validation: false, - capture: false, - overlay: false, - }, - ) - } - .map_err(|e| anyhow::anyhow!("{:?}", e))?, - ); let config = BladeSurfaceConfig { - size: gpu::Extent::default(), + size: Default::default(), transparent, }; - - Ok(BladeRenderer::new(gpu, config)) + BladeRenderer::new(context, &raw, config) } struct RawWindow { diff --git a/crates/media/Cargo.toml b/crates/media/Cargo.toml index 70478eeb75..1f2cfb0854 100644 --- a/crates/media/Cargo.toml +++ b/crates/media/Cargo.toml @@ -19,7 +19,7 @@ anyhow.workspace = true core-foundation.workspace = true ctor.workspace = true foreign-types = "0.5" -metal = "0.29" +metal.workspace = true objc = "0.2" [build-dependencies]