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 | 61 ++++--- .../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, 381 insertions(+), 342 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 45aa6431b6325175486cb5fb025cfa45508da78e..d4e18f46fee2dd92b2d149962c6770f2c3c84d1f 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 12eb379fffdfc97f6ea611080c8013cb4f2abc05..a43e85ce65b1bfb9a0987c1af5e0cb534c663715 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 347d70853ac035548a4f6cba8645b9fce5d708c1..aed1c25168754b378d94213318922423817c68f4 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 736c1888d894c95530e98115ba782be0ef5438d8..9d966d8a4e069a1c5ad904930f7fa9364b501e04 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 0000000000000000000000000000000000000000..b1baab8854aca67dd25b70c3f03e288edeeab6dc --- /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 b876d5bb9be9e5efe24059f92622198bd3cce9f6..9e666d71bf02f64e33903c10b07a646d497cee6d 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 0000000000000000000000000000000000000000..f03fff01e0539d1babb71d69d3786d84e1a0bb67 --- /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 92becf2342446f6b406129624f2f10c589d21535..55a53e8e9f70555c1456f3c19a12d908b6cd7141 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 d0c0f1768e0f6c33dfa03bf04fd83ab52f3445f5..c5a39fddd8c595645c871fdc5a3964b418b2ed13 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 2cafffa72534de61102879437bb972777dc080bb..622f4587542b0903a9135b22e5a48f7c27f686bb 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 calloop::timer::{TimeoutAction, Timer}; -use calloop::{EventLoop, LoopHandle}; +use std::{ + cell::{RefCell, RefMut}, + hash::Hash, + os::fd::{AsRawFd, BorrowedFd}, + path::PathBuf, + rc::{Rc, Weak}, + time::{Duration, Instant}, +}; + +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::{ - point, px, size, Bounds, DevicePixels, FileDropEvent, ForegroundExecutor, MouseExitEvent, Size, - DOUBLE_CLICK_INTERVAL, SCROLL_LINES, -}; +use crate::platform::{blade::BladeContext, PlatformWindow}; use crate::{ - AnyWindowHandle, CursorStyle, DisplayId, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, - ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, - NavigationDirection, Pixels, PlatformDisplay, PlatformInput, Point, ScaledPixels, ScrollDelta, - ScrollWheelEvent, TouchPhase, + 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::{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 bb0485272b70da84f51d976b9eeb14ee8f3b376b..bb1a687c32eb802c8fbeb81ea1bace97ba4f7e42 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 a0c9ab47943fbbf70a5fc60cdebaaed7026d2f6e..dd6b022fa0a8a444adf86aca1d46f6dd7b47fdc2 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 8fb378072f108e9a8b2b2bcac347ed344cb527c3..9a685773a2c8595eba36e3e357355ef34a8f825c 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 0c23a4ef7ad141abfa66a9c541f5eeb27ab813c2..1da9dbc0cf30e12c199866b7872b3946704e88b6 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 be3deab6310324229dc18d679db7b7553cf9b880..cb8bde647d5949486c761199827d31206b15c3c4 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 70478eeb759f822195459501f73794410418d186..1f2cfb085461aa463f43ea72f8350da41053fcaf 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]