From ef4ef5f0e8448db07cf2423fc0adbb9b2ac773eb Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 24 Jan 2024 21:19:46 -0800 Subject: [PATCH 01/51] Add blade dependency --- Cargo.toml | 4 ++++ crates/cli/Cargo.toml | 6 +++--- crates/gpui/Cargo.toml | 2 ++ crates/gpui/build.rs | 10 +++++----- crates/gpui/src/gpui.rs | 3 +-- rust-toolchain.toml | 2 +- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f46d32a53c84b..a3eeb2d653b77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -181,6 +181,10 @@ wasmtime = "16" tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "1d8975319c2d5de1bf710e7e21db25b0eee4bc66" } wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "v16.0.0" } +# TODO - Remove when corresponding Blade versions are published +blade-graphics = { path = "/x/Code/blade/blade-graphics" } +blade-macros = { path = "/x/Code/blade/blade-macros" } + [profile.dev] split-debuginfo = "unpacked" debug = "limited" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index d790644e99511..aa5ff1dcc33c7 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -23,6 +23,6 @@ serde_derive.workspace = true util = { path = "../util" } [target.'cfg(target_os = "macos")'.dependencies] -core-foundation = "0.9" -core-services = "0.2" -plist = "1.3" +#core-foundation = "0.9" +#core-services = "0.2" +#plist = "1.3" diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 96c7b4b02c4b9..15e3e8d5c905c 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -26,6 +26,8 @@ anyhow.workspace = true async-task = "4.7" backtrace = { version = "0.3", optional = true } bitflags = "2.4.0" +blade-graphics = "0.3" +blade-macros = "0.2" collections = { path = "../collections" } ctor.workspace = true derive_more.workspace = true diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 4c86fe9a622be..a70f4fb637728 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -6,12 +6,12 @@ use std::{ use cbindgen::Config; fn main() { - generate_dispatch_bindings(); + //generate_dispatch_bindings(); let header_path = generate_shader_bindings(); - #[cfg(feature = "runtime_shaders")] - emit_stitched_shaders(&header_path); - #[cfg(not(feature = "runtime_shaders"))] - compile_metal_shaders(&header_path); + //#[cfg(feature = "runtime_shaders")] + //emit_stitched_shaders(&header_path); + //#[cfg(not(feature = "runtime_shaders"))] + //compile_metal_shaders(&header_path); } fn generate_dispatch_bindings() { diff --git a/crates/gpui/src/gpui.rs b/crates/gpui/src/gpui.rs index 4fe5f621118fa..4fa2f525a7caf 100644 --- a/crates/gpui/src/gpui.rs +++ b/crates/gpui/src/gpui.rs @@ -6,8 +6,7 @@ //! ## Getting Started //! //! GPUI is still in active development as we work on the Zed code editor and isn't yet on crates.io. -//! You'll also need to use the latest version of stable rust and be on macOS. Add the following to your -//! Cargo.toml: +//! You'll also need to use the latest version of stable rust. Add the following to your Cargo.toml: //! //! ``` //! gpui = { git = "https://github.com/zed-industries/zed" } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 5cdc76def26ae..af32046cb18b7 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,4 +2,4 @@ channel = "1.75" profile = "minimal" components = [ "rustfmt", "clippy" ] -targets = [ "x86_64-apple-darwin", "aarch64-apple-darwin", "wasm32-wasi" ] +targets = [ "x86_64-apple-darwin", "aarch64-apple-darwin", "x86_64-linux-gnu", "wasm32-wasi" ] From d675abf70cf4eddce4ab44cf81c3891afe369512 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 24 Jan 2024 22:04:47 -0800 Subject: [PATCH 02/51] Add Linux platform, gate usage of CVImageBuffer by macOS --- crates/gpui/build.rs | 6 +- crates/gpui/src/elements/img.rs | 4 + crates/gpui/src/platform.rs | 8 + crates/gpui/src/platform/linux.rs | 3 + crates/gpui/src/platform/linux/platform.rs | 242 +++++++++++++++++++++ crates/gpui/src/scene.rs | 1 + crates/gpui/src/window/element_cx.rs | 6 +- 7 files changed, 265 insertions(+), 5 deletions(-) create mode 100644 crates/gpui/src/platform/linux.rs create mode 100644 crates/gpui/src/platform/linux/platform.rs diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index a70f4fb637728..32da465007a93 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -7,14 +7,14 @@ use cbindgen::Config; fn main() { //generate_dispatch_bindings(); - let header_path = generate_shader_bindings(); + let _header_path = generate_shader_bindings(); //#[cfg(feature = "runtime_shaders")] //emit_stitched_shaders(&header_path); //#[cfg(not(feature = "runtime_shaders"))] //compile_metal_shaders(&header_path); } -fn generate_dispatch_bindings() { +fn _generate_dispatch_bindings() { println!("cargo:rustc-link-lib=framework=System"); println!("cargo:rerun-if-changed=src/platform/mac/dispatch.h"); @@ -116,7 +116,7 @@ fn emit_stitched_shaders(header_path: &Path) { println!("cargo:rerun-if-changed={}", &shader_source_path); } #[cfg(not(feature = "runtime_shaders"))] -fn compile_metal_shaders(header_path: &Path) { +fn _compile_metal_shaders(header_path: &Path) { use std::process::{self, Command}; let shader_path = "./src/platform/mac/shaders.metal"; let air_output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.air"); diff --git a/crates/gpui/src/elements/img.rs b/crates/gpui/src/elements/img.rs index cb97309d36770..32009e04db59b 100644 --- a/crates/gpui/src/elements/img.rs +++ b/crates/gpui/src/elements/img.rs @@ -7,6 +7,7 @@ use crate::{ StyleRefinement, Styled, UriOrPath, }; use futures::FutureExt; +#[cfg(target_os = "macos")] use media::core_video::CVImageBuffer; use util::ResultExt; @@ -21,6 +22,7 @@ pub enum ImageSource { Data(Arc), // TODO: move surface definitions into mac platform module /// A CoreVideo image buffer + #[cfg(target_os = "macos")] Surface(CVImageBuffer), } @@ -54,6 +56,7 @@ impl From> for ImageSource { } } +#[cfg(target_os = "macos")] impl From for ImageSource { fn from(value: CVImageBuffer) -> Self { Self::Surface(value) @@ -144,6 +147,7 @@ impl Element for Img { .log_err(); } + #[cfg(target_os = "macos")] ImageSource::Surface(surface) => { let size = size(surface.width().into(), surface.height().into()); let new_bounds = preserve_aspect_ratio(bounds, size); diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index df886fc4d6ad3..194dfe1c54647 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -2,6 +2,8 @@ mod app_menu; mod keystroke; #[cfg(target_os = "macos")] mod mac; +#[cfg(target_os = "linux")] +mod linux; #[cfg(any(test, feature = "test-support"))] mod test; @@ -35,6 +37,8 @@ pub use app_menu::*; pub use keystroke::*; #[cfg(target_os = "macos")] pub(crate) use mac::*; +#[cfg(target_os = "linux")] +pub(crate) use linux::*; #[cfg(any(test, feature = "test-support"))] pub(crate) use test::*; use time::UtcOffset; @@ -44,6 +48,10 @@ pub use util::SemanticVersion; pub(crate) fn current_platform() -> Rc { Rc::new(MacPlatform::new()) } +#[cfg(target_os = "linux")] +pub(crate) fn current_platform() -> Rc { + Rc::new(LinuxPlatform::new()) +} pub(crate) trait Platform: 'static { fn background_executor(&self) -> BackgroundExecutor; diff --git a/crates/gpui/src/platform/linux.rs b/crates/gpui/src/platform/linux.rs new file mode 100644 index 0000000000000..a979749edd667 --- /dev/null +++ b/crates/gpui/src/platform/linux.rs @@ -0,0 +1,3 @@ +mod platform; + +pub(crate) use platform::*; diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs new file mode 100644 index 0000000000000..245cdbc11db51 --- /dev/null +++ b/crates/gpui/src/platform/linux/platform.rs @@ -0,0 +1,242 @@ +#![allow(unused)] + +use crate::{ + Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, + Keymap, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, + PlatformTextSystem, PlatformWindow, Result, SemanticVersion, Task, WindowOptions, +}; + +use futures::channel::oneshot; +use parking_lot::Mutex; + +use std::{ + path::{Path, PathBuf}, + rc::Rc, + sync::Arc, + time::Duration, +}; +use time::UtcOffset; + + +pub(crate) struct LinuxPlatform(Mutex); + +pub(crate) struct LinuxPlatformState { +} + +impl Default for LinuxPlatform { + fn default() -> Self { + Self::new() + } +} + +impl LinuxPlatform { + pub(crate) fn new() -> Self { + Self(Mutex::new(LinuxPlatformState { + })) + } +} + +impl Platform for LinuxPlatform { + fn background_executor(&self) -> BackgroundExecutor { + unimplemented!() + } + + fn foreground_executor(&self) -> crate::ForegroundExecutor { + unimplemented!() + } + + fn text_system(&self) -> Arc { + unimplemented!() + } + + fn run(&self, on_finish_launching: Box) { + unimplemented!() + } + + fn quit(&self) { + unimplemented!() + } + + fn restart(&self) { + unimplemented!() + } + + fn activate(&self, ignoring_other_apps: bool) { + unimplemented!() + } + + fn hide(&self) { + unimplemented!() + } + + fn hide_other_apps(&self) { + unimplemented!() + } + + fn unhide_other_apps(&self) { + unimplemented!() + } + + fn displays(&self) -> Vec> { + unimplemented!() + } + + fn display(&self, id: DisplayId) -> Option> { + unimplemented!() + } + + fn active_window(&self) -> Option { + unimplemented!() + } + + fn open_window( + &self, + handle: AnyWindowHandle, + options: WindowOptions, + ) -> Box { + unimplemented!() + } + + fn set_display_link_output_callback( + &self, + display_id: DisplayId, + callback: Box, + ) { + unimplemented!() + } + + fn start_display_link(&self, display_id: DisplayId) { + unimplemented!() + } + + fn stop_display_link(&self, display_id: DisplayId) { + unimplemented!() + } + + fn open_url(&self, url: &str) { + unimplemented!() + } + + fn on_open_urls(&self, callback: Box)>) { + unimplemented!() + } + + fn prompt_for_paths( + &self, + options: PathPromptOptions, + ) -> oneshot::Receiver>> { + unimplemented!() + } + + fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver> { + unimplemented!() + } + + fn reveal_path(&self, path: &Path) { + unimplemented!() + } + + fn on_become_active(&self, callback: Box) { + unimplemented!() + } + + fn on_resign_active(&self, callback: Box) { + unimplemented!() + } + + fn on_quit(&self, callback: Box) { + unimplemented!() + } + + fn on_reopen(&self, callback: Box) { + unimplemented!() + } + + fn on_event(&self, callback: Box bool>) { + unimplemented!() + } + + fn on_app_menu_action(&self, callback: Box) { + unimplemented!() + } + + fn on_will_open_app_menu(&self, callback: Box) { + unimplemented!() + } + + fn on_validate_app_menu_command(&self, callback: Box bool>) { + unimplemented!() + } + + fn os_name(&self) -> &'static str { + "Linux" + } + + fn double_click_interval(&self) -> Duration { + unimplemented!() + } + + fn os_version(&self) -> Result { + unimplemented!() + } + + fn app_version(&self) -> Result { + unimplemented!() + } + + fn app_path(&self) -> Result { + unimplemented!() + } + + fn set_menus(&self, menus: Vec, keymap: &Keymap) { + unimplemented!() + } + + fn local_timezone(&self) -> UtcOffset { + unimplemented!() + } + + fn path_for_auxiliary_executable(&self, name: &str) -> Result { + unimplemented!() + } + + fn set_cursor_style(&self, style: CursorStyle) { + unimplemented!() + } + + fn should_auto_hide_scrollbars(&self) -> bool { + unimplemented!() + } + + fn write_to_clipboard(&self, item: ClipboardItem) { + unimplemented!() + } + + fn read_from_clipboard(&self) -> Option { + unimplemented!() + } + + fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task> { + unimplemented!() + } + + fn read_credentials(&self, url: &str) -> Task)>>> { + unimplemented!() + } + + fn delete_credentials(&self, url: &str) -> Task> { + unimplemented!() + } +} + +#[cfg(test)] +mod tests { + use crate::ClipboardItem; + + use super::*; + + fn build_platform() -> LinuxPlatform { + let platform = LinuxPlatform::new(); + platform + } +} diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index 70e24030b1db9..e1aa7fda20752 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -671,6 +671,7 @@ pub(crate) struct Surface { pub order: DrawOrder, pub bounds: Bounds, pub content_mask: ContentMask, + #[cfg(target_os = "macos")] pub image_buffer: media::core_video::CVImageBuffer, } diff --git a/crates/gpui/src/window/element_cx.rs b/crates/gpui/src/window/element_cx.rs index c7814fc101007..4e21988a2ef37 100644 --- a/crates/gpui/src/window/element_cx.rs +++ b/crates/gpui/src/window/element_cx.rs @@ -23,6 +23,7 @@ use std::{ use anyhow::Result; use collections::{FxHashMap, FxHashSet}; use derive_more::{Deref, DerefMut}; +#[cfg(target_os = "macos")] use media::core_video::CVImageBuffer; use smallvec::SmallVec; use util::post_inc; @@ -34,7 +35,7 @@ use crate::{ InputHandler, IsZero, KeyContext, KeyEvent, KeymatchMode, LayoutId, MonochromeSprite, MouseEvent, PaintQuad, Path, Pixels, PlatformInputHandler, Point, PolychromeSprite, Quad, RenderGlyphParams, RenderImageParams, RenderSvgParams, Scene, Shadow, SharedString, Size, - StackingContext, StackingOrder, Style, Surface, TextStyleRefinement, Underline, UnderlineStyle, + StackingContext, StackingOrder, Style, TextStyleRefinement, Underline, UnderlineStyle, Window, WindowContext, SUBPIXEL_VARIANTS, }; @@ -962,6 +963,7 @@ impl<'a> ElementContext<'a> { } /// Paint a surface into the scene for the next frame at the current z-index. + #[cfg(target_os = "macos")] pub fn paint_surface(&mut self, bounds: Bounds, image_buffer: CVImageBuffer) { let scale_factor = self.scale_factor(); let bounds = bounds.scale(scale_factor); @@ -970,7 +972,7 @@ impl<'a> ElementContext<'a> { let window = &mut *self.window; window.next_frame.scene.insert( &window.next_frame.z_index_stack, - Surface { + crate::Surface { view_id: view_id.into(), layer_id: 0, order: 0, From ca62d22147377077418e9c2dc8ba626694a3c963 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 25 Jan 2024 23:08:48 -0800 Subject: [PATCH 03/51] linux: implement dispatcher, add dummy textsystem --- Cargo.lock | 13 +++ crates/gpui/Cargo.toml | 3 + crates/gpui/src/platform.rs | 8 +- crates/gpui/src/platform/linux.rs | 2 + crates/gpui/src/platform/linux/dispatcher.rs | 97 ++++++++++++++++++++ crates/gpui/src/platform/linux/platform.rs | 15 ++- crates/gpui/src/platform/test.rs | 2 + crates/gpui/src/platform/test/platform.rs | 6 +- crates/gpui/src/platform/test/text_system.rs | 58 ++++++++++++ crates/gpui/src/window/element_cx.rs | 4 +- 10 files changed, 194 insertions(+), 14 deletions(-) create mode 100644 crates/gpui/src/platform/linux/dispatcher.rs create mode 100644 crates/gpui/src/platform/test/text_system.rs diff --git a/Cargo.lock b/Cargo.lock index 4ae19e764cd6c..9644e2b61983e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2732,6 +2732,7 @@ checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" dependencies = [ "futures-core", "futures-sink", + "nanorand", "spin 0.9.8", ] @@ -3105,8 +3106,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if 1.0.0", + "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -3232,6 +3235,7 @@ dependencies = [ "dhat", "env_logger", "etagere", + "flume", "font-kit", "foreign-types 0.3.2", "futures 0.3.28", @@ -4644,6 +4648,15 @@ dependencies = [ "rand 0.8.5", ] +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom 0.2.10", +] + [[package]] name = "native-tls" version = "0.2.11" diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 15e3e8d5c905c..0a0a07382916c 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -94,3 +94,6 @@ log.workspace = true media = { path = "../media" } metal = "0.21.0" objc = "0.2" + +[target.'cfg(target_os = "linux")'.dependencies] +flume = "0.11" diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 194dfe1c54647..008baea33326d 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -1,9 +1,9 @@ mod app_menu; mod keystroke; -#[cfg(target_os = "macos")] -mod mac; #[cfg(target_os = "linux")] mod linux; +#[cfg(target_os = "macos")] +mod mac; #[cfg(any(test, feature = "test-support"))] mod test; @@ -35,10 +35,10 @@ use uuid::Uuid; pub use app_menu::*; pub use keystroke::*; -#[cfg(target_os = "macos")] -pub(crate) use mac::*; #[cfg(target_os = "linux")] pub(crate) use linux::*; +#[cfg(target_os = "macos")] +pub(crate) use mac::*; #[cfg(any(test, feature = "test-support"))] pub(crate) use test::*; use time::UtcOffset; diff --git a/crates/gpui/src/platform/linux.rs b/crates/gpui/src/platform/linux.rs index a979749edd667..7a361c70343ac 100644 --- a/crates/gpui/src/platform/linux.rs +++ b/crates/gpui/src/platform/linux.rs @@ -1,3 +1,5 @@ +mod dispatcher; mod platform; +pub(crate) use dispatcher::*; pub(crate) use platform::*; diff --git a/crates/gpui/src/platform/linux/dispatcher.rs b/crates/gpui/src/platform/linux/dispatcher.rs new file mode 100644 index 0000000000000..14072f040a5e4 --- /dev/null +++ b/crates/gpui/src/platform/linux/dispatcher.rs @@ -0,0 +1,97 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +use crate::{PlatformDispatcher, TaskLabel}; +use async_task::Runnable; +use parking::{Parker, Unparker}; +use parking_lot::Mutex; +use std::{ + panic, thread, + time::{Duration, Instant}, +}; + +pub(crate) struct LinuxDispatcher { + parker: Mutex, + timed_tasks: Mutex>, + main_sender: flume::Sender, + main_receiver: flume::Receiver, + background_sender: flume::Sender, + background_thread: thread::JoinHandle<()>, + main_thread_id: thread::ThreadId, +} + +impl Default for LinuxDispatcher { + fn default() -> Self { + Self::new() + } +} + +impl LinuxDispatcher { + pub fn new() -> Self { + let (main_sender, main_receiver) = flume::unbounded::(); + let (background_sender, background_receiver) = flume::unbounded::(); + let background_thread = thread::spawn(move || { + for runnable in background_receiver { + let _ignore_panic = panic::catch_unwind(|| runnable.run()); + } + }); + LinuxDispatcher { + parker: Mutex::new(Parker::new()), + timed_tasks: Mutex::new(Vec::new()), + main_sender, + main_receiver, + background_sender, + background_thread, + main_thread_id: thread::current().id(), + } + } +} + +impl PlatformDispatcher for LinuxDispatcher { + fn is_main_thread(&self) -> bool { + thread::current().id() == self.main_thread_id + } + + fn dispatch(&self, runnable: Runnable, _: Option) { + self.background_sender.send(runnable).unwrap(); + } + + fn dispatch_on_main_thread(&self, runnable: Runnable) { + self.main_sender.send(runnable).unwrap(); + } + + fn dispatch_after(&self, duration: Duration, runnable: Runnable) { + let moment = Instant::now() + duration; + let mut timed_tasks = self.timed_tasks.lock(); + timed_tasks.push((moment, runnable)); + timed_tasks.sort_unstable_by(|&(ref a, _), &(ref b, _)| b.cmp(a)); + } + + fn tick(&self, background_only: bool) -> bool { + let mut ran = false; + if self.is_main_thread() && !background_only { + for runnable in self.main_receiver.try_iter() { + runnable.run(); + ran = true; + } + } + let mut timed_tasks = self.timed_tasks.lock(); + while let Some(&(moment, _)) = timed_tasks.last() { + if moment <= Instant::now() { + let (_, runnable) = timed_tasks.pop().unwrap(); + runnable.run(); + ran = true; + } + } + ran + } + + fn park(&self) { + self.parker.lock().park() + } + + fn unparker(&self) -> Unparker { + self.parker.lock().unparker() + } +} diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 245cdbc11db51..a2b87b7ea139d 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -2,8 +2,9 @@ use crate::{ Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, - Keymap, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, - PlatformTextSystem, PlatformWindow, Result, SemanticVersion, Task, WindowOptions, + ForegroundExecutor, Keymap, LinuxDispatcher, Menu, PathPromptOptions, Platform, + PlatformDisplay, PlatformInput, PlatformTextSystem, PlatformWindow, Result, SemanticVersion, + Task, WindowOptions, }; use futures::channel::oneshot; @@ -17,10 +18,11 @@ use std::{ }; use time::UtcOffset; - pub(crate) struct LinuxPlatform(Mutex); pub(crate) struct LinuxPlatformState { + background_executor: BackgroundExecutor, + foreground_executor: ForegroundExecutor, } impl Default for LinuxPlatform { @@ -31,18 +33,21 @@ impl Default for LinuxPlatform { impl LinuxPlatform { pub(crate) fn new() -> Self { + let dispatcher = Arc::new(LinuxDispatcher::new()); Self(Mutex::new(LinuxPlatformState { + background_executor: BackgroundExecutor::new(dispatcher.clone()), + foreground_executor: ForegroundExecutor::new(dispatcher), })) } } impl Platform for LinuxPlatform { fn background_executor(&self) -> BackgroundExecutor { - unimplemented!() + self.0.lock().background_executor.clone() } fn foreground_executor(&self) -> crate::ForegroundExecutor { - unimplemented!() + self.0.lock().foreground_executor.clone() } fn text_system(&self) -> Arc { diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index d17739239eede..6cd833b681b9d 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -1,9 +1,11 @@ mod dispatcher; mod display; mod platform; +mod text_system; mod window; pub(crate) use dispatcher::*; pub(crate) use display::*; pub(crate) use platform::*; +pub(crate) use text_system::*; pub(crate) use window::*; diff --git a/crates/gpui/src/platform/test/platform.rs b/crates/gpui/src/platform/test/platform.rs index 5aadc4b760aea..464e38b0c405d 100644 --- a/crates/gpui/src/platform/test/platform.rs +++ b/crates/gpui/src/platform/test/platform.rs @@ -1,7 +1,7 @@ use crate::{ AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor, - Keymap, Platform, PlatformDisplay, PlatformTextSystem, Task, TestDisplay, TestWindow, - WindowOptions, + Keymap, Platform, PlatformDisplay, PlatformTextSystem, Task, TestDisplay, TestTextSystem, + TestWindow, WindowOptions, }; use anyhow::{anyhow, Result}; use collections::VecDeque; @@ -118,7 +118,7 @@ impl Platform for TestPlatform { } fn text_system(&self) -> Arc { - Arc::new(crate::platform::mac::MacTextSystem::new()) + Arc::new(TestTextSystem {}) } fn run(&self, _on_finish_launching: Box) { diff --git a/crates/gpui/src/platform/test/text_system.rs b/crates/gpui/src/platform/test/text_system.rs new file mode 100644 index 0000000000000..c72ee6f00c189 --- /dev/null +++ b/crates/gpui/src/platform/test/text_system.rs @@ -0,0 +1,58 @@ +use crate::{ + Bounds, DevicePixels, Font, FontId, FontMetrics, FontRun, GlyphId, LineLayout, Pixels, + PlatformTextSystem, RenderGlyphParams, Size, +}; +use anyhow::Result; +use std::sync::Arc; + +pub(crate) struct TestTextSystem {} + +#[allow(unused)] +impl PlatformTextSystem for TestTextSystem { + fn add_fonts(&self, fonts: &[Arc>]) -> Result<()> { + unimplemented!() + } + fn all_font_names(&self) -> Vec { + unimplemented!() + } + fn all_font_families(&self) -> Vec { + unimplemented!() + } + fn font_id(&self, descriptor: &Font) -> Result { + unimplemented!() + } + fn font_metrics(&self, font_id: FontId) -> FontMetrics { + unimplemented!() + } + fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { + unimplemented!() + } + fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { + unimplemented!() + } + fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option { + unimplemented!() + } + fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result> { + unimplemented!() + } + fn rasterize_glyph( + &self, + params: &RenderGlyphParams, + raster_bounds: Bounds, + ) -> Result<(Size, Vec)> { + unimplemented!() + } + fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout { + unimplemented!() + } + fn wrap_line( + &self, + text: &str, + font_id: FontId, + font_size: Pixels, + width: Pixels, + ) -> Vec { + unimplemented!() + } +} diff --git a/crates/gpui/src/window/element_cx.rs b/crates/gpui/src/window/element_cx.rs index 4e21988a2ef37..6e2e5bc7256d7 100644 --- a/crates/gpui/src/window/element_cx.rs +++ b/crates/gpui/src/window/element_cx.rs @@ -35,8 +35,8 @@ use crate::{ InputHandler, IsZero, KeyContext, KeyEvent, KeymatchMode, LayoutId, MonochromeSprite, MouseEvent, PaintQuad, Path, Pixels, PlatformInputHandler, Point, PolychromeSprite, Quad, RenderGlyphParams, RenderImageParams, RenderSvgParams, Scene, Shadow, SharedString, Size, - StackingContext, StackingOrder, Style, TextStyleRefinement, Underline, UnderlineStyle, - Window, WindowContext, SUBPIXEL_VARIANTS, + StackingContext, StackingOrder, Style, TextStyleRefinement, Underline, UnderlineStyle, Window, + WindowContext, SUBPIXEL_VARIANTS, }; type AnyMouseListener = Box; From b0376aaf8fe272e82552203f4d21e854bb1e34a1 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 26 Jan 2024 00:03:30 -0800 Subject: [PATCH 04/51] linux: start the text system --- crates/gpui/Cargo.toml | 1 - crates/gpui/src/platform/linux.rs | 2 + crates/gpui/src/platform/linux/platform.rs | 10 +- crates/gpui/src/platform/linux/text_system.rs | 103 ++++++++++++++++++ 4 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 crates/gpui/src/platform/linux/text_system.rs diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 0a0a07382916c..dbae48a5aaeb5 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -88,7 +88,6 @@ cocoa = "0.25" core-foundation = { version = "0.9.3", features = ["with-uuid"] } core-graphics = "0.22.3" core-text = "19.2" -font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "d97147f" } foreign-types = "0.3" log.workspace = true media = { path = "../media" } diff --git a/crates/gpui/src/platform/linux.rs b/crates/gpui/src/platform/linux.rs index 7a361c70343ac..c762c3135e9c6 100644 --- a/crates/gpui/src/platform/linux.rs +++ b/crates/gpui/src/platform/linux.rs @@ -1,5 +1,7 @@ mod dispatcher; mod platform; +mod text_system; pub(crate) use dispatcher::*; pub(crate) use platform::*; +pub(crate) use text_system::*; diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index a2b87b7ea139d..327ec59d16eb6 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -2,9 +2,9 @@ use crate::{ Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, - ForegroundExecutor, Keymap, LinuxDispatcher, Menu, PathPromptOptions, Platform, - PlatformDisplay, PlatformInput, PlatformTextSystem, PlatformWindow, Result, SemanticVersion, - Task, WindowOptions, + ForegroundExecutor, Keymap, LinuxDispatcher, LinuxTextSystem, Menu, PathPromptOptions, + Platform, PlatformDisplay, PlatformInput, PlatformTextSystem, PlatformWindow, Result, + SemanticVersion, Task, WindowOptions, }; use futures::channel::oneshot; @@ -23,6 +23,7 @@ pub(crate) struct LinuxPlatform(Mutex); pub(crate) struct LinuxPlatformState { background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, + text_system: Arc, } impl Default for LinuxPlatform { @@ -37,6 +38,7 @@ impl LinuxPlatform { Self(Mutex::new(LinuxPlatformState { background_executor: BackgroundExecutor::new(dispatcher.clone()), foreground_executor: ForegroundExecutor::new(dispatcher), + text_system: Arc::new(LinuxTextSystem::new()), })) } } @@ -51,7 +53,7 @@ impl Platform for LinuxPlatform { } fn text_system(&self) -> Arc { - unimplemented!() + self.0.lock().text_system.clone() } fn run(&self, on_finish_launching: Box) { diff --git a/crates/gpui/src/platform/linux/text_system.rs b/crates/gpui/src/platform/linux/text_system.rs new file mode 100644 index 0000000000000..30f65c0ece7ca --- /dev/null +++ b/crates/gpui/src/platform/linux/text_system.rs @@ -0,0 +1,103 @@ +use crate::{ + Bounds, DevicePixels, Font, FontId, FontMetrics, FontRun, GlyphId, LineLayout, Pixels, + PlatformTextSystem, RenderGlyphParams, SharedString, Size, +}; +use anyhow::Result; +use collections::HashMap; +use font_kit::{ + font::Font as FontKitFont, + handle::Handle, + hinting::HintingOptions, + metrics::Metrics, + properties::{Style as FontkitStyle, Weight as FontkitWeight}, + source::SystemSource, + sources::mem::MemSource, +}; +use parking_lot::RwLock; +use smallvec::SmallVec; +use std::sync::Arc; + +pub(crate) struct LinuxTextSystem(RwLock); + +struct LinuxTextSystemState { + memory_source: MemSource, + system_source: SystemSource, + fonts: Vec, + font_selections: HashMap, + font_ids_by_postscript_name: HashMap, + font_ids_by_family_name: HashMap>, + postscript_names_by_font_id: HashMap, +} + +unsafe impl Send for LinuxTextSystemState {} +unsafe impl Sync for LinuxTextSystemState {} + +impl LinuxTextSystem { + pub(crate) fn new() -> Self { + Self(RwLock::new(LinuxTextSystemState { + memory_source: MemSource::empty(), + system_source: SystemSource::new(), + fonts: Vec::new(), + font_selections: HashMap::default(), + font_ids_by_postscript_name: HashMap::default(), + font_ids_by_family_name: HashMap::default(), + postscript_names_by_font_id: HashMap::default(), + })) + } +} + +impl Default for LinuxTextSystem { + fn default() -> Self { + Self::new() + } +} + +#[allow(unused)] +impl PlatformTextSystem for LinuxTextSystem { + fn add_fonts(&self, fonts: &[Arc>]) -> Result<()> { + unimplemented!() + } + fn all_font_names(&self) -> Vec { + unimplemented!() + } + fn all_font_families(&self) -> Vec { + unimplemented!() + } + fn font_id(&self, descriptor: &Font) -> Result { + unimplemented!() + } + fn font_metrics(&self, font_id: FontId) -> FontMetrics { + unimplemented!() + } + fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { + unimplemented!() + } + fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { + unimplemented!() + } + fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option { + unimplemented!() + } + fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result> { + unimplemented!() + } + fn rasterize_glyph( + &self, + params: &RenderGlyphParams, + raster_bounds: Bounds, + ) -> Result<(Size, Vec)> { + unimplemented!() + } + fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout { + unimplemented!() + } + fn wrap_line( + &self, + text: &str, + font_id: FontId, + font_size: Pixels, + width: Pixels, + ) -> Vec { + unimplemented!() + } +} From e95bf24a1f8b6196fb423e886017bd2a121a08ec Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 26 Jan 2024 23:51:35 -0800 Subject: [PATCH 05/51] linux: basic window, display, and atlas --- crates/gpui/Cargo.toml | 2 +- crates/gpui/src/platform/linux.rs | 9 + crates/gpui/src/platform/linux/blade_atlas.rs | 258 ++++++++++++++++++ crates/gpui/src/platform/linux/blade_belt.rs | 84 ++++++ crates/gpui/src/platform/linux/display.rs | 23 ++ crates/gpui/src/platform/linux/platform.rs | 141 +++++----- crates/gpui/src/platform/linux/text_system.rs | 6 +- crates/gpui/src/platform/linux/window.rs | 143 ++++++++++ 8 files changed, 582 insertions(+), 84 deletions(-) create mode 100644 crates/gpui/src/platform/linux/blade_atlas.rs create mode 100644 crates/gpui/src/platform/linux/blade_belt.rs create mode 100644 crates/gpui/src/platform/linux/display.rs create mode 100644 crates/gpui/src/platform/linux/window.rs diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index dbae48a5aaeb5..42978e203bb80 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -26,7 +26,7 @@ anyhow.workspace = true async-task = "4.7" backtrace = { version = "0.3", optional = true } bitflags = "2.4.0" -blade-graphics = "0.3" +blade = { package = "blade-graphics", version = "0.3" } blade-macros = "0.2" collections = { path = "../collections" } ctor.workspace = true diff --git a/crates/gpui/src/platform/linux.rs b/crates/gpui/src/platform/linux.rs index c762c3135e9c6..ba70c6fe67bde 100644 --- a/crates/gpui/src/platform/linux.rs +++ b/crates/gpui/src/platform/linux.rs @@ -1,7 +1,16 @@ +mod blade_atlas; +mod blade_belt; mod dispatcher; +mod display; mod platform; mod text_system; +mod window; +pub(crate) use blade_atlas::*; pub(crate) use dispatcher::*; +pub(crate) use display::*; pub(crate) use platform::*; pub(crate) use text_system::*; +pub(crate) use window::*; + +use blade_belt::*; diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs new file mode 100644 index 0000000000000..24a9bbde8d8e3 --- /dev/null +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -0,0 +1,258 @@ +use super::{BladeBelt, BladeBeltDescriptor}; +use crate::{ + AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas, + Point, Size, +}; +use anyhow::Result; +use collections::FxHashMap; +use derive_more::{Deref, DerefMut}; +use etagere::BucketedAtlasAllocator; +use parking_lot::Mutex; +use std::{borrow::Cow, sync::Arc}; + +pub(crate) struct BladeAtlas(Mutex); + +struct BladeAtlasState { + gpu: Arc, + gpu_encoder: blade::CommandEncoder, + upload_belt: BladeBelt, + monochrome_textures: Vec, + polychrome_textures: Vec, + path_textures: Vec, + tiles_by_key: FxHashMap, +} + +impl BladeAtlas { + pub(crate) fn new(gpu: &Arc) -> Self { + BladeAtlas(Mutex::new(BladeAtlasState { + gpu: Arc::clone(gpu), + gpu_encoder: gpu.create_command_encoder(blade::CommandEncoderDesc { + name: "atlas", + buffer_count: 3, + }), + upload_belt: BladeBelt::new(BladeBeltDescriptor { + memory: blade::Memory::Upload, + min_chunk_size: 0x10000, + }), + monochrome_textures: Default::default(), + polychrome_textures: Default::default(), + path_textures: Default::default(), + tiles_by_key: Default::default(), + })) + } + + pub(crate) fn clear_textures(&self, texture_kind: AtlasTextureKind) { + let mut lock = self.0.lock(); + let textures = match texture_kind { + AtlasTextureKind::Monochrome => &mut lock.monochrome_textures, + AtlasTextureKind::Polychrome => &mut lock.polychrome_textures, + AtlasTextureKind::Path => &mut lock.path_textures, + }; + for texture in textures { + texture.clear(); + } + } + + pub fn start_frame(&self) { + let mut lock = self.0.lock(); + lock.gpu_encoder.start(); + } + + pub fn finish_frame(&self) -> blade::SyncPoint { + let mut lock = self.0.lock(); + let gpu = lock.gpu.clone(); + let sync_point = gpu.submit(&mut lock.gpu_encoder); + lock.upload_belt.flush(&sync_point); + sync_point + } +} + +impl PlatformAtlas for BladeAtlas { + fn get_or_insert_with<'a>( + &self, + key: &AtlasKey, + build: &mut dyn FnMut() -> Result<(Size, Cow<'a, [u8]>)>, + ) -> Result { + let mut lock = self.0.lock(); + if let Some(tile) = lock.tiles_by_key.get(key) { + Ok(tile.clone()) + } else { + let (size, bytes) = build()?; + let tile = lock.allocate(size, key.texture_kind()); + lock.upload_texture(tile.texture_id, tile.bounds, &bytes); + lock.tiles_by_key.insert(key.clone(), tile.clone()); + Ok(tile) + } + } +} + +impl BladeAtlasState { + fn allocate(&mut self, size: Size, texture_kind: AtlasTextureKind) -> AtlasTile { + let textures = match texture_kind { + AtlasTextureKind::Monochrome => &mut self.monochrome_textures, + AtlasTextureKind::Polychrome => &mut self.polychrome_textures, + AtlasTextureKind::Path => &mut self.path_textures, + }; + textures + .iter_mut() + .rev() + .find_map(|texture| texture.allocate(size)) + .unwrap_or_else(|| { + let texture = self.push_texture(size, texture_kind); + texture.allocate(size).unwrap() + }) + } + + fn push_texture( + &mut self, + min_size: Size, + kind: AtlasTextureKind, + ) -> &mut BladeAtlasTexture { + const DEFAULT_ATLAS_SIZE: Size = Size { + width: DevicePixels(1024), + height: DevicePixels(1024), + }; + + let size = min_size.max(&DEFAULT_ATLAS_SIZE); + let format; + let usage; + match kind { + AtlasTextureKind::Monochrome => { + format = blade::TextureFormat::R8Unorm; + usage = blade::TextureUsage::COPY | blade::TextureUsage::RESOURCE; + } + AtlasTextureKind::Polychrome => { + format = blade::TextureFormat::Bgra8Unorm; + usage = blade::TextureUsage::COPY | blade::TextureUsage::RESOURCE; + } + AtlasTextureKind::Path => { + format = blade::TextureFormat::R16Float; + usage = blade::TextureUsage::COPY + | blade::TextureUsage::RESOURCE + | blade::TextureUsage::TARGET; + } + } + + let raw = self.gpu.create_texture(blade::TextureDesc { + name: "", + format, + size: blade::Extent { + width: size.width.into(), + height: size.height.into(), + depth: 1, + }, + array_layer_count: 1, + mip_level_count: 1, + dimension: blade::TextureDimension::D2, + usage, + }); + + let textures = match kind { + AtlasTextureKind::Monochrome => &mut self.monochrome_textures, + AtlasTextureKind::Polychrome => &mut self.polychrome_textures, + AtlasTextureKind::Path => &mut self.path_textures, + }; + let atlas_texture = BladeAtlasTexture { + id: AtlasTextureId { + index: textures.len() as u32, + kind, + }, + allocator: etagere::BucketedAtlasAllocator::new(size.into()), + format, + raw, + }; + textures.push(atlas_texture); + textures.last_mut().unwrap() + } + + fn upload_texture(&mut self, id: AtlasTextureId, bounds: Bounds, bytes: &[u8]) { + let textures = match id.kind { + crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, + crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, + crate::AtlasTextureKind::Path => &self.path_textures, + }; + let texture = &textures[id.index as usize]; + + let src_data = self.upload_belt.alloc_data(bytes, &self.gpu); + + let mut transfers = self.gpu_encoder.transfer(); + transfers.copy_buffer_to_texture( + src_data, + bounds.size.width.to_bytes(texture.bytes_per_pixel()), + blade::TexturePiece { + texture: texture.raw, + mip_level: 0, + array_layer: 0, + origin: [bounds.origin.x.into(), bounds.origin.y.into(), 0], + }, + blade::Extent { + width: bounds.size.width.into(), + height: bounds.size.height.into(), + depth: 1, + }, + ); + } +} + +struct BladeAtlasTexture { + id: AtlasTextureId, + allocator: BucketedAtlasAllocator, + raw: blade::Texture, + format: blade::TextureFormat, +} + +impl BladeAtlasTexture { + fn clear(&mut self) { + self.allocator.clear(); + } + + fn allocate(&mut self, size: Size) -> Option { + let allocation = self.allocator.allocate(size.into())?; + let tile = AtlasTile { + texture_id: self.id, + tile_id: allocation.id.into(), + bounds: Bounds { + origin: allocation.rectangle.min.into(), + size, + }, + }; + Some(tile) + } + + fn bytes_per_pixel(&self) -> u8 { + self.format.block_info().size + } +} + +impl From> for etagere::Size { + fn from(size: Size) -> Self { + etagere::Size::new(size.width.into(), size.height.into()) + } +} + +impl From for Point { + fn from(value: etagere::Point) -> Self { + Point { + x: DevicePixels::from(value.x), + y: DevicePixels::from(value.y), + } + } +} + +impl From for Size { + fn from(size: etagere::Size) -> Self { + Size { + width: DevicePixels::from(size.width), + height: DevicePixels::from(size.height), + } + } +} + +impl From for Bounds { + fn from(rectangle: etagere::Rectangle) -> Self { + Bounds { + origin: rectangle.min.into(), + size: rectangle.size().into(), + } + } +} diff --git a/crates/gpui/src/platform/linux/blade_belt.rs b/crates/gpui/src/platform/linux/blade_belt.rs new file mode 100644 index 0000000000000..ff3d5c66929cf --- /dev/null +++ b/crates/gpui/src/platform/linux/blade_belt.rs @@ -0,0 +1,84 @@ +struct ReusableBuffer { + raw: blade::Buffer, + size: u64, +} + +pub struct BladeBeltDescriptor { + pub memory: blade::Memory, + pub min_chunk_size: u64, +} + +/// A belt of buffers, used by the BladeAtlas to cheaply +/// find staging space for uploads. +pub struct BladeBelt { + desc: BladeBeltDescriptor, + buffers: Vec<(ReusableBuffer, blade::SyncPoint)>, + active: Vec<(ReusableBuffer, u64)>, +} + +impl BladeBelt { + pub fn new(desc: BladeBeltDescriptor) -> Self { + Self { + desc, + buffers: Vec::new(), + active: Vec::new(), + } + } + + pub fn destroy(&mut self, gpu: &blade::Context) { + for (buffer, _) in self.buffers.drain(..) { + gpu.destroy_buffer(buffer.raw); + } + for (buffer, _) in self.active.drain(..) { + gpu.destroy_buffer(buffer.raw); + } + } + + pub fn alloc(&mut self, size: u64, gpu: &blade::Context) -> blade::BufferPiece { + for &mut (ref rb, ref mut offset) in self.active.iter_mut() { + if *offset + size <= rb.size { + let piece = rb.raw.at(*offset); + *offset += size; + return piece; + } + } + + let index_maybe = self + .buffers + .iter() + .position(|&(ref rb, ref sp)| size <= rb.size && gpu.wait_for(sp, 0)); + if let Some(index) = index_maybe { + let (rb, _) = self.buffers.remove(index); + let piece = rb.raw.into(); + self.active.push((rb, size)); + return piece; + } + + let chunk_index = self.buffers.len() + self.active.len(); + let chunk_size = size.max(self.desc.min_chunk_size); + let chunk = gpu.create_buffer(blade::BufferDesc { + name: &format!("chunk-{}", chunk_index), + size: chunk_size, + memory: self.desc.memory, + }); + let rb = ReusableBuffer { + raw: chunk, + size: chunk_size, + }; + self.active.push((rb, size)); + chunk.into() + } + + pub fn alloc_data(&mut self, data: &[u8], gpu: &blade::Context) -> blade::BufferPiece { + let bp = self.alloc(data.len() as u64, gpu); + unsafe { + std::ptr::copy_nonoverlapping(data.as_ptr(), bp.data(), data.len()); + } + bp + } + + pub fn flush(&mut self, sp: &blade::SyncPoint) { + self.buffers + .extend(self.active.drain(..).map(|(rb, _)| (rb, sp.clone()))); + } +} diff --git a/crates/gpui/src/platform/linux/display.rs b/crates/gpui/src/platform/linux/display.rs new file mode 100644 index 0000000000000..507edad3b016c --- /dev/null +++ b/crates/gpui/src/platform/linux/display.rs @@ -0,0 +1,23 @@ +use crate::{point, size, Bounds, DisplayId, GlobalPixels, PlatformDisplay}; +use anyhow::Result; +use uuid::Uuid; + +#[derive(Debug)] +pub(crate) struct LinuxDisplay; + +impl PlatformDisplay for LinuxDisplay { + fn id(&self) -> DisplayId { + DisplayId(0) + } + + fn uuid(&self) -> Result { + Ok(Uuid::from_bytes([0; 16])) + } + + fn bounds(&self) -> Bounds { + Bounds { + origin: point(GlobalPixels(0.0), GlobalPixels(0.0)), + size: size(GlobalPixels(100.0), GlobalPixels(100.0)), + } + } +} diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 327ec59d16eb6..8136b77292bb3 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -2,9 +2,9 @@ use crate::{ Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, - ForegroundExecutor, Keymap, LinuxDispatcher, LinuxTextSystem, Menu, PathPromptOptions, - Platform, PlatformDisplay, PlatformInput, PlatformTextSystem, PlatformWindow, Result, - SemanticVersion, Task, WindowOptions, + ForegroundExecutor, Keymap, LinuxDispatcher, LinuxDisplay, LinuxTextSystem, LinuxWindow, Menu, + PathPromptOptions, Platform, PlatformDisplay, PlatformInput, PlatformTextSystem, + PlatformWindow, Result, SemanticVersion, Task, WindowOptions, }; use futures::channel::oneshot; @@ -21,6 +21,7 @@ use time::UtcOffset; pub(crate) struct LinuxPlatform(Mutex); pub(crate) struct LinuxPlatformState { + gpu: Arc, background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, text_system: Arc, @@ -35,7 +36,17 @@ impl Default for LinuxPlatform { impl LinuxPlatform { pub(crate) fn new() -> Self { let dispatcher = Arc::new(LinuxDispatcher::new()); + let gpu = Arc::new( + unsafe { + blade::Context::init(blade::ContextDesc { + validation: true, //FIXME + capture: false, + }) + } + .unwrap(), + ); Self(Mutex::new(LinuxPlatformState { + gpu, background_executor: BackgroundExecutor::new(dispatcher.clone()), foreground_executor: ForegroundExecutor::new(dispatcher), text_system: Arc::new(LinuxTextSystem::new()), @@ -57,43 +68,31 @@ impl Platform for LinuxPlatform { } fn run(&self, on_finish_launching: Box) { - unimplemented!() + on_finish_launching() } - fn quit(&self) { - unimplemented!() - } + fn quit(&self) {} - fn restart(&self) { - unimplemented!() - } + fn restart(&self) {} - fn activate(&self, ignoring_other_apps: bool) { - unimplemented!() - } + fn activate(&self, ignoring_other_apps: bool) {} - fn hide(&self) { - unimplemented!() - } + fn hide(&self) {} - fn hide_other_apps(&self) { - unimplemented!() - } + fn hide_other_apps(&self) {} - fn unhide_other_apps(&self) { - unimplemented!() - } + fn unhide_other_apps(&self) {} fn displays(&self) -> Vec> { - unimplemented!() + Vec::new() } fn display(&self, id: DisplayId) -> Option> { - unimplemented!() + None } fn active_window(&self) -> Option { - unimplemented!() + None } fn open_window( @@ -101,7 +100,13 @@ impl Platform for LinuxPlatform { handle: AnyWindowHandle, options: WindowOptions, ) -> Box { - unimplemented!() + let lock = self.0.lock(); + Box::new(LinuxWindow::new( + options, + handle, + Rc::new(LinuxDisplay), + &lock.gpu, + )) } fn set_display_link_output_callback( @@ -112,21 +117,13 @@ impl Platform for LinuxPlatform { unimplemented!() } - fn start_display_link(&self, display_id: DisplayId) { - unimplemented!() - } + fn start_display_link(&self, display_id: DisplayId) {} - fn stop_display_link(&self, display_id: DisplayId) { - unimplemented!() - } + fn stop_display_link(&self, display_id: DisplayId) {} - fn open_url(&self, url: &str) { - unimplemented!() - } + fn open_url(&self, url: &str) {} - fn on_open_urls(&self, callback: Box)>) { - unimplemented!() - } + fn on_open_urls(&self, callback: Box)>) {} fn prompt_for_paths( &self, @@ -139,88 +136,72 @@ impl Platform for LinuxPlatform { unimplemented!() } - fn reveal_path(&self, path: &Path) { - unimplemented!() - } + fn reveal_path(&self, path: &Path) {} - fn on_become_active(&self, callback: Box) { - unimplemented!() - } + fn on_become_active(&self, callback: Box) {} - fn on_resign_active(&self, callback: Box) { - unimplemented!() - } + fn on_resign_active(&self, callback: Box) {} - fn on_quit(&self, callback: Box) { - unimplemented!() - } + fn on_quit(&self, callback: Box) {} - fn on_reopen(&self, callback: Box) { - unimplemented!() - } + fn on_reopen(&self, callback: Box) {} - fn on_event(&self, callback: Box bool>) { - unimplemented!() - } + fn on_event(&self, callback: Box bool>) {} - fn on_app_menu_action(&self, callback: Box) { - unimplemented!() - } + fn on_app_menu_action(&self, callback: Box) {} - fn on_will_open_app_menu(&self, callback: Box) { - unimplemented!() - } + fn on_will_open_app_menu(&self, callback: Box) {} - fn on_validate_app_menu_command(&self, callback: Box bool>) { - unimplemented!() - } + fn on_validate_app_menu_command(&self, callback: Box bool>) {} fn os_name(&self) -> &'static str { "Linux" } fn double_click_interval(&self) -> Duration { - unimplemented!() + Duration::default() } fn os_version(&self) -> Result { - unimplemented!() + Ok(SemanticVersion { + major: 1, + minor: 0, + patch: 0, + }) } fn app_version(&self) -> Result { - unimplemented!() + Ok(SemanticVersion { + major: 1, + minor: 0, + patch: 0, + }) } fn app_path(&self) -> Result { unimplemented!() } - fn set_menus(&self, menus: Vec, keymap: &Keymap) { - unimplemented!() - } + fn set_menus(&self, menus: Vec, keymap: &Keymap) {} fn local_timezone(&self) -> UtcOffset { - unimplemented!() + UtcOffset::UTC } fn path_for_auxiliary_executable(&self, name: &str) -> Result { unimplemented!() } - fn set_cursor_style(&self, style: CursorStyle) { - unimplemented!() - } + fn set_cursor_style(&self, style: CursorStyle) {} fn should_auto_hide_scrollbars(&self) -> bool { - unimplemented!() + false } - fn write_to_clipboard(&self, item: ClipboardItem) { - unimplemented!() - } + fn write_to_clipboard(&self, item: ClipboardItem) {} fn read_from_clipboard(&self) -> Option { - unimplemented!() + None } fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Task> { diff --git a/crates/gpui/src/platform/linux/text_system.rs b/crates/gpui/src/platform/linux/text_system.rs index 30f65c0ece7ca..09913b7077c4e 100644 --- a/crates/gpui/src/platform/linux/text_system.rs +++ b/crates/gpui/src/platform/linux/text_system.rs @@ -55,13 +55,13 @@ impl Default for LinuxTextSystem { #[allow(unused)] impl PlatformTextSystem for LinuxTextSystem { fn add_fonts(&self, fonts: &[Arc>]) -> Result<()> { - unimplemented!() + Ok(()) //TODO } fn all_font_names(&self) -> Vec { - unimplemented!() + Vec::new() } fn all_font_families(&self) -> Vec { - unimplemented!() + Vec::new() } fn font_id(&self, descriptor: &Font) -> Result { unimplemented!() diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs new file mode 100644 index 0000000000000..72155dacab045 --- /dev/null +++ b/crates/gpui/src/platform/linux/window.rs @@ -0,0 +1,143 @@ +use crate::{ + px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, BladeAtlas, Bounds, KeyDownEvent, + Keystroke, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, + PlatformWindow, Point, Size, TileId, WindowAppearance, WindowBounds, WindowOptions, +}; +use collections::HashMap; +use parking_lot::Mutex; +use std::{ + rc::{Rc, Weak}, + sync::{self, Arc}, +}; + +pub(crate) struct LinuxWindowState { + display: Rc, + sprite_atlas: Arc, +} + +#[derive(Clone)] +pub(crate) struct LinuxWindow(pub(crate) Arc>); + +impl LinuxWindow { + pub fn new( + options: WindowOptions, + handle: AnyWindowHandle, + display: Rc, + gpu: &Arc, + ) -> Self { + Self(Arc::new(Mutex::new(LinuxWindowState { + display, + sprite_atlas: Arc::new(BladeAtlas::new(gpu)), + }))) + } +} + +impl PlatformWindow for LinuxWindow { + fn bounds(&self) -> WindowBounds { + unimplemented!() + } + + fn content_size(&self) -> Size { + unimplemented!() + } + + fn scale_factor(&self) -> f32 { + 1.0 + } + + fn titlebar_height(&self) -> Pixels { + unimplemented!() + } + + fn appearance(&self) -> WindowAppearance { + unimplemented!() + } + + fn display(&self) -> Rc { + Rc::clone(&self.0.lock().display) + } + + fn mouse_position(&self) -> Point { + Point::default() + } + + fn modifiers(&self) -> crate::Modifiers { + crate::Modifiers::default() + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } + + fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {} + + fn take_input_handler(&mut self) -> Option { + None + } + + fn prompt( + &self, + _level: crate::PromptLevel, + _msg: &str, + _detail: Option<&str>, + _answers: &[&str], + ) -> futures::channel::oneshot::Receiver { + unimplemented!() + } + + fn activate(&self) {} + + fn set_title(&mut self, title: &str) {} + + fn set_edited(&mut self, edited: bool) {} + + fn show_character_palette(&self) { + unimplemented!() + } + + fn minimize(&self) { + unimplemented!() + } + + fn zoom(&self) { + unimplemented!() + } + + fn toggle_full_screen(&self) { + unimplemented!() + } + + fn on_request_frame(&self, _callback: Box) {} + + fn on_input(&self, callback: Box bool>) {} + + fn on_active_status_change(&self, callback: Box) {} + + fn on_resize(&self, callback: Box, f32)>) {} + + fn on_fullscreen(&self, _callback: Box) {} + + fn on_moved(&self, callback: Box) {} + + fn on_should_close(&self, callback: Box bool>) {} + + fn on_close(&self, _callback: Box) { + unimplemented!() + } + + fn on_appearance_changed(&self, _callback: Box) { + unimplemented!() + } + + fn is_topmost_for_position(&self, _position: crate::Point) -> bool { + unimplemented!() + } + + fn invalidate(&self) {} + + fn draw(&self, _scene: &crate::Scene) {} + + fn sprite_atlas(&self) -> sync::Arc { + self.0.lock().sprite_atlas.clone() + } +} From cefc98258fe2d2f0963aac2321ba14ea7ff707b1 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sat, 27 Jan 2024 22:59:30 -0800 Subject: [PATCH 06/51] linux: hook up X11rb for Window creation --- Cargo.lock | 28 ++++++ crates/gpui/Cargo.toml | 1 + crates/gpui/src/platform/linux/display.rs | 35 +++++-- crates/gpui/src/platform/linux/platform.rs | 27 +++++- crates/gpui/src/platform/linux/window.rs | 101 +++++++++++++++++++-- 5 files changed, 174 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9644e2b61983e..291e86f0b5226 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3088,6 +3088,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -3277,6 +3287,7 @@ dependencies = [ "util", "uuid 1.4.1", "waker-fn", + "x11rb", ] [[package]] @@ -10273,6 +10284,23 @@ dependencies = [ "tap", ] +[[package]] +name = "x11rb" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" +dependencies = [ + "gethostname", + "rustix 0.38.30", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" + [[package]] name = "xattr" version = "0.2.3" diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 42978e203bb80..d5a901df4cacf 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -96,3 +96,4 @@ objc = "0.2" [target.'cfg(target_os = "linux")'.dependencies] flume = "0.11" +x11rb = "0.13" diff --git a/crates/gpui/src/platform/linux/display.rs b/crates/gpui/src/platform/linux/display.rs index 507edad3b016c..7941daf6d0b6e 100644 --- a/crates/gpui/src/platform/linux/display.rs +++ b/crates/gpui/src/platform/linux/display.rs @@ -1,23 +1,42 @@ -use crate::{point, size, Bounds, DisplayId, GlobalPixels, PlatformDisplay}; +use crate::{point, size, Bounds, DisplayId, GlobalPixels, PlatformDisplay, Size}; use anyhow::Result; use uuid::Uuid; +use x11rb::{connection::Connection as _, rust_connection::RustConnection}; #[derive(Debug)] -pub(crate) struct LinuxDisplay; +pub(crate) struct LinuxDisplay { + x11_screen_index: usize, + bounds: Bounds, + uuid: Uuid, +} + +impl LinuxDisplay { + pub(crate) fn new(xc: &RustConnection, x11_screen_index: usize) -> Self { + let screen = &xc.setup().roots[x11_screen_index]; + Self { + x11_screen_index, + bounds: Bounds { + origin: Default::default(), + size: Size { + width: GlobalPixels(screen.width_in_pixels as f32), + height: GlobalPixels(screen.height_in_pixels as f32), + }, + }, + uuid: Uuid::from_bytes([0; 16]), + } + } +} impl PlatformDisplay for LinuxDisplay { fn id(&self) -> DisplayId { - DisplayId(0) + DisplayId(self.x11_screen_index as u32) } fn uuid(&self) -> Result { - Ok(Uuid::from_bytes([0; 16])) + Ok(self.uuid) } fn bounds(&self) -> Bounds { - Bounds { - origin: point(GlobalPixels(0.0), GlobalPixels(0.0)), - size: size(GlobalPixels(100.0), GlobalPixels(100.0)), - } + self.bounds } } diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 8136b77292bb3..18016358e638d 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -17,10 +17,13 @@ use std::{ time::Duration, }; use time::UtcOffset; +use x11rb::{connection::Connection as _, rust_connection::RustConnection}; pub(crate) struct LinuxPlatform(Mutex); pub(crate) struct LinuxPlatformState { + x11_connection: RustConnection, + x11_root_index: usize, gpu: Arc, background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, @@ -35,17 +38,22 @@ impl Default for LinuxPlatform { impl LinuxPlatform { pub(crate) fn new() -> Self { + let (x11_connection, x11_root_index) = x11rb::connect(None).unwrap(); + let dispatcher = Arc::new(LinuxDispatcher::new()); let gpu = Arc::new( unsafe { blade::Context::init(blade::ContextDesc { - validation: true, //FIXME + validation: cfg!(debug_assertions), capture: false, }) } .unwrap(), ); + Self(Mutex::new(LinuxPlatformState { + x11_connection, + x11_root_index, gpu, background_executor: BackgroundExecutor::new(dispatcher.clone()), foreground_executor: ForegroundExecutor::new(dispatcher), @@ -84,11 +92,21 @@ impl Platform for LinuxPlatform { fn unhide_other_apps(&self) {} fn displays(&self) -> Vec> { - Vec::new() + let lock = self.0.lock(); + let setup = lock.x11_connection.setup(); + (0..setup.roots.len()) + .map(|id| { + Rc::new(LinuxDisplay::new(&lock.x11_connection, id)) as Rc + }) + .collect() } fn display(&self, id: DisplayId) -> Option> { - None + let lock = self.0.lock(); + Some(Rc::new(LinuxDisplay::new( + &lock.x11_connection, + id.0 as usize, + ))) } fn active_window(&self) -> Option { @@ -104,7 +122,8 @@ impl Platform for LinuxPlatform { Box::new(LinuxWindow::new( options, handle, - Rc::new(LinuxDisplay), + &lock.x11_connection, + lock.x11_root_index, &lock.gpu, )) } diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 72155dacab045..ffc8372dbd491 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -1,7 +1,8 @@ use crate::{ px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, BladeAtlas, Bounds, KeyDownEvent, - Keystroke, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, - PlatformWindow, Point, Size, TileId, WindowAppearance, WindowBounds, WindowOptions, + Keystroke, LinuxDisplay, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, + PlatformInputHandler, PlatformWindow, Point, Size, TileId, WindowAppearance, WindowBounds, + WindowOptions, }; use collections::HashMap; use parking_lot::Mutex; @@ -9,9 +10,18 @@ use std::{ rc::{Rc, Weak}, sync::{self, Arc}, }; +use x11rb::{ + connection::Connection as _, + protocol::xproto::{ + AtomEnum, ConnectionExt as _, CreateWindowAux, EventMask, PropMode, WindowClass, + }, + rust_connection::RustConnection, + wrapper::ConnectionExt as _, +}; pub(crate) struct LinuxWindowState { - display: Rc, + display: Rc, + win_id: u32, sprite_atlas: Arc, } @@ -22,11 +32,90 @@ impl LinuxWindow { pub fn new( options: WindowOptions, handle: AnyWindowHandle, - display: Rc, + x11_connection: &RustConnection, + x11_main_screen_index: usize, gpu: &Arc, ) -> Self { + let x11_screen_index = options + .display_id + .map_or(x11_main_screen_index, |did| did.0 as usize); + let screen = &x11_connection.setup().roots[x11_screen_index]; + + let win_id = x11_connection.generate_id().unwrap(); + let win_aux = CreateWindowAux::new() + .event_mask( + EventMask::EXPOSURE | EventMask::STRUCTURE_NOTIFY | EventMask::POINTER_MOTION, + ) + .background_pixel(screen.white_pixel); + + let wm_protocols = x11_connection + .intern_atom(false, b"WM_PROTOCOLS") + .unwrap() + .reply() + .unwrap() + .atom; + let wm_delete_window = x11_connection + .intern_atom(false, b"WM_DELETE_WINDOW") + .unwrap() + .reply() + .unwrap() + .atom; + let (bound_x, bound_y, bound_width, bound_height) = match options.bounds { + WindowBounds::Fullscreen | WindowBounds::Maximized => { + (0, 0, screen.width_in_pixels, screen.height_in_pixels) + } + WindowBounds::Fixed(bounds) => ( + bounds.origin.x.0 as i16, + bounds.origin.y.0 as i16, + bounds.size.width.0 as u16, + bounds.size.height.0 as u16, + ), + }; + + x11_connection + .create_window( + x11rb::COPY_DEPTH_FROM_PARENT, + win_id, + screen.root, + bound_x, + bound_y, + bound_width, + bound_height, + 0, + WindowClass::INPUT_OUTPUT, + 0, + &win_aux, + ) + .unwrap(); + + if let Some(titlebar) = options.titlebar { + if let Some(title) = titlebar.title { + x11_connection + .change_property8( + PropMode::REPLACE, + win_id, + AtomEnum::WM_NAME, + AtomEnum::STRING, + title.as_bytes(), + ) + .unwrap(); + } + } + x11_connection + .change_property32( + PropMode::REPLACE, + win_id, + wm_protocols, + AtomEnum::ATOM, + &[wm_delete_window], + ) + .unwrap(); + + x11_connection.map_window(win_id).unwrap(); + Self(Arc::new(Mutex::new(LinuxWindowState { - display, + display: Rc::new(LinuxDisplay::new(x11_connection, x11_screen_index)), + win_id, sprite_atlas: Arc::new(BladeAtlas::new(gpu)), }))) } @@ -53,7 +142,7 @@ impl PlatformWindow for LinuxWindow { unimplemented!() } - fn display(&self) -> Rc { + fn display(&self) -> Rc { Rc::clone(&self.0.lock().display) } From aed363d3c78182134ac41098b13d410263cea238 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 28 Jan 2024 01:19:22 -0800 Subject: [PATCH 07/51] x11: create window and route events --- crates/gpui/src/platform/linux.rs | 2 + crates/gpui/src/platform/linux/blade_atlas.rs | 20 +++ .../gpui/src/platform/linux/blade_renderer.rs | 11 ++ crates/gpui/src/platform/linux/platform.rs | 123 +++++++++++++++--- crates/gpui/src/platform/linux/window.rs | 90 ++++++++----- 5 files changed, 194 insertions(+), 52 deletions(-) create mode 100644 crates/gpui/src/platform/linux/blade_renderer.rs diff --git a/crates/gpui/src/platform/linux.rs b/crates/gpui/src/platform/linux.rs index ba70c6fe67bde..f76e791a56187 100644 --- a/crates/gpui/src/platform/linux.rs +++ b/crates/gpui/src/platform/linux.rs @@ -1,5 +1,6 @@ mod blade_atlas; mod blade_belt; +mod blade_renderer; mod dispatcher; mod display; mod platform; @@ -14,3 +15,4 @@ pub(crate) use text_system::*; pub(crate) use window::*; use blade_belt::*; +use blade_renderer::*; diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index 24a9bbde8d8e3..2ee2b6d54862d 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -22,6 +22,22 @@ struct BladeAtlasState { tiles_by_key: FxHashMap, } +impl BladeAtlasState { + fn destroy(&mut self) { + for texture in self.monochrome_textures.drain(..) { + self.gpu.destroy_texture(texture.raw); + } + for texture in self.polychrome_textures.drain(..) { + self.gpu.destroy_texture(texture.raw); + } + for texture in self.path_textures.drain(..) { + self.gpu.destroy_texture(texture.raw); + } + self.gpu.destroy_command_encoder(&mut self.gpu_encoder); + self.upload_belt.destroy(&self.gpu); + } +} + impl BladeAtlas { pub(crate) fn new(gpu: &Arc) -> Self { BladeAtlas(Mutex::new(BladeAtlasState { @@ -41,6 +57,10 @@ impl BladeAtlas { })) } + pub(crate) fn destroy(&self) { + self.0.lock().destroy(); + } + pub(crate) fn clear_textures(&self, texture_kind: AtlasTextureKind) { let mut lock = self.0.lock(); let textures = match texture_kind { diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs new file mode 100644 index 0000000000000..1f87b30efe96a --- /dev/null +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -0,0 +1,11 @@ +use std::sync::Arc; + +pub struct BladeRenderer { + gpu: Arc, +} + +impl BladeRenderer { + pub fn new(gpu: Arc) -> Self { + Self { gpu } + } +} diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 18016358e638d..3817eeff34635 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -2,11 +2,13 @@ use crate::{ Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, - ForegroundExecutor, Keymap, LinuxDispatcher, LinuxDisplay, LinuxTextSystem, LinuxWindow, Menu, - PathPromptOptions, Platform, PlatformDisplay, PlatformInput, PlatformTextSystem, - PlatformWindow, Result, SemanticVersion, Task, WindowOptions, + ForegroundExecutor, Keymap, LinuxDispatcher, LinuxDisplay, LinuxTextSystem, LinuxWindow, + LinuxWindowState, LinuxWindowStatePtr, Menu, PathPromptOptions, Platform, PlatformDisplay, + PlatformInput, PlatformTextSystem, PlatformWindow, Result, SemanticVersion, Task, + WindowOptions, }; +use collections::{HashMap, HashSet}; use futures::channel::oneshot; use parking_lot::Mutex; @@ -17,16 +19,48 @@ use std::{ time::Duration, }; use time::UtcOffset; -use x11rb::{connection::Connection as _, rust_connection::RustConnection}; +use x11rb::{ + connection::Connection as _, + protocol::{ + xproto::{Atom, ConnectionExt as _}, + Event, + }, + rust_connection::RustConnection, +}; pub(crate) struct LinuxPlatform(Mutex); +pub(crate) struct WmAtoms { + pub protocols: Atom, + pub delete_window: Atom, +} + +impl WmAtoms { + fn new(x11_connection: &RustConnection) -> Self { + Self { + protocols: x11_connection + .intern_atom(false, b"WM_PROTOCOLS") + .unwrap() + .reply() + .unwrap() + .atom, + delete_window: x11_connection + .intern_atom(false, b"WM_DELETE_WINDOW") + .unwrap() + .reply() + .unwrap() + .atom, + } + } +} + pub(crate) struct LinuxPlatformState { x11_connection: RustConnection, x11_root_index: usize, - gpu: Arc, + atoms: WmAtoms, background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, + windows: HashMap, text_system: Arc, } @@ -39,24 +73,17 @@ impl Default for LinuxPlatform { impl LinuxPlatform { pub(crate) fn new() -> Self { let (x11_connection, x11_root_index) = x11rb::connect(None).unwrap(); + let atoms = WmAtoms::new(&x11_connection); let dispatcher = Arc::new(LinuxDispatcher::new()); - let gpu = Arc::new( - unsafe { - blade::Context::init(blade::ContextDesc { - validation: cfg!(debug_assertions), - capture: false, - }) - } - .unwrap(), - ); Self(Mutex::new(LinuxPlatformState { x11_connection, x11_root_index, - gpu, + atoms, background_executor: BackgroundExecutor::new(dispatcher.clone()), foreground_executor: ForegroundExecutor::new(dispatcher), + windows: HashMap::default(), text_system: Arc::new(LinuxTextSystem::new()), })) } @@ -76,7 +103,58 @@ impl Platform for LinuxPlatform { } fn run(&self, on_finish_launching: Box) { - on_finish_launching() + on_finish_launching(); + + let mut need_repaint = HashSet::::default(); + + while !self.0.lock().windows.is_empty() { + let event = self.0.lock().x11_connection.wait_for_event().unwrap(); + let mut event_option = Some(event); + while let Some(event) = event_option { + match event { + Event::Expose(event) => { + if event.count == 0 { + need_repaint.insert(event.window); + } + } + Event::ConfigureNotify(event) => { + let lock = self.0.lock(); + let mut window = lock.windows[&event.window].lock(); + window.resize(event.width, event.height); + } + Event::MotionNotify(_event) => { + //mouse_position = (event.event_x, event.event_y); + //need_repaint.insert(event.window); + } + Event::MapNotify(_) => {} + Event::ClientMessage(event) => { + let mut lock = self.0.lock(); + let data = event.data.as_data32(); + if data[0] == lock.atoms.delete_window { + { + let mut window = lock.windows[&event.window].lock(); + window.destroy(); + } + lock.windows.remove(&event.window); + } + } + Event::Error(error) => { + log::error!("X11 error {:?}", error); + } + _ => {} + } + + let lock = self.0.lock(); + event_option = lock.x11_connection.poll_for_event().unwrap(); + } + + for x11_window in need_repaint.drain() { + let lock = self.0.lock(); + let mut window = lock.windows[&x11_window].lock(); + window.paint(); + lock.x11_connection.flush().unwrap(); + } + } } fn quit(&self) {} @@ -118,14 +196,19 @@ impl Platform for LinuxPlatform { handle: AnyWindowHandle, options: WindowOptions, ) -> Box { - let lock = self.0.lock(); - Box::new(LinuxWindow::new( + let mut lock = self.0.lock(); + let win_id = lock.x11_connection.generate_id().unwrap(); + + let window_ptr = LinuxWindowState::new_ptr( options, handle, &lock.x11_connection, lock.x11_root_index, - &lock.gpu, - )) + win_id, + &lock.atoms, + ); + lock.windows.insert(win_id, window_ptr.clone()); + Box::new(LinuxWindow(window_ptr)) } fn set_display_link_output_callback( diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index ffc8372dbd491..63cbb15e5a591 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -1,8 +1,9 @@ +use super::BladeRenderer; use crate::{ px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, BladeAtlas, Bounds, KeyDownEvent, Keystroke, LinuxDisplay, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, Size, TileId, WindowAppearance, WindowBounds, - WindowOptions, + WindowOptions, WmAtoms, }; use collections::HashMap; use parking_lot::Mutex; @@ -21,45 +22,37 @@ use x11rb::{ pub(crate) struct LinuxWindowState { display: Rc, - win_id: u32, + x11_window: u32, + window_bounds: WindowBounds, + content_size: Size, sprite_atlas: Arc, + renderer: BladeRenderer, } +pub(crate) type LinuxWindowStatePtr = Arc>; #[derive(Clone)] -pub(crate) struct LinuxWindow(pub(crate) Arc>); +pub(crate) struct LinuxWindow(pub(crate) LinuxWindowStatePtr); -impl LinuxWindow { - pub fn new( +impl LinuxWindowState { + pub fn new_ptr( options: WindowOptions, handle: AnyWindowHandle, x11_connection: &RustConnection, x11_main_screen_index: usize, - gpu: &Arc, - ) -> Self { + x11_window: u32, + atoms: &WmAtoms, + ) -> LinuxWindowStatePtr { let x11_screen_index = options .display_id .map_or(x11_main_screen_index, |did| did.0 as usize); let screen = &x11_connection.setup().roots[x11_screen_index]; - let win_id = x11_connection.generate_id().unwrap(); let win_aux = CreateWindowAux::new() .event_mask( EventMask::EXPOSURE | EventMask::STRUCTURE_NOTIFY | EventMask::POINTER_MOTION, ) .background_pixel(screen.white_pixel); - let wm_protocols = x11_connection - .intern_atom(false, b"WM_PROTOCOLS") - .unwrap() - .reply() - .unwrap() - .atom; - let wm_delete_window = x11_connection - .intern_atom(false, b"WM_DELETE_WINDOW") - .unwrap() - .reply() - .unwrap() - .atom; let (bound_x, bound_y, bound_width, bound_height) = match options.bounds { WindowBounds::Fullscreen | WindowBounds::Maximized => { (0, 0, screen.width_in_pixels, screen.height_in_pixels) @@ -75,7 +68,7 @@ impl LinuxWindow { x11_connection .create_window( x11rb::COPY_DEPTH_FROM_PARENT, - win_id, + x11_window, screen.root, bound_x, bound_y, @@ -93,7 +86,7 @@ impl LinuxWindow { x11_connection .change_property8( PropMode::REPLACE, - win_id, + x11_window, AtomEnum::WM_NAME, AtomEnum::STRING, title.as_bytes(), @@ -104,30 +97,63 @@ impl LinuxWindow { x11_connection .change_property32( PropMode::REPLACE, - win_id, - wm_protocols, + x11_window, + atoms.protocols, AtomEnum::ATOM, - &[wm_delete_window], + &[atoms.delete_window], ) .unwrap(); - x11_connection.map_window(win_id).unwrap(); + x11_connection.map_window(x11_window).unwrap(); + x11_connection.flush().unwrap(); - Self(Arc::new(Mutex::new(LinuxWindowState { + let gpu = Arc::new( + unsafe { + blade::Context::init(blade::ContextDesc { + validation: cfg!(debug_assertions), + capture: false, + }) + } + .unwrap(), + ); + + Arc::new(Mutex::new(Self { display: Rc::new(LinuxDisplay::new(x11_connection, x11_screen_index)), - win_id, - sprite_atlas: Arc::new(BladeAtlas::new(gpu)), - }))) + x11_window, + window_bounds: options.bounds, + content_size: Size { + width: Pixels(bound_width as f32), + height: Pixels(bound_height as f32), + }, + sprite_atlas: Arc::new(BladeAtlas::new(&gpu)), + renderer: BladeRenderer::new(gpu), + })) + } + + pub fn resize(&mut self, width: u16, height: u16) { + self.content_size = Size { + width: Pixels(width as f32), + height: Pixels(height as f32), + }; + } + + pub fn destroy(&mut self) { + self.sprite_atlas.destroy(); + } + + pub fn paint(&mut self) { + //TODO } } impl PlatformWindow for LinuxWindow { fn bounds(&self) -> WindowBounds { - unimplemented!() + //TODO: update when window moves + self.0.lock().window_bounds } fn content_size(&self) -> Size { - unimplemented!() + self.0.lock().content_size } fn scale_factor(&self) -> f32 { From 7f8c64aa6c6bcc012accdda69f7b858ea8deb4bd Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 28 Jan 2024 21:50:58 -0800 Subject: [PATCH 08/51] linux: port from x11rb to xcb and hook up RawWindowHandle --- crates/gpui/Cargo.toml | 6 +- crates/gpui/src/platform/linux/blade_atlas.rs | 1 - crates/gpui/src/platform/linux/display.rs | 17 +- crates/gpui/src/platform/linux/platform.rs | 145 +++++++--------- crates/gpui/src/platform/linux/text_system.rs | 4 +- crates/gpui/src/platform/linux/window.rs | 157 ++++++++++-------- 6 files changed, 162 insertions(+), 168 deletions(-) diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index d5a901df4cacf..08c5b609a2387 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -36,6 +36,7 @@ env_logger = { version = "0.9", optional = true } etagere = "0.2" futures.workspace = true gpui_macros = { path = "../gpui_macros" } +font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "d97147f" } image = "0.23" itertools = "0.10" lazy_static.workspace = true @@ -48,7 +49,7 @@ parking_lot.workspace = true pathfinder_geometry = "0.5" postage.workspace = true rand.workspace = true -raw-window-handle = "0.6.0" +raw-window-handle = "0.5.0" refineable.workspace = true resvg = "0.14" schemars.workspace = true @@ -96,4 +97,5 @@ objc = "0.2" [target.'cfg(target_os = "linux")'.dependencies] flume = "0.11" -x11rb = "0.13" +xcb = { version = "1.3", features = ["as-raw-xcb-connection"] } +as-raw-xcb-connection = "1" diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index 2ee2b6d54862d..b81b2829259ac 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -5,7 +5,6 @@ use crate::{ }; use anyhow::Result; use collections::FxHashMap; -use derive_more::{Deref, DerefMut}; use etagere::BucketedAtlasAllocator; use parking_lot::Mutex; use std::{borrow::Cow, sync::Arc}; diff --git a/crates/gpui/src/platform/linux/display.rs b/crates/gpui/src/platform/linux/display.rs index 7941daf6d0b6e..cdca59c435549 100644 --- a/crates/gpui/src/platform/linux/display.rs +++ b/crates/gpui/src/platform/linux/display.rs @@ -1,25 +1,24 @@ -use crate::{point, size, Bounds, DisplayId, GlobalPixels, PlatformDisplay, Size}; +use crate::{Bounds, DisplayId, GlobalPixels, PlatformDisplay, Size}; use anyhow::Result; use uuid::Uuid; -use x11rb::{connection::Connection as _, rust_connection::RustConnection}; #[derive(Debug)] pub(crate) struct LinuxDisplay { - x11_screen_index: usize, + x_screen_index: i32, bounds: Bounds, uuid: Uuid, } impl LinuxDisplay { - pub(crate) fn new(xc: &RustConnection, x11_screen_index: usize) -> Self { - let screen = &xc.setup().roots[x11_screen_index]; + pub(crate) fn new(xc: &xcb::Connection, x_screen_index: i32) -> Self { + let screen = xc.get_setup().roots().nth(x_screen_index as usize).unwrap(); Self { - x11_screen_index, + x_screen_index, bounds: Bounds { origin: Default::default(), size: Size { - width: GlobalPixels(screen.width_in_pixels as f32), - height: GlobalPixels(screen.height_in_pixels as f32), + width: GlobalPixels(screen.width_in_pixels() as f32), + height: GlobalPixels(screen.height_in_pixels() as f32), }, }, uuid: Uuid::from_bytes([0; 16]), @@ -29,7 +28,7 @@ impl LinuxDisplay { impl PlatformDisplay for LinuxDisplay { fn id(&self) -> DisplayId { - DisplayId(self.x11_screen_index as u32) + DisplayId(self.x_screen_index as u32) } fn uuid(&self) -> Result { diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 3817eeff34635..91eacc9d42478 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -19,48 +19,28 @@ use std::{ time::Duration, }; use time::UtcOffset; -use x11rb::{ - connection::Connection as _, - protocol::{ - xproto::{Atom, ConnectionExt as _}, - Event, - }, - rust_connection::RustConnection, -}; - -pub(crate) struct LinuxPlatform(Mutex); - -pub(crate) struct WmAtoms { - pub protocols: Atom, - pub delete_window: Atom, -} - -impl WmAtoms { - fn new(x11_connection: &RustConnection) -> Self { - Self { - protocols: x11_connection - .intern_atom(false, b"WM_PROTOCOLS") - .unwrap() - .reply() - .unwrap() - .atom, - delete_window: x11_connection - .intern_atom(false, b"WM_DELETE_WINDOW") - .unwrap() - .reply() - .unwrap() - .atom, - } +use xcb::{x, Xid as _}; + +xcb::atoms_struct! { + #[derive(Debug)] + pub(crate) struct XcbAtoms { + pub wm_protocols => b"WM_PROTOCOLS", + pub wm_del_window => b"WM_DELETE_WINDOW", + wm_state => b"_NET_WM_STATE", + wm_state_maxv => b"_NET_WM_STATE_MAXIMIZED_VERT", + wm_state_maxh => b"_NET_WM_STATE_MAXIMIZED_HORZ", } } +pub(crate) struct LinuxPlatform(Mutex); + pub(crate) struct LinuxPlatformState { - x11_connection: RustConnection, - x11_root_index: usize, - atoms: WmAtoms, + xcb_connection: xcb::Connection, + x_root_index: i32, + atoms: XcbAtoms, background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, - windows: HashMap, + windows: HashMap, text_system: Arc, } @@ -72,14 +52,14 @@ impl Default for LinuxPlatform { impl LinuxPlatform { pub(crate) fn new() -> Self { - let (x11_connection, x11_root_index) = x11rb::connect(None).unwrap(); - let atoms = WmAtoms::new(&x11_connection); + let (xcb_connection, x_root_index) = xcb::Connection::connect(None).unwrap(); + let atoms = XcbAtoms::intern_all(&xcb_connection).unwrap(); let dispatcher = Arc::new(LinuxDispatcher::new()); Self(Mutex::new(LinuxPlatformState { - x11_connection, - x11_root_index, + xcb_connection, + x_root_index, atoms, background_executor: BackgroundExecutor::new(dispatcher.clone()), foreground_executor: ForegroundExecutor::new(dispatcher), @@ -105,54 +85,44 @@ impl Platform for LinuxPlatform { fn run(&self, on_finish_launching: Box) { on_finish_launching(); - let mut need_repaint = HashSet::::default(); + let mut need_repaint = HashSet::::default(); while !self.0.lock().windows.is_empty() { - let event = self.0.lock().x11_connection.wait_for_event().unwrap(); - let mut event_option = Some(event); - while let Some(event) = event_option { - match event { - Event::Expose(event) => { - if event.count == 0 { - need_repaint.insert(event.window); - } - } - Event::ConfigureNotify(event) => { - let lock = self.0.lock(); - let mut window = lock.windows[&event.window].lock(); - window.resize(event.width, event.height); - } - Event::MotionNotify(_event) => { - //mouse_position = (event.event_x, event.event_y); - //need_repaint.insert(event.window); - } - Event::MapNotify(_) => {} - Event::ClientMessage(event) => { + let event = self.0.lock().xcb_connection.wait_for_event().unwrap(); + match event { + xcb::Event::X(x::Event::ClientMessage(ev)) => { + if let x::ClientMessageData::Data32([atom, ..]) = ev.data() { let mut lock = self.0.lock(); - let data = event.data.as_data32(); - if data[0] == lock.atoms.delete_window { + if atom == lock.atoms.wm_del_window.resource_id() { + // window "x" button clicked by user, we gracefully exit { - let mut window = lock.windows[&event.window].lock(); + let mut window = lock.windows[&ev.window()].lock(); window.destroy(); } - lock.windows.remove(&event.window); + lock.windows.remove(&ev.window()); + break; } } - Event::Error(error) => { - log::error!("X11 error {:?}", error); - } - _ => {} } - - let lock = self.0.lock(); - event_option = lock.x11_connection.poll_for_event().unwrap(); + _ => {} /* + Event::Expose(event) => { + if event.count == 0 { + need_repaint.insert(event.window); + } + } + Event::ConfigureNotify(event) => { + let lock = self.0.lock(); + let mut window = lock.windows[&event.window].lock(); + window.resize(event.width, event.height); + } + _ => {}*/ } - for x11_window in need_repaint.drain() { + for x_window in need_repaint.drain() { let lock = self.0.lock(); - let mut window = lock.windows[&x11_window].lock(); + let mut window = lock.windows[&x_window].lock(); window.paint(); - lock.x11_connection.flush().unwrap(); + lock.xcb_connection.flush(); } } } @@ -171,10 +141,13 @@ impl Platform for LinuxPlatform { fn displays(&self) -> Vec> { let lock = self.0.lock(); - let setup = lock.x11_connection.setup(); - (0..setup.roots.len()) - .map(|id| { - Rc::new(LinuxDisplay::new(&lock.x11_connection, id)) as Rc + let setup = lock.xcb_connection.get_setup(); + setup + .roots() + .enumerate() + .map(|(root_id, _)| { + Rc::new(LinuxDisplay::new(&lock.xcb_connection, root_id as i32)) + as Rc }) .collect() } @@ -182,8 +155,8 @@ impl Platform for LinuxPlatform { fn display(&self, id: DisplayId) -> Option> { let lock = self.0.lock(); Some(Rc::new(LinuxDisplay::new( - &lock.x11_connection, - id.0 as usize, + &lock.xcb_connection, + id.0 as i32, ))) } @@ -197,17 +170,17 @@ impl Platform for LinuxPlatform { options: WindowOptions, ) -> Box { let mut lock = self.0.lock(); - let win_id = lock.x11_connection.generate_id().unwrap(); + let x_window = lock.xcb_connection.generate_id(); let window_ptr = LinuxWindowState::new_ptr( options, handle, - &lock.x11_connection, - lock.x11_root_index, - win_id, + &lock.xcb_connection, + lock.x_root_index, + x_window, &lock.atoms, ); - lock.windows.insert(win_id, window_ptr.clone()); + lock.windows.insert(x_window, window_ptr.clone()); Box::new(LinuxWindow(window_ptr)) } diff --git a/crates/gpui/src/platform/linux/text_system.rs b/crates/gpui/src/platform/linux/text_system.rs index 09913b7077c4e..c21c8adfd109c 100644 --- a/crates/gpui/src/platform/linux/text_system.rs +++ b/crates/gpui/src/platform/linux/text_system.rs @@ -15,7 +15,7 @@ use font_kit::{ }; use parking_lot::RwLock; use smallvec::SmallVec; -use std::sync::Arc; +use std::{borrow::Cow}; pub(crate) struct LinuxTextSystem(RwLock); @@ -54,7 +54,7 @@ impl Default for LinuxTextSystem { #[allow(unused)] impl PlatformTextSystem for LinuxTextSystem { - fn add_fonts(&self, fonts: &[Arc>]) -> Result<()> { + fn add_fonts(&self, fonts: Vec>) -> Result<()> { Ok(()) //TODO } fn all_font_names(&self) -> Vec { diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 63cbb15e5a591..9f6851ca93335 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -1,28 +1,19 @@ use super::BladeRenderer; use crate::{ - px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, BladeAtlas, Bounds, KeyDownEvent, - Keystroke, LinuxDisplay, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, - PlatformInputHandler, PlatformWindow, Point, Size, TileId, WindowAppearance, WindowBounds, - WindowOptions, WmAtoms, + AnyWindowHandle, BladeAtlas, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler, + PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, XcbAtoms, }; -use collections::HashMap; use parking_lot::Mutex; use std::{ - rc::{Rc, Weak}, + ffi::c_void, + rc::Rc, sync::{self, Arc}, }; -use x11rb::{ - connection::Connection as _, - protocol::xproto::{ - AtomEnum, ConnectionExt as _, CreateWindowAux, EventMask, PropMode, WindowClass, - }, - rust_connection::RustConnection, - wrapper::ConnectionExt as _, -}; +use xcb::{x, Xid as _}; pub(crate) struct LinuxWindowState { display: Rc, - x11_window: u32, + x_window: x::Window, window_bounds: WindowBounds, content_size: Size, sprite_atlas: Arc, @@ -33,29 +24,53 @@ pub(crate) type LinuxWindowStatePtr = Arc>; #[derive(Clone)] pub(crate) struct LinuxWindow(pub(crate) LinuxWindowStatePtr); +struct RawWindow { + connection: *mut c_void, + screen_id: i32, + window_id: u32, +} +unsafe impl raw_window_handle::HasRawWindowHandle for RawWindow { + fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { + let mut wh = raw_window_handle::XcbWindowHandle::empty(); + wh.window = self.window_id; + wh.into() + } +} +unsafe impl raw_window_handle::HasRawDisplayHandle for RawWindow { + fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle { + let mut dh = raw_window_handle::XcbDisplayHandle::empty(); + dh.connection = self.connection; + dh.screen = self.screen_id; + dh.into() + } +} + impl LinuxWindowState { pub fn new_ptr( options: WindowOptions, handle: AnyWindowHandle, - x11_connection: &RustConnection, - x11_main_screen_index: usize, - x11_window: u32, - atoms: &WmAtoms, + xcb_connection: &xcb::Connection, + x_main_screen_index: i32, + x_window: x::Window, + atoms: &XcbAtoms, ) -> LinuxWindowStatePtr { - let x11_screen_index = options + let x_screen_index = options .display_id - .map_or(x11_main_screen_index, |did| did.0 as usize); - let screen = &x11_connection.setup().roots[x11_screen_index]; + .map_or(x_main_screen_index, |did| did.0 as i32); + let screen = xcb_connection + .get_setup() + .roots() + .nth(x_screen_index as usize) + .unwrap(); - let win_aux = CreateWindowAux::new() - .event_mask( - EventMask::EXPOSURE | EventMask::STRUCTURE_NOTIFY | EventMask::POINTER_MOTION, - ) - .background_pixel(screen.white_pixel); + let xcb_values = [ + x::Cw::BackPixel(screen.white_pixel()), + x::Cw::EventMask(x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS), + ]; let (bound_x, bound_y, bound_width, bound_height) = match options.bounds { WindowBounds::Fullscreen | WindowBounds::Maximized => { - (0, 0, screen.width_in_pixels, screen.height_in_pixels) + (0, 0, screen.width_in_pixels(), screen.height_in_pixels()) } WindowBounds::Fixed(bounds) => ( bounds.origin.x.0 as i16, @@ -65,61 +80,67 @@ impl LinuxWindowState { ), }; - x11_connection - .create_window( - x11rb::COPY_DEPTH_FROM_PARENT, - x11_window, - screen.root, - bound_x, - bound_y, - bound_width, - bound_height, - 0, - WindowClass::INPUT_OUTPUT, - 0, - &win_aux, - ) - .unwrap(); + xcb_connection.send_request(&x::CreateWindow { + depth: x::COPY_FROM_PARENT as u8, + wid: x_window, + parent: screen.root(), + x: bound_x, + y: bound_y, + width: bound_width, + height: bound_height, + border_width: 0, + class: x::WindowClass::InputOutput, + visual: screen.root_visual(), + value_list: &xcb_values, + }); if let Some(titlebar) = options.titlebar { if let Some(title) = titlebar.title { - x11_connection - .change_property8( - PropMode::REPLACE, - x11_window, - AtomEnum::WM_NAME, - AtomEnum::STRING, - title.as_bytes(), - ) - .unwrap(); + xcb_connection.send_request(&x::ChangeProperty { + mode: x::PropMode::Replace, + window: x_window, + property: x::ATOM_WM_NAME, + r#type: x::ATOM_STRING, + data: title.as_bytes(), + }); } } - x11_connection - .change_property32( - PropMode::REPLACE, - x11_window, - atoms.protocols, - AtomEnum::ATOM, - &[atoms.delete_window], - ) + xcb_connection + .send_and_check_request(&x::ChangeProperty { + mode: x::PropMode::Replace, + window: x_window, + property: atoms.wm_protocols, + r#type: x::ATOM_ATOM, + data: &[atoms.wm_del_window], + }) .unwrap(); - x11_connection.map_window(x11_window).unwrap(); - x11_connection.flush().unwrap(); + xcb_connection.send_request(&x::MapWindow { window: x_window }); + xcb_connection.flush().unwrap(); + let raw_window = RawWindow { + connection: as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection( + xcb_connection, + ) as *mut _, + screen_id: x_screen_index, + window_id: x_window.resource_id(), + }; let gpu = Arc::new( unsafe { - blade::Context::init(blade::ContextDesc { - validation: cfg!(debug_assertions), - capture: false, - }) + blade::Context::init_windowed( + &raw_window, + blade::ContextDesc { + validation: cfg!(debug_assertions), + capture: false, + }, + ) } .unwrap(), ); Arc::new(Mutex::new(Self { - display: Rc::new(LinuxDisplay::new(x11_connection, x11_screen_index)), - x11_window, + display: Rc::new(LinuxDisplay::new(xcb_connection, x_screen_index)), + x_window, window_bounds: options.bounds, content_size: Size { width: Pixels(bound_width as f32), From 74fde5967bd5b72f5636b92806cbacd371be4d4c Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 28 Jan 2024 23:30:19 -0800 Subject: [PATCH 09/51] linux: hook up render event, basic renderer command buffer --- .../gpui/src/platform/linux/blade_renderer.rs | 51 +++++++++++++- crates/gpui/src/platform/linux/platform.rs | 70 ++++++++++--------- crates/gpui/src/platform/linux/window.rs | 60 +++++++++++----- 3 files changed, 129 insertions(+), 52 deletions(-) diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 1f87b30efe96a..e37129d622600 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -1,11 +1,58 @@ +use crate::Scene; + use std::sync::Arc; +const SURFACE_FRAME_COUNT: u32 = 3; +const MAX_FRAME_TIME_MS: u32 = 1000; + pub struct BladeRenderer { gpu: Arc, + command_encoder: blade::CommandEncoder, + last_sync_point: Option, } impl BladeRenderer { - pub fn new(gpu: Arc) -> Self { - Self { gpu } + pub fn new(gpu: Arc, size: blade::Extent) -> Self { + let _surface_format = gpu.resize(blade::SurfaceConfig { + size, + usage: blade::TextureUsage::TARGET, + frame_count: SURFACE_FRAME_COUNT, + }); + let command_encoder = gpu.create_command_encoder(blade::CommandEncoderDesc { + name: "main", + buffer_count: 2, + }); + Self { + gpu, + command_encoder, + last_sync_point: None, + } + } + + pub fn destroy(&mut self) { + self.gpu.destroy_command_encoder(&mut self.command_encoder); + } + + pub fn resize(&mut self, size: blade::Extent) { + self.gpu.resize(blade::SurfaceConfig { + size, + usage: blade::TextureUsage::TARGET, + frame_count: SURFACE_FRAME_COUNT, + }); + } + + pub fn draw(&mut self, scene: &Scene) { + let frame = self.gpu.acquire_frame(); + self.command_encoder.start(); + + self.command_encoder.present(frame); + + let sync_point = self.gpu.submit(&mut self.command_encoder); + if let Some(ref last_sp) = self.last_sync_point { + if !self.gpu.wait_for(last_sp, MAX_FRAME_TIME_MS) { + panic!("GPU hung"); + } + } + self.last_sync_point = Some(sync_point); } } diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 91eacc9d42478..b708f28e4d979 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -85,44 +85,46 @@ impl Platform for LinuxPlatform { fn run(&self, on_finish_launching: Box) { on_finish_launching(); - let mut need_repaint = HashSet::::default(); - while !self.0.lock().windows.is_empty() { let event = self.0.lock().xcb_connection.wait_for_event().unwrap(); + let mut repaint_x_window = None; match event { xcb::Event::X(x::Event::ClientMessage(ev)) => { if let x::ClientMessageData::Data32([atom, ..]) = ev.data() { - let mut lock = self.0.lock(); - if atom == lock.atoms.wm_del_window.resource_id() { + let mut this = self.0.lock(); + if atom == this.atoms.wm_del_window.resource_id() { // window "x" button clicked by user, we gracefully exit { - let mut window = lock.windows[&ev.window()].lock(); + let mut window = this.windows[&ev.window()].lock(); window.destroy(); } - lock.windows.remove(&ev.window()); + this.xcb_connection.send_request(&x::UnmapWindow { + window: ev.window(), + }); + this.xcb_connection.send_request(&x::DestroyWindow { + window: ev.window(), + }); + this.windows.remove(&ev.window()); break; } } } - _ => {} /* - Event::Expose(event) => { - if event.count == 0 { - need_repaint.insert(event.window); - } - } - Event::ConfigureNotify(event) => { - let lock = self.0.lock(); - let mut window = lock.windows[&event.window].lock(); - window.resize(event.width, event.height); - } - _ => {}*/ + xcb::Event::X(x::Event::Expose(ev)) => { + repaint_x_window = Some(ev.window()); + } + xcb::Event::X(x::Event::ResizeRequest(ev)) => { + let this = self.0.lock(); + let mut window = this.windows[&ev.window()].lock(); + window.resize(ev.width(), ev.height()); + } + _ => {} } - for x_window in need_repaint.drain() { - let lock = self.0.lock(); - let mut window = lock.windows[&x_window].lock(); - window.paint(); - lock.xcb_connection.flush(); + if let Some(x_window) = repaint_x_window { + let this = self.0.lock(); + let mut window = this.windows[&x_window].lock(); + window.request_frame(); + this.xcb_connection.flush(); } } } @@ -140,22 +142,22 @@ impl Platform for LinuxPlatform { fn unhide_other_apps(&self) {} fn displays(&self) -> Vec> { - let lock = self.0.lock(); - let setup = lock.xcb_connection.get_setup(); + let this = self.0.lock(); + let setup = this.xcb_connection.get_setup(); setup .roots() .enumerate() .map(|(root_id, _)| { - Rc::new(LinuxDisplay::new(&lock.xcb_connection, root_id as i32)) + Rc::new(LinuxDisplay::new(&this.xcb_connection, root_id as i32)) as Rc }) .collect() } fn display(&self, id: DisplayId) -> Option> { - let lock = self.0.lock(); + let this = self.0.lock(); Some(Rc::new(LinuxDisplay::new( - &lock.xcb_connection, + &this.xcb_connection, id.0 as i32, ))) } @@ -169,18 +171,18 @@ impl Platform for LinuxPlatform { handle: AnyWindowHandle, options: WindowOptions, ) -> Box { - let mut lock = self.0.lock(); - let x_window = lock.xcb_connection.generate_id(); + let mut this = self.0.lock(); + let x_window = this.xcb_connection.generate_id(); let window_ptr = LinuxWindowState::new_ptr( options, handle, - &lock.xcb_connection, - lock.x_root_index, + &this.xcb_connection, + this.x_root_index, x_window, - &lock.atoms, + &this.atoms, ); - lock.windows.insert(x_window, window_ptr.clone()); + this.windows.insert(x_window, window_ptr.clone()); Box::new(LinuxWindow(window_ptr)) } diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 9f6851ca93335..ce91b3523f481 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -11,6 +11,12 @@ use std::{ }; use xcb::{x, Xid as _}; +#[derive(Default)] +struct Callbacks { + request_frame: Option>, + resize: Option, f32)>>, +} + pub(crate) struct LinuxWindowState { display: Rc, x_window: x::Window, @@ -18,6 +24,7 @@ pub(crate) struct LinuxWindowState { content_size: Size, sprite_atlas: Arc, renderer: BladeRenderer, + callbacks: Callbacks, } pub(crate) type LinuxWindowStatePtr = Arc>; @@ -65,7 +72,9 @@ impl LinuxWindowState { let xcb_values = [ x::Cw::BackPixel(screen.white_pixel()), - x::Cw::EventMask(x::EventMask::EXPOSURE | x::EventMask::KEY_PRESS), + x::Cw::EventMask( + x::EventMask::EXPOSURE | x::EventMask::RESIZE_REDIRECT | x::EventMask::KEY_PRESS, + ), ]; let (bound_x, bound_y, bound_width, bound_height) = match options.bounds { @@ -137,6 +146,11 @@ impl LinuxWindowState { } .unwrap(), ); + let gpu_extent = blade::Extent { + width: bound_width as u32, + height: bound_height as u32, + depth: 1, + }; Arc::new(Mutex::new(Self { display: Rc::new(LinuxDisplay::new(xcb_connection, x_screen_index)), @@ -147,7 +161,8 @@ impl LinuxWindowState { height: Pixels(bound_height as f32), }, sprite_atlas: Arc::new(BladeAtlas::new(&gpu)), - renderer: BladeRenderer::new(gpu), + renderer: BladeRenderer::new(gpu, gpu_extent), + callbacks: Callbacks::default(), })) } @@ -156,14 +171,25 @@ impl LinuxWindowState { width: Pixels(width as f32), height: Pixels(height as f32), }; + self.renderer.resize(blade::Extent { + width: width as u32, + height: height as u32, + depth: 1, + }); + if let Some(ref mut fun) = self.callbacks.resize { + fun(self.content_size, 1.0); + } } - pub fn destroy(&mut self) { - self.sprite_atlas.destroy(); + pub fn request_frame(&mut self) { + if let Some(ref mut fun) = self.callbacks.request_frame { + fun(); + } } - pub fn paint(&mut self) { - //TODO + pub fn destroy(&mut self) { + self.sprite_atlas.destroy(); + self.renderer.destroy(); } } @@ -243,27 +269,27 @@ impl PlatformWindow for LinuxWindow { unimplemented!() } - fn on_request_frame(&self, _callback: Box) {} + fn on_request_frame(&self, callback: Box) { + self.0.lock().callbacks.request_frame = Some(callback); + } fn on_input(&self, callback: Box bool>) {} fn on_active_status_change(&self, callback: Box) {} - fn on_resize(&self, callback: Box, f32)>) {} + fn on_resize(&self, callback: Box, f32)>) { + self.0.lock().callbacks.resize = Some(callback); + } fn on_fullscreen(&self, _callback: Box) {} fn on_moved(&self, callback: Box) {} - fn on_should_close(&self, callback: Box bool>) {} + fn on_should_close(&self, _callback: Box bool>) {} - fn on_close(&self, _callback: Box) { - unimplemented!() - } + fn on_close(&self, _callback: Box) {} - fn on_appearance_changed(&self, _callback: Box) { - unimplemented!() - } + fn on_appearance_changed(&self, _callback: Box) {} fn is_topmost_for_position(&self, _position: crate::Point) -> bool { unimplemented!() @@ -271,7 +297,9 @@ impl PlatformWindow for LinuxWindow { fn invalidate(&self) {} - fn draw(&self, _scene: &crate::Scene) {} + fn draw(&self, scene: &crate::Scene) { + self.0.lock().renderer.draw(scene); + } fn sprite_atlas(&self) -> sync::Arc { self.0.lock().sprite_atlas.clone() From 503ac7a251db62f5743e5e0ff82d1635d1f5ef99 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 28 Jan 2024 23:59:04 -0800 Subject: [PATCH 10/51] linux: work around the mutex locks for request_frame and resize --- .../gpui/src/platform/linux/blade_renderer.rs | 16 +++++--- crates/gpui/src/platform/linux/platform.rs | 6 +-- crates/gpui/src/platform/linux/text_system.rs | 4 +- crates/gpui/src/platform/linux/window.rs | 39 ++++++++++++------- 4 files changed, 40 insertions(+), 25 deletions(-) diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index e37129d622600..59b95d7274e66 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -33,7 +33,16 @@ impl BladeRenderer { self.gpu.destroy_command_encoder(&mut self.command_encoder); } + fn wait_for_gpu(&mut self) { + if let Some(last_sp) = self.last_sync_point.take() { + if !self.gpu.wait_for(&last_sp, MAX_FRAME_TIME_MS) { + panic!("GPU hung"); + } + } + } + pub fn resize(&mut self, size: blade::Extent) { + self.wait_for_gpu(); self.gpu.resize(blade::SurfaceConfig { size, usage: blade::TextureUsage::TARGET, @@ -44,15 +53,12 @@ impl BladeRenderer { pub fn draw(&mut self, scene: &Scene) { let frame = self.gpu.acquire_frame(); self.command_encoder.start(); + self.command_encoder.init_texture(frame.texture()); self.command_encoder.present(frame); let sync_point = self.gpu.submit(&mut self.command_encoder); - if let Some(ref last_sp) = self.last_sync_point { - if !self.gpu.wait_for(last_sp, MAX_FRAME_TIME_MS) { - panic!("GPU hung"); - } - } + self.wait_for_gpu(); self.last_sync_point = Some(sync_point); } } diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index b708f28e4d979..5a5494cda8028 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -114,16 +114,14 @@ impl Platform for LinuxPlatform { } xcb::Event::X(x::Event::ResizeRequest(ev)) => { let this = self.0.lock(); - let mut window = this.windows[&ev.window()].lock(); - window.resize(ev.width(), ev.height()); + LinuxWindowState::resize(&this.windows[&ev.window()], ev.width(), ev.height()); } _ => {} } if let Some(x_window) = repaint_x_window { let this = self.0.lock(); - let mut window = this.windows[&x_window].lock(); - window.request_frame(); + LinuxWindowState::request_frame(&this.windows[&x_window]); this.xcb_connection.flush(); } } diff --git a/crates/gpui/src/platform/linux/text_system.rs b/crates/gpui/src/platform/linux/text_system.rs index c21c8adfd109c..36ed155f6aba8 100644 --- a/crates/gpui/src/platform/linux/text_system.rs +++ b/crates/gpui/src/platform/linux/text_system.rs @@ -64,7 +64,7 @@ impl PlatformTextSystem for LinuxTextSystem { Vec::new() } fn font_id(&self, descriptor: &Font) -> Result { - unimplemented!() + Ok(FontId(0)) //TODO } fn font_metrics(&self, font_id: FontId) -> FontMetrics { unimplemented!() @@ -89,7 +89,7 @@ impl PlatformTextSystem for LinuxTextSystem { unimplemented!() } fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout { - unimplemented!() + LineLayout::default() //TODO } fn wrap_line( &self, diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index ce91b3523f481..85ede0ab5a2be 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -166,30 +166,41 @@ impl LinuxWindowState { })) } - pub fn resize(&mut self, width: u16, height: u16) { - self.content_size = Size { + pub fn destroy(&mut self) { + self.sprite_atlas.destroy(); + self.renderer.destroy(); + } + + pub fn resize(self_ptr: &LinuxWindowStatePtr, width: u16, height: u16) { + let content_size = Size { width: Pixels(width as f32), height: Pixels(height as f32), }; - self.renderer.resize(blade::Extent { + + let mut fun = match self_ptr.lock().callbacks.resize.take() { + Some(fun) => fun, + None => return, + }; + fun(content_size, 1.0); + + let mut this = self_ptr.lock(); + this.callbacks.resize = Some(fun); + this.content_size = content_size; + this.renderer.resize(blade::Extent { width: width as u32, height: height as u32, depth: 1, }); - if let Some(ref mut fun) = self.callbacks.resize { - fun(self.content_size, 1.0); - } } - pub fn request_frame(&mut self) { - if let Some(ref mut fun) = self.callbacks.request_frame { - fun(); - } - } + pub fn request_frame(self_ptr: &LinuxWindowStatePtr) { + let mut fun = match self_ptr.lock().callbacks.request_frame.take() { + Some(fun) => fun, + None => return, + }; + fun(); - pub fn destroy(&mut self) { - self.sprite_atlas.destroy(); - self.renderer.destroy(); + self_ptr.lock().callbacks.request_frame = Some(fun); } } From 8aa768765f4e1b542fb3d42f7fab5d0fbd178f47 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Tue, 30 Jan 2024 00:32:30 -0800 Subject: [PATCH 11/51] linux: basic quad renderer logic --- Cargo.lock | 1 + Cargo.toml | 1 + crates/gpui/Cargo.toml | 3 +- crates/gpui/src/platform/linux/blade_atlas.rs | 43 ++-- crates/gpui/src/platform/linux/blade_belt.rs | 31 ++- .../gpui/src/platform/linux/blade_renderer.rs | 127 ++++++++++-- crates/gpui/src/platform/linux/platform.rs | 1 + crates/gpui/src/platform/linux/shaders.wgsl | 183 ++++++++++++++++++ crates/gpui/src/platform/linux/window.rs | 15 +- 9 files changed, 352 insertions(+), 53 deletions(-) create mode 100644 crates/gpui/src/platform/linux/shaders.wgsl diff --git a/Cargo.lock b/Cargo.lock index 291e86f0b5226..c3df96e992bc4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3234,6 +3234,7 @@ dependencies = [ "bindgen 0.65.1", "bitflags 2.4.1", "block", + "bytemuck", "cbindgen", "cocoa", "collections", diff --git a/Cargo.toml b/Cargo.toml index a3eeb2d653b77..847fc77d62102 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -182,6 +182,7 @@ tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "1d897 wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "v16.0.0" } # TODO - Remove when corresponding Blade versions are published +# Currently in https://github.com/kvark/blade/tree/zed blade-graphics = { path = "/x/Code/blade/blade-graphics" } blade-macros = { path = "/x/Code/blade/blade-macros" } diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 08c5b609a2387..72cc1bd25326f 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -26,8 +26,9 @@ anyhow.workspace = true async-task = "4.7" backtrace = { version = "0.3", optional = true } bitflags = "2.4.0" -blade = { package = "blade-graphics", version = "0.3" } +blade-graphics = "0.3" blade-macros = "0.2" +bytemuck = "1" collections = { path = "../collections" } ctor.workspace = true derive_more.workspace = true diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index b81b2829259ac..56bb745764713 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -4,6 +4,7 @@ use crate::{ Point, Size, }; use anyhow::Result; +use blade_graphics as gpu; use collections::FxHashMap; use etagere::BucketedAtlasAllocator; use parking_lot::Mutex; @@ -12,8 +13,8 @@ use std::{borrow::Cow, sync::Arc}; pub(crate) struct BladeAtlas(Mutex); struct BladeAtlasState { - gpu: Arc, - gpu_encoder: blade::CommandEncoder, + gpu: Arc, + gpu_encoder: gpu::CommandEncoder, upload_belt: BladeBelt, monochrome_textures: Vec, polychrome_textures: Vec, @@ -38,15 +39,15 @@ impl BladeAtlasState { } impl BladeAtlas { - pub(crate) fn new(gpu: &Arc) -> Self { + pub(crate) fn new(gpu: &Arc) -> Self { BladeAtlas(Mutex::new(BladeAtlasState { gpu: Arc::clone(gpu), - gpu_encoder: gpu.create_command_encoder(blade::CommandEncoderDesc { + gpu_encoder: gpu.create_command_encoder(gpu::CommandEncoderDesc { name: "atlas", buffer_count: 3, }), upload_belt: BladeBelt::new(BladeBeltDescriptor { - memory: blade::Memory::Upload, + memory: gpu::Memory::Upload, min_chunk_size: 0x10000, }), monochrome_textures: Default::default(), @@ -77,7 +78,7 @@ impl BladeAtlas { lock.gpu_encoder.start(); } - pub fn finish_frame(&self) -> blade::SyncPoint { + pub fn finish_frame(&self) -> gpu::SyncPoint { let mut lock = self.0.lock(); let gpu = lock.gpu.clone(); let sync_point = gpu.submit(&mut lock.gpu_encoder); @@ -137,32 +138,32 @@ impl BladeAtlasState { let usage; match kind { AtlasTextureKind::Monochrome => { - format = blade::TextureFormat::R8Unorm; - usage = blade::TextureUsage::COPY | blade::TextureUsage::RESOURCE; + format = gpu::TextureFormat::R8Unorm; + usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE; } AtlasTextureKind::Polychrome => { - format = blade::TextureFormat::Bgra8Unorm; - usage = blade::TextureUsage::COPY | blade::TextureUsage::RESOURCE; + format = gpu::TextureFormat::Bgra8Unorm; + usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE; } AtlasTextureKind::Path => { - format = blade::TextureFormat::R16Float; - usage = blade::TextureUsage::COPY - | blade::TextureUsage::RESOURCE - | blade::TextureUsage::TARGET; + format = gpu::TextureFormat::R16Float; + usage = gpu::TextureUsage::COPY + | gpu::TextureUsage::RESOURCE + | gpu::TextureUsage::TARGET; } } - let raw = self.gpu.create_texture(blade::TextureDesc { + let raw = self.gpu.create_texture(gpu::TextureDesc { name: "", format, - size: blade::Extent { + size: gpu::Extent { width: size.width.into(), height: size.height.into(), depth: 1, }, array_layer_count: 1, mip_level_count: 1, - dimension: blade::TextureDimension::D2, + dimension: gpu::TextureDimension::D2, usage, }); @@ -198,13 +199,13 @@ impl BladeAtlasState { transfers.copy_buffer_to_texture( src_data, bounds.size.width.to_bytes(texture.bytes_per_pixel()), - blade::TexturePiece { + gpu::TexturePiece { texture: texture.raw, mip_level: 0, array_layer: 0, origin: [bounds.origin.x.into(), bounds.origin.y.into(), 0], }, - blade::Extent { + gpu::Extent { width: bounds.size.width.into(), height: bounds.size.height.into(), depth: 1, @@ -216,8 +217,8 @@ impl BladeAtlasState { struct BladeAtlasTexture { id: AtlasTextureId, allocator: BucketedAtlasAllocator, - raw: blade::Texture, - format: blade::TextureFormat, + raw: gpu::Texture, + format: gpu::TextureFormat, } impl BladeAtlasTexture { diff --git a/crates/gpui/src/platform/linux/blade_belt.rs b/crates/gpui/src/platform/linux/blade_belt.rs index ff3d5c66929cf..17f100beb2a3e 100644 --- a/crates/gpui/src/platform/linux/blade_belt.rs +++ b/crates/gpui/src/platform/linux/blade_belt.rs @@ -1,10 +1,13 @@ +use blade_graphics as gpu; +use std::mem; + struct ReusableBuffer { - raw: blade::Buffer, + raw: gpu::Buffer, size: u64, } pub struct BladeBeltDescriptor { - pub memory: blade::Memory, + pub memory: gpu::Memory, pub min_chunk_size: u64, } @@ -12,7 +15,7 @@ pub struct BladeBeltDescriptor { /// find staging space for uploads. pub struct BladeBelt { desc: BladeBeltDescriptor, - buffers: Vec<(ReusableBuffer, blade::SyncPoint)>, + buffers: Vec<(ReusableBuffer, gpu::SyncPoint)>, active: Vec<(ReusableBuffer, u64)>, } @@ -25,7 +28,7 @@ impl BladeBelt { } } - pub fn destroy(&mut self, gpu: &blade::Context) { + pub fn destroy(&mut self, gpu: &gpu::Context) { for (buffer, _) in self.buffers.drain(..) { gpu.destroy_buffer(buffer.raw); } @@ -34,7 +37,7 @@ impl BladeBelt { } } - pub fn alloc(&mut self, size: u64, gpu: &blade::Context) -> blade::BufferPiece { + pub fn alloc(&mut self, size: u64, gpu: &gpu::Context) -> gpu::BufferPiece { for &mut (ref rb, ref mut offset) in self.active.iter_mut() { if *offset + size <= rb.size { let piece = rb.raw.at(*offset); @@ -56,7 +59,7 @@ impl BladeBelt { let chunk_index = self.buffers.len() + self.active.len(); let chunk_size = size.max(self.desc.min_chunk_size); - let chunk = gpu.create_buffer(blade::BufferDesc { + let chunk = gpu.create_buffer(gpu::BufferDesc { name: &format!("chunk-{}", chunk_index), size: chunk_size, memory: self.desc.memory, @@ -69,15 +72,23 @@ impl BladeBelt { chunk.into() } - pub fn alloc_data(&mut self, data: &[u8], gpu: &blade::Context) -> blade::BufferPiece { - let bp = self.alloc(data.len() as u64, gpu); + //Note: assuming T: bytemuck::Zeroable + pub fn alloc_data(&mut self, data: &[T], gpu: &gpu::Context) -> gpu::BufferPiece { + assert!(!data.is_empty()); + let alignment = mem::align_of::() as u64; + let total_bytes = data.len() * mem::size_of::(); + let mut bp = self.alloc(alignment + (total_bytes - 1) as u64, gpu); + let rem = bp.offset % alignment; + if rem != 0 { + bp.offset += alignment - rem; + } unsafe { - std::ptr::copy_nonoverlapping(data.as_ptr(), bp.data(), data.len()); + std::ptr::copy_nonoverlapping(data.as_ptr() as *const u8, bp.data(), total_bytes); } bp } - pub fn flush(&mut self, sp: &blade::SyncPoint) { + pub fn flush(&mut self, sp: &gpu::SyncPoint) { self.buffers .extend(self.active.drain(..).map(|(rb, _)| (rb, sp.clone()))); } diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 59b95d7274e66..095c652e9554e 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -1,38 +1,93 @@ -use crate::Scene; +use super::{BladeBelt, BladeBeltDescriptor}; +use crate::{PrimitiveBatch, Quad, Scene}; +use bytemuck::{Pod, Zeroable}; +use blade_graphics as gpu; use std::sync::Arc; const SURFACE_FRAME_COUNT: u32 = 3; const MAX_FRAME_TIME_MS: u32 = 1000; +#[repr(C)] +#[derive(Clone, Copy, Pod, Zeroable)] +struct GlobalParams { + viewport_size: [f32; 2], + pad: [u32; 2], +} + +#[derive(blade_macros::ShaderData)] +struct ShaderQuadsData { + globals: GlobalParams, + quads: gpu::BufferPiece, +} + +struct BladePipelines { + quads: gpu::RenderPipeline, +} + +impl BladePipelines { + fn new(gpu: &gpu::Context, surface_format: gpu::TextureFormat) -> Self { + let shader = gpu.create_shader(gpu::ShaderDesc { + source: include_str!("shaders.wgsl"), + }); + shader.check_struct_size::(); + let layout = ::layout(); + Self { + quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc { + name: "quads", + data_layouts: &[&layout], + vertex: shader.at("vs_quads"), + primitive: gpu::PrimitiveState { + topology: gpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + fragment: shader.at("fs_quads"), + color_targets: &[gpu::ColorTargetState { + format: surface_format, + blend: Some(gpu::BlendState::ALPHA_BLENDING), + write_mask: gpu::ColorWrites::default(), + }], + }), + } + } +} + pub struct BladeRenderer { - gpu: Arc, - command_encoder: blade::CommandEncoder, - last_sync_point: Option, + gpu: Arc, + command_encoder: gpu::CommandEncoder, + last_sync_point: Option, + pipelines: BladePipelines, + instance_belt: BladeBelt, + viewport_size: gpu::Extent, } impl BladeRenderer { - pub fn new(gpu: Arc, size: blade::Extent) -> Self { - let _surface_format = gpu.resize(blade::SurfaceConfig { + pub fn new(gpu: Arc, size: gpu::Extent) -> Self { + let surface_format = gpu.resize(gpu::SurfaceConfig { size, - usage: blade::TextureUsage::TARGET, + usage: gpu::TextureUsage::TARGET, frame_count: SURFACE_FRAME_COUNT, }); - let command_encoder = gpu.create_command_encoder(blade::CommandEncoderDesc { + let command_encoder = gpu.create_command_encoder(gpu::CommandEncoderDesc { name: "main", buffer_count: 2, }); + let pipelines = BladePipelines::new(&gpu, surface_format); + let instance_belt = BladeBelt::new(BladeBeltDescriptor { + memory: gpu::Memory::Shared, + min_chunk_size: 0x1000, + }); Self { gpu, command_encoder, last_sync_point: None, + pipelines, + instance_belt, + viewport_size: size, } } - pub fn destroy(&mut self) { - self.gpu.destroy_command_encoder(&mut self.command_encoder); - } - fn wait_for_gpu(&mut self) { if let Some(last_sp) = self.last_sync_point.take() { if !self.gpu.wait_for(&last_sp, MAX_FRAME_TIME_MS) { @@ -41,13 +96,20 @@ impl BladeRenderer { } } - pub fn resize(&mut self, size: blade::Extent) { + pub fn destroy(&mut self) { self.wait_for_gpu(); - self.gpu.resize(blade::SurfaceConfig { + self.instance_belt.destroy(&self.gpu); + self.gpu.destroy_command_encoder(&mut self.command_encoder); + } + + pub fn resize(&mut self, size: gpu::Extent) { + self.wait_for_gpu(); + self.gpu.resize(gpu::SurfaceConfig { size, - usage: blade::TextureUsage::TARGET, + usage: gpu::TextureUsage::TARGET, frame_count: SURFACE_FRAME_COUNT, }); + self.viewport_size = size; } pub fn draw(&mut self, scene: &Scene) { @@ -55,9 +117,42 @@ impl BladeRenderer { self.command_encoder.start(); self.command_encoder.init_texture(frame.texture()); - self.command_encoder.present(frame); + 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, + }) { + for batch in scene.batches() { + match batch { + PrimitiveBatch::Quads(quads) => { + let instances = self.instance_belt.alloc_data(quads, &self.gpu); + let mut encoder = pass.with(&self.pipelines.quads); + encoder.bind( + 0, + &ShaderQuadsData { + globals: GlobalParams { + viewport_size: [ + self.viewport_size.width as f32, + self.viewport_size.height as f32, + ], + pad: [0; 2], + }, + quads: instances, + }, + ); + encoder.draw(0, 4, 0, quads.len() as u32); + } + _ => continue, + } + } + } + self.command_encoder.present(frame); let sync_point = self.gpu.submit(&mut self.command_encoder); + self.instance_belt.flush(&sync_point); self.wait_for_gpu(); self.last_sync_point = Some(sync_point); } diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 5a5494cda8028..08bf08185391a 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -115,6 +115,7 @@ impl Platform for LinuxPlatform { xcb::Event::X(x::Event::ResizeRequest(ev)) => { let this = self.0.lock(); LinuxWindowState::resize(&this.windows[&ev.window()], ev.width(), ev.height()); + repaint_x_window = Some(ev.window()); } _ => {} } diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl new file mode 100644 index 0000000000000..0cc70e5220f27 --- /dev/null +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -0,0 +1,183 @@ +struct Bounds { + origin: vec2, + size: vec2, +} +struct Corners { + top_left: f32, + top_right: f32, + bottom_right: f32, + bottom_left: f32, +} +struct Edges { + top: f32, + right: f32, + bottom: f32, + left: f32, +} +struct Hsla { + h: f32, + s: f32, + l: f32, + a: f32, +} + +struct Quad { + view_id: vec2, + layer_id: u32, + order: u32, + bounds: Bounds, + content_mask: Bounds, + background: Hsla, + border_color: Hsla, + corner_radii: Corners, + border_widths: Edges, +} + +struct Globals { + viewport_size: vec2, + pad: vec2, +} + +var globals: Globals; +var quads: array; + +struct QuadsVarying { + @builtin(position) position: vec4, + @location(0) @interpolate(flat) background_color: vec4, + @location(1) @interpolate(flat) border_color: vec4, + @location(2) @interpolate(flat) quad_id: u32, + //TODO: use `clip_distance` once Naga supports it + @location(3) clip_distances: vec4, +} + +fn to_device_position(unit_vertex: vec2, bounds: Bounds) -> vec4 { + let position = unit_vertex * vec2(bounds.size) + bounds.origin; + let device_position = position / globals.viewport_size * vec2(2.0, -2.0) + vec2(-1.0, 1.0); + return vec4(device_position, 0.0, 1.0); +} + +fn distance_from_clip_rect(unit_vertex: vec2, bounds: Bounds, clip_bounds: Bounds) -> vec4 { + let position = unit_vertex * vec2(bounds.size) + bounds.origin; + let tl = position - clip_bounds.origin; + let br = clip_bounds.origin + clip_bounds.size - position; + return vec4(tl.x, br.x, tl.y, br.y); +} + +fn hsla_to_rgba(hsla: Hsla) -> vec4 { + let h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range + let s = hsla.s; + let l = hsla.l; + let a = hsla.a; + + let c = (1.0 - abs(2.0 * l - 1.0)) * s; + let x = c * (1.0 - abs(h % 2.0 - 1.0)); + let m = l - c / 2.0; + + var color = vec4(m, m, m, a); + + if (h >= 0.0 && h < 1.0) { + color.r += c; + color.g += x; + } else if (h >= 1.0 && h < 2.0) { + color.r += x; + color.g += c; + } else if (h >= 2.0 && h < 3.0) { + color.g += c; + color.b += x; + } else if (h >= 3.0 && h < 4.0) { + color.g += x; + color.b += c; + } else if (h >= 4.0 && h < 5.0) { + color.r += x; + color.b += c; + } else { + color.r += c; + color.b += x; + } + + return color; +} + +fn over(below: vec4, above: vec4) -> vec4 { + let alpha = above.a + below.a * (1.0 - above.a); + let color = (above.rgb * above.a + below.rgb * below.a * (1.0 - above.a)) / alpha; + return vec4(color, alpha); +} + +@vertex +fn vs_quads(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> QuadsVarying { + let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); + let quad = quads[instance_id]; + + var out = QuadsVarying(); + out.position = to_device_position(unit_vertex, quad.bounds); + out.background_color = hsla_to_rgba(quad.background); + out.border_color = hsla_to_rgba(quad.border_color); + out.quad_id = instance_id; + out.clip_distances = distance_from_clip_rect(unit_vertex, quad.bounds, quad.content_mask); + return out; +} + +@fragment +fn fs_quads(input: QuadsVarying) -> @location(0) vec4 { + // Alpha clip first, since we don't have `clip_distance`. + let min_distance = min( + min(input.clip_distances.x, input.clip_distances.y), + min(input.clip_distances.z, input.clip_distances.w) + ); + if min_distance <= 0.0 { + return vec4(0.0); + } + + let quad = quads[input.quad_id]; + let half_size = quad.bounds.size / 2.0; + let center = quad.bounds.origin + half_size; + let center_to_point = input.position.xy - center; + + var corner_radius = 0.0; + if (center_to_point.x < 0.0) { + if (center_to_point.y < 0.0) { + corner_radius = quad.corner_radii.top_left; + } else { + corner_radius = quad.corner_radii.bottom_left; + } + } else { + if (center_to_point.y < 0.) { + corner_radius = quad.corner_radii.top_right; + } else { + corner_radius = quad.corner_radii.bottom_right; + } + } + + let rounded_edge_to_point = abs(center_to_point) - half_size + corner_radius; + let distance = + length(max(vec2(0.0), rounded_edge_to_point)) + + min(0.0, max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - + corner_radius; + + let vertical_border = select(quad.border_widths.left, quad.border_widths.right, center_to_point.x > 0.0); + let horizontal_border = select(quad.border_widths.top, quad.border_widths.bottom, center_to_point.y > 0.0); + let inset_size = half_size - corner_radius - vec2(vertical_border, horizontal_border); + let point_to_inset_corner = abs(center_to_point) - inset_size; + + var border_width = 0.0; + if (point_to_inset_corner.x < 0.0 && point_to_inset_corner.y < 0.0) { + border_width = 0.0; + } else if (point_to_inset_corner.y > point_to_inset_corner.x) { + border_width = horizontal_border; + } else { + border_width = vertical_border; + } + + var color = input.background_color; + if (border_width > 0.0) { + let inset_distance = distance + border_width; + // Blend the border on top of the background and then linearly interpolate + // between the two as we slide inside the background. + let blended_border = over(input.background_color, input.border_color); + color = mix(blended_border, input.background_color, + saturate(0.5 - inset_distance)); + } + + return color * vec4(1.0, 1.0, 1.0, saturate(0.5 - distance)); +} \ No newline at end of file diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 85ede0ab5a2be..a0d8157aabb06 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -3,6 +3,7 @@ use crate::{ AnyWindowHandle, BladeAtlas, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, XcbAtoms, }; +use blade_graphics as gpu; use parking_lot::Mutex; use std::{ ffi::c_void, @@ -15,6 +16,7 @@ use xcb::{x, Xid as _}; struct Callbacks { request_frame: Option>, resize: Option, f32)>>, + moved: Option>, } pub(crate) struct LinuxWindowState { @@ -24,6 +26,7 @@ pub(crate) struct LinuxWindowState { content_size: Size, sprite_atlas: Arc, renderer: BladeRenderer, + //TODO: move out into a separate struct callbacks: Callbacks, } @@ -136,9 +139,9 @@ impl LinuxWindowState { }; let gpu = Arc::new( unsafe { - blade::Context::init_windowed( + gpu::Context::init_windowed( &raw_window, - blade::ContextDesc { + gpu::ContextDesc { validation: cfg!(debug_assertions), capture: false, }, @@ -146,7 +149,7 @@ impl LinuxWindowState { } .unwrap(), ); - let gpu_extent = blade::Extent { + let gpu_extent = gpu::Extent { width: bound_width as u32, height: bound_height as u32, depth: 1, @@ -186,7 +189,7 @@ impl LinuxWindowState { let mut this = self_ptr.lock(); this.callbacks.resize = Some(fun); this.content_size = content_size; - this.renderer.resize(blade::Extent { + this.renderer.resize(gpu::Extent { width: width as u32, height: height as u32, depth: 1, @@ -294,7 +297,9 @@ impl PlatformWindow for LinuxWindow { fn on_fullscreen(&self, _callback: Box) {} - fn on_moved(&self, callback: Box) {} + fn on_moved(&self, callback: Box) { + self.0.lock().callbacks.moved = Some(callback); + } fn on_should_close(&self, _callback: Box bool>) {} From ecf4955899c97b44d57c21de58c33e684d35ba79 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Tue, 30 Jan 2024 21:37:54 -0800 Subject: [PATCH 12/51] hide MacOS dependencie of live_kit_client and media --- crates/live_kit_client/Cargo.toml | 8 +++++++- crates/media/Cargo.toml | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/live_kit_client/Cargo.toml b/crates/live_kit_client/Cargo.toml index 32c180c48c63d..3aeba54af0496 100644 --- a/crates/live_kit_client/Cargo.toml +++ b/crates/live_kit_client/Cargo.toml @@ -52,7 +52,6 @@ block = "0.1" byteorder = "1.4" bytes = "1.2" collections = { path = "../collections", features = ["test-support"] } -foreign-types = "0.3" futures.workspace = true gpui = { path = "../gpui", features = ["test-support"] } hmac = "0.12" @@ -66,6 +65,13 @@ serde_derive.workspace = true sha2 = "0.10" simplelog = "0.9" +[target.'cfg(target_os = "macos")'.dev-dependencies] +cocoa = "0.25" +core-foundation = "0.9.3" +core-graphics = "0.22.3" +foreign-types = "0.3" +objc = "0.2" + [build-dependencies] serde.workspace = true serde_derive.workspace = true diff --git a/crates/media/Cargo.toml b/crates/media/Cargo.toml index 351ae3725289a..bb9751e9daf52 100644 --- a/crates/media/Cargo.toml +++ b/crates/media/Cargo.toml @@ -13,10 +13,10 @@ doctest = false anyhow.workspace = true block = "0.1" bytes = "1.2" -foreign-types = "0.3" [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.9.3" +foreign-types = "0.3" metal = "0.21.0" objc = "0.2" From 666b134d2067d4f1bb153908230e3e8e69d86535 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 31 Jan 2024 00:04:05 -0800 Subject: [PATCH 13/51] linux: shadow rendering --- Cargo.toml | 7 +- crates/gpui/Cargo.toml | 4 +- crates/gpui/build.rs | 4 +- .../gpui/src/platform/linux/blade_renderer.rs | 70 ++++-- crates/gpui/src/platform/linux/platform.rs | 5 +- crates/gpui/src/platform/linux/shaders.wgsl | 200 ++++++++++++++---- crates/gpui/src/platform/linux/window.rs | 6 +- crates/gpui/src/scene.rs | 1 + crates/gpui/src/window/element_cx.rs | 1 + 9 files changed, 236 insertions(+), 62 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 847fc77d62102..019cb3f356d24 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -182,7 +182,7 @@ tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "1d897 wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "v16.0.0" } # TODO - Remove when corresponding Blade versions are published -# Currently in https://github.com/kvark/blade/tree/zed +[patch."https://github.com/kvark/blade"] blade-graphics = { path = "/x/Code/blade/blade-graphics" } blade-macros = { path = "/x/Code/blade/blade-macros" } @@ -190,6 +190,11 @@ blade-macros = { path = "/x/Code/blade/blade-macros" } split-debuginfo = "unpacked" debug = "limited" +# TODO - Remove this +[profile.dev.package.blade-graphics] +split-debuginfo = "off" +debug = "full" + [profile.dev.package.taffy] opt-level = 3 diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 72cc1bd25326f..6dd64752cc80c 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -26,8 +26,8 @@ anyhow.workspace = true async-task = "4.7" backtrace = { version = "0.3", optional = true } bitflags = "2.4.0" -blade-graphics = "0.3" -blade-macros = "0.2" +blade-graphics = { git = "https://github.com/kvark/blade", branch = "zed" } +blade-macros = { git = "https://github.com/kvark/blade", branch = "zed" } bytemuck = "1" collections = { path = "../collections" } ctor.workspace = true diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 32da465007a93..70c3d8d8e43de 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -7,7 +7,7 @@ use cbindgen::Config; fn main() { //generate_dispatch_bindings(); - let _header_path = generate_shader_bindings(); + //let header_path = generate_shader_bindings(); //#[cfg(feature = "runtime_shaders")] //emit_stitched_shaders(&header_path); //#[cfg(not(feature = "runtime_shaders"))] @@ -38,7 +38,7 @@ fn _generate_dispatch_bindings() { .expect("couldn't write dispatch bindings"); } -fn generate_shader_bindings() -> PathBuf { +fn _generate_shader_bindings() -> PathBuf { let output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("scene.h"); let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); let mut config = Config::default(); diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 095c652e9554e..4d58cbd1e6cda 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -1,5 +1,8 @@ +// Doing `if let` gives you nice scoping with passes/encoders +#![allow(irrefutable_let_patterns)] + use super::{BladeBelt, BladeBeltDescriptor}; -use crate::{PrimitiveBatch, Quad, Scene}; +use crate::{PrimitiveBatch, Quad, Scene, Shadow}; use bytemuck::{Pod, Zeroable}; use blade_graphics as gpu; @@ -18,11 +21,18 @@ struct GlobalParams { #[derive(blade_macros::ShaderData)] struct ShaderQuadsData { globals: GlobalParams, - quads: gpu::BufferPiece, + b_quads: gpu::BufferPiece, +} + +#[derive(blade_macros::ShaderData)] +struct ShaderShadowsData { + globals: GlobalParams, + b_shadows: gpu::BufferPiece, } struct BladePipelines { quads: gpu::RenderPipeline, + shadows: gpu::RenderPipeline, } impl BladePipelines { @@ -31,18 +41,36 @@ impl BladePipelines { source: include_str!("shaders.wgsl"), }); shader.check_struct_size::(); - let layout = ::layout(); + shader.check_struct_size::(); + let quads_layout = ::layout(); + let shadows_layout = ::layout(); Self { quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc { name: "quads", - data_layouts: &[&layout], - vertex: shader.at("vs_quads"), + data_layouts: &[&quads_layout], + vertex: shader.at("vs_quad"), + primitive: gpu::PrimitiveState { + topology: gpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + fragment: shader.at("fs_quad"), + color_targets: &[gpu::ColorTargetState { + format: surface_format, + blend: Some(gpu::BlendState::ALPHA_BLENDING), + write_mask: gpu::ColorWrites::default(), + }], + }), + shadows: gpu.create_render_pipeline(gpu::RenderPipelineDesc { + name: "shadows", + data_layouts: &[&shadows_layout], + vertex: shader.at("vs_shadow"), primitive: gpu::PrimitiveState { topology: gpu::PrimitiveTopology::TriangleStrip, ..Default::default() }, depth_stencil: None, - fragment: shader.at("fs_quads"), + fragment: shader.at("fs_shadow"), color_targets: &[gpu::ColorTargetState { format: surface_format, blend: Some(gpu::BlendState::ALPHA_BLENDING), @@ -117,6 +145,14 @@ impl BladeRenderer { self.command_encoder.start(); self.command_encoder.init_texture(frame.texture()); + let globals = GlobalParams { + viewport_size: [ + self.viewport_size.width as f32, + self.viewport_size.height as f32, + ], + pad: [0; 2], + }; + if let mut pass = self.command_encoder.render(gpu::RenderTargetSet { colors: &[gpu::RenderTarget { view: frame.texture_view(), @@ -133,18 +169,24 @@ impl BladeRenderer { encoder.bind( 0, &ShaderQuadsData { - globals: GlobalParams { - viewport_size: [ - self.viewport_size.width as f32, - self.viewport_size.height as f32, - ], - pad: [0; 2], - }, - quads: instances, + globals, + b_quads: instances, }, ); encoder.draw(0, 4, 0, quads.len() as u32); } + PrimitiveBatch::Shadows(shadows) => { + let instances = self.instance_belt.alloc_data(shadows, &self.gpu); + let mut encoder = pass.with(&self.pipelines.shadows); + encoder.bind( + 0, + &ShaderShadowsData { + globals, + b_shadows: instances, + }, + ); + encoder.draw(0, 4, 0, shadows.len() as u32); + } _ => continue, } } diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 08bf08185391a..398d741ff3976 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -112,10 +112,10 @@ impl Platform for LinuxPlatform { xcb::Event::X(x::Event::Expose(ev)) => { repaint_x_window = Some(ev.window()); } - xcb::Event::X(x::Event::ResizeRequest(ev)) => { + xcb::Event::X(x::Event::ConfigureNotify(ev)) => { let this = self.0.lock(); LinuxWindowState::resize(&this.windows[&ev.window()], ev.width(), ev.height()); - repaint_x_window = Some(ev.window()); + this.xcb_connection.flush(); } _ => {} } @@ -175,7 +175,6 @@ impl Platform for LinuxPlatform { let window_ptr = LinuxWindowState::new_ptr( options, - handle, &this.xcb_connection, this.x_root_index, x_window, diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl index 0cc70e5220f27..a4e280cbefaf6 100644 --- a/crates/gpui/src/platform/linux/shaders.wgsl +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -1,3 +1,17 @@ +struct Globals { + viewport_size: vec2, + pad: vec2, +} + +var globals: Globals; + +const M_PI_F: f32 = 3.1415926; + +struct ViewId { + lo: u32, + hi: u32, +} + struct Bounds { origin: vec2, size: vec2, @@ -21,35 +35,6 @@ struct Hsla { a: f32, } -struct Quad { - view_id: vec2, - layer_id: u32, - order: u32, - bounds: Bounds, - content_mask: Bounds, - background: Hsla, - border_color: Hsla, - corner_radii: Corners, - border_widths: Edges, -} - -struct Globals { - viewport_size: vec2, - pad: vec2, -} - -var globals: Globals; -var quads: array; - -struct QuadsVarying { - @builtin(position) position: vec4, - @location(0) @interpolate(flat) background_color: vec4, - @location(1) @interpolate(flat) border_color: vec4, - @location(2) @interpolate(flat) quad_id: u32, - //TODO: use `clip_distance` once Naga supports it - @location(3) clip_distances: vec4, -} - fn to_device_position(unit_vertex: vec2, bounds: Bounds) -> vec4 { let position = unit_vertex * vec2(bounds.size) + bounds.origin; let device_position = position / globals.viewport_size * vec2(2.0, -2.0) + vec2(-1.0, 1.0); @@ -99,17 +84,62 @@ fn hsla_to_rgba(hsla: Hsla) -> vec4 { } fn over(below: vec4, above: vec4) -> vec4 { - let alpha = above.a + below.a * (1.0 - above.a); - let color = (above.rgb * above.a + below.rgb * below.a * (1.0 - above.a)) / alpha; - return vec4(color, alpha); + let alpha = above.a + below.a * (1.0 - above.a); + let color = (above.rgb * above.a + below.rgb * below.a * (1.0 - above.a)) / alpha; + return vec4(color, alpha); +} + +// A standard gaussian function, used for weighting samples +fn gaussian(x: f32, sigma: f32) -> f32{ + return exp(-(x * x) / (2.0 * sigma * sigma)) / (sqrt(2.0 * M_PI_F) * sigma); +} + +// This approximates the error function, needed for the gaussian integral +fn erf(v: vec2) -> vec2 { + let s = sign(v); + let a = abs(v); + let r1 = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a; + let r2 = r1 * r1; + return s - s / (r2 * r2); +} + +fn blur_along_x(x: f32, y: f32, sigma: f32, corner: f32, half_size: vec2) -> f32 { + let delta = min(half_size.y - corner - abs(y), 0.0); + let curved = half_size.x - corner + sqrt(max(0.0, corner * corner - delta * delta)); + let integral = 0.5 + 0.5 * erf((x + vec2(-curved, curved)) * (sqrt(0.5) / sigma)); + return integral.y - integral.x; +} + +// --- quads --- // + +struct Quad { + view_id: ViewId, + layer_id: u32, + order: u32, + bounds: Bounds, + content_mask: Bounds, + background: Hsla, + border_color: Hsla, + corner_radii: Corners, + border_widths: Edges, +} +var b_quads: array; + +struct QuadVarying { + @builtin(position) position: vec4, + @location(0) @interpolate(flat) background_color: vec4, + @location(1) @interpolate(flat) border_color: vec4, + @location(2) @interpolate(flat) quad_id: u32, + //TODO: use `clip_distance` once Naga supports it + @location(3) clip_distances: vec4, } @vertex -fn vs_quads(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> QuadsVarying { +fn vs_quad(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> QuadVarying { let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); - let quad = quads[instance_id]; + let quad = b_quads[instance_id]; - var out = QuadsVarying(); + var out = QuadVarying(); out.position = to_device_position(unit_vertex, quad.bounds); out.background_color = hsla_to_rgba(quad.background); out.border_color = hsla_to_rgba(quad.border_color); @@ -119,7 +149,7 @@ fn vs_quads(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) inst } @fragment -fn fs_quads(input: QuadsVarying) -> @location(0) vec4 { +fn fs_quad(input: QuadVarying) -> @location(0) vec4 { // Alpha clip first, since we don't have `clip_distance`. let min_distance = min( min(input.clip_distances.x, input.clip_distances.y), @@ -129,7 +159,7 @@ fn fs_quads(input: QuadsVarying) -> @location(0) vec4 { return vec4(0.0); } - let quad = quads[input.quad_id]; + let quad = b_quads[input.quad_id]; let half_size = quad.bounds.size / 2.0; let center = quad.bounds.origin + half_size; let center_to_point = input.position.xy - center; @@ -180,4 +210,98 @@ fn fs_quads(input: QuadsVarying) -> @location(0) vec4 { } return color * vec4(1.0, 1.0, 1.0, saturate(0.5 - distance)); -} \ No newline at end of file +} + +// --- shadows --- // + +struct Shadow { + view_id: ViewId, + layer_id: u32, + order: u32, + bounds: Bounds, + corner_radii: Corners, + content_mask: Bounds, + color: Hsla, + blur_radius: f32, + pad: u32, +} +var b_shadows: array; + +struct ShadowVarying { + @builtin(position) position: vec4, + @location(0) @interpolate(flat) color: vec4, + @location(1) @interpolate(flat) shadow_id: u32, + //TODO: use `clip_distance` once Naga supports it + @location(3) clip_distances: vec4, +} + +@vertex +fn vs_shadow(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> ShadowVarying { + let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); + let shadow = b_shadows[instance_id]; + + let margin = 3.0 * shadow.blur_radius; + // Set the bounds of the shadow and adjust its size based on the shadow's + // spread radius to achieve the spreading effect + var bounds = shadow.bounds; + bounds.origin -= vec2(margin); + bounds.size += 2.0 * vec2(margin); + + var out = ShadowVarying(); + out.position = to_device_position(unit_vertex, shadow.bounds); + out.color = hsla_to_rgba(shadow.color); + out.shadow_id = instance_id; + out.clip_distances = distance_from_clip_rect(unit_vertex, shadow.bounds, shadow.content_mask); + return out; +} + +@fragment +fn fs_shadow(input: ShadowVarying) -> @location(0) vec4 { + // Alpha clip first, since we don't have `clip_distance`. + let min_distance = min( + min(input.clip_distances.x, input.clip_distances.y), + min(input.clip_distances.z, input.clip_distances.w) + ); + if min_distance <= 0.0 { + return vec4(0.0); + } + + let shadow = b_shadows[input.shadow_id]; + let half_size = shadow.bounds.size / 2.0; + let center = shadow.bounds.origin + half_size; + let center_to_point = input.position.xy - center; + + var corner_radius = 0.0; + if (center_to_point.x < 0.0) { + if (center_to_point.y < 0.0) { + corner_radius = shadow.corner_radii.top_left; + } else { + corner_radius = shadow.corner_radii.bottom_left; + } + } else { + if (center_to_point.y < 0.) { + corner_radius = shadow.corner_radii.top_right; + } else { + corner_radius = shadow.corner_radii.bottom_right; + } + } + + // The signal is only non-zero in a limited range, so don't waste samples + let low = center_to_point.y - half_size.y; + let high = center_to_point.y + half_size.y; + let start = clamp(-3.0 * shadow.blur_radius, low, high); + let end = clamp(3.0 * shadow.blur_radius, low, high); + + // Accumulate samples (we can get away with surprisingly few samples) + let step = (end - start) / 4.0; + var y = start + step * 0.5; + var alpha = 0.0; + for (var i = 0; i < 4; i += 1) { + let blur = blur_along_x(center_to_point.x, center_to_point.y - y, + shadow.blur_radius, corner_radius, half_size); + alpha += blur * gaussian(y, shadow.blur_radius) * step; + y += step; + } + + return input.color * vec4(1.0, 1.0, 1.0, alpha); +} diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index a0d8157aabb06..0f0916b0dbc7f 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -38,11 +38,13 @@ struct RawWindow { connection: *mut c_void, screen_id: i32, window_id: u32, + visual_id: u32, } unsafe impl raw_window_handle::HasRawWindowHandle for RawWindow { fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { let mut wh = raw_window_handle::XcbWindowHandle::empty(); wh.window = self.window_id; + wh.visual_id = self.visual_id; wh.into() } } @@ -58,7 +60,6 @@ unsafe impl raw_window_handle::HasRawDisplayHandle for RawWindow { impl LinuxWindowState { pub fn new_ptr( options: WindowOptions, - handle: AnyWindowHandle, xcb_connection: &xcb::Connection, x_main_screen_index: i32, x_window: x::Window, @@ -76,7 +77,7 @@ impl LinuxWindowState { let xcb_values = [ x::Cw::BackPixel(screen.white_pixel()), x::Cw::EventMask( - x::EventMask::EXPOSURE | x::EventMask::RESIZE_REDIRECT | x::EventMask::KEY_PRESS, + x::EventMask::EXPOSURE | x::EventMask::STRUCTURE_NOTIFY | x::EventMask::KEY_PRESS, ), ]; @@ -136,6 +137,7 @@ impl LinuxWindowState { ) as *mut _, screen_id: x_screen_index, window_id: x_window.resource_id(), + visual_id: screen.root_visual(), }; let gpu = Arc::new( unsafe { diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index e1aa7fda20752..f5fcade711e99 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -577,6 +577,7 @@ pub(crate) struct Shadow { pub content_mask: ContentMask, pub color: Hsla, pub blur_radius: ScaledPixels, + pub pad: u32, // align to 8 bytes } impl Ord for Shadow { diff --git a/crates/gpui/src/window/element_cx.rs b/crates/gpui/src/window/element_cx.rs index 6e2e5bc7256d7..cde5f17b05efa 100644 --- a/crates/gpui/src/window/element_cx.rs +++ b/crates/gpui/src/window/element_cx.rs @@ -677,6 +677,7 @@ impl<'a> ElementContext<'a> { corner_radii: corner_radii.scale(scale_factor), color: shadow.color, blur_radius: shadow.blur_radius.scale(scale_factor), + pad: 0, }, ); } From c9ec3370349181c8aee6cc9eb1d8f94768073cf5 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 31 Jan 2024 00:12:44 -0800 Subject: [PATCH 14/51] linux: share corner picking code between shaders --- crates/gpui/src/platform/linux/shaders.wgsl | 62 ++++++++------------- 1 file changed, 22 insertions(+), 40 deletions(-) diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl index a4e280cbefaf6..c25456ff3fd01 100644 --- a/crates/gpui/src/platform/linux/shaders.wgsl +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -8,8 +8,8 @@ var globals: Globals; const M_PI_F: f32 = 3.1415926; struct ViewId { - lo: u32, - hi: u32, + lo: u32, + hi: u32, } struct Bounds { @@ -110,6 +110,22 @@ fn blur_along_x(x: f32, y: f32, sigma: f32, corner: f32, half_size: vec2) - return integral.y - integral.x; } +fn pick_corner_radius(point: vec2, radii: Corners) -> f32 { + if (point.x < 0.0) { + if (point.y < 0.0) { + return radii.top_left; + } else { + return radii.bottom_left; + } + } else { + if (point.y < 0.0) { + return radii.top_right; + } else { + return radii.bottom_right; + } + } +} + // --- quads --- // struct Quad { @@ -151,11 +167,7 @@ fn vs_quad(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) insta @fragment fn fs_quad(input: QuadVarying) -> @location(0) vec4 { // Alpha clip first, since we don't have `clip_distance`. - let min_distance = min( - min(input.clip_distances.x, input.clip_distances.y), - min(input.clip_distances.z, input.clip_distances.w) - ); - if min_distance <= 0.0 { + if (any(input.clip_distances < vec4(0.0))) { return vec4(0.0); } @@ -164,20 +176,7 @@ fn fs_quad(input: QuadVarying) -> @location(0) vec4 { let center = quad.bounds.origin + half_size; let center_to_point = input.position.xy - center; - var corner_radius = 0.0; - if (center_to_point.x < 0.0) { - if (center_to_point.y < 0.0) { - corner_radius = quad.corner_radii.top_left; - } else { - corner_radius = quad.corner_radii.bottom_left; - } - } else { - if (center_to_point.y < 0.) { - corner_radius = quad.corner_radii.top_right; - } else { - corner_radius = quad.corner_radii.bottom_right; - } - } + let corner_radius = pick_corner_radius(center_to_point, quad.corner_radii); let rounded_edge_to_point = abs(center_to_point) - half_size + corner_radius; let distance = @@ -258,11 +257,7 @@ fn vs_shadow(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) ins @fragment fn fs_shadow(input: ShadowVarying) -> @location(0) vec4 { // Alpha clip first, since we don't have `clip_distance`. - let min_distance = min( - min(input.clip_distances.x, input.clip_distances.y), - min(input.clip_distances.z, input.clip_distances.w) - ); - if min_distance <= 0.0 { + if (any(input.clip_distances < vec4(0.0))) { return vec4(0.0); } @@ -271,20 +266,7 @@ fn fs_shadow(input: ShadowVarying) -> @location(0) vec4 { let center = shadow.bounds.origin + half_size; let center_to_point = input.position.xy - center; - var corner_radius = 0.0; - if (center_to_point.x < 0.0) { - if (center_to_point.y < 0.0) { - corner_radius = shadow.corner_radii.top_left; - } else { - corner_radius = shadow.corner_radii.bottom_left; - } - } else { - if (center_to_point.y < 0.) { - corner_radius = shadow.corner_radii.top_right; - } else { - corner_radius = shadow.corner_radii.bottom_right; - } - } + let corner_radius = pick_corner_radius(center_to_point, shadow.corner_radii); // The signal is only non-zero in a limited range, so don't waste samples let low = center_to_point.y - half_size.y; From ce84a2a67196876dc5d1d76ecb1c0b37cd8cb9c4 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 1 Feb 2024 00:01:50 -0800 Subject: [PATCH 15/51] linux: refactor window structure, support move callback --- crates/gpui/examples/hello_world.rs | 5 +- crates/gpui/src/platform/linux/platform.rs | 55 +++-- crates/gpui/src/platform/linux/window.rs | 225 +++++++++++++-------- 3 files changed, 170 insertions(+), 115 deletions(-) diff --git a/crates/gpui/examples/hello_world.rs b/crates/gpui/examples/hello_world.rs index 736fd1445020b..d0578e66811a6 100644 --- a/crates/gpui/examples/hello_world.rs +++ b/crates/gpui/examples/hello_world.rs @@ -9,9 +9,12 @@ impl Render for HelloWorld { div() .flex() .bg(rgb(0x2e7d32)) - .size_full() + .size(Length::Definite(Pixels(300.0).into())) .justify_center() .items_center() + .shadow_lg() + .border() + .border_color(rgb(0x0000ff)) .text_xl() .text_color(rgb(0xffffff)) .child(format!("Hello, {}!", &self.text)) diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 398d741ff3976..f5f251f1effec 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -1,11 +1,10 @@ #![allow(unused)] use crate::{ - Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, + Action, AnyWindowHandle, BackgroundExecutor, Bounds, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor, Keymap, LinuxDispatcher, LinuxDisplay, LinuxTextSystem, LinuxWindow, - LinuxWindowState, LinuxWindowStatePtr, Menu, PathPromptOptions, Platform, PlatformDisplay, - PlatformInput, PlatformTextSystem, PlatformWindow, Result, SemanticVersion, Task, - WindowOptions, + LinuxWindowState, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, + PlatformTextSystem, PlatformWindow, Point, Result, SemanticVersion, Size, Task, WindowOptions, }; use collections::{HashMap, HashSet}; @@ -35,12 +34,12 @@ xcb::atoms_struct! { pub(crate) struct LinuxPlatform(Mutex); pub(crate) struct LinuxPlatformState { - xcb_connection: xcb::Connection, + xcb_connection: Arc, x_root_index: i32, atoms: XcbAtoms, background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, - windows: HashMap, + windows: HashMap>, text_system: Arc, } @@ -58,7 +57,7 @@ impl LinuxPlatform { let dispatcher = Arc::new(LinuxDispatcher::new()); Self(Mutex::new(LinuxPlatformState { - xcb_connection, + xcb_connection: Arc::new(xcb_connection), x_root_index, atoms, background_executor: BackgroundExecutor::new(dispatcher.clone()), @@ -87,44 +86,38 @@ impl Platform for LinuxPlatform { while !self.0.lock().windows.is_empty() { let event = self.0.lock().xcb_connection.wait_for_event().unwrap(); - let mut repaint_x_window = None; match event { xcb::Event::X(x::Event::ClientMessage(ev)) => { if let x::ClientMessageData::Data32([atom, ..]) = ev.data() { let mut this = self.0.lock(); if atom == this.atoms.wm_del_window.resource_id() { // window "x" button clicked by user, we gracefully exit - { - let mut window = this.windows[&ev.window()].lock(); - window.destroy(); - } - this.xcb_connection.send_request(&x::UnmapWindow { - window: ev.window(), - }); - this.xcb_connection.send_request(&x::DestroyWindow { - window: ev.window(), - }); - this.windows.remove(&ev.window()); + let window = this.windows.remove(&ev.window()).unwrap(); + window.destroy(); break; } } } xcb::Event::X(x::Event::Expose(ev)) => { - repaint_x_window = Some(ev.window()); + let this = self.0.lock(); + this.windows[&ev.window()].expose(); } xcb::Event::X(x::Event::ConfigureNotify(ev)) => { + let bounds = Bounds { + origin: Point { + x: ev.x().into(), + y: ev.y().into(), + }, + size: Size { + width: ev.width().into(), + height: ev.height().into(), + }, + }; let this = self.0.lock(); - LinuxWindowState::resize(&this.windows[&ev.window()], ev.width(), ev.height()); - this.xcb_connection.flush(); + this.windows[&ev.window()].configure(bounds); } _ => {} } - - if let Some(x_window) = repaint_x_window { - let this = self.0.lock(); - LinuxWindowState::request_frame(&this.windows[&x_window]); - this.xcb_connection.flush(); - } } } @@ -173,14 +166,14 @@ impl Platform for LinuxPlatform { let mut this = self.0.lock(); let x_window = this.xcb_connection.generate_id(); - let window_ptr = LinuxWindowState::new_ptr( + let window_ptr = Arc::new(LinuxWindowState::new( options, &this.xcb_connection, this.x_root_index, x_window, &this.atoms, - ); - this.windows.insert(x_window, window_ptr.clone()); + )); + this.windows.insert(x_window, Arc::clone(&window_ptr)); Box::new(LinuxWindow(window_ptr)) } diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 0f0916b0dbc7f..71c80df1bac16 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -1,12 +1,13 @@ use super::BladeRenderer; use crate::{ - AnyWindowHandle, BladeAtlas, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler, + BladeAtlas, Bounds, GlobalPixels, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, XcbAtoms, }; use blade_graphics as gpu; use parking_lot::Mutex; use std::{ ffi::c_void, + mem, rc::Rc, sync::{self, Arc}, }; @@ -15,24 +16,52 @@ use xcb::{x, Xid as _}; #[derive(Default)] struct Callbacks { request_frame: Option>, + input: Option bool>>, + active_status_change: Option>, resize: Option, f32)>>, + fullscreen: Option>, moved: Option>, + should_close: Option bool>>, + close: Option>, + appearance_changed: Option>, +} + +struct LinuxWindowInner { + bounds: Bounds, + title_height: i32, + border_width: i32, + scale_factor: f32, + renderer: BladeRenderer, +} + +impl LinuxWindowInner { + fn render_extent(&self) -> gpu::Extent { + gpu::Extent { + width: (self.bounds.size.width - 2 * self.border_width) as u32, + height: (self.bounds.size.height - 2 * self.border_width - self.title_height) as u32, + depth: 1, + } + } + fn content_size(&self) -> Size { + let extent = self.render_extent(); + Size { + width: extent.width.into(), + height: extent.height.into(), + } + } } pub(crate) struct LinuxWindowState { + xcb_connection: Arc, display: Rc, x_window: x::Window, - window_bounds: WindowBounds, - content_size: Size, + callbacks: Mutex, + inner: Mutex, sprite_atlas: Arc, - renderer: BladeRenderer, - //TODO: move out into a separate struct - callbacks: Callbacks, } -pub(crate) type LinuxWindowStatePtr = Arc>; #[derive(Clone)] -pub(crate) struct LinuxWindow(pub(crate) LinuxWindowStatePtr); +pub(crate) struct LinuxWindow(pub(crate) Arc); struct RawWindow { connection: *mut c_void, @@ -58,13 +87,13 @@ unsafe impl raw_window_handle::HasRawDisplayHandle for RawWindow { } impl LinuxWindowState { - pub fn new_ptr( + pub fn new( options: WindowOptions, - xcb_connection: &xcb::Connection, + xcb_connection: &Arc, x_main_screen_index: i32, x_window: x::Window, atoms: &XcbAtoms, - ) -> LinuxWindowStatePtr { + ) -> Self { let x_screen_index = options .display_id .map_or(x_main_screen_index, |did| did.0 as i32); @@ -81,27 +110,27 @@ impl LinuxWindowState { ), ]; - let (bound_x, bound_y, bound_width, bound_height) = match options.bounds { - WindowBounds::Fullscreen | WindowBounds::Maximized => { - (0, 0, screen.width_in_pixels(), screen.height_in_pixels()) - } - WindowBounds::Fixed(bounds) => ( - bounds.origin.x.0 as i16, - bounds.origin.y.0 as i16, - bounds.size.width.0 as u16, - bounds.size.height.0 as u16, - ), + let bounds = match options.bounds { + WindowBounds::Fullscreen | WindowBounds::Maximized => Bounds { + origin: Point::default(), + size: Size { + width: screen.width_in_pixels() as i32, + height: screen.height_in_pixels() as i32, + }, + }, + WindowBounds::Fixed(bounds) => bounds.map(|p| p.0 as i32), }; + let border_width = 0i32; xcb_connection.send_request(&x::CreateWindow { depth: x::COPY_FROM_PARENT as u8, wid: x_window, parent: screen.root(), - x: bound_x, - y: bound_y, - width: bound_width, - height: bound_height, - border_width: 0, + x: bounds.origin.x as i16, + y: bounds.origin.y as i16, + width: bounds.size.width as u16, + height: bounds.size.height as u16, + border_width: border_width as u16, class: x::WindowClass::InputOutput, visual: screen.root_visual(), value_list: &xcb_values, @@ -151,76 +180,93 @@ impl LinuxWindowState { } .unwrap(), ); + let gpu_extent = gpu::Extent { - width: bound_width as u32, - height: bound_height as u32, + width: bounds.size.width as u32, + height: bounds.size.height as u32, depth: 1, }; + let sprite_atlas = Arc::new(BladeAtlas::new(&gpu)); - Arc::new(Mutex::new(Self { + Self { + xcb_connection: Arc::clone(xcb_connection), display: Rc::new(LinuxDisplay::new(xcb_connection, x_screen_index)), x_window, - window_bounds: options.bounds, - content_size: Size { - width: Pixels(bound_width as f32), - height: Pixels(bound_height as f32), - }, - sprite_atlas: Arc::new(BladeAtlas::new(&gpu)), - renderer: BladeRenderer::new(gpu, gpu_extent), - callbacks: Callbacks::default(), - })) + callbacks: Mutex::new(Callbacks::default()), + inner: Mutex::new(LinuxWindowInner { + bounds, + title_height: 0, //TODO + border_width, + scale_factor: 1.0, + renderer: BladeRenderer::new(gpu, gpu_extent), + }), + sprite_atlas, + } } - pub fn destroy(&mut self) { + pub fn destroy(&self) { self.sprite_atlas.destroy(); - self.renderer.destroy(); + { + let mut inner = self.inner.lock(); + inner.renderer.destroy(); + } + self.xcb_connection.send_request(&x::UnmapWindow { + window: self.x_window, + }); + self.xcb_connection.send_request(&x::DestroyWindow { + window: self.x_window, + }); + if let Some(fun) = self.callbacks.lock().close.take() { + fun(); + } } - pub fn resize(self_ptr: &LinuxWindowStatePtr, width: u16, height: u16) { - let content_size = Size { - width: Pixels(width as f32), - height: Pixels(height as f32), - }; - - let mut fun = match self_ptr.lock().callbacks.resize.take() { - Some(fun) => fun, - None => return, - }; - fun(content_size, 1.0); - - let mut this = self_ptr.lock(); - this.callbacks.resize = Some(fun); - this.content_size = content_size; - this.renderer.resize(gpu::Extent { - width: width as u32, - height: height as u32, - depth: 1, - }); + pub fn expose(&self) { + let mut cb = self.callbacks.lock(); + if let Some(ref mut fun) = cb.request_frame { + fun(); + } } - pub fn request_frame(self_ptr: &LinuxWindowStatePtr) { - let mut fun = match self_ptr.lock().callbacks.request_frame.take() { - Some(fun) => fun, - None => return, - }; - fun(); + pub fn configure(&self, bounds: Bounds) { + let mut resize_args = None; + let mut do_move = false; + { + let mut inner = self.inner.lock(); + let old_bounds = mem::replace(&mut inner.bounds, bounds); + do_move = old_bounds.origin != bounds.origin; + if old_bounds.size != bounds.size { + let extent = inner.render_extent(); + inner.renderer.resize(extent); + resize_args = Some((inner.content_size(), inner.scale_factor)); + } + } - self_ptr.lock().callbacks.request_frame = Some(fun); + let mut callbacks = self.callbacks.lock(); + if let Some((content_size, scale_factor)) = resize_args { + if let Some(ref mut fun) = callbacks.resize { + fun(content_size, scale_factor) + } + } + if do_move { + if let Some(ref mut fun) = callbacks.moved { + fun() + } + } } } impl PlatformWindow for LinuxWindow { fn bounds(&self) -> WindowBounds { - //TODO: update when window moves - self.0.lock().window_bounds + WindowBounds::Fixed(self.0.inner.lock().bounds.map(|v| GlobalPixels(v as f32))) } fn content_size(&self) -> Size { - self.0.lock().content_size + self.0.inner.lock().content_size() } fn scale_factor(&self) -> f32 { - 1.0 + self.0.inner.lock().scale_factor } fn titlebar_height(&self) -> Pixels { @@ -232,7 +278,7 @@ impl PlatformWindow for LinuxWindow { } fn display(&self) -> Rc { - Rc::clone(&self.0.lock().display) + Rc::clone(&self.0.display) } fn mouse_position(&self) -> Point { @@ -286,28 +332,40 @@ impl PlatformWindow for LinuxWindow { } fn on_request_frame(&self, callback: Box) { - self.0.lock().callbacks.request_frame = Some(callback); + self.0.callbacks.lock().request_frame = Some(callback); } - fn on_input(&self, callback: Box bool>) {} + fn on_input(&self, callback: Box bool>) { + self.0.callbacks.lock().input = Some(callback); + } - fn on_active_status_change(&self, callback: Box) {} + fn on_active_status_change(&self, callback: Box) { + self.0.callbacks.lock().active_status_change = Some(callback); + } fn on_resize(&self, callback: Box, f32)>) { - self.0.lock().callbacks.resize = Some(callback); + self.0.callbacks.lock().resize = Some(callback); } - fn on_fullscreen(&self, _callback: Box) {} + fn on_fullscreen(&self, callback: Box) { + self.0.callbacks.lock().fullscreen = Some(callback); + } fn on_moved(&self, callback: Box) { - self.0.lock().callbacks.moved = Some(callback); + self.0.callbacks.lock().moved = Some(callback); } - fn on_should_close(&self, _callback: Box bool>) {} + fn on_should_close(&self, callback: Box bool>) { + self.0.callbacks.lock().should_close = Some(callback); + } - fn on_close(&self, _callback: Box) {} + fn on_close(&self, callback: Box) { + self.0.callbacks.lock().close = Some(callback); + } - fn on_appearance_changed(&self, _callback: Box) {} + fn on_appearance_changed(&self, callback: Box) { + self.0.callbacks.lock().appearance_changed = Some(callback); + } fn is_topmost_for_position(&self, _position: crate::Point) -> bool { unimplemented!() @@ -316,10 +374,11 @@ impl PlatformWindow for LinuxWindow { fn invalidate(&self) {} fn draw(&self, scene: &crate::Scene) { - self.0.lock().renderer.draw(scene); + let mut inner = self.0.inner.lock(); + inner.renderer.draw(scene); } fn sprite_atlas(&self) -> sync::Arc { - self.0.lock().sprite_atlas.clone() + self.0.sprite_atlas.clone() } } From ed679c9347e2a790f11ad9b23d763e4fa41ffe01 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 1 Feb 2024 00:42:38 -0800 Subject: [PATCH 16/51] WIP path rasterization --- crates/gpui/src/platform/linux/blade_atlas.rs | 33 +++++++- .../gpui/src/platform/linux/blade_renderer.rs | 76 ++++++++++++++++++- crates/gpui/src/platform/linux/shaders.wgsl | 2 + crates/gpui/src/platform/linux/window.rs | 14 +--- 4 files changed, 113 insertions(+), 12 deletions(-) diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index 56bb745764713..8de5a562eaaf0 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -10,6 +10,8 @@ use etagere::BucketedAtlasAllocator; use parking_lot::Mutex; use std::{borrow::Cow, sync::Arc}; +pub(crate) const PATH_TEXTURE_FORMAT: gpu::TextureFormat = gpu::TextureFormat::R16Float; + pub(crate) struct BladeAtlas(Mutex); struct BladeAtlasState { @@ -32,6 +34,7 @@ impl BladeAtlasState { } for texture in self.path_textures.drain(..) { self.gpu.destroy_texture(texture.raw); + self.gpu.destroy_texture_view(texture.raw_view.unwrap()); } self.gpu.destroy_command_encoder(&mut self.gpu_encoder); self.upload_belt.destroy(&self.gpu); @@ -78,6 +81,11 @@ impl BladeAtlas { lock.gpu_encoder.start(); } + pub fn allocate(&self, size: Size, texture_kind: AtlasTextureKind) -> AtlasTile { + let mut lock = self.0.lock(); + lock.allocate(size, texture_kind) + } + pub fn finish_frame(&self) -> gpu::SyncPoint { let mut lock = self.0.lock(); let gpu = lock.gpu.clone(); @@ -85,6 +93,16 @@ impl BladeAtlas { lock.upload_belt.flush(&sync_point); sync_point } + + pub fn get_texture_view(&self, id: AtlasTextureId) -> gpu::TextureView { + let lock = self.0.lock(); + let textures = match id.kind { + crate::AtlasTextureKind::Monochrome => &lock.monochrome_textures, + crate::AtlasTextureKind::Polychrome => &lock.polychrome_textures, + crate::AtlasTextureKind::Path => &lock.path_textures, + }; + textures[id.index as usize].raw_view.unwrap() + } } impl PlatformAtlas for BladeAtlas { @@ -146,7 +164,7 @@ impl BladeAtlasState { usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE; } AtlasTextureKind::Path => { - format = gpu::TextureFormat::R16Float; + format = PATH_TEXTURE_FORMAT; usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE | gpu::TextureUsage::TARGET; @@ -166,6 +184,17 @@ impl BladeAtlasState { dimension: gpu::TextureDimension::D2, usage, }); + let raw_view = if usage.contains(gpu::TextureUsage::TARGET) { + Some(self.gpu.create_texture_view(gpu::TextureViewDesc { + name: "", + texture: raw, + format, + dimension: gpu::ViewDimension::D2, + subresources: &Default::default(), + })) + } else { + None + }; let textures = match kind { AtlasTextureKind::Monochrome => &mut self.monochrome_textures, @@ -180,6 +209,7 @@ impl BladeAtlasState { allocator: etagere::BucketedAtlasAllocator::new(size.into()), format, raw, + raw_view, }; textures.push(atlas_texture); textures.last_mut().unwrap() @@ -218,6 +248,7 @@ struct BladeAtlasTexture { id: AtlasTextureId, allocator: BucketedAtlasAllocator, raw: gpu::Texture, + raw_view: Option, format: gpu::TextureFormat, } diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 4d58cbd1e6cda..0ade2b5df3fd5 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -2,8 +2,12 @@ #![allow(irrefutable_let_patterns)] use super::{BladeBelt, BladeBeltDescriptor}; -use crate::{PrimitiveBatch, Quad, Scene, Shadow}; +use crate::{ + AtlasTextureKind, AtlasTile, BladeAtlas, ContentMask, Path, PathId, PathVertex, PrimitiveBatch, + Quad, ScaledPixels, Scene, Shadow, PATH_TEXTURE_FORMAT, +}; use bytemuck::{Pod, Zeroable}; +use collections::HashMap; use blade_graphics as gpu; use std::sync::Arc; @@ -33,6 +37,7 @@ struct ShaderShadowsData { struct BladePipelines { quads: gpu::RenderPipeline, shadows: gpu::RenderPipeline, + path_rasterization: gpu::RenderPipeline, } impl BladePipelines { @@ -77,6 +82,22 @@ impl BladePipelines { write_mask: gpu::ColorWrites::default(), }], }), + path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc { + name: "path_rasterization", + data_layouts: &[&shadows_layout], + vertex: shader.at("vs_path_rasterization"), + primitive: gpu::PrimitiveState { + topology: gpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + fragment: shader.at("fs_path_rasterization"), + color_targets: &[gpu::ColorTargetState { + format: PATH_TEXTURE_FORMAT, + blend: Some(gpu::BlendState::ALPHA_BLENDING), + write_mask: gpu::ColorWrites::default(), + }], + }), } } } @@ -88,6 +109,8 @@ pub struct BladeRenderer { pipelines: BladePipelines, instance_belt: BladeBelt, viewport_size: gpu::Extent, + path_tiles: HashMap, + atlas: Arc, } impl BladeRenderer { @@ -106,6 +129,8 @@ impl BladeRenderer { memory: gpu::Memory::Shared, min_chunk_size: 0x1000, }); + let atlas = Arc::new(BladeAtlas::new(&gpu)); + Self { gpu, command_encoder, @@ -113,6 +138,8 @@ impl BladeRenderer { pipelines, instance_belt, viewport_size: size, + path_tiles: HashMap::default(), + atlas, } } @@ -126,6 +153,7 @@ impl BladeRenderer { pub fn destroy(&mut self) { self.wait_for_gpu(); + self.atlas.destroy(); self.instance_belt.destroy(&self.gpu); self.gpu.destroy_command_encoder(&mut self.command_encoder); } @@ -140,11 +168,56 @@ impl BladeRenderer { self.viewport_size = size; } + pub fn atlas(&self) -> &Arc { + &self.atlas + } + + fn rasterize_paths(&mut self, paths: &[Path]) { + self.path_tiles.clear(); + let mut vertices_by_texture_id = HashMap::default(); + + for path in paths { + let clipped_bounds = path.bounds.intersect(&path.content_mask.bounds); + let tile = self + .atlas + .allocate(clipped_bounds.size.map(Into::into), AtlasTextureKind::Path); + vertices_by_texture_id + .entry(tile.texture_id) + .or_insert(Vec::new()) + .extend(path.vertices.iter().map(|vertex| PathVertex { + xy_position: vertex.xy_position - clipped_bounds.origin + + tile.bounds.origin.map(Into::into), + st_position: vertex.st_position, + content_mask: ContentMask { + bounds: tile.bounds.map(Into::into), + }, + })); + self.path_tiles.insert(path.id, tile); + } + + for (texture_id, vertices) in vertices_by_texture_id { + let instances = self.instance_belt.alloc_data(&vertices, &self.gpu); + let mut pass = self.command_encoder.render(gpu::RenderTargetSet { + colors: &[gpu::RenderTarget { + view: self.atlas.get_texture_view(texture_id), + 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.draw(0, vertices.len() as u32, 0, 1); + } + } + pub fn draw(&mut self, scene: &Scene) { let frame = self.gpu.acquire_frame(); self.command_encoder.start(); self.command_encoder.init_texture(frame.texture()); + self.rasterize_paths(scene.paths()); + let globals = GlobalParams { viewport_size: [ self.viewport_size.width as f32, @@ -187,6 +260,7 @@ impl BladeRenderer { ); encoder.draw(0, 4, 0, shadows.len() as u32); } + PrimitiveBatch::Paths(paths) => {} _ => continue, } } diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl index c25456ff3fd01..4b943fcd557ed 100644 --- a/crates/gpui/src/platform/linux/shaders.wgsl +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -287,3 +287,5 @@ fn fs_shadow(input: ShadowVarying) -> @location(0) vec4 { return input.color * vec4(1.0, 1.0, 1.0, alpha); } + +// --- path rasterization --- // diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 71c80df1bac16..87d4d013875f1 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -1,6 +1,6 @@ use super::BladeRenderer; use crate::{ - BladeAtlas, Bounds, GlobalPixels, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler, + Bounds, GlobalPixels, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, XcbAtoms, }; use blade_graphics as gpu; @@ -57,7 +57,6 @@ pub(crate) struct LinuxWindowState { x_window: x::Window, callbacks: Mutex, inner: Mutex, - sprite_atlas: Arc, } #[derive(Clone)] @@ -186,7 +185,6 @@ impl LinuxWindowState { height: bounds.size.height as u32, depth: 1, }; - let sprite_atlas = Arc::new(BladeAtlas::new(&gpu)); Self { xcb_connection: Arc::clone(xcb_connection), @@ -200,16 +198,11 @@ impl LinuxWindowState { scale_factor: 1.0, renderer: BladeRenderer::new(gpu, gpu_extent), }), - sprite_atlas, } } pub fn destroy(&self) { - self.sprite_atlas.destroy(); - { - let mut inner = self.inner.lock(); - inner.renderer.destroy(); - } + self.inner.lock().renderer.destroy(); self.xcb_connection.send_request(&x::UnmapWindow { window: self.x_window, }); @@ -379,6 +372,7 @@ impl PlatformWindow for LinuxWindow { } fn sprite_atlas(&self) -> sync::Arc { - self.0.sprite_atlas.clone() + let mut inner = self.0.inner.lock(); + inner.renderer.atlas().clone() } } From 05c42211fe2e88faa8e469a477141fd0687e41fc Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 1 Feb 2024 22:05:59 -0800 Subject: [PATCH 17/51] linux: implement RWH for LinuxWindow --- crates/gpui/src/platform/linux/window.rs | 49 +++++++++++++++++------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 87d4d013875f1..3efade0c89d52 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -5,6 +5,7 @@ use crate::{ }; use blade_graphics as gpu; use parking_lot::Mutex; +use raw_window_handle as rwh; use std::{ ffi::c_void, mem, @@ -51,9 +52,17 @@ impl LinuxWindowInner { } } +struct RawWindow { + connection: *mut c_void, + screen_id: i32, + window_id: u32, + visual_id: u32, +} + pub(crate) struct LinuxWindowState { xcb_connection: Arc, display: Rc, + raw: RawWindow, x_window: x::Window, callbacks: Mutex, inner: Mutex, @@ -62,29 +71,40 @@ pub(crate) struct LinuxWindowState { #[derive(Clone)] pub(crate) struct LinuxWindow(pub(crate) Arc); -struct RawWindow { - connection: *mut c_void, - screen_id: i32, - window_id: u32, - visual_id: u32, -} -unsafe impl raw_window_handle::HasRawWindowHandle for RawWindow { - fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { - let mut wh = raw_window_handle::XcbWindowHandle::empty(); +unsafe impl rwh::HasRawWindowHandle for RawWindow { + fn raw_window_handle(&self) -> rwh::RawWindowHandle { + let mut wh = rwh::XcbWindowHandle::empty(); wh.window = self.window_id; wh.visual_id = self.visual_id; wh.into() } } -unsafe impl raw_window_handle::HasRawDisplayHandle for RawWindow { - fn raw_display_handle(&self) -> raw_window_handle::RawDisplayHandle { - let mut dh = raw_window_handle::XcbDisplayHandle::empty(); +unsafe impl rwh::HasRawDisplayHandle for RawWindow { + fn raw_display_handle(&self) -> rwh::RawDisplayHandle { + let mut dh = rwh::XcbDisplayHandle::empty(); dh.connection = self.connection; dh.screen = self.screen_id; dh.into() } } +impl rwh::HasWindowHandle for LinuxWindow { + fn window_handle(&self) -> Result { + Ok(unsafe { + let raw_handle = rwh::HasRawWindowHandle::raw_window_handle(&self.0.raw); + rwh::WindowHandle::borrow_raw(raw_handle, rwh::ActiveHandle::new()) + }) + } +} +impl rwh::HasDisplayHandle for LinuxWindow { + fn display_handle(&self) -> Result { + Ok(unsafe { + let raw_handle = rwh::HasRawDisplayHandle::raw_display_handle(&self.0.raw); + rwh::DisplayHandle::borrow_raw(raw_handle) + }) + } +} + impl LinuxWindowState { pub fn new( options: WindowOptions, @@ -159,7 +179,7 @@ impl LinuxWindowState { xcb_connection.send_request(&x::MapWindow { window: x_window }); xcb_connection.flush().unwrap(); - let raw_window = RawWindow { + let raw = RawWindow { connection: as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection( xcb_connection, ) as *mut _, @@ -170,7 +190,7 @@ impl LinuxWindowState { let gpu = Arc::new( unsafe { gpu::Context::init_windowed( - &raw_window, + &raw, gpu::ContextDesc { validation: cfg!(debug_assertions), capture: false, @@ -189,6 +209,7 @@ impl LinuxWindowState { Self { xcb_connection: Arc::clone(xcb_connection), display: Rc::new(LinuxDisplay::new(xcb_connection, x_screen_index)), + raw, x_window, callbacks: Mutex::new(Callbacks::default()), inner: Mutex::new(LinuxWindowInner { From fdaffdbfff0140251e4c51d64cf394f1f85360c2 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 1 Feb 2024 22:56:50 -0800 Subject: [PATCH 18/51] linux: path rasterization shader --- crates/gpui/src/platform/linux/blade_atlas.rs | 18 ++++++- .../gpui/src/platform/linux/blade_renderer.rs | 41 +++++++++++--- crates/gpui/src/platform/linux/shaders.wgsl | 53 +++++++++++++++++-- crates/gpui/src/platform/linux/text_system.rs | 2 +- 4 files changed, 100 insertions(+), 14 deletions(-) diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index 8de5a562eaaf0..3c661c2191b70 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -41,6 +41,11 @@ impl BladeAtlasState { } } +pub struct BladeTextureInfo { + pub size: gpu::Extent, + pub raw_view: Option, +} + impl BladeAtlas { pub(crate) fn new(gpu: &Arc) -> Self { BladeAtlas(Mutex::new(BladeAtlasState { @@ -94,14 +99,23 @@ impl BladeAtlas { sync_point } - pub fn get_texture_view(&self, id: AtlasTextureId) -> gpu::TextureView { + pub fn get_texture_info(&self, id: AtlasTextureId) -> BladeTextureInfo { let lock = self.0.lock(); let textures = match id.kind { crate::AtlasTextureKind::Monochrome => &lock.monochrome_textures, crate::AtlasTextureKind::Polychrome => &lock.polychrome_textures, crate::AtlasTextureKind::Path => &lock.path_textures, }; - textures[id.index as usize].raw_view.unwrap() + let texture = &textures[id.index as usize]; + let size = texture.allocator.size(); + BladeTextureInfo { + size: gpu::Extent { + width: size.width as u32, + height: size.height as u32, + depth: 1, + }, + raw_view: texture.raw_view, + } } } diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 0ade2b5df3fd5..ef2a9fead8c43 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -10,7 +10,7 @@ use bytemuck::{Pod, Zeroable}; use collections::HashMap; use blade_graphics as gpu; -use std::sync::Arc; +use std::{mem, sync::Arc}; const SURFACE_FRAME_COUNT: u32 = 3; const MAX_FRAME_TIME_MS: u32 = 1000; @@ -34,6 +34,12 @@ struct ShaderShadowsData { b_shadows: gpu::BufferPiece, } +#[derive(blade_macros::ShaderData)] +struct ShaderPathRasterizationData { + globals: GlobalParams, + b_path_vertices: gpu::BufferPiece, +} + struct BladePipelines { quads: gpu::RenderPipeline, shadows: gpu::RenderPipeline, @@ -47,8 +53,14 @@ impl BladePipelines { }); shader.check_struct_size::(); shader.check_struct_size::(); + assert_eq!( + mem::size_of::>(), + shader.get_struct_size("PathVertex") as usize, + ); let quads_layout = ::layout(); let shadows_layout = ::layout(); + let path_rasterization_layout = ::layout(); + Self { quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc { name: "quads", @@ -84,7 +96,7 @@ impl BladePipelines { }), path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc { name: "path_rasterization", - data_layouts: &[&shadows_layout], + data_layouts: &[&path_rasterization_layout], vertex: shader.at("vs_path_rasterization"), primitive: gpu::PrimitiveState { topology: gpu::PrimitiveTopology::TriangleStrip, @@ -196,10 +208,16 @@ impl BladeRenderer { } for (texture_id, vertices) in vertices_by_texture_id { - let instances = self.instance_belt.alloc_data(&vertices, &self.gpu); + let tex_info = self.atlas.get_texture_info(texture_id); + let globals = GlobalParams { + viewport_size: [tex_info.size.width as f32, tex_info.size.height as f32], + pad: [0; 2], + }; + + let vertex_buf = self.instance_belt.alloc_data(&vertices, &self.gpu); let mut pass = self.command_encoder.render(gpu::RenderTargetSet { colors: &[gpu::RenderTarget { - view: self.atlas.get_texture_view(texture_id), + view: tex_info.raw_view.unwrap(), init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack), finish_op: gpu::FinishOp::Store, }], @@ -207,6 +225,13 @@ impl BladeRenderer { }); let mut encoder = pass.with(&self.pipelines.path_rasterization); + encoder.bind( + 0, + &ShaderPathRasterizationData { + globals, + b_path_vertices: vertex_buf, + }, + ); encoder.draw(0, vertices.len() as u32, 0, 1); } } @@ -237,25 +262,25 @@ impl BladeRenderer { for batch in scene.batches() { match batch { PrimitiveBatch::Quads(quads) => { - let instances = self.instance_belt.alloc_data(quads, &self.gpu); + let instance_buf = self.instance_belt.alloc_data(quads, &self.gpu); let mut encoder = pass.with(&self.pipelines.quads); encoder.bind( 0, &ShaderQuadsData { globals, - b_quads: instances, + b_quads: instance_buf, }, ); encoder.draw(0, 4, 0, quads.len() as u32); } PrimitiveBatch::Shadows(shadows) => { - let instances = self.instance_belt.alloc_data(shadows, &self.gpu); + let instance_buf = self.instance_belt.alloc_data(shadows, &self.gpu); let mut encoder = pass.with(&self.pipelines.shadows); encoder.bind( 0, &ShaderShadowsData { globals, - b_shadows: instances, + b_shadows: instance_buf, }, ); encoder.draw(0, 4, 0, shadows.len() as u32); diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl index 4b943fcd557ed..ddba4be846da7 100644 --- a/crates/gpui/src/platform/linux/shaders.wgsl +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -35,19 +35,27 @@ struct Hsla { a: f32, } -fn to_device_position(unit_vertex: vec2, bounds: Bounds) -> vec4 { - let position = unit_vertex * vec2(bounds.size) + bounds.origin; +fn to_device_position_impl(position: vec2) -> vec4 { let device_position = position / globals.viewport_size * vec2(2.0, -2.0) + vec2(-1.0, 1.0); return vec4(device_position, 0.0, 1.0); } -fn distance_from_clip_rect(unit_vertex: vec2, bounds: Bounds, clip_bounds: Bounds) -> vec4 { +fn to_device_position(unit_vertex: vec2, bounds: Bounds) -> vec4 { let position = unit_vertex * vec2(bounds.size) + bounds.origin; + return to_device_position_impl(position); +} + +fn distance_from_clip_rect_impl(position: vec2, clip_bounds: Bounds) -> vec4 { let tl = position - clip_bounds.origin; let br = clip_bounds.origin + clip_bounds.size - position; return vec4(tl.x, br.x, tl.y, br.y); } +fn distance_from_clip_rect(unit_vertex: vec2, bounds: Bounds, clip_bounds: Bounds) -> vec4 { + let position = unit_vertex * vec2(bounds.size) + bounds.origin; + return distance_from_clip_rect_impl(position, clip_bounds); +} + fn hsla_to_rgba(hsla: Hsla) -> vec4 { let h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range let s = hsla.s; @@ -289,3 +297,42 @@ fn fs_shadow(input: ShadowVarying) -> @location(0) vec4 { } // --- path rasterization --- // + +struct PathVertex { + xy_position: vec2, + st_position: vec2, + content_mask: Bounds, +} +var b_path_vertices: array; + +struct PathRasterizationVarying { + @builtin(position) position: vec4, + @location(0) st_position: vec2, + //TODO: use `clip_distance` once Naga supports it + @location(3) clip_distances: vec4, +} + +@vertex +fn vs_path_rasterization(@builtin(vertex_index) vertex_id: u32) -> PathRasterizationVarying { + let v = b_path_vertices[vertex_id]; + + var out = PathRasterizationVarying(); + out.position = to_device_position_impl(v.xy_position); + out.st_position = v.st_position; + out.clip_distances = distance_from_clip_rect_impl(v.xy_position, v.content_mask); + return out; +} + +@fragment +fn fs_path_rasterization(input: PathRasterizationVarying) -> @location(0) f32 { + let dx = dpdx(input.st_position); + let dy = dpdy(input.st_position); + if (any(input.clip_distances < vec4(0.0))) { + return 0.0; + } + + let gradient = 2.0 * input.st_position * vec2(dx.x, dy.x) - vec2(dx.y, dy.y); + let f = input.st_position.x * input.st_position.x - input.st_position.y; + let distance = f / length(gradient); + return saturate(0.5 - distance); +} diff --git a/crates/gpui/src/platform/linux/text_system.rs b/crates/gpui/src/platform/linux/text_system.rs index 36ed155f6aba8..62935fc232f10 100644 --- a/crates/gpui/src/platform/linux/text_system.rs +++ b/crates/gpui/src/platform/linux/text_system.rs @@ -15,7 +15,7 @@ use font_kit::{ }; use parking_lot::RwLock; use smallvec::SmallVec; -use std::{borrow::Cow}; +use std::borrow::Cow; pub(crate) struct LinuxTextSystem(RwLock); From c000d2e16b9e1063c0ea43de1ef895f5bdec8989 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 2 Feb 2024 00:34:08 -0800 Subject: [PATCH 19/51] blade: path sprite rendering --- crates/gpui/src/platform.rs | 1 + crates/gpui/src/platform/linux/blade_atlas.rs | 1 + .../gpui/src/platform/linux/blade_renderer.rs | 78 ++++++++++++++++++- crates/gpui/src/platform/linux/shaders.wgsl | 66 ++++++++++++++-- crates/gpui/src/platform/test/window.rs | 1 + 5 files changed, 138 insertions(+), 9 deletions(-) diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 008baea33326d..5bd985a74b832 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -304,6 +304,7 @@ pub(crate) trait PlatformAtlas: Send + Sync { pub(crate) struct AtlasTile { pub(crate) texture_id: AtlasTextureId, pub(crate) tile_id: TileId, + pub(crate) padding: u32, pub(crate) bounds: Bounds, } diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index 3c661c2191b70..7f27bf68a4125 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -276,6 +276,7 @@ impl BladeAtlasTexture { let tile = AtlasTile { texture_id: self.id, tile_id: allocation.id.into(), + padding: 0, bounds: Bounds { origin: allocation.rectangle.min.into(), size, diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index ef2a9fead8c43..4d64b184f0e9e 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -3,8 +3,8 @@ use super::{BladeBelt, BladeBeltDescriptor}; use crate::{ - AtlasTextureKind, AtlasTile, BladeAtlas, ContentMask, Path, PathId, PathVertex, PrimitiveBatch, - Quad, ScaledPixels, Scene, Shadow, PATH_TEXTURE_FORMAT, + AtlasTextureKind, AtlasTile, BladeAtlas, Bounds, ContentMask, Hsla, Path, PathId, PathVertex, + PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, PATH_TEXTURE_FORMAT, }; use bytemuck::{Pod, Zeroable}; use collections::HashMap; @@ -40,10 +40,27 @@ struct ShaderPathRasterizationData { b_path_vertices: gpu::BufferPiece, } +#[derive(blade_macros::ShaderData)] +struct ShaderPathsData { + globals: GlobalParams, + t_tile: gpu::TextureView, + s_tile: gpu::Sampler, + b_path_sprites: gpu::BufferPiece, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[repr(C)] +struct PathSprite { + bounds: Bounds, + color: Hsla, + tile: AtlasTile, +} + struct BladePipelines { quads: gpu::RenderPipeline, shadows: gpu::RenderPipeline, path_rasterization: gpu::RenderPipeline, + paths: gpu::RenderPipeline, } impl BladePipelines { @@ -57,9 +74,12 @@ impl BladePipelines { mem::size_of::>(), shader.get_struct_size("PathVertex") as usize, ); + shader.check_struct_size::(); + let quads_layout = ::layout(); let shadows_layout = ::layout(); let path_rasterization_layout = ::layout(); + let paths_layout = ::layout(); Self { quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc { @@ -110,6 +130,22 @@ impl BladePipelines { write_mask: gpu::ColorWrites::default(), }], }), + paths: gpu.create_render_pipeline(gpu::RenderPipelineDesc { + name: "paths", + data_layouts: &[&paths_layout], + vertex: shader.at("vs_path"), + primitive: gpu::PrimitiveState { + topology: gpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + fragment: shader.at("fs_path"), + color_targets: &[gpu::ColorTargetState { + format: surface_format, + blend: Some(gpu::BlendState::ALPHA_BLENDING), + write_mask: gpu::ColorWrites::default(), + }], + }), } } } @@ -123,6 +159,7 @@ pub struct BladeRenderer { viewport_size: gpu::Extent, path_tiles: HashMap, atlas: Arc, + atlas_sampler: gpu::Sampler, } impl BladeRenderer { @@ -142,6 +179,12 @@ impl BladeRenderer { min_chunk_size: 0x1000, }); let atlas = Arc::new(BladeAtlas::new(&gpu)); + let atlas_sampler = gpu.create_sampler(gpu::SamplerDesc { + name: "atlas", + mag_filter: gpu::FilterMode::Linear, + min_filter: gpu::FilterMode::Linear, + ..Default::default() + }); Self { gpu, @@ -152,6 +195,7 @@ impl BladeRenderer { viewport_size: size, path_tiles: HashMap::default(), atlas, + atlas_sampler, } } @@ -285,7 +329,35 @@ impl BladeRenderer { ); encoder.draw(0, 4, 0, shadows.len() as u32); } - PrimitiveBatch::Paths(paths) => {} + PrimitiveBatch::Paths(paths) => { + let mut encoder = pass.with(&self.pipelines.paths); + //TODO: group by texture ID + for path in paths { + let tile = &self.path_tiles[&path.id]; + let tex_info = self.atlas.get_texture_info(tile.texture_id); + let origin = path.bounds.intersect(&path.content_mask.bounds).origin; + let sprites = [PathSprite { + bounds: Bounds { + origin: origin.map(|p| p.floor()), + size: tile.bounds.size.map(Into::into), + }, + color: path.color, + tile: (*tile).clone(), + }]; + + let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu); + encoder.bind( + 0, + &ShaderPathsData { + globals, + t_tile: tex_info.raw_view.unwrap(), + s_tile: self.atlas_sampler, + b_path_sprites: instance_buf, + }, + ); + encoder.draw(0, 4, 0, sprites.len() as u32); + } + } _ => continue, } } diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl index ddba4be846da7..3a7853142ad97 100644 --- a/crates/gpui/src/platform/linux/shaders.wgsl +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -4,6 +4,8 @@ struct Globals { } var globals: Globals; +var t_tile: texture_2d; +var s_tile: sampler; const M_PI_F: f32 = 3.1415926; @@ -35,6 +37,18 @@ struct Hsla { a: f32, } +struct AtlasTextureId { + index: u32, + kind: u32, +} + +struct AtlasTile { + texture_id: AtlasTextureId, + tile_id: u32, + padding: u32, + bounds: Bounds, +} + fn to_device_position_impl(position: vec2) -> vec4 { let device_position = position / globals.viewport_size * vec2(2.0, -2.0) + vec2(-1.0, 1.0); return vec4(device_position, 0.0, 1.0); @@ -45,6 +59,11 @@ fn to_device_position(unit_vertex: vec2, bounds: Bounds) -> vec4 { return to_device_position_impl(position); } +fn to_tile_position(unit_vertex: vec2, tile: AtlasTile) -> vec2 { + let atlas_size = vec2(textureDimensions(t_tile, 0)); + return (tile.bounds.origin + unit_vertex * tile.bounds.size) / atlas_size; +} + fn distance_from_clip_rect_impl(position: vec2, clip_bounds: Bounds) -> vec4 { let tl = position - clip_bounds.origin; let br = clip_bounds.origin + clip_bounds.size - position; @@ -325,14 +344,49 @@ fn vs_path_rasterization(@builtin(vertex_index) vertex_id: u32) -> PathRasteriza @fragment fn fs_path_rasterization(input: PathRasterizationVarying) -> @location(0) f32 { - let dx = dpdx(input.st_position); - let dy = dpdy(input.st_position); + let dx = dpdx(input.st_position); + let dy = dpdy(input.st_position); if (any(input.clip_distances < vec4(0.0))) { return 0.0; } - let gradient = 2.0 * input.st_position * vec2(dx.x, dy.x) - vec2(dx.y, dy.y); - let f = input.st_position.x * input.st_position.x - input.st_position.y; - let distance = f / length(gradient); - return saturate(0.5 - distance); + let gradient = 2.0 * input.st_position * vec2(dx.x, dy.x) - vec2(dx.y, dy.y); + let f = input.st_position.x * input.st_position.x - input.st_position.y; + let distance = f / length(gradient); + return saturate(0.5 - distance); +} + +// --- paths --- // + +struct PathSprite { + bounds: Bounds, + color: Hsla, + tile: AtlasTile, +} +var b_path_sprites: array; + +struct PathVarying { + @builtin(position) position: vec4, + @location(0) tile_position: vec2, + @location(1) color: vec4, +} + +@vertex +fn vs_path(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> PathVarying { + let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); + let sprite = b_path_sprites[instance_id]; + // Don't apply content mask because it was already accounted for when rasterizing the path. + + var out = PathVarying(); + out.position = to_device_position(unit_vertex, sprite.bounds); + out.tile_position = to_tile_position(unit_vertex, sprite.tile); + out.color = hsla_to_rgba(sprite.color); + return out; +} + +@fragment +fn fs_path(input: PathVarying) -> @location(0) vec4 { + let sample = textureSample(t_tile, s_tile, input.tile_position).r; + let mask = 1.0 - abs(1.0 - sample % 2.0); + return input.color * mask; } diff --git a/crates/gpui/src/platform/test/window.rs b/crates/gpui/src/platform/test/window.rs index b310084fc490d..f7e52f8c16e36 100644 --- a/crates/gpui/src/platform/test/window.rs +++ b/crates/gpui/src/platform/test/window.rs @@ -344,6 +344,7 @@ impl PlatformAtlas for TestAtlas { kind: crate::AtlasTextureKind::Path, }, tile_id: TileId(tile_id), + padding: 0, bounds: crate::Bounds { origin: Point::default(), size, From 04e49dc4931f8e20742dc2e80b54f1d13fdfb000 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 2 Feb 2024 22:22:18 -0800 Subject: [PATCH 20/51] blade: refactor the atlas to not own the command encoder --- crates/gpui/src/platform/linux/blade_atlas.rs | 85 ++++++++++--------- .../gpui/src/platform/linux/blade_renderer.rs | 5 ++ crates/gpui/src/platform/linux/window.rs | 4 +- 3 files changed, 53 insertions(+), 41 deletions(-) diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index 7f27bf68a4125..6f5b3da255737 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -14,14 +14,20 @@ pub(crate) const PATH_TEXTURE_FORMAT: gpu::TextureFormat = gpu::TextureFormat::R pub(crate) struct BladeAtlas(Mutex); +struct PendingUpload { + id: AtlasTextureId, + bounds: Bounds, + data: gpu::BufferPiece, +} + struct BladeAtlasState { gpu: Arc, - gpu_encoder: gpu::CommandEncoder, upload_belt: BladeBelt, monochrome_textures: Vec, polychrome_textures: Vec, path_textures: Vec, tiles_by_key: FxHashMap, + uploads: Vec, } impl BladeAtlasState { @@ -36,7 +42,6 @@ impl BladeAtlasState { self.gpu.destroy_texture(texture.raw); self.gpu.destroy_texture_view(texture.raw_view.unwrap()); } - self.gpu.destroy_command_encoder(&mut self.gpu_encoder); self.upload_belt.destroy(&self.gpu); } } @@ -50,10 +55,6 @@ impl BladeAtlas { pub(crate) fn new(gpu: &Arc) -> Self { BladeAtlas(Mutex::new(BladeAtlasState { gpu: Arc::clone(gpu), - gpu_encoder: gpu.create_command_encoder(gpu::CommandEncoderDesc { - name: "atlas", - buffer_count: 3, - }), upload_belt: BladeBelt::new(BladeBeltDescriptor { memory: gpu::Memory::Upload, min_chunk_size: 0x10000, @@ -62,6 +63,7 @@ impl BladeAtlas { polychrome_textures: Default::default(), path_textures: Default::default(), tiles_by_key: Default::default(), + uploads: Vec::new(), })) } @@ -81,22 +83,19 @@ impl BladeAtlas { } } - pub fn start_frame(&self) { + pub fn allocate(&self, size: Size, texture_kind: AtlasTextureKind) -> AtlasTile { let mut lock = self.0.lock(); - lock.gpu_encoder.start(); + lock.allocate(size, texture_kind) } - pub fn allocate(&self, size: Size, texture_kind: AtlasTextureKind) -> AtlasTile { + pub fn before_frame(&self, gpu_encoder: &mut gpu::CommandEncoder) { let mut lock = self.0.lock(); - lock.allocate(size, texture_kind) + lock.flush(gpu_encoder.transfer()); } - pub fn finish_frame(&self) -> gpu::SyncPoint { + pub fn after_frame(&self, sync_point: &gpu::SyncPoint) { let mut lock = self.0.lock(); - let gpu = lock.gpu.clone(); - let sync_point = gpu.submit(&mut lock.gpu_encoder); - lock.upload_belt.flush(&sync_point); - sync_point + lock.upload_belt.flush(sync_point); } pub fn get_texture_info(&self, id: AtlasTextureId) -> BladeTextureInfo { @@ -186,7 +185,7 @@ impl BladeAtlasState { } let raw = self.gpu.create_texture(gpu::TextureDesc { - name: "", + name: "atlas", format, size: gpu::Extent { width: size.width.into(), @@ -230,31 +229,39 @@ impl BladeAtlasState { } fn upload_texture(&mut self, id: AtlasTextureId, bounds: Bounds, bytes: &[u8]) { - let textures = match id.kind { - crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, - crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, - crate::AtlasTextureKind::Path => &self.path_textures, - }; - let texture = &textures[id.index as usize]; + let data = self.upload_belt.alloc_data(bytes, &self.gpu); + self.uploads.push(PendingUpload { id, bounds, data }); + } - let src_data = self.upload_belt.alloc_data(bytes, &self.gpu); + fn flush(&mut self, mut transfers: gpu::TransferCommandEncoder) { + for upload in self.uploads.drain(..) { + let textures = match upload.id.kind { + crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, + crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, + crate::AtlasTextureKind::Path => &self.path_textures, + }; + let texture = &textures[upload.id.index as usize]; - let mut transfers = self.gpu_encoder.transfer(); - transfers.copy_buffer_to_texture( - src_data, - bounds.size.width.to_bytes(texture.bytes_per_pixel()), - gpu::TexturePiece { - texture: texture.raw, - mip_level: 0, - array_layer: 0, - origin: [bounds.origin.x.into(), bounds.origin.y.into(), 0], - }, - gpu::Extent { - width: bounds.size.width.into(), - height: bounds.size.height.into(), - depth: 1, - }, - ); + transfers.copy_buffer_to_texture( + upload.data, + upload.bounds.size.width.to_bytes(texture.bytes_per_pixel()), + gpu::TexturePiece { + texture: texture.raw, + mip_level: 0, + array_layer: 0, + origin: [ + upload.bounds.origin.x.into(), + upload.bounds.origin.y.into(), + 0, + ], + }, + gpu::Extent { + width: upload.bounds.size.width.into(), + height: upload.bounds.size.height.into(), + depth: 1, + }, + ); + } } } diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 4d64b184f0e9e..184b607ca6357 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -285,6 +285,7 @@ impl BladeRenderer { self.command_encoder.start(); self.command_encoder.init_texture(frame.texture()); + self.atlas.before_frame(&mut self.command_encoder); self.rasterize_paths(scene.paths()); let globals = GlobalParams { @@ -365,7 +366,11 @@ impl BladeRenderer { self.command_encoder.present(frame); let sync_point = self.gpu.submit(&mut self.command_encoder); + self.instance_belt.flush(&sync_point); + self.atlas.after_frame(&sync_point); + self.atlas.clear_textures(AtlasTextureKind::Path); + self.wait_for_gpu(); self.last_sync_point = Some(sync_point); } diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 3efade0c89d52..4e136da3d6232 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -244,7 +244,7 @@ impl LinuxWindowState { pub fn configure(&self, bounds: Bounds) { let mut resize_args = None; - let mut do_move = false; + let do_move; { let mut inner = self.inner.lock(); let old_bounds = mem::replace(&mut inner.bounds, bounds); @@ -393,7 +393,7 @@ impl PlatformWindow for LinuxWindow { } fn sprite_atlas(&self) -> sync::Arc { - let mut inner = self.0.inner.lock(); + let inner = self.0.inner.lock(); inner.renderer.atlas().clone() } } From 7c7aad5e76a99ed6d20ca12d80ddf3ea750c729d Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Fri, 2 Feb 2024 23:04:56 -0800 Subject: [PATCH 21/51] blade: port underline shader --- .../gpui/src/platform/linux/blade_renderer.rs | 39 ++++++++++- crates/gpui/src/platform/linux/shaders.wgsl | 70 +++++++++++++++++-- crates/gpui/src/scene.rs | 2 +- crates/gpui/src/window/element_cx.rs | 2 +- 4 files changed, 106 insertions(+), 7 deletions(-) diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 184b607ca6357..0a143f79764e9 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -4,7 +4,7 @@ use super::{BladeBelt, BladeBeltDescriptor}; use crate::{ AtlasTextureKind, AtlasTile, BladeAtlas, Bounds, ContentMask, Hsla, Path, PathId, PathVertex, - PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, PATH_TEXTURE_FORMAT, + PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Underline, PATH_TEXTURE_FORMAT, }; use bytemuck::{Pod, Zeroable}; use collections::HashMap; @@ -48,6 +48,12 @@ struct ShaderPathsData { b_path_sprites: gpu::BufferPiece, } +#[derive(blade_macros::ShaderData)] +struct ShaderUnderlinesData { + globals: GlobalParams, + b_underlines: gpu::BufferPiece, +} + #[derive(Clone, Debug, Eq, PartialEq)] #[repr(C)] struct PathSprite { @@ -61,6 +67,7 @@ struct BladePipelines { shadows: gpu::RenderPipeline, path_rasterization: gpu::RenderPipeline, paths: gpu::RenderPipeline, + underlines: gpu::RenderPipeline, } impl BladePipelines { @@ -75,11 +82,13 @@ impl BladePipelines { shader.get_struct_size("PathVertex") as usize, ); shader.check_struct_size::(); + shader.check_struct_size::(); let quads_layout = ::layout(); let shadows_layout = ::layout(); let path_rasterization_layout = ::layout(); let paths_layout = ::layout(); + let underlines_layout = ::layout(); Self { quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc { @@ -146,6 +155,22 @@ impl BladePipelines { write_mask: gpu::ColorWrites::default(), }], }), + underlines: gpu.create_render_pipeline(gpu::RenderPipelineDesc { + name: "underlines", + data_layouts: &[&underlines_layout], + vertex: shader.at("vs_underline"), + primitive: gpu::PrimitiveState { + topology: gpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + fragment: shader.at("fs_underline"), + color_targets: &[gpu::ColorTargetState { + format: surface_format, + blend: Some(gpu::BlendState::ALPHA_BLENDING), + write_mask: gpu::ColorWrites::default(), + }], + }), } } } @@ -359,6 +384,18 @@ impl BladeRenderer { encoder.draw(0, 4, 0, sprites.len() as u32); } } + PrimitiveBatch::Underlines(underlines) => { + let instance_buf = self.instance_belt.alloc_data(underlines, &self.gpu); + let mut encoder = pass.with(&self.pipelines.underlines); + encoder.bind( + 0, + &ShaderUnderlinesData { + globals, + b_underlines: instance_buf, + }, + ); + encoder.draw(0, 4, 0, underlines.len() as u32); + } _ => continue, } } diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl index 3a7853142ad97..e0990c6c1bf19 100644 --- a/crates/gpui/src/platform/linux/shaders.wgsl +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -373,7 +373,7 @@ struct PathVarying { @vertex fn vs_path(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> PathVarying { - let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); + let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); let sprite = b_path_sprites[instance_id]; // Don't apply content mask because it was already accounted for when rasterizing the path. @@ -386,7 +386,69 @@ fn vs_path(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) insta @fragment fn fs_path(input: PathVarying) -> @location(0) vec4 { - let sample = textureSample(t_tile, s_tile, input.tile_position).r; - let mask = 1.0 - abs(1.0 - sample % 2.0); - return input.color * mask; + let sample = textureSample(t_tile, s_tile, input.tile_position).r; + let mask = 1.0 - abs(1.0 - sample % 2.0); + return input.color * mask; +} + +// --- underlines --- // + +struct Underline { + view_id: ViewId, + layer_id: u32, + order: u32, + bounds: Bounds, + content_mask: Bounds, + color: Hsla, + thickness: f32, + wavy: u32, +} +var b_underlines: array; + +struct UnderlineVarying { + @builtin(position) position: vec4, + @location(0) @interpolate(flat) color: vec4, + @location(1) @interpolate(flat) underline_id: u32, + //TODO: use `clip_distance` once Naga supports it + @location(3) clip_distances: vec4, +} + +@vertex +fn vs_underline(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> UnderlineVarying { + let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); + let underline = b_underlines[instance_id]; + + var out = UnderlineVarying(); + out.position = to_device_position(unit_vertex, underline.bounds); + out.color = hsla_to_rgba(underline.color); + out.underline_id = instance_id; + out.clip_distances = distance_from_clip_rect(unit_vertex, underline.bounds, underline.content_mask); + return out; +} + +@fragment +fn fs_underline(input: UnderlineVarying) -> @location(0) vec4 { + // Alpha clip first, since we don't have `clip_distance`. + if (any(input.clip_distances < vec4(0.0))) { + return vec4(0.0); + } + + let underline = b_underlines[input.underline_id]; + if (underline.wavy == 0u) + { + return vec4(0.0); + } + + let half_thickness = underline.thickness * 0.5; + let st = (input.position.xy - underline.bounds.origin) / underline.bounds.size.y - vec2(0.0, 0.5); + let frequency = M_PI_F * 3.0 * underline.thickness / 8.0; + let amplitude = 1.0 / (2.0 * underline.thickness); + let sine = sin(st.x * frequency) * amplitude; + let dSine = cos(st.x * frequency) * amplitude * frequency; + let distance = (st.y - sine) / sqrt(1.0 + dSine * dSine); + let distance_in_pixels = distance * underline.bounds.size.y; + let distance_from_top_border = distance_in_pixels - half_thickness; + let distance_from_bottom_border = distance_in_pixels + half_thickness; + let alpha = saturate(0.5 - max(-distance_from_bottom_border, distance_from_top_border)); + return input.color * vec4(1.0, 1.0, 1.0, alpha); } diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index f5fcade711e99..dcf3f2cbeace3 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -543,8 +543,8 @@ pub(crate) struct Underline { pub order: DrawOrder, pub bounds: Bounds, pub content_mask: ContentMask, - pub thickness: ScaledPixels, pub color: Hsla, + pub thickness: ScaledPixels, pub wavy: bool, } diff --git a/crates/gpui/src/window/element_cx.rs b/crates/gpui/src/window/element_cx.rs index cde5f17b05efa..bc38db3ab51a3 100644 --- a/crates/gpui/src/window/element_cx.rs +++ b/crates/gpui/src/window/element_cx.rs @@ -753,8 +753,8 @@ impl<'a> ElementContext<'a> { order: 0, bounds: bounds.scale(scale_factor), content_mask: content_mask.scale(scale_factor), - thickness: style.thickness.scale(scale_factor), color: style.color.unwrap_or_default(), + thickness: style.thickness.scale(scale_factor), wavy: style.wavy, }, ); From 2e32f5867ef13dfe0c1cd17e046e85e07eb5bb0f Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sat, 3 Feb 2024 00:06:20 -0800 Subject: [PATCH 22/51] linux: various fixes across the crates to make it compile --- crates/gpui/src/platform/test/text_system.rs | 4 ++-- crates/live_kit_client/Cargo.toml | 4 ++-- crates/live_kit_client/src/test.rs | 5 ++--- crates/media/src/media.rs | 1 + crates/project/src/terminals.rs | 2 +- crates/zed/src/app_menus.rs | 1 - crates/zed/src/main.rs | 3 +++ 7 files changed, 11 insertions(+), 9 deletions(-) diff --git a/crates/gpui/src/platform/test/text_system.rs b/crates/gpui/src/platform/test/text_system.rs index c72ee6f00c189..d7847f1012878 100644 --- a/crates/gpui/src/platform/test/text_system.rs +++ b/crates/gpui/src/platform/test/text_system.rs @@ -3,13 +3,13 @@ use crate::{ PlatformTextSystem, RenderGlyphParams, Size, }; use anyhow::Result; -use std::sync::Arc; +use std::borrow::Cow; pub(crate) struct TestTextSystem {} #[allow(unused)] impl PlatformTextSystem for TestTextSystem { - fn add_fonts(&self, fonts: &[Arc>]) -> Result<()> { + fn add_fonts(&self, fonts: Vec>) -> Result<()> { unimplemented!() } fn all_font_names(&self) -> Vec { diff --git a/crates/live_kit_client/Cargo.toml b/crates/live_kit_client/Cargo.toml index 3aeba54af0496..a187a17292949 100644 --- a/crates/live_kit_client/Cargo.toml +++ b/crates/live_kit_client/Cargo.toml @@ -19,7 +19,7 @@ test-support = [ "collections/test-support", "gpui/test-support", "live_kit_server", - "nanoid", + #"nanoid", ] [dependencies] @@ -32,7 +32,7 @@ gpui = { path = "../gpui", optional = true } live_kit_server = { path = "../live_kit_server", optional = true } log.workspace = true media = { path = "../media" } -nanoid = { version ="0.4", optional = true} +nanoid = "0.4" #TODO: optional parking_lot.workspace = true postage.workspace = true diff --git a/crates/live_kit_client/src/test.rs b/crates/live_kit_client/src/test.rs index 0de5bada341bf..aa2c91febb9c1 100644 --- a/crates/live_kit_client/src/test.rs +++ b/crates/live_kit_client/src/test.rs @@ -3,7 +3,7 @@ use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; use collections::{BTreeMap, HashMap, HashSet}; use futures::Stream; -use gpui::BackgroundExecutor; +use gpui::{BackgroundExecutor, ImageSource}; use live_kit_server::{proto, token}; #[cfg(target_os = "macos")] use media::core_video::CVImageBuffer; @@ -846,8 +846,7 @@ impl Frame { self.height } - #[cfg(target_os = "macos")] - pub fn image(&self) -> CVImageBuffer { + pub fn image(&self) -> ImageSource { unimplemented!("you can't call this in test mode") } } diff --git a/crates/media/src/media.rs b/crates/media/src/media.rs index 8d24e45cf215f..8757249c31940 100644 --- a/crates/media/src/media.rs +++ b/crates/media/src/media.rs @@ -8,6 +8,7 @@ use core_foundation::{ base::{CFTypeID, TCFType}, declare_TCFType, impl_CFTypeDescription, impl_TCFType, }; +#[cfg(target_os = "macos")] use std::ffi::c_void; #[cfg(target_os = "macos")] diff --git a/crates/project/src/terminals.rs b/crates/project/src/terminals.rs index 0f314f50335ee..8c5156f3cbeb2 100644 --- a/crates/project/src/terminals.rs +++ b/crates/project/src/terminals.rs @@ -124,7 +124,7 @@ impl Project { // Paths are not strings so we need to jump through some hoops to format the command without `format!` let mut command = Vec::from(activate_command.as_bytes()); command.push(b' '); - command.extend_from_slice(activate_script.as_os_str().as_bytes()); + command.extend_from_slice(activate_script.as_os_str().as_encoded_bytes()); command.push(b'\n'); terminal_handle.update(cx, |this, _| this.input_bytes(command)); diff --git a/crates/zed/src/app_menus.rs b/crates/zed/src/app_menus.rs index fc063a620f18a..40d20d61ef309 100644 --- a/crates/zed/src/app_menus.rs +++ b/crates/zed/src/app_menus.rs @@ -1,6 +1,5 @@ use gpui::{Menu, MenuItem, OsAction}; -#[cfg(target_os = "macos")] pub fn app_menus() -> Vec> { use zed_actions::Quit; diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 1666b23f07a76..1ae1feffde601 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -11,6 +11,7 @@ use db::kvp::KEY_VALUE_STORE; use editor::Editor; use env_logger::Builder; use fs::RealFs; +#[cfg(target_os = "macos")] use fsevent::StreamFlags; use futures::StreamExt; use gpui::{App, AppContext, AsyncAppContext, Context, SemanticVersion, Task}; @@ -173,6 +174,7 @@ fn main() { assistant::init(cx); load_user_themes_in_background(fs.clone(), cx); + #[cfg(target_os = "macos")] watch_themes(fs.clone(), cx); cx.spawn(|_| watch_languages(fs.clone(), languages.clone())) @@ -916,6 +918,7 @@ fn load_user_themes_in_background(fs: Arc, cx: &mut AppContext) { } /// Spawns a background task to watch the themes directory for changes. +#[cfg(target_os = "macos")] fn watch_themes(fs: Arc, cx: &mut AppContext) { cx.spawn(|cx| async move { let mut events = fs From 59642bf29a1763898d0a48f8a771caa26e94eae0 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sat, 3 Feb 2024 14:13:36 -0800 Subject: [PATCH 23/51] blade: point to master, remove the override --- Cargo.toml | 5 ----- crates/gpui/Cargo.toml | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 019cb3f356d24..26cd9ab0393bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -181,11 +181,6 @@ wasmtime = "16" tree-sitter = { git = "https://github.com/tree-sitter/tree-sitter", rev = "1d8975319c2d5de1bf710e7e21db25b0eee4bc66" } wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "v16.0.0" } -# TODO - Remove when corresponding Blade versions are published -[patch."https://github.com/kvark/blade"] -blade-graphics = { path = "/x/Code/blade/blade-graphics" } -blade-macros = { path = "/x/Code/blade/blade-macros" } - [profile.dev] split-debuginfo = "unpacked" debug = "limited" diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 6dd64752cc80c..9a6530eeefecd 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -26,8 +26,8 @@ anyhow.workspace = true async-task = "4.7" backtrace = { version = "0.3", optional = true } bitflags = "2.4.0" -blade-graphics = { git = "https://github.com/kvark/blade", branch = "zed" } -blade-macros = { git = "https://github.com/kvark/blade", branch = "zed" } +blade-graphics = { git = "https://github.com/kvark/blade", rev = "62eb18d312f720a5aac8f508fe223146a24fc7f0" } +blade-macros = { git = "https://github.com/kvark/blade", rev = "62eb18d312f720a5aac8f508fe223146a24fc7f0" } bytemuck = "1" collections = { path = "../collections" } ctor.workspace = true From d0a0ce18851c0d50e12d309d3a5bfe23e0fbe06b Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sat, 3 Feb 2024 21:30:47 -0800 Subject: [PATCH 24/51] blade: mono/poly chrome sprite rendering --- .../gpui/src/platform/linux/blade_renderer.rs | 114 +++++++++++++++--- crates/gpui/src/platform/linux/shaders.wgsl | 113 ++++++++++++++++- crates/gpui/src/scene.rs | 1 + crates/gpui/src/window/element_cx.rs | 2 + 4 files changed, 208 insertions(+), 22 deletions(-) diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 0a143f79764e9..7ac3acda19d0d 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -4,7 +4,7 @@ use super::{BladeBelt, BladeBeltDescriptor}; use crate::{ AtlasTextureKind, AtlasTile, BladeAtlas, Bounds, ContentMask, Hsla, Path, PathId, PathVertex, - PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Underline, PATH_TEXTURE_FORMAT, + PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Underline, MonochromeSprite, PolychromeSprite, PATH_TEXTURE_FORMAT, }; use bytemuck::{Pod, Zeroable}; use collections::HashMap; @@ -43,8 +43,8 @@ struct ShaderPathRasterizationData { #[derive(blade_macros::ShaderData)] struct ShaderPathsData { globals: GlobalParams, - t_tile: gpu::TextureView, - s_tile: gpu::Sampler, + t_sprite: gpu::TextureView, + s_sprite: gpu::Sampler, b_path_sprites: gpu::BufferPiece, } @@ -54,6 +54,22 @@ struct ShaderUnderlinesData { b_underlines: gpu::BufferPiece, } +#[derive(blade_macros::ShaderData)] +struct ShaderMonoSpritesData { + globals: GlobalParams, + t_sprite: gpu::TextureView, + s_sprite: gpu::Sampler, + b_mono_sprites: gpu::BufferPiece, +} + +#[derive(blade_macros::ShaderData)] +struct ShaderPolySpritesData { + globals: GlobalParams, + t_sprite: gpu::TextureView, + s_sprite: gpu::Sampler, + b_poly_sprites: gpu::BufferPiece, +} + #[derive(Clone, Debug, Eq, PartialEq)] #[repr(C)] struct PathSprite { @@ -68,10 +84,14 @@ struct BladePipelines { path_rasterization: gpu::RenderPipeline, paths: gpu::RenderPipeline, underlines: gpu::RenderPipeline, + mono_sprites: gpu::RenderPipeline, + poly_sprites: gpu::RenderPipeline, } impl BladePipelines { fn new(gpu: &gpu::Context, surface_format: gpu::TextureFormat) -> Self { + use gpu::ShaderData as _; + let shader = gpu.create_shader(gpu::ShaderDesc { source: include_str!("shaders.wgsl"), }); @@ -83,17 +103,13 @@ impl BladePipelines { ); shader.check_struct_size::(); shader.check_struct_size::(); - - let quads_layout = ::layout(); - let shadows_layout = ::layout(); - let path_rasterization_layout = ::layout(); - let paths_layout = ::layout(); - let underlines_layout = ::layout(); + shader.check_struct_size::(); + shader.check_struct_size::(); Self { quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc { name: "quads", - data_layouts: &[&quads_layout], + data_layouts: &[&ShaderQuadsData::layout()], vertex: shader.at("vs_quad"), primitive: gpu::PrimitiveState { topology: gpu::PrimitiveTopology::TriangleStrip, @@ -109,7 +125,7 @@ impl BladePipelines { }), shadows: gpu.create_render_pipeline(gpu::RenderPipelineDesc { name: "shadows", - data_layouts: &[&shadows_layout], + data_layouts: &[&ShaderShadowsData::layout()], vertex: shader.at("vs_shadow"), primitive: gpu::PrimitiveState { topology: gpu::PrimitiveTopology::TriangleStrip, @@ -125,7 +141,7 @@ impl BladePipelines { }), path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc { name: "path_rasterization", - data_layouts: &[&path_rasterization_layout], + data_layouts: &[&ShaderPathRasterizationData::layout()], vertex: shader.at("vs_path_rasterization"), primitive: gpu::PrimitiveState { topology: gpu::PrimitiveTopology::TriangleStrip, @@ -141,7 +157,7 @@ impl BladePipelines { }), paths: gpu.create_render_pipeline(gpu::RenderPipelineDesc { name: "paths", - data_layouts: &[&paths_layout], + data_layouts: &[&ShaderPathsData::layout()], vertex: shader.at("vs_path"), primitive: gpu::PrimitiveState { topology: gpu::PrimitiveTopology::TriangleStrip, @@ -157,7 +173,7 @@ impl BladePipelines { }), underlines: gpu.create_render_pipeline(gpu::RenderPipelineDesc { name: "underlines", - data_layouts: &[&underlines_layout], + data_layouts: &[&ShaderUnderlinesData::layout()], vertex: shader.at("vs_underline"), primitive: gpu::PrimitiveState { topology: gpu::PrimitiveTopology::TriangleStrip, @@ -171,6 +187,38 @@ impl BladePipelines { write_mask: gpu::ColorWrites::default(), }], }), + mono_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc { + name: "mono-sprites", + data_layouts: &[&ShaderMonoSpritesData::layout()], + vertex: shader.at("vs_mono_sprite"), + primitive: gpu::PrimitiveState { + topology: gpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + fragment: shader.at("fs_mono_sprite"), + color_targets: &[gpu::ColorTargetState { + format: surface_format, + blend: Some(gpu::BlendState::ALPHA_BLENDING), + write_mask: gpu::ColorWrites::default(), + }], + }), + poly_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc { + name: "poly-sprites", + data_layouts: &[&ShaderPolySpritesData::layout()], + vertex: shader.at("vs_poly_sprite"), + primitive: gpu::PrimitiveState { + topology: gpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + fragment: shader.at("fs_poly_sprite"), + color_targets: &[gpu::ColorTargetState { + format: surface_format, + blend: Some(gpu::BlendState::ALPHA_BLENDING), + write_mask: gpu::ColorWrites::default(), + }], + }), } } } @@ -376,8 +424,8 @@ impl BladeRenderer { 0, &ShaderPathsData { globals, - t_tile: tex_info.raw_view.unwrap(), - s_tile: self.atlas_sampler, + t_sprite: tex_info.raw_view.unwrap(), + s_sprite: self.atlas_sampler, b_path_sprites: instance_buf, }, ); @@ -396,7 +444,39 @@ impl BladeRenderer { ); encoder.draw(0, 4, 0, underlines.len() as u32); } - _ => continue, + PrimitiveBatch::MonochromeSprites { texture_id, sprites } => { + let tex_info = self.atlas.get_texture_info(texture_id); + let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu); + let mut encoder = pass.with(&self.pipelines.mono_sprites); + encoder.bind( + 0, + &ShaderMonoSpritesData { + globals, + t_sprite: tex_info.raw_view.unwrap(), + s_sprite: self.atlas_sampler, + b_mono_sprites: instance_buf, + }, + ); + encoder.draw(0, 4, 0, sprites.len() as u32); + } + PrimitiveBatch::PolychromeSprites { texture_id, sprites } => { + let tex_info = self.atlas.get_texture_info(texture_id); + let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu); + let mut encoder = pass.with(&self.pipelines.poly_sprites); + encoder.bind( + 0, + &ShaderPolySpritesData { + globals, + t_sprite: tex_info.raw_view.unwrap(), + s_sprite: self.atlas_sampler, + b_poly_sprites: instance_buf, + }, + ); + encoder.draw(0, 4, 0, sprites.len() as u32); + } + PrimitiveBatch::Surfaces {..} => { + unimplemented!() + } } } } diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl index e0990c6c1bf19..7d0c9d53aee38 100644 --- a/crates/gpui/src/platform/linux/shaders.wgsl +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -4,10 +4,11 @@ struct Globals { } var globals: Globals; -var t_tile: texture_2d; -var s_tile: sampler; +var t_sprite: texture_2d; +var s_sprite: sampler; const M_PI_F: f32 = 3.1415926; +const GRAYSCALE_FACTORS: vec3 = vec3(0.2126, 0.7152, 0.0722); struct ViewId { lo: u32, @@ -60,7 +61,7 @@ fn to_device_position(unit_vertex: vec2, bounds: Bounds) -> vec4 { } fn to_tile_position(unit_vertex: vec2, tile: AtlasTile) -> vec2 { - let atlas_size = vec2(textureDimensions(t_tile, 0)); + let atlas_size = vec2(textureDimensions(t_sprite, 0)); return (tile.bounds.origin + unit_vertex * tile.bounds.size) / atlas_size; } @@ -153,6 +154,17 @@ fn pick_corner_radius(point: vec2, radii: Corners) -> f32 { } } +fn quad_sdf(point: vec2, bounds: Bounds, corner_radii: Corners) -> f32 { + let half_size = bounds.size / 2.0; + let center = bounds.origin + half_size; + let center_to_point = point - center; + let corner_radius = pick_corner_radius(center_to_point, corner_radii); + let rounded_edge_to_point = abs(center_to_point) - half_size + corner_radius; + return length(max(vec2(0.0), rounded_edge_to_point)) + + min(0.0, max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - + corner_radius; +} + // --- quads --- // struct Quad { @@ -386,7 +398,7 @@ fn vs_path(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) insta @fragment fn fs_path(input: PathVarying) -> @location(0) vec4 { - let sample = textureSample(t_tile, s_tile, input.tile_position).r; + let sample = textureSample(t_sprite, s_sprite, input.tile_position).r; let mask = 1.0 - abs(1.0 - sample % 2.0); return input.color * mask; } @@ -434,7 +446,7 @@ fn fs_underline(input: UnderlineVarying) -> @location(0) vec4 { } let underline = b_underlines[input.underline_id]; - if (underline.wavy == 0u) + if ((underline.wavy & 0xFFu) == 0u) { return vec4(0.0); } @@ -452,3 +464,94 @@ fn fs_underline(input: UnderlineVarying) -> @location(0) vec4 { let alpha = saturate(0.5 - max(-distance_from_bottom_border, distance_from_top_border)); return input.color * vec4(1.0, 1.0, 1.0, alpha); } + +// --- monochrome sprites --- // + +struct MonochromeSprite { + view_id: ViewId, + layer_id: u32, + order: u32, + bounds: Bounds, + content_mask: Bounds, + color: Hsla, + tile: AtlasTile, +} +var b_mono_sprites: array; + +struct MonoSpriteVarying { + @builtin(position) position: vec4, + @location(0) tile_position: vec2, + @location(1) @interpolate(flat) color: vec4, + @location(3) clip_distances: vec4, +} + +@vertex +fn vs_mono_sprite(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> MonoSpriteVarying { + let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); + let sprite = b_mono_sprites[instance_id]; + + var out = MonoSpriteVarying(); + out.position = to_device_position(unit_vertex, sprite.bounds); + out.tile_position = to_tile_position(unit_vertex, sprite.tile); + out.color = hsla_to_rgba(sprite.color); + out.clip_distances = distance_from_clip_rect(unit_vertex, sprite.bounds, sprite.content_mask); + return out; +} + +@fragment +fn fs_mono_sprite(input: MonoSpriteVarying) -> @location(0) vec4 { + let sample = textureSample(t_sprite, s_sprite, input.tile_position).r; + return input.color * vec4(1.0, 1.0, 1.0, sample); +} + +// --- polychrome sprites --- // + +struct PolychromeSprite { + view_id: ViewId, + layer_id: u32, + order: u32, + bounds: Bounds, + content_mask: Bounds, + corner_radii: Corners, + tile: AtlasTile, + grayscale: u32, + pad: u32, +} +var b_poly_sprites: array; + +struct PolySpriteVarying { + @builtin(position) position: vec4, + @location(0) tile_position: vec2, + @location(1) @interpolate(flat) sprite_id: u32, + @location(3) clip_distances: vec4, +} + +@vertex +fn vs_poly_sprite(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> PolySpriteVarying { + let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); + let sprite = b_poly_sprites[instance_id]; + + var out = PolySpriteVarying(); + out.position = to_device_position(unit_vertex, sprite.bounds); + out.tile_position = to_tile_position(unit_vertex, sprite.tile); + out.sprite_id = instance_id; + out.clip_distances = distance_from_clip_rect(unit_vertex, sprite.bounds, sprite.content_mask); + return out; +} + +@fragment +fn fs_poly_sprite(input: PolySpriteVarying) -> @location(0) vec4 { + let sample = textureSample(t_sprite, s_sprite, input.tile_position); + let sprite = b_poly_sprites[input.sprite_id]; + let distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii); + + var color = sample; + if ((sprite.grayscale & 0xFFu) != 0u) { + let grayscale = dot(color.rgb, GRAYSCALE_FACTORS); + color = vec4(vec3(grayscale), sample.a); + } + color.a *= saturate(0.5 - distance); + return color;; +} + +// --- surface sprites --- // diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index dcf3f2cbeace3..4b16e2075f481 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -642,6 +642,7 @@ pub(crate) struct PolychromeSprite { pub corner_radii: Corners, pub tile: AtlasTile, pub grayscale: bool, + pub pad: u32, // align to 8 bytes } impl Ord for PolychromeSprite { diff --git a/crates/gpui/src/window/element_cx.rs b/crates/gpui/src/window/element_cx.rs index bc38db3ab51a3..8b321b337763d 100644 --- a/crates/gpui/src/window/element_cx.rs +++ b/crates/gpui/src/window/element_cx.rs @@ -874,6 +874,7 @@ impl<'a> ElementContext<'a> { content_mask, tile, grayscale: false, + pad: 0, }, ); } @@ -958,6 +959,7 @@ impl<'a> ElementContext<'a> { corner_radii, tile, grayscale, + pad: 0, }, ); Ok(()) From cf71fe8bf1945f51d96c977cf717ede69b33c63b Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sat, 3 Feb 2024 22:39:44 -0800 Subject: [PATCH 25/51] fix MacOS build, switch external RWH to 0.6 leaving blade-internal RWH as 0.5 until this is fixed: https://github.com/ash-rs/ash/issues/864 --- crates/gpui/Cargo.toml | 3 ++- crates/gpui/build.rs | 24 ++++++++++++------- .../gpui/src/platform/linux/blade_renderer.rs | 17 +++++++++---- crates/gpui/src/platform/linux/window.rs | 24 +++++++++++-------- crates/gpui/src/platform/mac/metal_atlas.rs | 1 + 5 files changed, 44 insertions(+), 25 deletions(-) diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 9a6530eeefecd..cc14ee4dfed64 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -50,7 +50,8 @@ parking_lot.workspace = true pathfinder_geometry = "0.5" postage.workspace = true rand.workspace = true -raw-window-handle = "0.5.0" +raw-window-handle = "0.6" +blade-rwh = { package = "raw-window-handle", version = "0.5" } refineable.workspace = true resvg = "0.14" schemars.workspace = true diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 70c3d8d8e43de..802185ac85360 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(target_os = "macos"), allow(unused))] + use std::{ env, path::{Path, PathBuf}, @@ -6,15 +8,19 @@ use std::{ use cbindgen::Config; fn main() { - //generate_dispatch_bindings(); - //let header_path = generate_shader_bindings(); - //#[cfg(feature = "runtime_shaders")] - //emit_stitched_shaders(&header_path); - //#[cfg(not(feature = "runtime_shaders"))] - //compile_metal_shaders(&header_path); + #[cfg(target_os = "macos")] + generate_dispatch_bindings(); + #[cfg(target_os = "macos")] + let header_path = generate_shader_bindings(); + #[cfg(target_os = "macos")] + #[cfg(feature = "runtime_shaders")] + emit_stitched_shaders(&header_path); + #[cfg(target_os = "macos")] + #[cfg(not(feature = "runtime_shaders"))] + compile_metal_shaders(&header_path); } -fn _generate_dispatch_bindings() { +fn generate_dispatch_bindings() { println!("cargo:rustc-link-lib=framework=System"); println!("cargo:rerun-if-changed=src/platform/mac/dispatch.h"); @@ -38,7 +44,7 @@ fn _generate_dispatch_bindings() { .expect("couldn't write dispatch bindings"); } -fn _generate_shader_bindings() -> PathBuf { +fn generate_shader_bindings() -> PathBuf { let output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("scene.h"); let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); let mut config = Config::default(); @@ -116,7 +122,7 @@ fn emit_stitched_shaders(header_path: &Path) { println!("cargo:rerun-if-changed={}", &shader_source_path); } #[cfg(not(feature = "runtime_shaders"))] -fn _compile_metal_shaders(header_path: &Path) { +fn compile_metal_shaders(header_path: &Path) { use std::process::{self, Command}; let shader_path = "./src/platform/mac/shaders.metal"; let air_output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("shaders.air"); diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 7ac3acda19d0d..1b7db6bcd4fb9 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -3,8 +3,9 @@ use super::{BladeBelt, BladeBeltDescriptor}; use crate::{ - AtlasTextureKind, AtlasTile, BladeAtlas, Bounds, ContentMask, Hsla, Path, PathId, PathVertex, - PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Underline, MonochromeSprite, PolychromeSprite, PATH_TEXTURE_FORMAT, + AtlasTextureKind, AtlasTile, BladeAtlas, Bounds, ContentMask, Hsla, MonochromeSprite, Path, + PathId, PathVertex, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, + Underline, PATH_TEXTURE_FORMAT, }; use bytemuck::{Pod, Zeroable}; use collections::HashMap; @@ -444,7 +445,10 @@ impl BladeRenderer { ); encoder.draw(0, 4, 0, underlines.len() as u32); } - PrimitiveBatch::MonochromeSprites { texture_id, sprites } => { + PrimitiveBatch::MonochromeSprites { + texture_id, + sprites, + } => { let tex_info = self.atlas.get_texture_info(texture_id); let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu); let mut encoder = pass.with(&self.pipelines.mono_sprites); @@ -459,7 +463,10 @@ impl BladeRenderer { ); encoder.draw(0, 4, 0, sprites.len() as u32); } - PrimitiveBatch::PolychromeSprites { texture_id, sprites } => { + PrimitiveBatch::PolychromeSprites { + texture_id, + sprites, + } => { let tex_info = self.atlas.get_texture_info(texture_id); let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu); let mut encoder = pass.with(&self.pipelines.poly_sprites); @@ -474,7 +481,7 @@ impl BladeRenderer { ); encoder.draw(0, 4, 0, sprites.len() as u32); } - PrimitiveBatch::Surfaces {..} => { + PrimitiveBatch::Surfaces { .. } => { unimplemented!() } } diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 4e136da3d6232..3818ec2c657a4 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -9,6 +9,8 @@ use raw_window_handle as rwh; use std::{ ffi::c_void, mem, + num::NonZeroU32, + ptr::NonNull, rc::Rc, sync::{self, Arc}, }; @@ -71,17 +73,17 @@ pub(crate) struct LinuxWindowState { #[derive(Clone)] pub(crate) struct LinuxWindow(pub(crate) Arc); -unsafe impl rwh::HasRawWindowHandle for RawWindow { - fn raw_window_handle(&self) -> rwh::RawWindowHandle { - let mut wh = rwh::XcbWindowHandle::empty(); +unsafe impl blade_rwh::HasRawWindowHandle for RawWindow { + fn raw_window_handle(&self) -> blade_rwh::RawWindowHandle { + let mut wh = blade_rwh::XcbWindowHandle::empty(); wh.window = self.window_id; wh.visual_id = self.visual_id; wh.into() } } -unsafe impl rwh::HasRawDisplayHandle for RawWindow { - fn raw_display_handle(&self) -> rwh::RawDisplayHandle { - let mut dh = rwh::XcbDisplayHandle::empty(); +unsafe impl blade_rwh::HasRawDisplayHandle for RawWindow { + fn raw_display_handle(&self) -> blade_rwh::RawDisplayHandle { + let mut dh = blade_rwh::XcbDisplayHandle::empty(); dh.connection = self.connection; dh.screen = self.screen_id; dh.into() @@ -91,16 +93,18 @@ unsafe impl rwh::HasRawDisplayHandle for RawWindow { impl rwh::HasWindowHandle for LinuxWindow { fn window_handle(&self) -> Result { Ok(unsafe { - let raw_handle = rwh::HasRawWindowHandle::raw_window_handle(&self.0.raw); - rwh::WindowHandle::borrow_raw(raw_handle, rwh::ActiveHandle::new()) + let non_zero = NonZeroU32::new(self.0.raw.window_id).unwrap(); + let handle = rwh::XcbWindowHandle::new(non_zero); + rwh::WindowHandle::borrow_raw(handle.into()) }) } } impl rwh::HasDisplayHandle for LinuxWindow { fn display_handle(&self) -> Result { Ok(unsafe { - let raw_handle = rwh::HasRawDisplayHandle::raw_display_handle(&self.0.raw); - rwh::DisplayHandle::borrow_raw(raw_handle) + let non_zero = NonNull::new(self.0.raw.connection).unwrap(); + let handle = rwh::XcbDisplayHandle::new(Some(non_zero), self.0.raw.screen_id); + rwh::DisplayHandle::borrow_raw(handle.into()) }) } } diff --git a/crates/gpui/src/platform/mac/metal_atlas.rs b/crates/gpui/src/platform/mac/metal_atlas.rs index 95f78a446539d..7c23fafcba131 100644 --- a/crates/gpui/src/platform/mac/metal_atlas.rs +++ b/crates/gpui/src/platform/mac/metal_atlas.rs @@ -174,6 +174,7 @@ impl MetalAtlasTexture { origin: allocation.rectangle.min.into(), size, }, + padding: 0, }; Some(tile) } From c5ff46e14f8d3510a67fdc40f0c85c6940326bd5 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sat, 3 Feb 2024 23:50:17 -0800 Subject: [PATCH 26/51] blade: fix shadow vertex bounds --- crates/gpui/src/platform/linux/shaders.wgsl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl index 7d0c9d53aee38..05174c0d7f7fe 100644 --- a/crates/gpui/src/platform/linux/shaders.wgsl +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -276,14 +276,13 @@ struct ShadowVarying { @vertex fn vs_shadow(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> ShadowVarying { let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); - let shadow = b_shadows[instance_id]; + var shadow = b_shadows[instance_id]; let margin = 3.0 * shadow.blur_radius; // Set the bounds of the shadow and adjust its size based on the shadow's // spread radius to achieve the spreading effect - var bounds = shadow.bounds; - bounds.origin -= vec2(margin); - bounds.size += 2.0 * vec2(margin); + shadow.bounds.origin -= vec2(margin); + shadow.bounds.size += 2.0 * vec2(margin); var out = ShadowVarying(); out.position = to_device_position(unit_vertex, shadow.bounds); From d6bbcf503b7c687a909d74ec6f2c47c78c71a8cb Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 4 Feb 2024 00:09:32 -0800 Subject: [PATCH 27/51] blade: enforce alignment in the belt --- crates/gpui/src/platform/linux/blade_atlas.rs | 1 + crates/gpui/src/platform/linux/blade_belt.rs | 23 +++++++++++-------- .../gpui/src/platform/linux/blade_renderer.rs | 1 + 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index 6f5b3da255737..f664694508383 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -58,6 +58,7 @@ impl BladeAtlas { upload_belt: BladeBelt::new(BladeBeltDescriptor { memory: gpu::Memory::Upload, min_chunk_size: 0x10000, + alignment: 4, }), monochrome_textures: Default::default(), polychrome_textures: Default::default(), diff --git a/crates/gpui/src/platform/linux/blade_belt.rs b/crates/gpui/src/platform/linux/blade_belt.rs index 17f100beb2a3e..b0e4be589398e 100644 --- a/crates/gpui/src/platform/linux/blade_belt.rs +++ b/crates/gpui/src/platform/linux/blade_belt.rs @@ -9,6 +9,7 @@ struct ReusableBuffer { pub struct BladeBeltDescriptor { pub memory: gpu::Memory, pub min_chunk_size: u64, + pub alignment: u64, } /// A belt of buffers, used by the BladeAtlas to cheaply @@ -21,6 +22,7 @@ pub struct BladeBelt { impl BladeBelt { pub fn new(desc: BladeBeltDescriptor) -> Self { + assert_ne!(desc.alignment, 0); Self { desc, buffers: Vec::new(), @@ -39,9 +41,10 @@ impl BladeBelt { pub fn alloc(&mut self, size: u64, gpu: &gpu::Context) -> gpu::BufferPiece { for &mut (ref rb, ref mut offset) in self.active.iter_mut() { - if *offset + size <= rb.size { - let piece = rb.raw.at(*offset); - *offset += size; + let aligned = offset.next_multiple_of(self.desc.alignment); + if aligned + size <= rb.size { + let piece = rb.raw.at(aligned); + *offset = aligned + size; return piece; } } @@ -75,13 +78,15 @@ impl BladeBelt { //Note: assuming T: bytemuck::Zeroable pub fn alloc_data(&mut self, data: &[T], gpu: &gpu::Context) -> gpu::BufferPiece { assert!(!data.is_empty()); - let alignment = mem::align_of::() as u64; + let type_alignment = mem::align_of::() as u64; + assert_eq!( + self.desc.alignment % type_alignment, + 0, + "Type alignment {} is too big", + type_alignment + ); let total_bytes = data.len() * mem::size_of::(); - let mut bp = self.alloc(alignment + (total_bytes - 1) as u64, gpu); - let rem = bp.offset % alignment; - if rem != 0 { - bp.offset += alignment - rem; - } + let bp = self.alloc(total_bytes as u64, gpu); unsafe { std::ptr::copy_nonoverlapping(data.as_ptr() as *const u8, bp.data(), total_bytes); } diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 1b7db6bcd4fb9..0e6a9f980a235 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -251,6 +251,7 @@ impl BladeRenderer { let instance_belt = BladeBelt::new(BladeBeltDescriptor { memory: gpu::Memory::Shared, min_chunk_size: 0x1000, + alignment: 0x100, // required by DX12 }); let atlas = Arc::new(BladeAtlas::new(&gpu)); let atlas_sampler = gpu.create_sampler(gpu::SamplerDesc { From 13ba8b6b54fac0324e86cd707cc8f2ccd850cfad Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 4 Feb 2024 11:34:57 -0800 Subject: [PATCH 28/51] Fix linux target in rust-toolchain.toml Co-authored-by: Ilia <43654815+istudyatuni@users.noreply.github.com> --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index af32046cb18b7..cda382660135d 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,4 +2,4 @@ channel = "1.75" profile = "minimal" components = [ "rustfmt", "clippy" ] -targets = [ "x86_64-apple-darwin", "aarch64-apple-darwin", "x86_64-linux-gnu", "wasm32-wasi" ] +targets = [ "x86_64-apple-darwin", "aarch64-apple-darwin", "x86_64-unknown-linux-gnu", "wasm32-wasi" ] From aae532987f003b1850c8ff9e7a5518f02bed1657 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 4 Feb 2024 10:18:00 -0800 Subject: [PATCH 29/51] blade: tune belt alignment to match Intel Iris Xe --- crates/gpui/src/platform/linux/blade_atlas.rs | 2 +- crates/gpui/src/platform/linux/blade_renderer.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index f664694508383..2abb28fd64787 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -58,7 +58,7 @@ impl BladeAtlas { upload_belt: BladeBelt::new(BladeBeltDescriptor { memory: gpu::Memory::Upload, min_chunk_size: 0x10000, - alignment: 4, + alignment: 64, // Vulkan `optimalBufferCopyOffsetAlignment` on Intel XE }), monochrome_textures: Default::default(), polychrome_textures: Default::default(), diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 0e6a9f980a235..05cb84850d92f 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -251,7 +251,7 @@ impl BladeRenderer { let instance_belt = BladeBelt::new(BladeBeltDescriptor { memory: gpu::Memory::Shared, min_chunk_size: 0x1000, - alignment: 0x100, // required by DX12 + alignment: 0x40, // Vulkan `minStorageBufferOffsetAlignment` on Intel Xe }); let atlas = Arc::new(BladeAtlas::new(&gpu)); let atlas_sampler = gpu.create_sampler(gpu::SamplerDesc { From 61fa5e93a8e9f73d585626ebb67af329332d4ea7 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 4 Feb 2024 12:15:41 -0800 Subject: [PATCH 30/51] blade: always create texture views for atlas tiles --- crates/gpui/src/platform/linux/blade_atlas.rs | 40 +++++++++---------- .../gpui/src/platform/linux/blade_renderer.rs | 8 ++-- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index 2abb28fd64787..949599d4fda63 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -32,15 +32,14 @@ struct BladeAtlasState { impl BladeAtlasState { fn destroy(&mut self) { - for texture in self.monochrome_textures.drain(..) { - self.gpu.destroy_texture(texture.raw); + for mut texture in self.monochrome_textures.drain(..) { + texture.destroy(&self.gpu); } - for texture in self.polychrome_textures.drain(..) { - self.gpu.destroy_texture(texture.raw); + for mut texture in self.polychrome_textures.drain(..) { + texture.destroy(&self.gpu); } - for texture in self.path_textures.drain(..) { - self.gpu.destroy_texture(texture.raw); - self.gpu.destroy_texture_view(texture.raw_view.unwrap()); + for mut texture in self.path_textures.drain(..) { + texture.destroy(&self.gpu); } self.upload_belt.destroy(&self.gpu); } @@ -48,7 +47,7 @@ impl BladeAtlasState { pub struct BladeTextureInfo { pub size: gpu::Extent, - pub raw_view: Option, + pub raw_view: gpu::TextureView, } impl BladeAtlas { @@ -198,17 +197,13 @@ impl BladeAtlasState { dimension: gpu::TextureDimension::D2, usage, }); - let raw_view = if usage.contains(gpu::TextureUsage::TARGET) { - Some(self.gpu.create_texture_view(gpu::TextureViewDesc { - name: "", - texture: raw, - format, - dimension: gpu::ViewDimension::D2, - subresources: &Default::default(), - })) - } else { - None - }; + let raw_view = self.gpu.create_texture_view(gpu::TextureViewDesc { + name: "", + texture: raw, + format, + dimension: gpu::ViewDimension::D2, + subresources: &Default::default(), + }); let textures = match kind { AtlasTextureKind::Monochrome => &mut self.monochrome_textures, @@ -270,7 +265,7 @@ struct BladeAtlasTexture { id: AtlasTextureId, allocator: BucketedAtlasAllocator, raw: gpu::Texture, - raw_view: Option, + raw_view: gpu::TextureView, format: gpu::TextureFormat, } @@ -293,6 +288,11 @@ impl BladeAtlasTexture { Some(tile) } + fn destroy(&mut self, gpu: &gpu::Context) { + gpu.destroy_texture(self.raw); + gpu.destroy_texture_view(self.raw_view); + } + fn bytes_per_pixel(&self) -> u8 { self.format.block_info().size } diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 05cb84850d92f..1e3853b4752e4 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -336,7 +336,7 @@ impl BladeRenderer { let vertex_buf = self.instance_belt.alloc_data(&vertices, &self.gpu); let mut pass = self.command_encoder.render(gpu::RenderTargetSet { colors: &[gpu::RenderTarget { - view: tex_info.raw_view.unwrap(), + view: tex_info.raw_view, init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack), finish_op: gpu::FinishOp::Store, }], @@ -426,7 +426,7 @@ impl BladeRenderer { 0, &ShaderPathsData { globals, - t_sprite: tex_info.raw_view.unwrap(), + t_sprite: tex_info.raw_view, s_sprite: self.atlas_sampler, b_path_sprites: instance_buf, }, @@ -457,7 +457,7 @@ impl BladeRenderer { 0, &ShaderMonoSpritesData { globals, - t_sprite: tex_info.raw_view.unwrap(), + t_sprite: tex_info.raw_view, s_sprite: self.atlas_sampler, b_mono_sprites: instance_buf, }, @@ -475,7 +475,7 @@ impl BladeRenderer { 0, &ShaderPolySpritesData { globals, - t_sprite: tex_info.raw_view.unwrap(), + t_sprite: tex_info.raw_view, s_sprite: self.atlas_sampler, b_poly_sprites: instance_buf, }, From 26ca79870750790caf842126cdce4316754a3fde Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 4 Feb 2024 12:45:18 -0800 Subject: [PATCH 31/51] blade: initialize atlas textures --- crates/gpui/src/platform/linux/blade_atlas.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index 949599d4fda63..ca57b4cb01621 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -27,6 +27,7 @@ struct BladeAtlasState { polychrome_textures: Vec, path_textures: Vec, tiles_by_key: FxHashMap, + initializations: Vec, uploads: Vec, } @@ -63,6 +64,7 @@ impl BladeAtlas { polychrome_textures: Default::default(), path_textures: Default::default(), tiles_by_key: Default::default(), + initializations: Vec::new(), uploads: Vec::new(), })) } @@ -90,7 +92,7 @@ impl BladeAtlas { pub fn before_frame(&self, gpu_encoder: &mut gpu::CommandEncoder) { let mut lock = self.0.lock(); - lock.flush(gpu_encoder.transfer()); + lock.flush(gpu_encoder); } pub fn after_frame(&self, sync_point: &gpu::SyncPoint) { @@ -220,6 +222,8 @@ impl BladeAtlasState { raw, raw_view, }; + + self.initializations.push(atlas_texture.id); textures.push(atlas_texture); textures.last_mut().unwrap() } @@ -229,7 +233,18 @@ impl BladeAtlasState { self.uploads.push(PendingUpload { id, bounds, data }); } - fn flush(&mut self, mut transfers: gpu::TransferCommandEncoder) { + fn flush(&mut self, encoder: &mut gpu::CommandEncoder) { + for id in self.initializations.drain(..) { + let textures = match id.kind { + crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, + crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, + crate::AtlasTextureKind::Path => &self.path_textures, + }; + let texture = &textures[id.index as usize]; + encoder.init_texture(texture.raw); + } + + let mut transfers = encoder.transfer(); for upload in self.uploads.drain(..) { let textures = match upload.id.kind { crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, From 0a5ebee9e560fdf4e508ec5caefad6e6ef804626 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 4 Feb 2024 13:03:16 -0800 Subject: [PATCH 32/51] blade: encapsulate BladeAtlasStorage for indexing --- crates/gpui/src/platform/linux/blade_atlas.rs | 114 ++++++++++-------- 1 file changed, 64 insertions(+), 50 deletions(-) diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index ca57b4cb01621..188a554dbe39d 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -8,7 +8,7 @@ use blade_graphics as gpu; use collections::FxHashMap; use etagere::BucketedAtlasAllocator; use parking_lot::Mutex; -use std::{borrow::Cow, sync::Arc}; +use std::{borrow::Cow, ops, sync::Arc}; pub(crate) const PATH_TEXTURE_FORMAT: gpu::TextureFormat = gpu::TextureFormat::R16Float; @@ -23,9 +23,7 @@ struct PendingUpload { struct BladeAtlasState { gpu: Arc, upload_belt: BladeBelt, - monochrome_textures: Vec, - polychrome_textures: Vec, - path_textures: Vec, + storage: BladeAtlasStorage, tiles_by_key: FxHashMap, initializations: Vec, uploads: Vec, @@ -33,15 +31,7 @@ struct BladeAtlasState { impl BladeAtlasState { fn destroy(&mut self) { - for mut texture in self.monochrome_textures.drain(..) { - texture.destroy(&self.gpu); - } - for mut texture in self.polychrome_textures.drain(..) { - texture.destroy(&self.gpu); - } - for mut texture in self.path_textures.drain(..) { - texture.destroy(&self.gpu); - } + self.storage.destroy(&self.gpu); self.upload_belt.destroy(&self.gpu); } } @@ -60,9 +50,7 @@ impl BladeAtlas { min_chunk_size: 0x10000, alignment: 64, // Vulkan `optimalBufferCopyOffsetAlignment` on Intel XE }), - monochrome_textures: Default::default(), - polychrome_textures: Default::default(), - path_textures: Default::default(), + storage: BladeAtlasStorage::default(), tiles_by_key: Default::default(), initializations: Vec::new(), uploads: Vec::new(), @@ -75,11 +63,7 @@ impl BladeAtlas { pub(crate) fn clear_textures(&self, texture_kind: AtlasTextureKind) { let mut lock = self.0.lock(); - let textures = match texture_kind { - AtlasTextureKind::Monochrome => &mut lock.monochrome_textures, - AtlasTextureKind::Polychrome => &mut lock.polychrome_textures, - AtlasTextureKind::Path => &mut lock.path_textures, - }; + let textures = &mut lock.storage[texture_kind]; for texture in textures { texture.clear(); } @@ -102,12 +86,7 @@ impl BladeAtlas { pub fn get_texture_info(&self, id: AtlasTextureId) -> BladeTextureInfo { let lock = self.0.lock(); - let textures = match id.kind { - crate::AtlasTextureKind::Monochrome => &lock.monochrome_textures, - crate::AtlasTextureKind::Polychrome => &lock.polychrome_textures, - crate::AtlasTextureKind::Path => &lock.path_textures, - }; - let texture = &textures[id.index as usize]; + let texture = &lock.storage[id]; let size = texture.allocator.size(); BladeTextureInfo { size: gpu::Extent { @@ -141,11 +120,7 @@ impl PlatformAtlas for BladeAtlas { impl BladeAtlasState { fn allocate(&mut self, size: Size, texture_kind: AtlasTextureKind) -> AtlasTile { - let textures = match texture_kind { - AtlasTextureKind::Monochrome => &mut self.monochrome_textures, - AtlasTextureKind::Polychrome => &mut self.polychrome_textures, - AtlasTextureKind::Path => &mut self.path_textures, - }; + let textures = &mut self.storage[texture_kind]; textures .iter_mut() .rev() @@ -207,11 +182,7 @@ impl BladeAtlasState { subresources: &Default::default(), }); - let textures = match kind { - AtlasTextureKind::Monochrome => &mut self.monochrome_textures, - AtlasTextureKind::Polychrome => &mut self.polychrome_textures, - AtlasTextureKind::Path => &mut self.path_textures, - }; + let textures = &mut self.storage[kind]; let atlas_texture = BladeAtlasTexture { id: AtlasTextureId { index: textures.len() as u32, @@ -235,24 +206,13 @@ impl BladeAtlasState { fn flush(&mut self, encoder: &mut gpu::CommandEncoder) { for id in self.initializations.drain(..) { - let textures = match id.kind { - crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, - crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, - crate::AtlasTextureKind::Path => &self.path_textures, - }; - let texture = &textures[id.index as usize]; + let texture = &self.storage[id]; encoder.init_texture(texture.raw); } let mut transfers = encoder.transfer(); for upload in self.uploads.drain(..) { - let textures = match upload.id.kind { - crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, - crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, - crate::AtlasTextureKind::Path => &self.path_textures, - }; - let texture = &textures[upload.id.index as usize]; - + let texture = &self.storage[upload.id]; transfers.copy_buffer_to_texture( upload.data, upload.bounds.size.width.to_bytes(texture.bytes_per_pixel()), @@ -276,6 +236,60 @@ impl BladeAtlasState { } } +#[derive(Default)] +struct BladeAtlasStorage { + monochrome_textures: Vec, + polychrome_textures: Vec, + path_textures: Vec, +} + +impl ops::Index for BladeAtlasStorage { + type Output = Vec; + fn index(&self, kind: AtlasTextureKind) -> &Self::Output { + match kind { + crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, + crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, + crate::AtlasTextureKind::Path => &self.path_textures, + } + } +} + +impl ops::IndexMut for BladeAtlasStorage { + fn index_mut(&mut self, kind: AtlasTextureKind) -> &mut Self::Output { + match kind { + crate::AtlasTextureKind::Monochrome => &mut self.monochrome_textures, + crate::AtlasTextureKind::Polychrome => &mut self.polychrome_textures, + crate::AtlasTextureKind::Path => &mut self.path_textures, + } + } +} + +impl ops::Index for BladeAtlasStorage { + type Output = BladeAtlasTexture; + fn index(&self, id: AtlasTextureId) -> &Self::Output { + let textures = match id.kind { + crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, + crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, + crate::AtlasTextureKind::Path => &self.path_textures, + }; + &textures[id.index as usize] + } +} + +impl BladeAtlasStorage { + fn destroy(&mut self, gpu: &gpu::Context) { + for mut texture in self.monochrome_textures.drain(..) { + texture.destroy(gpu); + } + for mut texture in self.polychrome_textures.drain(..) { + texture.destroy(gpu); + } + for mut texture in self.path_textures.drain(..) { + texture.destroy(gpu); + } + } +} + struct BladeAtlasTexture { id: AtlasTextureId, allocator: BucketedAtlasAllocator, From 224fe13f9f7c8052c381456ebf297f88fcd04001 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 4 Feb 2024 13:59:41 -0800 Subject: [PATCH 33/51] blade: fix tile bounds shader FFI --- crates/gpui/src/platform/linux/platform.rs | 12 ++++++++---- crates/gpui/src/platform/linux/shaders.wgsl | 8 ++++++-- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index f5f251f1effec..b7d51971a1964 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -3,8 +3,9 @@ use crate::{ Action, AnyWindowHandle, BackgroundExecutor, Bounds, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor, Keymap, LinuxDispatcher, LinuxDisplay, LinuxTextSystem, LinuxWindow, - LinuxWindowState, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, - PlatformTextSystem, PlatformWindow, Point, Result, SemanticVersion, Size, Task, WindowOptions, + LinuxWindowState, Menu, PathPromptOptions, Platform, PlatformDispatcher as _, PlatformDisplay, + PlatformInput, PlatformTextSystem, PlatformWindow, Point, Result, SemanticVersion, Size, Task, + WindowOptions, }; use collections::{HashMap, HashSet}; @@ -39,6 +40,7 @@ pub(crate) struct LinuxPlatformState { atoms: XcbAtoms, background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, + dispatcher: Arc, windows: HashMap>, text_system: Arc, } @@ -61,7 +63,8 @@ impl LinuxPlatform { x_root_index, atoms, background_executor: BackgroundExecutor::new(dispatcher.clone()), - foreground_executor: ForegroundExecutor::new(dispatcher), + foreground_executor: ForegroundExecutor::new(dispatcher.clone()), + dispatcher, windows: HashMap::default(), text_system: Arc::new(LinuxTextSystem::new()), })) @@ -118,6 +121,7 @@ impl Platform for LinuxPlatform { } _ => {} } + self.0.lock().dispatcher.tick(false); } } @@ -182,7 +186,7 @@ impl Platform for LinuxPlatform { display_id: DisplayId, callback: Box, ) { - unimplemented!() + log::warn!("unimplemented: set_display_link_output_callback"); } fn start_display_link(&self, display_id: DisplayId) {} diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl index 05174c0d7f7fe..58dd3265bf9be 100644 --- a/crates/gpui/src/platform/linux/shaders.wgsl +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -43,11 +43,15 @@ struct AtlasTextureId { kind: u32, } +struct AtlasBounds { + origin: vec2, + size: vec2, +} struct AtlasTile { texture_id: AtlasTextureId, tile_id: u32, padding: u32, - bounds: Bounds, + bounds: AtlasBounds, } fn to_device_position_impl(position: vec2) -> vec4 { @@ -62,7 +66,7 @@ fn to_device_position(unit_vertex: vec2, bounds: Bounds) -> vec4 { fn to_tile_position(unit_vertex: vec2, tile: AtlasTile) -> vec2 { let atlas_size = vec2(textureDimensions(t_sprite, 0)); - return (tile.bounds.origin + unit_vertex * tile.bounds.size) / atlas_size; + return (vec2(tile.bounds.origin) + unit_vertex * vec2(tile.bounds.size)) / atlas_size; } fn distance_from_clip_rect_impl(position: vec2, clip_bounds: Bounds) -> vec4 { From b13b5726c56da3653043fa76775b8f65d36de699 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 4 Feb 2024 15:58:29 -0800 Subject: [PATCH 34/51] linux: disable mac-os specific build commands --- crates/zed/build.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/crates/zed/build.rs b/crates/zed/build.rs index 0b13f5bd2f578..6905d492e1ef7 100644 --- a/crates/zed/build.rs +++ b/crates/zed/build.rs @@ -1,25 +1,27 @@ use std::process::Command; fn main() { - println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.15.7"); - - println!("cargo:rerun-if-env-changed=ZED_BUNDLE"); - if std::env::var("ZED_BUNDLE").ok().as_deref() == Some("true") { - // Find WebRTC.framework in the Frameworks folder when running as part of an application bundle. - println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/../Frameworks"); - } else { - // Find WebRTC.framework as a sibling of the executable when running outside of an application bundle. - println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path"); - } + if cfg!(target_os = "macos") { + println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.15.7"); + + println!("cargo:rerun-if-env-changed=ZED_BUNDLE"); + if std::env::var("ZED_BUNDLE").ok().as_deref() == Some("true") { + // Find WebRTC.framework in the Frameworks folder when running as part of an application bundle. + println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/../Frameworks"); + } else { + // Find WebRTC.framework as a sibling of the executable when running outside of an application bundle. + println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path"); + } - // Weakly link ReplayKit to ensure Zed can be used on macOS 10.15+. - println!("cargo:rustc-link-arg=-Wl,-weak_framework,ReplayKit"); + // Weakly link ReplayKit to ensure Zed can be used on macOS 10.15+. + println!("cargo:rustc-link-arg=-Wl,-weak_framework,ReplayKit"); - // Seems to be required to enable Swift concurrency - println!("cargo:rustc-link-arg=-Wl,-rpath,/usr/lib/swift"); + // Seems to be required to enable Swift concurrency + println!("cargo:rustc-link-arg=-Wl,-rpath,/usr/lib/swift"); - // Register exported Objective-C selectors, protocols, etc - println!("cargo:rustc-link-arg=-Wl,-ObjC"); + // Register exported Objective-C selectors, protocols, etc + println!("cargo:rustc-link-arg=-Wl,-ObjC"); + } // Populate git sha environment variable if git is available println!("cargo:rerun-if-changed=../../.git/logs/HEAD"); From f92be4b817c2fe05ad456d5cc26d6826e6a2e5a3 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 4 Feb 2024 15:59:07 -0800 Subject: [PATCH 35/51] linux: temporarily disable purescript tree sitter Link error: = note: /nix/store/idiaraknw071d20nlqp49s18gbvw4wa0-binutils-2.40/bin/ld: /x/Code/zed/target/x86_64-unknown-linux-gnu/debug/deps/libtree_sitter_haskell-7323f782ad886c6d.rlib(scanner.o): in function `state_new': /home/kvark/.cargo/git/checkouts/tree-sitter-haskell-74c278e7a2ef8d7d/cf98de2/src/scanner.c:218: multiple definition of `state_new'; /x/Code/zed/target/x86_64-unknown-linux-gnu/debug/deps/libtree_sitter_purescript-b0a95fb604a5817c.rlib(scanner.o):/home/kvark/.cargo/git/checkouts/tree-sitter-purescript-88dd3ec656c48026/a37140f/src/scanner.c:218: first defined here --- crates/zed/src/languages.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index add9f9c1924ad..0448cb8ec6c61 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -280,6 +280,8 @@ pub fn init( ], ); + /// Produces a link error on linux due to duplicated `state_new` symbol + #[cfg(not(target_os = "linux"))] language( "purescript", tree_sitter_purescript::language(), From 1c410c1b996020a7138bb6468f561238fe632a70 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 4 Feb 2024 19:36:58 -0800 Subject: [PATCH 36/51] linux: only tick the main thread tasks and one at a time in the event loop --- crates/gpui/screenshot.png | Bin 0 -> 9562 bytes crates/gpui/src/platform/linux/dispatcher.rs | 7 +++++++ crates/gpui/src/platform/linux/platform.rs | 7 +++---- 3 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 crates/gpui/screenshot.png diff --git a/crates/gpui/screenshot.png b/crates/gpui/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..50597680f03024039943557738252f4270e19c3f GIT binary patch literal 9562 zcmaKSbyQT}7w;XqYv>k5Qc6091__657*a*LOBgyVqyzyOKw#()6{I_*yFp5j5@CQL z-r)DG-+%ATT4(mzpL2HJd)7MV>>Cf&RVO84Bmw|{R8vDm9{{j0T`YhA8$&t@@AYB` z$Xj1s38)xi{)J(1o+#=l0zma!;!9gx0Kf*I+J>sPlRzLsLP8=UA_4*ee0+RtY-}tn zEHD_nx3~A_&!3~Cqn|&2wzsz@CnrC9_Dn}dM_yiDLPA17K!BB%m7JU$4-ao=XXobT z=KTD8Yip~&zrVb^JS{CPHa0dQA|fy_P)|=!Qd08Xy?blYgV)#B zXf*ot^mKoJe{F4TX=!O;VPSD`ab#rV#fukED3qU{pPHJwtgP(ImoIPLyz%t(w6n9b zwzjshv4O#0=H}*FT3Vu_qAV;dmzS5v$H(L2gwvs%F6QcGCMmvGcz*>2L~S?pQ57TlP6EIva&`;N8i7HpPHJQn3xz65@KOtK}<}% zv9a;v$B&|-B5!YRXJ=<0AD`0F(!YQI{{H=&ii&D`d;8a~UvqPFEiEmDg@te5zAY{; z?&#=XU|`_oQ~rlx3UXy)hVhlhuIdV12+)48~~yu7?JGc$#RglK7L-Q3)2 zYiku06dD^FtE#Hh)YR_ZzyIyqHwOm?BobLsQ6VNKR##US9v*&haFCam_x=0#*RNmS zy?Zw-EbP^*SB8d$)6>(sy1J2(k)J<*=H}*ZXlU^D^+g~MmX?-AMn*h5Ja_KgVPs@{ z`t+%jlaq;w2^$;Rg9i^jeE1L@9c^oC8x#~YFfh>C+8PrR7(9ReysN7# zKR@5z-rm^QxVyXC!^0ybB}GL=1rCQ-S64f(WlCYz0HKG5nKuBCb=)>kk2}%<0Pgf@ zswf%;Snn<3!>A{x@DB&?&vJrPagwYOI1;U#zzIsN5$uE-S{kiNbP))=87LMvQIs>c zVu5U8US6OQ5-Sg$fX7D5%1sM}7RwX=0SdMaCJyEonb{rZvt%3(2G4VO8oUqohpx7C z8y*n?J-%aUn zqVDg0@3SJv+J$pDMXMdY-Y<7++B=mo|K|2$E8G8jON5NY^6K@EtI4aDErkO*6wb%x zgQw%C6@G-CPN<}=#n%DXl;woJxpI?9N#+GFVjE=qTsEePz29$IjgRS&J&N#&fW<#l z#R?$FXA*s-)|v8Za9{3&U*pyidjDXy)TDBW^#$YKf|JK#INm{5=dRzd@;A9l4RfWG#Z@apu|wk$&fuq$w!Sgs{qTApRVOU% za3PW%0{4o+{v)ZoYNBqkHAl$IU7u(2waeR#dtT3Ly$61{Ze3Y-l&>>|ME5kwKDK_b zfF_U)K3~L4LWa~zBzatLSyl*S6D1xZ6N zSRpVH3S8@v|IZWL))iAM2pZ%A$9PvuAS5YG@8F>=2ka57v0=uZvb3&n19-RU(r!gr z5R{5|>Sz$jn<-2y#z$NMm-(#!n}Z3o8V7!gy$#&TQo=kYu%C9MiaPF)ju)CdaJ_id z_V~@gY6OpAI~9D}97;&|ZhJ6Cs{ROhN?ywf-Z);0kKlAOJ`s;RU=?amFO=4V&4THr z@Oek0=q-d$#XYe)=|Zc&irB&mJK|IVWclm$GY5m6+FxaU9~yq+l^(e~p&eIRi#S&% z{+ZfBbytfV`jC)U@BNmar*H??X@=3xwMT>$?;hJrHOH9wFIrX%8JaJ10{!~Zab(uq ziX)oF#xy&mJ0)o@P5D8{cjT!^)xoT$GNW%B!v!1X&7dsDsbj%cHEo@9pI{4-3O-u6DB)4=WAIDM_ta(-YGT=Z(j&b62*ohJT4W`U2VZh;Z5BG zd?q}ej>c&~t===WB13Cqe@#6CtsyEU>l>bEs3ExGa&o#N&f%(~ve(Xe)KSMjx?=)!@4x~C5FFyXYO_RUq?p$)cYxs9Q?5|x` zUyHpZywP=g#fN!i?Lt8H{PGO#gMkq3(1+k1L&a+gnUvjgK8iWgQWC+z7)SLHX{55+ z(5yjE-%RveEb@zZ`q`AY(eM1AFnALy5@3h1LBY+38PTYApll=@@IoLg9 zaK;_JyC{69zr!>~vV_B^4CJNTT$ZfaszOUIf1}8tUOA987=!*r$tYM8|j#10PR@b!%lMl%v_OzjR*R9#2V+{@G@^U?p4M}M0QkhRWTWcr-i*7 zYe^Dme$K)tjtX~n3j#94}7Zrf)pYnvLscRp-KU(LFcrXA@J-qbdPe1xsVwn>O_2ga%5E?w(5kedfJ?%wTF zKn4xbB4w!4>ddZ!zW(}Si0+S%k7nSx-uk%xNYmkor;O$V;d7S1F`G%G0 zJ*z_o`$A_xfdEDeFu}e!&QA=qeGIue%6{#Y(C{SXV@}2X&z8Oj&5#`vroxnWbplVP z?@q`b8IJdFgXOHM+2lw4LbGld{0^&F??Fxap9d!)+p?5s$<@cTxLO)UdH+YumSftV{7MCOgC?VX64lpI=i{m4>v_}e*7rL~SAyXN&`87Q z%Cc~diOXLK&+RxX*Jh+mB8V!yWGiQ+l)7%pJv5Y`TB{bDcncEQvDSSm zJo|PB1)!D8a48bLm$mOd%d+~iKxA-{(fz8lA9SzDz|fCHE-k614Z6sqCmE;*9 zN-0%ui<~AAM3$|y-8eEtPH8u-9zO65dsgBzvtASmgvLiPhDgt7j=u~>)Mqg;=r&8= zQ0?HeKarnU=_phU5m)G$Acy=ex;}N`Y83pLDyZ>vf*gMAyF37_lAf!(Bq9zo!@YCy z`tbH*c>5{d9M|D&uzkm`f&~Mmbxn8^dI?oV@%g^@xI+MG2^QyWeOF$5z)`2nL5V`%Kp`z~sGm6Se!lQ3zGZ@CnT!G*-s zDLYhaUwP}E!!{raW8O>hypG2%igmN}Ty1)LHXl}UcNoIlOc_?K>3f^(x&Nu&NTLKE=Yx>#m-Upj2g?(gL}zJkorU%T?DtV4a;^Li!ob>g|-#4G&3+F*8* z;DIt&+luSex~r&S_v=tMA=*iuOtRONKwSP%3WF}=d0XI68ur)tP%Mfo3S4&X>R+F{ zrhitspGQ{8v)jkz1P`j{AOIYi8@1@q{mfkQQC#PVB9p_}p2xalk6=O>kI(8FEY}NI z4@KDuMWZCFbsHRo!utb=T5DJi=Nu=Vc1@=L=?kq$LIulW0>W$$(c@cd;~|ji0XSdp zR{`>+0ER%cC?jqA=xA$(R)~!$>_u=ooczhnpCe&@1&QdQ$!kJM;z-5do) zPJekie3a|UT-F8`u%@YyoqBGpgFmU67FO2YH~OoGy7?<2j2U-@Fo*cDKT1mZbA9Z? zt)?0YiK>vWS9|+8{E|eiLSdk~PBWlP&f47EIX3NA#So)gpBFWp?W)n}_`!meIsZm) z42xJ(6C8ScLDw_jZxr|oBi1gW{H)T8f6NPygJ$V+zH@3=VfiA>y=B~wG$scyRTqc9 z7brTAO@|BGg@))3wW^e(>~c}qNQL`V>d%+apliE&t232aH3^G43ZU#v@0pbjE+ zP}P+4PkY&dkzJX953oaOU{}zdPXMPm?A7NxcWh%yftk=UkKA9q@jl_2_bqW-&FlBK zYtSRSlbP6$DVdwGV0%k7=1Y?VovxM=$)#<8EddMM8yeo<8Dal3N$WJ%@!;%?AhrSC zP9hC3A+RQ;hCecUt)R%=ZqUU?wO2i4O?S7|kPvJ;C+%|chcrzz>7JOCY|Hj<{j^p> zL90yS09mwKx0YcHDdgT>*f_omkbsa~WflkUbN>jvx6fJOki*0V9pJg^F}Obs}Lr(SW@6M z;RK-z@clMrEATI*#1_81fnA?W66~WLJ)WDbmd@- zp2Vz0`Aa}vKKe^X&DGdI@PK#p8Mm$^mq0jeY8(~B*UMJ*BVYCQSQ0?ddg!cq2+=21$=PTvoPOX4nrT~ zxDxSJ7s1D8R%x6^nn>LU4QW0V)t6Upg|20|$wKE$&dKcWhlupv{)J9NABN;Bqr5iB>NcA;9bQYs5_!PGSXC38+(#g`amz;Y zyD*})`>gQ#HH)xW@ehXNt@fcNuKdq!sqDUdB$qT4^rF`8);$5V|AqzRvn|GLi&(g) zb2YOhyH^r&04_g7AGGf8wF!~Vf<2I%Aq+SApYYjVmg5I-Lx8-@HWjrkOTfF6Yt=Pf zo2*0-%N(!&RPFm&;`BV|`Vj|*d2fANSrEC0_{v`Y@W>D#VIh;wB5&rvK=0hTRW zk=dt|fG3;b#^f3{&}Stv%WbtWB3vtDdWknN<#y5&EYhc{a0@vkKO8C7J^I~0aK^{yTi5@ z7P+nRW4fTbTgkE@54?GXl~h!insl(N*b3w@PBM@jD}_FW05031r$4Ry0z^2T3=6sf zKWU9U{i&9&Yixz^B6OG``RnW+L4@6~EHodfjx9)2IuDaJvx=DGOngGQLUuVZGNmw= z9gQVz!g+|7kfWO3u!|40g;dC zo9}jxlZ53bB?}t5Al*6Pu|N>PO+-8m;+&(k{BSPuz}&80^GR&1(>~dqt6`oR@+uWc zoK|N!whLrbqlK@iCDan!ACSe*`8Y!rhyBqtW(OAS>Pw z597BFZrz33aJoPPmhtZc_pTLIIfrAM5(&C(M2=D1ia|}%B&}DZFSV?YqB!e7bhwBT0 zw)s$mB?Z|h-uZ^h3HzJB0lmL@};EysNLdm}RXm?kV4Tp5Tn$6dRf0*j`z) zlBxv0@;rM>`zZd#V65f=hn+FL(RXoefWtbxJt5=rbJSvl+iXWWnm}*o^lNzKE2Gi2 zgNfPY*F>#osHqgsV_c?tCPQT?{O|221d40lBgg*yp5ntN;v=PjlR`~3?_w`L9H;Av zAPp{wnkU3zz@GML7OZ`Uc=XWS*cDAn1CO0NvluwD7~@TjrTFqQHGrEHud@$5=azJN zXQ9T%NU#?P^m-4E!7PJ6*)(EGPy4ZNd1Jd`a=mB6o0Eb=D|aVlN%3glSG>MZqmPkv zDJs38*FSiP$nuE7OMB*S+>&dBgHz929_>FV1!&;18z^X66)B;--58g5`;4Yww%`;j z6*7oy{5_)&-gjSio#OFD3mO8<&gFULb0Y8N15h(E&|W`AF-}%*DIAQy4e@`SL-Tf!E=`p$xJ0Z)n7tYqJHU|kh`v8 zd61_V$4W}9$We1rF7VTOTl$&jJQiH~YTGUPjnw+Rz>5%+q!YMottKfR($dDqY)B*X02ASWO&bTx@q_DSIp zY1k}lHX{rCu*A2QB7T?21N0PBU^HQG-n;eIvo}QZNBG8$Gte`#Wag0re&a{_}t&Yym(lx3<4?a=5Qsr&&J)xN8)fXCfxn+2T5_ubp<~R(yu8T`p&yQ?z8^Q5Q;cJWn$_gYdJronH}m4u%kn(E%cMs!}4LN^<4 zGO~KsQ|{f%F=NpUyR}QS4Wc}(zx1z84NsT)vtG}lI@sa80qfgoa#Kn7yEMrShI5p^wL9n9 zl-L)l3;mRKhvMR+ytq4iV@{PHwM?dS%y|=&rKY+~NJVZRKQaefYp&KPe#m1VrG>d$ zsgbG+RDWc5IctNuH1_X30^4&;GN`gUkY=08Se_ooDoum9IrG)q`Z|UcYD%rx}(2A7H|J=FQ%-tt4;Emq}URI0EkT>}j>SYqQCV0#}v3yHox_buGc67Y?YAh2N4W zk$jwxA!s@{ZAq%me!R+h9);gTm0pmK_OpEVZsK=u`+nD>Sp`D(5FyrddObfP_3Rb2%vtT;hw1tBb9x50y7q zA3l)~FqI9}K~F~VdQWY zp_8pOJj4fI)IU<`hOk80lYl&s0rT{E)J)g?MD2@E{8iIC%78T-Q-z}sg2(w{0D3JT zr6pQY4^yox`Vf(xo}T6IAyY+3*_HE=DA3m2elQ@B0xa{`;)w*|ccCj=Bor3kJ5 z3s4pzsM-Y!flERRFO0iFjEp*b$pa`rFa&=M83I9f$?|S=++Ixn0&=ItG7MMm`G4sAf{(R($NeX(8DMlNX0FWXCEP4 zlOCZk2j}0#=oQCf1pnkiMtYR7VJtI9i$#SIBZP%@$IE}7eZyu1+q>VvWXYEWGl*Ke z4UYj6@h>0IxG+J8z1=-1d25Rr2ATB<6}nLqW+v$!x-GkIswh6^fC4@UFV3erQ8+}v zjYyBuKZJb)j6pt711zS5&^5z1Q{G@9h!67c@y0nI zbWZswTzzr`vG`vx%ysJb6=z_NhXEHt*GW9`F=@0KcyY!!XHcU-1Q*bbK26b{Ih?FK zGQp2Y5O}ZgGfK)wqJuCmLaIc+BPKH;LEP0@vZXsWOnX-r)tka&FqC8S*=aH^U8x_N zoVLbN@GFO6RY3aQC}F*_`}eao+qXfnyOS~tUFb;kb2$|GNB8}ng`3nv+12y&CVSuI zlI-NiCTx0d2SG3{Q|5Q6&Sf?p$e@#*ShD(?<3Ay&%g}R+KJO(#6V{`Ule#~Vv8b)m zw{=ClS7n(fN>t0hm%y;Uv`4xD=bi`lRtF`1t3NS?lq5Sl*_*(wl#8|wbkq9#f#JZg zqq++)_puAlEAoc02Z5aiE$E`~{7u~e``r_*h9G&J6WXQ6Aq)evwrZS`Yk5s;D-CG; zvr+Vep{wEJNi=-xPQcXh_(qOa02|MF?nM5Qz6tBGHukW!K-kGUs->QOS<{`OZOI0y zh3kI8|KBGQ|GuTjgU7|);Aeb(a;0Ywnuqz)gUr!X(@94M;KlR_Fzxm?W{`YKG*xv~ JDwJRm{|DkQwW$CA literal 0 HcmV?d00001 diff --git a/crates/gpui/src/platform/linux/dispatcher.rs b/crates/gpui/src/platform/linux/dispatcher.rs index 14072f040a5e4..d669e56ab6e2c 100644 --- a/crates/gpui/src/platform/linux/dispatcher.rs +++ b/crates/gpui/src/platform/linux/dispatcher.rs @@ -46,6 +46,13 @@ impl LinuxDispatcher { main_thread_id: thread::current().id(), } } + + pub fn tick_main(&self) { + assert!(self.is_main_thread()); + if let Ok(runnable) = self.main_receiver.try_recv() { + runnable.run(); + } + } } impl PlatformDispatcher for LinuxDispatcher { diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index b7d51971a1964..e1d21ef69d41e 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -3,9 +3,8 @@ use crate::{ Action, AnyWindowHandle, BackgroundExecutor, Bounds, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor, Keymap, LinuxDispatcher, LinuxDisplay, LinuxTextSystem, LinuxWindow, - LinuxWindowState, Menu, PathPromptOptions, Platform, PlatformDispatcher as _, PlatformDisplay, - PlatformInput, PlatformTextSystem, PlatformWindow, Point, Result, SemanticVersion, Size, Task, - WindowOptions, + LinuxWindowState, Menu, PathPromptOptions, Platform, PlatformDisplay, PlatformInput, + PlatformTextSystem, PlatformWindow, Point, Result, SemanticVersion, Size, Task, WindowOptions, }; use collections::{HashMap, HashSet}; @@ -121,7 +120,7 @@ impl Platform for LinuxPlatform { } _ => {} } - self.0.lock().dispatcher.tick(false); + self.0.lock().dispatcher.tick_main(); } } From d03f3b9f181ab33c6585c712f61226f0db7eaa8c Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 4 Feb 2024 20:14:11 -0800 Subject: [PATCH 37/51] linux: switch folders to use CONFIG_DIR --- crates/util/src/paths.rs | 60 ++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 12 deletions(-) diff --git a/crates/util/src/paths.rs b/crates/util/src/paths.rs index 6e7fc3f65368b..1b276854cea92 100644 --- a/crates/util/src/paths.rs +++ b/crates/util/src/paths.rs @@ -9,18 +9,54 @@ use serde::{Deserialize, Serialize}; lazy_static::lazy_static! { pub static ref HOME: PathBuf = dirs::home_dir().expect("failed to determine home directory"); pub static ref CONFIG_DIR: PathBuf = HOME.join(".config").join("zed"); - pub static ref CONVERSATIONS_DIR: PathBuf = HOME.join(".config/zed/conversations"); - pub static ref EMBEDDINGS_DIR: PathBuf = HOME.join(".config/zed/embeddings"); - pub static ref THEMES_DIR: PathBuf = HOME.join(".config/zed/themes"); - pub static ref LOGS_DIR: PathBuf = HOME.join("Library/Logs/Zed"); - pub static ref SUPPORT_DIR: PathBuf = HOME.join("Library/Application Support/Zed"); - pub static ref PLUGINS_DIR: PathBuf = HOME.join("Library/Application Support/Zed/plugins"); - pub static ref LANGUAGES_DIR: PathBuf = HOME.join("Library/Application Support/Zed/languages"); - pub static ref COPILOT_DIR: PathBuf = HOME.join("Library/Application Support/Zed/copilot"); - pub static ref DEFAULT_PRETTIER_DIR: PathBuf = HOME.join("Library/Application Support/Zed/prettier"); - pub static ref DB_DIR: PathBuf = HOME.join("Library/Application Support/Zed/db"); - pub static ref CRASHES_DIR: PathBuf = HOME.join("Library/Logs/DiagnosticReports"); - pub static ref CRASHES_RETIRED_DIR: PathBuf = HOME.join("Library/Logs/DiagnosticReports/Retired"); + pub static ref CONVERSATIONS_DIR: PathBuf = CONFIG_DIR.join("conversations"); + pub static ref EMBEDDINGS_DIR: PathBuf = CONFIG_DIR.join("embeddings"); + pub static ref THEMES_DIR: PathBuf = CONFIG_DIR.join("themes"); + pub static ref LOGS_DIR: PathBuf = if cfg!(target_os="macos") { + HOME.join("Library/Logs/Zed") + } else { + CONFIG_DIR.join("logs") + }; + pub static ref SUPPORT_DIR: PathBuf = if cfg!(target_os="macos") { + HOME.join("Library/Application Support/Zed") + } else { + CONFIG_DIR.join("support") + }; + pub static ref PLUGINS_DIR: PathBuf = if cfg!(target_os="macos") { + HOME.join("Library/Application Support/Zed/plugins") + } else { + CONFIG_DIR.join("plugins") + }; + pub static ref LANGUAGES_DIR: PathBuf = if cfg!(target_os="macos") { + HOME.join("Library/Application Support/Zed/languages") + } else { + CONFIG_DIR.join("languages") + }; + pub static ref COPILOT_DIR: PathBuf = if cfg!(target_os="macos") { + HOME.join("Library/Application Support/Zed/copilot") + } else { + CONFIG_DIR.join("copilot") + }; + pub static ref DEFAULT_PRETTIER_DIR: PathBuf = if cfg!(target_os="macos") { + HOME.join("Library/Application Support/Zed/prettier") + } else { + CONFIG_DIR.join("prettier") + }; + pub static ref DB_DIR: PathBuf = if cfg!(target_os="macos") { + HOME.join("Library/Application Support/Zed/db") + } else { + CONFIG_DIR.join("db") + }; + pub static ref CRASHES_DIR: PathBuf = if cfg!(target_os="macos") { + HOME.join("Library/Logs/DiagnosticReports") + } else { + CONFIG_DIR.join("crashes") + }; + pub static ref CRASHES_RETIRED_DIR: PathBuf = if cfg!(target_os="macos") { + HOME.join("Library/Logs/DiagnosticReports/Retired") + } else { + CRASHES_DIR.join("retired") + }; pub static ref SETTINGS: PathBuf = CONFIG_DIR.join("settings.json"); pub static ref KEYMAP: PathBuf = CONFIG_DIR.join("keymap.json"); pub static ref LAST_USERNAME: PathBuf = CONFIG_DIR.join("last-username.txt"); From 8d339ede1c3aa62f6abb018f3eab43203fc552bc Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 4 Feb 2024 20:23:06 -0800 Subject: [PATCH 38/51] linux: HACK disable watcher and crash uploader temporarily --- crates/fs/src/fs.rs | 2 ++ crates/zed/src/main.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index edd3da101cb8a..05c961c4cfeb6 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -301,6 +301,8 @@ impl Fs for RealFs { .configure(Config::default().with_poll_interval(latency)) .unwrap(); + //TODO: unblock this + #[cfg(not(target_os = "linux"))] watcher .watch(path, notify::RecursiveMode::Recursive) .unwrap(); diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 1ae1feffde601..73acfdef343de 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -257,6 +257,8 @@ fn main() { initialize_workspace(app_state.clone(), cx); if stdout_is_a_pty() { + //TODO: unblock this + #[cfg(not(target_os = "linux"))] upload_panics_and_crashes(http.clone(), cx); cx.activate(true); let urls = collect_url_args(); From 282cc71df9ccc4aac1d635ec643d278fa6e12820 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 4 Feb 2024 22:37:34 -0800 Subject: [PATCH 39/51] linux: refactor LinuxPlatform to avoid recursive locking from the window callbacks --- crates/gpui/src/platform/linux/dispatcher.rs | 2 + crates/gpui/src/platform/linux/platform.rs | 81 ++++++++++++-------- 2 files changed, 49 insertions(+), 34 deletions(-) diff --git a/crates/gpui/src/platform/linux/dispatcher.rs b/crates/gpui/src/platform/linux/dispatcher.rs index d669e56ab6e2c..49f10449f8fcf 100644 --- a/crates/gpui/src/platform/linux/dispatcher.rs +++ b/crates/gpui/src/platform/linux/dispatcher.rs @@ -89,6 +89,8 @@ impl PlatformDispatcher for LinuxDispatcher { let (_, runnable) = timed_tasks.pop().unwrap(); runnable.run(); ran = true; + } else { + break; } } ran diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index e1d21ef69d41e..3ce766624616f 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -31,17 +31,19 @@ xcb::atoms_struct! { } } -pub(crate) struct LinuxPlatform(Mutex); - -pub(crate) struct LinuxPlatformState { +pub(crate) struct LinuxPlatform { xcb_connection: Arc, x_root_index: i32, atoms: XcbAtoms, background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, dispatcher: Arc, - windows: HashMap>, text_system: Arc, + state: Mutex, +} + +pub(crate) struct LinuxPlatformState { + windows: HashMap>, } impl Default for LinuxPlatform { @@ -57,52 +59,58 @@ impl LinuxPlatform { let dispatcher = Arc::new(LinuxDispatcher::new()); - Self(Mutex::new(LinuxPlatformState { + Self { xcb_connection: Arc::new(xcb_connection), x_root_index, atoms, background_executor: BackgroundExecutor::new(dispatcher.clone()), foreground_executor: ForegroundExecutor::new(dispatcher.clone()), dispatcher, - windows: HashMap::default(), text_system: Arc::new(LinuxTextSystem::new()), - })) + state: Mutex::new(LinuxPlatformState { + windows: HashMap::default(), + }), + } } } impl Platform for LinuxPlatform { fn background_executor(&self) -> BackgroundExecutor { - self.0.lock().background_executor.clone() + self.background_executor.clone() } - fn foreground_executor(&self) -> crate::ForegroundExecutor { - self.0.lock().foreground_executor.clone() + fn foreground_executor(&self) -> ForegroundExecutor { + self.foreground_executor.clone() } fn text_system(&self) -> Arc { - self.0.lock().text_system.clone() + self.text_system.clone() } fn run(&self, on_finish_launching: Box) { on_finish_launching(); + //Note: here and below, don't keep the lock() open when calling + // into window functions as they may invoke callbacks that need + // to immediately access the platform (self). - while !self.0.lock().windows.is_empty() { - let event = self.0.lock().xcb_connection.wait_for_event().unwrap(); + while !self.state.lock().windows.is_empty() { + let event = self.xcb_connection.wait_for_event().unwrap(); match event { xcb::Event::X(x::Event::ClientMessage(ev)) => { if let x::ClientMessageData::Data32([atom, ..]) = ev.data() { - let mut this = self.0.lock(); - if atom == this.atoms.wm_del_window.resource_id() { + if atom == self.atoms.wm_del_window.resource_id() { // window "x" button clicked by user, we gracefully exit - let window = this.windows.remove(&ev.window()).unwrap(); + let window = self.state.lock().windows.remove(&ev.window()).unwrap(); window.destroy(); - break; } } } xcb::Event::X(x::Event::Expose(ev)) => { - let this = self.0.lock(); - this.windows[&ev.window()].expose(); + let window = { + let state = self.state.lock(); + Arc::clone(&state.windows[&ev.window()]) + }; + window.expose(); } xcb::Event::X(x::Event::ConfigureNotify(ev)) => { let bounds = Bounds { @@ -115,12 +123,17 @@ impl Platform for LinuxPlatform { height: ev.height().into(), }, }; - let this = self.0.lock(); - this.windows[&ev.window()].configure(bounds); + let window = { + let state = self.state.lock(); + Arc::clone(&state.windows[&ev.window()]) + }; + window.configure(bounds) + } + ref other => { + println!("Other event {:?}", other); } - _ => {} } - self.0.lock().dispatcher.tick_main(); + self.dispatcher.tick_main(); } } @@ -137,22 +150,20 @@ impl Platform for LinuxPlatform { fn unhide_other_apps(&self) {} fn displays(&self) -> Vec> { - let this = self.0.lock(); - let setup = this.xcb_connection.get_setup(); + let setup = self.xcb_connection.get_setup(); setup .roots() .enumerate() .map(|(root_id, _)| { - Rc::new(LinuxDisplay::new(&this.xcb_connection, root_id as i32)) + Rc::new(LinuxDisplay::new(&self.xcb_connection, root_id as i32)) as Rc }) .collect() } fn display(&self, id: DisplayId) -> Option> { - let this = self.0.lock(); Some(Rc::new(LinuxDisplay::new( - &this.xcb_connection, + &self.xcb_connection, id.0 as i32, ))) } @@ -166,17 +177,19 @@ impl Platform for LinuxPlatform { handle: AnyWindowHandle, options: WindowOptions, ) -> Box { - let mut this = self.0.lock(); - let x_window = this.xcb_connection.generate_id(); + let x_window = self.xcb_connection.generate_id(); let window_ptr = Arc::new(LinuxWindowState::new( options, - &this.xcb_connection, - this.x_root_index, + &self.xcb_connection, + self.x_root_index, x_window, - &this.atoms, + &self.atoms, )); - this.windows.insert(x_window, Arc::clone(&window_ptr)); + self.state + .lock() + .windows + .insert(x_window, Arc::clone(&window_ptr)); Box::new(LinuxWindow(window_ptr)) } From 521b2b12e463e0d7aa0cd713198b7adf771407ed Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Sun, 4 Feb 2024 23:52:22 -0800 Subject: [PATCH 40/51] linux: create a hidden window inside the platform It allows us to receive messages from the dispatcher, which breaks us out of waiting and lets us execute main thread runnables as a part of the main loop. --- crates/gpui/src/platform/linux/dispatcher.rs | 82 +++++++++++++------ crates/gpui/src/platform/linux/platform.rs | 34 +++++--- crates/gpui/src/platform/linux/text_system.rs | 2 +- crates/gpui/src/platform/linux/window.rs | 1 + 4 files changed, 80 insertions(+), 39 deletions(-) diff --git a/crates/gpui/src/platform/linux/dispatcher.rs b/crates/gpui/src/platform/linux/dispatcher.rs index 49f10449f8fcf..28526d360a428 100644 --- a/crates/gpui/src/platform/linux/dispatcher.rs +++ b/crates/gpui/src/platform/linux/dispatcher.rs @@ -7,29 +7,50 @@ use async_task::Runnable; use parking::{Parker, Unparker}; use parking_lot::Mutex; use std::{ - panic, thread, + panic, + sync::Arc, + thread, time::{Duration, Instant}, }; +use xcb::x; pub(crate) struct LinuxDispatcher { + xcb_connection: Arc, + x_listener_window: x::Window, parker: Mutex, timed_tasks: Mutex>, main_sender: flume::Sender, - main_receiver: flume::Receiver, background_sender: flume::Sender, - background_thread: thread::JoinHandle<()>, + _background_thread: thread::JoinHandle<()>, main_thread_id: thread::ThreadId, } -impl Default for LinuxDispatcher { - fn default() -> Self { - Self::new() - } -} - impl LinuxDispatcher { - pub fn new() -> Self { - let (main_sender, main_receiver) = flume::unbounded::(); + pub fn new( + main_sender: flume::Sender, + xcb_connection: &Arc, + x_root_index: i32, + ) -> Self { + let x_listener_window = xcb_connection.generate_id(); + let screen = xcb_connection + .get_setup() + .roots() + .nth(x_root_index as usize) + .unwrap(); + xcb_connection.send_request(&x::CreateWindow { + depth: 0, + wid: x_listener_window, + parent: screen.root(), + x: 0, + y: 0, + width: 1, + height: 1, + border_width: 0, + class: x::WindowClass::InputOnly, + visual: screen.root_visual(), + value_list: &[], + }); + let (background_sender, background_receiver) = flume::unbounded::(); let background_thread = thread::spawn(move || { for runnable in background_receiver { @@ -37,21 +58,23 @@ impl LinuxDispatcher { } }); LinuxDispatcher { + xcb_connection: Arc::clone(xcb_connection), + x_listener_window, parker: Mutex::new(Parker::new()), timed_tasks: Mutex::new(Vec::new()), main_sender, - main_receiver, background_sender, - background_thread, + _background_thread: background_thread, main_thread_id: thread::current().id(), } } +} - pub fn tick_main(&self) { - assert!(self.is_main_thread()); - if let Ok(runnable) = self.main_receiver.try_recv() { - runnable.run(); - } +impl Drop for LinuxDispatcher { + fn drop(&mut self) { + self.xcb_connection.send_request(&x::DestroyWindow { + window: self.x_listener_window, + }); } } @@ -66,6 +89,18 @@ impl PlatformDispatcher for LinuxDispatcher { fn dispatch_on_main_thread(&self, runnable: Runnable) { self.main_sender.send(runnable).unwrap(); + // Send a message to the invisible window, forcing + // tha main loop to wake up and dispatch the runnable. + self.xcb_connection.send_request(&x::SendEvent { + propagate: false, + destination: x::SendEventDest::Window(self.x_listener_window), + event_mask: x::EventMask::NO_EVENT, + event: &x::VisibilityNotifyEvent::new( + self.x_listener_window, + x::Visibility::Unobscured, + ), + }); + self.xcb_connection.flush().unwrap(); } fn dispatch_after(&self, duration: Duration, runnable: Runnable) { @@ -76,24 +111,17 @@ impl PlatformDispatcher for LinuxDispatcher { } fn tick(&self, background_only: bool) -> bool { - let mut ran = false; - if self.is_main_thread() && !background_only { - for runnable in self.main_receiver.try_iter() { - runnable.run(); - ran = true; - } - } let mut timed_tasks = self.timed_tasks.lock(); + let old_count = timed_tasks.len(); while let Some(&(moment, _)) = timed_tasks.last() { if moment <= Instant::now() { let (_, runnable) = timed_tasks.pop().unwrap(); runnable.run(); - ran = true; } else { break; } } - ran + timed_tasks.len() != old_count } fn park(&self) { diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 3ce766624616f..60cf135412d34 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -7,6 +7,7 @@ use crate::{ PlatformTextSystem, PlatformWindow, Point, Result, SemanticVersion, Size, Task, WindowOptions, }; +use async_task::Runnable; use collections::{HashMap, HashSet}; use futures::channel::oneshot; use parking_lot::Mutex; @@ -37,12 +38,13 @@ pub(crate) struct LinuxPlatform { atoms: XcbAtoms, background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, - dispatcher: Arc, + main_receiver: flume::Receiver, text_system: Arc, state: Mutex, } pub(crate) struct LinuxPlatformState { + quit_requested: bool, windows: HashMap>, } @@ -57,17 +59,24 @@ impl LinuxPlatform { let (xcb_connection, x_root_index) = xcb::Connection::connect(None).unwrap(); let atoms = XcbAtoms::intern_all(&xcb_connection).unwrap(); - let dispatcher = Arc::new(LinuxDispatcher::new()); + let xcb_connection = Arc::new(xcb_connection); + let (main_sender, main_receiver) = flume::unbounded::(); + let dispatcher = Arc::new(LinuxDispatcher::new( + main_sender, + &xcb_connection, + x_root_index, + )); Self { - xcb_connection: Arc::new(xcb_connection), + xcb_connection, x_root_index, atoms, background_executor: BackgroundExecutor::new(dispatcher.clone()), foreground_executor: ForegroundExecutor::new(dispatcher.clone()), - dispatcher, + main_receiver, text_system: Arc::new(LinuxTextSystem::new()), state: Mutex::new(LinuxPlatformState { + quit_requested: false, windows: HashMap::default(), }), } @@ -92,8 +101,7 @@ impl Platform for LinuxPlatform { //Note: here and below, don't keep the lock() open when calling // into window functions as they may invoke callbacks that need // to immediately access the platform (self). - - while !self.state.lock().windows.is_empty() { + while !self.state.lock().quit_requested { let event = self.xcb_connection.wait_for_event().unwrap(); match event { xcb::Event::X(x::Event::ClientMessage(ev)) => { @@ -129,15 +137,18 @@ impl Platform for LinuxPlatform { }; window.configure(bounds) } - ref other => { - println!("Other event {:?}", other); - } + _ => {} + } + + if let Ok(runnable) = self.main_receiver.try_recv() { + runnable.run(); } - self.dispatcher.tick_main(); } } - fn quit(&self) {} + fn quit(&self) { + self.state.lock().quit_requested = true; + } fn restart(&self) {} @@ -186,6 +197,7 @@ impl Platform for LinuxPlatform { x_window, &self.atoms, )); + self.state .lock() .windows diff --git a/crates/gpui/src/platform/linux/text_system.rs b/crates/gpui/src/platform/linux/text_system.rs index 62935fc232f10..187ffb526abc0 100644 --- a/crates/gpui/src/platform/linux/text_system.rs +++ b/crates/gpui/src/platform/linux/text_system.rs @@ -76,7 +76,7 @@ impl PlatformTextSystem for LinuxTextSystem { unimplemented!() } fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option { - unimplemented!() + None } fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result> { unimplemented!() diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 3818ec2c657a4..031dcb0cd6ede 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -237,6 +237,7 @@ impl LinuxWindowState { if let Some(fun) = self.callbacks.lock().close.take() { fun(); } + self.xcb_connection.flush().unwrap(); } pub fn expose(&self) { From fde159fea1887f7175384d50fdc0be7b79959be4 Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Sun, 4 Feb 2024 23:08:05 -0800 Subject: [PATCH 41/51] build: add Blade font-config sys deps on Linux --- Cargo.lock | 321 +++++++++++++++++++++++++++++++++++++++------------ script/linux | 24 ++-- 2 files changed, 265 insertions(+), 80 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c3df96e992bc4..37f3b09130bb3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -267,12 +267,38 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + [[package]] name = "ascii" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" +[[package]] +name = "ash" +version = "0.37.3+1.3.251" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" +dependencies = [ + "libloading 0.7.4", +] + +[[package]] +name = "ash-window" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b912285a7c29f3a8f87ca6f55afc48768624e5e33ec17dbd2f2075903f5e35ab" +dependencies = [ + "ash", + "raw-window-handle 0.5.2", + "raw-window-metal", +] + [[package]] name = "assets" version = "0.1.0" @@ -908,6 +934,46 @@ dependencies = [ "wyz", ] +[[package]] +name = "blade-graphics" +version = "0.3.0" +source = "git+https://github.com/kvark/blade?rev=62eb18d312f720a5aac8f508fe223146a24fc7f0#62eb18d312f720a5aac8f508fe223146a24fc7f0" +dependencies = [ + "ash", + "ash-window", + "bitflags 2.4.1", + "block", + "bytemuck", + "codespan-reporting", + "core-graphics-types", + "glow", + "gpu-alloc", + "gpu-alloc-ash", + "hidden-trait", + "js-sys", + "khronos-egl", + "libloading 0.8.0", + "log", + "metal 0.25.0", + "mint", + "naga", + "objc", + "raw-window-handle 0.5.2", + "slab", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "blade-macros" +version = "0.2.1" +source = "git+https://github.com/kvark/blade?rev=62eb18d312f720a5aac8f508fe223146a24fc7f0#62eb18d312f720a5aac8f508fe223146a24fc7f0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "block" version = "0.1.6" @@ -1065,6 +1131,20 @@ name = "bytemuck" version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] [[package]] name = "byteorder" @@ -1341,11 +1421,8 @@ version = "0.1.0" dependencies = [ "anyhow", "clap 3.2.25", - "core-foundation", - "core-services", "dirs 3.0.2", "ipc-channel", - "plist", "serde", "serde_derive", "util", @@ -1438,6 +1515,16 @@ dependencies = [ "objc", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "collab" version = "0.44.0" @@ -1786,15 +1873,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core-services" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92567e81db522550ebaf742c5d875624ec7820c2c7ee5f8c60e4ce7c2ae3c0fd" -dependencies = [ - "core-foundation", -] - [[package]] name = "core-text" version = "19.2.0" @@ -3088,16 +3166,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "gethostname" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" -dependencies = [ - "libc", - "windows-targets 0.48.5", -] - [[package]] name = "getrandom" version = "0.1.16" @@ -3207,6 +3275,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "glow" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "go_to_line" version = "0.1.0" @@ -3224,15 +3304,48 @@ dependencies = [ "workspace", ] +[[package]] +name = "gpu-alloc" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" +dependencies = [ + "bitflags 2.4.1", + "gpu-alloc-types", +] + +[[package]] +name = "gpu-alloc-ash" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2424bc9be88170e1a56e57c25d3d0e2dfdd22e8f328e892786aeb4da1415732" +dependencies = [ + "ash", + "gpu-alloc-types", + "tinyvec", +] + +[[package]] +name = "gpu-alloc-types" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" +dependencies = [ + "bitflags 2.4.1", +] + [[package]] name = "gpui" version = "0.1.0" dependencies = [ "anyhow", + "as-raw-xcb-connection", "async-task", "backtrace", "bindgen 0.65.1", "bitflags 2.4.1", + "blade-graphics", + "blade-macros", "block", "bytemuck", "cbindgen", @@ -3257,7 +3370,7 @@ dependencies = [ "linkme", "log", "media", - "metal", + "metal 0.21.0", "num_cpus", "objc", "ordered-float 2.10.0", @@ -3267,6 +3380,7 @@ dependencies = [ "png", "postage", "rand 0.8.5", + "raw-window-handle 0.5.2", "raw-window-handle 0.6.0", "refineable", "resvg", @@ -3288,7 +3402,7 @@ dependencies = [ "util", "uuid 1.4.1", "waker-fn", - "x11rb", + "xcb", ] [[package]] @@ -3425,6 +3539,23 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "hidden-trait" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ed9e850438ac849bec07e7d09fbe9309cbd396a5988c30b010580ce08860df" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "hkdf" version = "0.12.3" @@ -3914,6 +4045,16 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "khronos-egl" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1382b16c04aeb821453d6215a3c80ba78f24c6595c5aa85653378aabe0c83e3" +dependencies = [ + "libc", + "libloading 0.8.0", +] + [[package]] name = "kqueue" version = "1.0.8" @@ -4148,15 +4289,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "line-wrap" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" -dependencies = [ - "safemem", -] - [[package]] name = "linkme" version = "0.3.17" @@ -4209,8 +4341,10 @@ dependencies = [ "block", "byteorder", "bytes 1.5.0", + "cocoa", "collections", "core-foundation", + "core-graphics 0.22.3", "foreign-types 0.3.2", "futures 0.3.28", "gpui", @@ -4220,6 +4354,7 @@ dependencies = [ "log", "media", "nanoid", + "objc", "parking_lot 0.11.2", "postage", "serde", @@ -4407,7 +4542,7 @@ dependencies = [ "bytes 1.5.0", "core-foundation", "foreign-types 0.3.2", - "metal", + "metal 0.21.0", "objc", ] @@ -4475,6 +4610,21 @@ dependencies = [ "objc", ] +[[package]] +name = "metal" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "550b24b0cd4cf923f36bae78eca457b3a10d8a6a14a9c84cb2687b527e6a84af" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-graphics-types", + "foreign-types 0.5.0", + "log", + "objc", + "paste", +] + [[package]] name = "mimalloc" version = "0.1.39" @@ -4524,6 +4674,12 @@ dependencies = [ "adler", ] +[[package]] +name = "mint" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" + [[package]] name = "mintex" version = "0.1.2" @@ -4651,6 +4807,26 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "naga" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae585df4b6514cf8842ac0f1ab4992edc975892704835b549cf818dc0191249e" +dependencies = [ + "bit-set", + "bitflags 2.4.1", + "codespan-reporting", + "hexf-parse", + "indexmap 2.0.0", + "log", + "num-traits", + "rustc-hash", + "spirv", + "termcolor", + "thiserror", + "unicode-xid", +] + [[package]] name = "nanoid" version = "0.4.0" @@ -5600,20 +5776,6 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" -[[package]] -name = "plist" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdc0001cfea3db57a2e24bc0d818e9e20e554b5f97fabb9bc231dc240269ae06" -dependencies = [ - "base64 0.21.4", - "indexmap 1.9.3", - "line-wrap", - "quick-xml", - "serde", - "time", -] - [[package]] name = "plugin" version = "0.1.0" @@ -6071,9 +6233,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b9228215d82c7b61490fec1de287136b5de6f5700f6e58ea9ad61a7964ca51" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" dependencies = [ "memchr", ] @@ -6188,6 +6350,18 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" +[[package]] +name = "raw-window-metal" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac4ea493258d54c24cb46aa9345d099e58e2ea3f30dd63667fc54fc892f18e76" +dependencies = [ + "cocoa", + "core-graphics 0.23.1", + "objc", + "raw-window-handle 0.5.2", +] + [[package]] name = "rawpointer" version = "0.2.1" @@ -6836,12 +7010,6 @@ dependencies = [ "bytemuck", ] -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - [[package]] name = "salsa20" version = "0.8.1" @@ -7552,6 +7720,16 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spirv" +version = "0.2.0+1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246bfa38fe3db3f1dfc8ca5a2cdeb7348c78be2112740cc0ec8ef18b6d94f830" +dependencies = [ + "bitflags 1.3.2", + "num-traits", +] + [[package]] name = "spki" version = "0.7.2" @@ -9276,6 +9454,12 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +[[package]] +name = "unicode-xid" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" + [[package]] name = "unicode_categories" version = "0.1.1" @@ -10286,29 +10470,24 @@ dependencies = [ ] [[package]] -name = "x11rb" -version = "0.13.0" +name = "xattr" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" dependencies = [ - "gethostname", - "rustix 0.38.30", - "x11rb-protocol", + "libc", ] [[package]] -name = "x11rb-protocol" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" - -[[package]] -name = "xattr" -version = "0.2.3" +name = "xcb" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +checksum = "5d27b37e69b8c05bfadcd968eb1a4fe27c9c52565b727f88512f43b89567e262" dependencies = [ + "as-raw-xcb-connection", + "bitflags 1.3.2", "libc", + "quick-xml", ] [[package]] diff --git a/script/linux b/script/linux index 1acf7a6e1659d..7a3865bce1118 100755 --- a/script/linux +++ b/script/linux @@ -8,31 +8,37 @@ maysudo=$(command -v sudo || true) export maysudo # Ubuntu, Debian, etc. +# https://packages.ubuntu.com/ apt=$(command -v apt-get || true) -deps=( - libasound2-dev -) if [[ -n $apt ]]; then + deps=( + libasound2-dev + libfontconfig-dev + ) $maysudo "$apt" install -y "${deps[@]}" exit 0 fi # Fedora, CentOS, RHEL, etc. +# https://packages.fedoraproject.org/ dnf=$(command -v dnf || true) -deps=( - alsa-lib-devel -) if [[ -n $dnf ]]; then + deps=( + alsa-lib-devel + fontconfig-devel + ) $maysudo "$dnf" install -y "${deps[@]}" exit 0 fi # Arch, Manjaro, etc. +# https://archlinux.org/packages pacman=$(command -v pacman || true) -deps=( - alsa-lib -) if [[ -n $pacman ]]; then + deps=( + alsa-lib + fontconfig + ) $maysudo "$pacman" -S --noconfirm "${deps[@]}" exit 0 fi From 81bfa5fac432acfff5710bf2ea17786ac4ea20ca Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Sun, 4 Feb 2024 23:32:51 -0800 Subject: [PATCH 42/51] fix: create the settings and keymaps to unblock file watching --- crates/fs/src/fs.rs | 2 -- script/linux | 9 ++++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index 05c961c4cfeb6..edd3da101cb8a 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -301,8 +301,6 @@ impl Fs for RealFs { .configure(Config::default().with_poll_interval(latency)) .unwrap(); - //TODO: unblock this - #[cfg(not(target_os = "linux"))] watcher .watch(path, notify::RecursiveMode::Recursive) .unwrap(); diff --git a/script/linux b/script/linux index 7a3865bce1118..209c254f1bdd2 100755 --- a/script/linux +++ b/script/linux @@ -1,8 +1,15 @@ -#!/usr/bin/env bash +#!/usr/bin/bash -e # if not on Linux, do nothing [[ $(uname) == "Linux" ]] || exit 0 +# Copy settings and keymap to the user's home directory if they don't exist +mkdir -p "$HOME/.config/zed" +test -f "$HOME/.config/zed/settings.json" || + cp -uL ./assets/settings/initial_user_settings.json "$HOME/.config/zed/settings.json" +test -f "$HOME/.config/zed/keymap.json" || + cp -uL ./assets/keymaps/default.json "$HOME/.config/zed/keymap.json" + # if sudo is not installed, define an empty alias maysudo=$(command -v sudo || true) export maysudo From 7721b55808d6fa8f9b1c74ec4820661a30e4aebc Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 5 Feb 2024 21:49:00 -0800 Subject: [PATCH 43/51] Refactor cli and gpui dependnecies based on PR review feedback --- Cargo.lock | 41 +++++++++++++++++++++++++++++++++++++++++ crates/cli/Cargo.toml | 6 +++--- crates/gpui/Cargo.toml | 7 ++++--- 3 files changed, 48 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 37f3b09130bb3..5b1b221dda4c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1421,8 +1421,11 @@ version = "0.1.0" dependencies = [ "anyhow", "clap 3.2.25", + "core-foundation", + "core-services", "dirs 3.0.2", "ipc-channel", + "plist", "serde", "serde_derive", "util", @@ -1873,6 +1876,15 @@ dependencies = [ "libc", ] +[[package]] +name = "core-services" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92567e81db522550ebaf742c5d875624ec7820c2c7ee5f8c60e4ce7c2ae3c0fd" +dependencies = [ + "core-foundation", +] + [[package]] name = "core-text" version = "19.2.0" @@ -4289,6 +4301,15 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "line-wrap" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" +dependencies = [ + "safemem", +] + [[package]] name = "linkme" version = "0.3.17" @@ -5776,6 +5797,20 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "plist" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a4a0cfc5fb21a09dc6af4bf834cf10d4a32fccd9e2ea468c4b1751a097487aa" +dependencies = [ + "base64 0.21.4", + "indexmap 1.9.3", + "line-wrap", + "quick-xml", + "serde", + "time", +] + [[package]] name = "plugin" version = "0.1.0" @@ -7010,6 +7045,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + [[package]] name = "salsa20" version = "0.8.1" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index aa5ff1dcc33c7..d790644e99511 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -23,6 +23,6 @@ serde_derive.workspace = true util = { path = "../util" } [target.'cfg(target_os = "macos")'.dependencies] -#core-foundation = "0.9" -#core-services = "0.2" -#plist = "1.3" +core-foundation = "0.9" +core-services = "0.2" +plist = "1.3" diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index cc14ee4dfed64..2ba8a1f6f027a 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -26,9 +26,6 @@ anyhow.workspace = true async-task = "4.7" backtrace = { version = "0.3", optional = true } bitflags = "2.4.0" -blade-graphics = { git = "https://github.com/kvark/blade", rev = "62eb18d312f720a5aac8f508fe223146a24fc7f0" } -blade-macros = { git = "https://github.com/kvark/blade", rev = "62eb18d312f720a5aac8f508fe223146a24fc7f0" } -bytemuck = "1" collections = { path = "../collections" } ctor.workspace = true derive_more.workspace = true @@ -101,3 +98,7 @@ objc = "0.2" flume = "0.11" xcb = { version = "1.3", features = ["as-raw-xcb-connection"] } as-raw-xcb-connection = "1" +#TODO: use these on all platforms +blade-graphics = { git = "https://github.com/kvark/blade", rev = "62eb18d312f720a5aac8f508fe223146a24fc7f0" } +blade-macros = { git = "https://github.com/kvark/blade", rev = "62eb18d312f720a5aac8f508fe223146a24fc7f0" } +bytemuck = "1" From 78f32f30f57d39d95589549daae55547c6c7687f Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 5 Feb 2024 21:49:19 -0800 Subject: [PATCH 44/51] linux: introduce platform callbacks --- crates/gpui/src/platform/linux/platform.rs | 72 ++++++++++++++++++---- 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index 60cf135412d34..eae7893054b09 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -32,6 +32,19 @@ xcb::atoms_struct! { } } +#[derive(Default)] +struct Callbacks { + open_urls: Option)>>, + become_active: Option>, + resign_active: Option>, + quit: Option>, + reopen: Option>, + event: Option bool>>, + app_menu_action: Option>, + will_open_app_menu: Option>, + validate_app_menu_command: Option bool>>, +} + pub(crate) struct LinuxPlatform { xcb_connection: Arc, x_root_index: i32, @@ -40,6 +53,7 @@ pub(crate) struct LinuxPlatform { foreground_executor: ForegroundExecutor, main_receiver: flume::Receiver, text_system: Arc, + callbacks: Mutex, state: Mutex, } @@ -75,6 +89,7 @@ impl LinuxPlatform { foreground_executor: ForegroundExecutor::new(dispatcher.clone()), main_receiver, text_system: Arc::new(LinuxTextSystem::new()), + callbacks: Mutex::new(Callbacks::default()), state: Mutex::new(LinuxPlatformState { quit_requested: false, windows: HashMap::default(), @@ -110,6 +125,11 @@ impl Platform for LinuxPlatform { // window "x" button clicked by user, we gracefully exit let window = self.state.lock().windows.remove(&ev.window()).unwrap(); window.destroy(); + if self.state.lock().windows.is_empty() { + if let Some(ref mut fun) = self.callbacks.lock().quit { + fun(); + } + } } } } @@ -213,13 +233,21 @@ impl Platform for LinuxPlatform { log::warn!("unimplemented: set_display_link_output_callback"); } - fn start_display_link(&self, display_id: DisplayId) {} + fn start_display_link(&self, display_id: DisplayId) { + unimplemented!() + } - fn stop_display_link(&self, display_id: DisplayId) {} + fn stop_display_link(&self, display_id: DisplayId) { + unimplemented!() + } - fn open_url(&self, url: &str) {} + fn open_url(&self, url: &str) { + unimplemented!() + } - fn on_open_urls(&self, callback: Box)>) {} + fn on_open_urls(&self, callback: Box)>) { + self.callbacks.lock().open_urls = Some(callback); + } fn prompt_for_paths( &self, @@ -232,23 +260,41 @@ impl Platform for LinuxPlatform { unimplemented!() } - fn reveal_path(&self, path: &Path) {} + fn reveal_path(&self, path: &Path) { + unimplemented!() + } - fn on_become_active(&self, callback: Box) {} + fn on_become_active(&self, callback: Box) { + self.callbacks.lock().become_active = Some(callback); + } - fn on_resign_active(&self, callback: Box) {} + fn on_resign_active(&self, callback: Box) { + self.callbacks.lock().resign_active = Some(callback); + } - fn on_quit(&self, callback: Box) {} + fn on_quit(&self, callback: Box) { + self.callbacks.lock().quit = Some(callback); + } - fn on_reopen(&self, callback: Box) {} + fn on_reopen(&self, callback: Box) { + self.callbacks.lock().reopen = Some(callback); + } - fn on_event(&self, callback: Box bool>) {} + fn on_event(&self, callback: Box bool>) { + self.callbacks.lock().event = Some(callback); + } - fn on_app_menu_action(&self, callback: Box) {} + fn on_app_menu_action(&self, callback: Box) { + self.callbacks.lock().app_menu_action = Some(callback); + } - fn on_will_open_app_menu(&self, callback: Box) {} + fn on_will_open_app_menu(&self, callback: Box) { + self.callbacks.lock().will_open_app_menu = Some(callback); + } - fn on_validate_app_menu_command(&self, callback: Box bool>) {} + fn on_validate_app_menu_command(&self, callback: Box bool>) { + self.callbacks.lock().validate_app_menu_command = Some(callback); + } fn os_name(&self) -> &'static str { "Linux" From 7509677003da3398d0df796eca9c5435a12f576e Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 5 Feb 2024 21:51:22 -0800 Subject: [PATCH 45/51] add a few more libraries to the linux script --- script/linux | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/script/linux b/script/linux index 209c254f1bdd2..2dee0ac9fe4ce 100755 --- a/script/linux +++ b/script/linux @@ -21,6 +21,13 @@ if [[ -n $apt ]]; then deps=( libasound2-dev libfontconfig-dev + libxcb-dev + alsa-base + cmake + fontconfig + libssl-dev + build-essential + ) $maysudo "$apt" install -y "${deps[@]}" exit 0 From 11964dc731fd08c679f1a59d7d8ab78bfc478dd1 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Mon, 5 Feb 2024 22:50:25 -0800 Subject: [PATCH 46/51] blade: cull mask support for sprites --- Cargo.lock | 3 +-- crates/gpui/Cargo.toml | 4 ++-- crates/gpui/src/platform/linux/shaders.wgsl | 9 +++++++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5b1b221dda4c1..817eca20f6c41 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,7 +937,6 @@ dependencies = [ [[package]] name = "blade-graphics" version = "0.3.0" -source = "git+https://github.com/kvark/blade?rev=62eb18d312f720a5aac8f508fe223146a24fc7f0#62eb18d312f720a5aac8f508fe223146a24fc7f0" dependencies = [ "ash", "ash-window", @@ -967,7 +966,7 @@ dependencies = [ [[package]] name = "blade-macros" version = "0.2.1" -source = "git+https://github.com/kvark/blade?rev=62eb18d312f720a5aac8f508fe223146a24fc7f0#62eb18d312f720a5aac8f508fe223146a24fc7f0" +source = "git+https://github.com/kvark/blade?rev=f35bc605154e210ab6190291235889b6ddad73f1#f35bc605154e210ab6190291235889b6ddad73f1" dependencies = [ "proc-macro2", "quote", diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 2ba8a1f6f027a..864ed76935e76 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -99,6 +99,6 @@ flume = "0.11" xcb = { version = "1.3", features = ["as-raw-xcb-connection"] } as-raw-xcb-connection = "1" #TODO: use these on all platforms -blade-graphics = { git = "https://github.com/kvark/blade", rev = "62eb18d312f720a5aac8f508fe223146a24fc7f0" } -blade-macros = { git = "https://github.com/kvark/blade", rev = "62eb18d312f720a5aac8f508fe223146a24fc7f0" } +blade-graphics = { git = "https://github.com/kvark/blade", rev = "f35bc605154e210ab6190291235889b6ddad73f1" } +blade-macros = { git = "https://github.com/kvark/blade", rev = "f35bc605154e210ab6190291235889b6ddad73f1" } bytemuck = "1" diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl index 58dd3265bf9be..8a9fd31e96413 100644 --- a/crates/gpui/src/platform/linux/shaders.wgsl +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -504,6 +504,10 @@ fn vs_mono_sprite(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index @fragment fn fs_mono_sprite(input: MonoSpriteVarying) -> @location(0) vec4 { let sample = textureSample(t_sprite, s_sprite, input.tile_position).r; + // Alpha clip after using the derivatives. + if (any(input.clip_distances < vec4(0.0))) { + return vec4(0.0); + } return input.color * vec4(1.0, 1.0, 1.0, sample); } @@ -545,6 +549,11 @@ fn vs_poly_sprite(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index @fragment fn fs_poly_sprite(input: PolySpriteVarying) -> @location(0) vec4 { let sample = textureSample(t_sprite, s_sprite, input.tile_position); + // Alpha clip after using the derivatives. + if (any(input.clip_distances < vec4(0.0))) { + return vec4(0.0); + } + let sprite = b_poly_sprites[input.sprite_id]; let distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii); From d3562d4c9c8d95ee9f029a20349c1b0f5fbd0f7c Mon Sep 17 00:00:00 2001 From: Amin Yahyaabadi Date: Tue, 6 Feb 2024 22:54:40 -0800 Subject: [PATCH 47/51] Fixes for file-watching, user assets, and system dependencies (#2) * fix: avoid panics in case of non-existing path for watching * fix: copy the themes and plugins * Revert "add a few more libraries to the linux script" This reverts commit 7509677003da3398d0df796eca9c5435a12f576e. * fix: add vulkan validation layers to the system deps * fix: fix the themes paths --- Cargo.lock | 1 + crates/fs/src/fs.rs | 7 ++++++- script/linux | 19 +++++++++++-------- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 817eca20f6c41..1a1cc787d57bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -937,6 +937,7 @@ dependencies = [ [[package]] name = "blade-graphics" version = "0.3.0" +source = "git+https://github.com/kvark/blade?rev=f35bc605154e210ab6190291235889b6ddad73f1#f35bc605154e210ab6190291235889b6ddad73f1" dependencies = [ "ash", "ash-window", diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index edd3da101cb8a..bdb32d7d76eb7 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -287,12 +287,17 @@ impl Fs for RealFs { ) -> Pin>>> { let (tx, rx) = smol::channel::unbounded(); + if !path.exists() { + log::error!("watch path does not exist: {}", path.display()); + return Box::pin(rx); + } + let mut watcher = notify::recommended_watcher(move |res| match res { Ok(event) => { let _ = tx.try_send(vec![event]); } Err(err) => { - eprintln!("watch error: {:?}", err); + log::error!("watch error: {}", err); } }) .unwrap(); diff --git a/script/linux b/script/linux index 2dee0ac9fe4ce..f672ed8b85dbe 100755 --- a/script/linux +++ b/script/linux @@ -3,10 +3,17 @@ # if not on Linux, do nothing [[ $(uname) == "Linux" ]] || exit 0 -# Copy settings and keymap to the user's home directory if they don't exist +# Copy assets to the user's home directory if they don't exist mkdir -p "$HOME/.config/zed" + +mkdir -p "$HOME/.config/zed/plugins" + +mkdir -p "$HOME/.config/zed/themes" +cp -ruL ./assets/themes/*/*.json "$HOME/.config/zed/themes" + test -f "$HOME/.config/zed/settings.json" || cp -uL ./assets/settings/initial_user_settings.json "$HOME/.config/zed/settings.json" + test -f "$HOME/.config/zed/keymap.json" || cp -uL ./assets/keymaps/default.json "$HOME/.config/zed/keymap.json" @@ -21,13 +28,7 @@ if [[ -n $apt ]]; then deps=( libasound2-dev libfontconfig-dev - libxcb-dev - alsa-base - cmake - fontconfig - libssl-dev - build-essential - + vulkan-validationlayers* ) $maysudo "$apt" install -y "${deps[@]}" exit 0 @@ -40,6 +41,7 @@ if [[ -n $dnf ]]; then deps=( alsa-lib-devel fontconfig-devel + vulkan-validation-layers ) $maysudo "$dnf" install -y "${deps[@]}" exit 0 @@ -52,6 +54,7 @@ if [[ -n $pacman ]]; then deps=( alsa-lib fontconfig + vulkan-validation-layers ) $maysudo "$pacman" -S --noconfirm "${deps[@]}" exit 0 From e3ae7c4fe089d35e342a74d62563548977f14821 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Wed, 7 Feb 2024 00:33:40 -0800 Subject: [PATCH 48/51] linux: query window geometry for determining the surface extents --- .../gpui/src/platform/linux/blade_renderer.rs | 4 ++ crates/gpui/src/platform/linux/window.rs | 49 +++++++++---------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 1e3853b4752e4..492b135cde0da 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -299,6 +299,10 @@ impl BladeRenderer { self.viewport_size = size; } + pub fn viewport_size(&self) -> gpu::Extent { + self.viewport_size + } + pub fn atlas(&self) -> &Arc { &self.atlas } diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 031dcb0cd6ede..7ac7ebf9b4b7f 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -31,29 +31,33 @@ struct Callbacks { struct LinuxWindowInner { bounds: Bounds, - title_height: i32, - border_width: i32, scale_factor: f32, renderer: BladeRenderer, } impl LinuxWindowInner { - fn render_extent(&self) -> gpu::Extent { - gpu::Extent { - width: (self.bounds.size.width - 2 * self.border_width) as u32, - height: (self.bounds.size.height - 2 * self.border_width - self.title_height) as u32, - depth: 1, - } - } fn content_size(&self) -> Size { - let extent = self.render_extent(); + let size = self.renderer.viewport_size(); Size { - width: extent.width.into(), - height: extent.height.into(), + width: size.width.into(), + height: size.height.into(), } } } +fn query_render_extent(xcb_connection: &xcb::Connection, x_window: x::Window) -> gpu::Extent { + let cookie = xcb_connection.send_request(&x::GetGeometry { + drawable: x::Drawable::Window(x_window), + }); + let reply = xcb_connection.wait_for_reply(cookie).unwrap(); + println!("Got geometry {:?}", reply); + gpu::Extent { + width: reply.width() as u32, + height: reply.height() as u32, + depth: 1, + } +} + struct RawWindow { connection: *mut c_void, screen_id: i32, @@ -143,7 +147,6 @@ impl LinuxWindowState { }, WindowBounds::Fixed(bounds) => bounds.map(|p| p.0 as i32), }; - let border_width = 0i32; xcb_connection.send_request(&x::CreateWindow { depth: x::COPY_FROM_PARENT as u8, @@ -153,7 +156,7 @@ impl LinuxWindowState { y: bounds.origin.y as i16, width: bounds.size.width as u16, height: bounds.size.height as u16, - border_width: border_width as u16, + border_width: 0, class: x::WindowClass::InputOutput, visual: screen.root_visual(), value_list: &xcb_values, @@ -183,6 +186,10 @@ impl LinuxWindowState { xcb_connection.send_request(&x::MapWindow { window: x_window }); xcb_connection.flush().unwrap(); + //Warning: it looks like this reported size is immediately invalidated + // on some platforms, followed by a "ConfigureNotify" event. + let gpu_extent = query_render_extent(&xcb_connection, x_window); + let raw = RawWindow { connection: as_raw_xcb_connection::AsRawXcbConnection::as_raw_xcb_connection( xcb_connection, @@ -204,12 +211,6 @@ impl LinuxWindowState { .unwrap(), ); - let gpu_extent = gpu::Extent { - width: bounds.size.width as u32, - height: bounds.size.height as u32, - depth: 1, - }; - Self { xcb_connection: Arc::clone(xcb_connection), display: Rc::new(LinuxDisplay::new(xcb_connection, x_screen_index)), @@ -218,8 +219,6 @@ impl LinuxWindowState { callbacks: Mutex::new(Callbacks::default()), inner: Mutex::new(LinuxWindowInner { bounds, - title_height: 0, //TODO - border_width, scale_factor: 1.0, renderer: BladeRenderer::new(gpu, gpu_extent), }), @@ -254,9 +253,9 @@ impl LinuxWindowState { let mut inner = self.inner.lock(); let old_bounds = mem::replace(&mut inner.bounds, bounds); do_move = old_bounds.origin != bounds.origin; - if old_bounds.size != bounds.size { - let extent = inner.render_extent(); - inner.renderer.resize(extent); + let gpu_size = query_render_extent(&self.xcb_connection, self.x_window); + if inner.renderer.viewport_size() != gpu_size { + inner.renderer.resize(gpu_size); resize_args = Some((inner.content_size(), inner.scale_factor)); } } From 3734a390b2757aa221b752f93c7219a816b0cefa Mon Sep 17 00:00:00 2001 From: Mikayla Date: Wed, 7 Feb 2024 11:39:26 -0800 Subject: [PATCH 49/51] Mark TODOs and prep for merging main --- Cargo.toml | 2 +- crates/gpui/screenshot.png | Bin 9562 -> 0 bytes crates/gpui/src/platform/linux/blade_belt.rs | 4 +-- .../gpui/src/platform/linux/blade_renderer.rs | 2 +- crates/gpui/src/platform/linux/dispatcher.rs | 2 +- crates/gpui/src/platform/linux/platform.rs | 15 ++++++++-- crates/gpui/src/platform/linux/text_system.rs | 28 ++++++++++++++++-- crates/gpui/src/platform/linux/window.rs | 22 ++++++++++++++ crates/gpui/src/platform/test/platform.rs | 6 +++- crates/gpui/src/platform/test/text_system.rs | 1 + crates/project/src/terminals.rs | 4 +-- crates/zed/src/languages.rs | 3 +- crates/zed/src/main.rs | 3 +- 13 files changed, 77 insertions(+), 15 deletions(-) delete mode 100644 crates/gpui/screenshot.png diff --git a/Cargo.toml b/Cargo.toml index 26cd9ab0393bf..a4e51e6aae2b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -185,7 +185,7 @@ wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", rev = "v16.0. split-debuginfo = "unpacked" debug = "limited" -# TODO - Remove this +# todo!(linux) - Remove this [profile.dev.package.blade-graphics] split-debuginfo = "off" debug = "full" diff --git a/crates/gpui/screenshot.png b/crates/gpui/screenshot.png deleted file mode 100644 index 50597680f03024039943557738252f4270e19c3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9562 zcmaKSbyQT}7w;XqYv>k5Qc6091__657*a*LOBgyVqyzyOKw#()6{I_*yFp5j5@CQL z-r)DG-+%ATT4(mzpL2HJd)7MV>>Cf&RVO84Bmw|{R8vDm9{{j0T`YhA8$&t@@AYB` z$Xj1s38)xi{)J(1o+#=l0zma!;!9gx0Kf*I+J>sPlRzLsLP8=UA_4*ee0+RtY-}tn zEHD_nx3~A_&!3~Cqn|&2wzsz@CnrC9_Dn}dM_yiDLPA17K!BB%m7JU$4-ao=XXobT z=KTD8Yip~&zrVb^JS{CPHa0dQA|fy_P)|=!Qd08Xy?blYgV)#B zXf*ot^mKoJe{F4TX=!O;VPSD`ab#rV#fukED3qU{pPHJwtgP(ImoIPLyz%t(w6n9b zwzjshv4O#0=H}*FT3Vu_qAV;dmzS5v$H(L2gwvs%F6QcGCMmvGcz*>2L~S?pQ57TlP6EIva&`;N8i7HpPHJQn3xz65@KOtK}<}% zv9a;v$B&|-B5!YRXJ=<0AD`0F(!YQI{{H=&ii&D`d;8a~UvqPFEiEmDg@te5zAY{; z?&#=XU|`_oQ~rlx3UXy)hVhlhuIdV12+)48~~yu7?JGc$#RglK7L-Q3)2 zYiku06dD^FtE#Hh)YR_ZzyIyqHwOm?BobLsQ6VNKR##US9v*&haFCam_x=0#*RNmS zy?Zw-EbP^*SB8d$)6>(sy1J2(k)J<*=H}*ZXlU^D^+g~MmX?-AMn*h5Ja_KgVPs@{ z`t+%jlaq;w2^$;Rg9i^jeE1L@9c^oC8x#~YFfh>C+8PrR7(9ReysN7# zKR@5z-rm^QxVyXC!^0ybB}GL=1rCQ-S64f(WlCYz0HKG5nKuBCb=)>kk2}%<0Pgf@ zswf%;Snn<3!>A{x@DB&?&vJrPagwYOI1;U#zzIsN5$uE-S{kiNbP))=87LMvQIs>c zVu5U8US6OQ5-Sg$fX7D5%1sM}7RwX=0SdMaCJyEonb{rZvt%3(2G4VO8oUqohpx7C z8y*n?J-%aUn zqVDg0@3SJv+J$pDMXMdY-Y<7++B=mo|K|2$E8G8jON5NY^6K@EtI4aDErkO*6wb%x zgQw%C6@G-CPN<}=#n%DXl;woJxpI?9N#+GFVjE=qTsEePz29$IjgRS&J&N#&fW<#l z#R?$FXA*s-)|v8Za9{3&U*pyidjDXy)TDBW^#$YKf|JK#INm{5=dRzd@;A9l4RfWG#Z@apu|wk$&fuq$w!Sgs{qTApRVOU% za3PW%0{4o+{v)ZoYNBqkHAl$IU7u(2waeR#dtT3Ly$61{Ze3Y-l&>>|ME5kwKDK_b zfF_U)K3~L4LWa~zBzatLSyl*S6D1xZ6N zSRpVH3S8@v|IZWL))iAM2pZ%A$9PvuAS5YG@8F>=2ka57v0=uZvb3&n19-RU(r!gr z5R{5|>Sz$jn<-2y#z$NMm-(#!n}Z3o8V7!gy$#&TQo=kYu%C9MiaPF)ju)CdaJ_id z_V~@gY6OpAI~9D}97;&|ZhJ6Cs{ROhN?ywf-Z);0kKlAOJ`s;RU=?amFO=4V&4THr z@Oek0=q-d$#XYe)=|Zc&irB&mJK|IVWclm$GY5m6+FxaU9~yq+l^(e~p&eIRi#S&% z{+ZfBbytfV`jC)U@BNmar*H??X@=3xwMT>$?;hJrHOH9wFIrX%8JaJ10{!~Zab(uq ziX)oF#xy&mJ0)o@P5D8{cjT!^)xoT$GNW%B!v!1X&7dsDsbj%cHEo@9pI{4-3O-u6DB)4=WAIDM_ta(-YGT=Z(j&b62*ohJT4W`U2VZh;Z5BG zd?q}ej>c&~t===WB13Cqe@#6CtsyEU>l>bEs3ExGa&o#N&f%(~ve(Xe)KSMjx?=)!@4x~C5FFyXYO_RUq?p$)cYxs9Q?5|x` zUyHpZywP=g#fN!i?Lt8H{PGO#gMkq3(1+k1L&a+gnUvjgK8iWgQWC+z7)SLHX{55+ z(5yjE-%RveEb@zZ`q`AY(eM1AFnALy5@3h1LBY+38PTYApll=@@IoLg9 zaK;_JyC{69zr!>~vV_B^4CJNTT$ZfaszOUIf1}8tUOA987=!*r$tYM8|j#10PR@b!%lMl%v_OzjR*R9#2V+{@G@^U?p4M}M0QkhRWTWcr-i*7 zYe^Dme$K)tjtX~n3j#94}7Zrf)pYnvLscRp-KU(LFcrXA@J-qbdPe1xsVwn>O_2ga%5E?w(5kedfJ?%wTF zKn4xbB4w!4>ddZ!zW(}Si0+S%k7nSx-uk%xNYmkor;O$V;d7S1F`G%G0 zJ*z_o`$A_xfdEDeFu}e!&QA=qeGIue%6{#Y(C{SXV@}2X&z8Oj&5#`vroxnWbplVP z?@q`b8IJdFgXOHM+2lw4LbGld{0^&F??Fxap9d!)+p?5s$<@cTxLO)UdH+YumSftV{7MCOgC?VX64lpI=i{m4>v_}e*7rL~SAyXN&`87Q z%Cc~diOXLK&+RxX*Jh+mB8V!yWGiQ+l)7%pJv5Y`TB{bDcncEQvDSSm zJo|PB1)!D8a48bLm$mOd%d+~iKxA-{(fz8lA9SzDz|fCHE-k614Z6sqCmE;*9 zN-0%ui<~AAM3$|y-8eEtPH8u-9zO65dsgBzvtASmgvLiPhDgt7j=u~>)Mqg;=r&8= zQ0?HeKarnU=_phU5m)G$Acy=ex;}N`Y83pLDyZ>vf*gMAyF37_lAf!(Bq9zo!@YCy z`tbH*c>5{d9M|D&uzkm`f&~Mmbxn8^dI?oV@%g^@xI+MG2^QyWeOF$5z)`2nL5V`%Kp`z~sGm6Se!lQ3zGZ@CnT!G*-s zDLYhaUwP}E!!{raW8O>hypG2%igmN}Ty1)LHXl}UcNoIlOc_?K>3f^(x&Nu&NTLKE=Yx>#m-Upj2g?(gL}zJkorU%T?DtV4a;^Li!ob>g|-#4G&3+F*8* z;DIt&+luSex~r&S_v=tMA=*iuOtRONKwSP%3WF}=d0XI68ur)tP%Mfo3S4&X>R+F{ zrhitspGQ{8v)jkz1P`j{AOIYi8@1@q{mfkQQC#PVB9p_}p2xalk6=O>kI(8FEY}NI z4@KDuMWZCFbsHRo!utb=T5DJi=Nu=Vc1@=L=?kq$LIulW0>W$$(c@cd;~|ji0XSdp zR{`>+0ER%cC?jqA=xA$(R)~!$>_u=ooczhnpCe&@1&QdQ$!kJM;z-5do) zPJekie3a|UT-F8`u%@YyoqBGpgFmU67FO2YH~OoGy7?<2j2U-@Fo*cDKT1mZbA9Z? zt)?0YiK>vWS9|+8{E|eiLSdk~PBWlP&f47EIX3NA#So)gpBFWp?W)n}_`!meIsZm) z42xJ(6C8ScLDw_jZxr|oBi1gW{H)T8f6NPygJ$V+zH@3=VfiA>y=B~wG$scyRTqc9 z7brTAO@|BGg@))3wW^e(>~c}qNQL`V>d%+apliE&t232aH3^G43ZU#v@0pbjE+ zP}P+4PkY&dkzJX953oaOU{}zdPXMPm?A7NxcWh%yftk=UkKA9q@jl_2_bqW-&FlBK zYtSRSlbP6$DVdwGV0%k7=1Y?VovxM=$)#<8EddMM8yeo<8Dal3N$WJ%@!;%?AhrSC zP9hC3A+RQ;hCecUt)R%=ZqUU?wO2i4O?S7|kPvJ;C+%|chcrzz>7JOCY|Hj<{j^p> zL90yS09mwKx0YcHDdgT>*f_omkbsa~WflkUbN>jvx6fJOki*0V9pJg^F}Obs}Lr(SW@6M z;RK-z@clMrEATI*#1_81fnA?W66~WLJ)WDbmd@- zp2Vz0`Aa}vKKe^X&DGdI@PK#p8Mm$^mq0jeY8(~B*UMJ*BVYCQSQ0?ddg!cq2+=21$=PTvoPOX4nrT~ zxDxSJ7s1D8R%x6^nn>LU4QW0V)t6Upg|20|$wKE$&dKcWhlupv{)J9NABN;Bqr5iB>NcA;9bQYs5_!PGSXC38+(#g`amz;Y zyD*})`>gQ#HH)xW@ehXNt@fcNuKdq!sqDUdB$qT4^rF`8);$5V|AqzRvn|GLi&(g) zb2YOhyH^r&04_g7AGGf8wF!~Vf<2I%Aq+SApYYjVmg5I-Lx8-@HWjrkOTfF6Yt=Pf zo2*0-%N(!&RPFm&;`BV|`Vj|*d2fANSrEC0_{v`Y@W>D#VIh;wB5&rvK=0hTRW zk=dt|fG3;b#^f3{&}Stv%WbtWB3vtDdWknN<#y5&EYhc{a0@vkKO8C7J^I~0aK^{yTi5@ z7P+nRW4fTbTgkE@54?GXl~h!insl(N*b3w@PBM@jD}_FW05031r$4Ry0z^2T3=6sf zKWU9U{i&9&Yixz^B6OG``RnW+L4@6~EHodfjx9)2IuDaJvx=DGOngGQLUuVZGNmw= z9gQVz!g+|7kfWO3u!|40g;dC zo9}jxlZ53bB?}t5Al*6Pu|N>PO+-8m;+&(k{BSPuz}&80^GR&1(>~dqt6`oR@+uWc zoK|N!whLrbqlK@iCDan!ACSe*`8Y!rhyBqtW(OAS>Pw z597BFZrz33aJoPPmhtZc_pTLIIfrAM5(&C(M2=D1ia|}%B&}DZFSV?YqB!e7bhwBT0 zw)s$mB?Z|h-uZ^h3HzJB0lmL@};EysNLdm}RXm?kV4Tp5Tn$6dRf0*j`z) zlBxv0@;rM>`zZd#V65f=hn+FL(RXoefWtbxJt5=rbJSvl+iXWWnm}*o^lNzKE2Gi2 zgNfPY*F>#osHqgsV_c?tCPQT?{O|221d40lBgg*yp5ntN;v=PjlR`~3?_w`L9H;Av zAPp{wnkU3zz@GML7OZ`Uc=XWS*cDAn1CO0NvluwD7~@TjrTFqQHGrEHud@$5=azJN zXQ9T%NU#?P^m-4E!7PJ6*)(EGPy4ZNd1Jd`a=mB6o0Eb=D|aVlN%3glSG>MZqmPkv zDJs38*FSiP$nuE7OMB*S+>&dBgHz929_>FV1!&;18z^X66)B;--58g5`;4Yww%`;j z6*7oy{5_)&-gjSio#OFD3mO8<&gFULb0Y8N15h(E&|W`AF-}%*DIAQy4e@`SL-Tf!E=`p$xJ0Z)n7tYqJHU|kh`v8 zd61_V$4W}9$We1rF7VTOTl$&jJQiH~YTGUPjnw+Rz>5%+q!YMottKfR($dDqY)B*X02ASWO&bTx@q_DSIp zY1k}lHX{rCu*A2QB7T?21N0PBU^HQG-n;eIvo}QZNBG8$Gte`#Wag0re&a{_}t&Yym(lx3<4?a=5Qsr&&J)xN8)fXCfxn+2T5_ubp<~R(yu8T`p&yQ?z8^Q5Q;cJWn$_gYdJronH}m4u%kn(E%cMs!}4LN^<4 zGO~KsQ|{f%F=NpUyR}QS4Wc}(zx1z84NsT)vtG}lI@sa80qfgoa#Kn7yEMrShI5p^wL9n9 zl-L)l3;mRKhvMR+ytq4iV@{PHwM?dS%y|=&rKY+~NJVZRKQaefYp&KPe#m1VrG>d$ zsgbG+RDWc5IctNuH1_X30^4&;GN`gUkY=08Se_ooDoum9IrG)q`Z|UcYD%rx}(2A7H|J=FQ%-tt4;Emq}URI0EkT>}j>SYqQCV0#}v3yHox_buGc67Y?YAh2N4W zk$jwxA!s@{ZAq%me!R+h9);gTm0pmK_OpEVZsK=u`+nD>Sp`D(5FyrddObfP_3Rb2%vtT;hw1tBb9x50y7q zA3l)~FqI9}K~F~VdQWY zp_8pOJj4fI)IU<`hOk80lYl&s0rT{E)J)g?MD2@E{8iIC%78T-Q-z}sg2(w{0D3JT zr6pQY4^yox`Vf(xo}T6IAyY+3*_HE=DA3m2elQ@B0xa{`;)w*|ccCj=Bor3kJ5 z3s4pzsM-Y!flERRFO0iFjEp*b$pa`rFa&=M83I9f$?|S=++Ixn0&=ItG7MMm`G4sAf{(R($NeX(8DMlNX0FWXCEP4 zlOCZk2j}0#=oQCf1pnkiMtYR7VJtI9i$#SIBZP%@$IE}7eZyu1+q>VvWXYEWGl*Ke z4UYj6@h>0IxG+J8z1=-1d25Rr2ATB<6}nLqW+v$!x-GkIswh6^fC4@UFV3erQ8+}v zjYyBuKZJb)j6pt711zS5&^5z1Q{G@9h!67c@y0nI zbWZswTzzr`vG`vx%ysJb6=z_NhXEHt*GW9`F=@0KcyY!!XHcU-1Q*bbK26b{Ih?FK zGQp2Y5O}ZgGfK)wqJuCmLaIc+BPKH;LEP0@vZXsWOnX-r)tka&FqC8S*=aH^U8x_N zoVLbN@GFO6RY3aQC}F*_`}eao+qXfnyOS~tUFb;kb2$|GNB8}ng`3nv+12y&CVSuI zlI-NiCTx0d2SG3{Q|5Q6&Sf?p$e@#*ShD(?<3Ay&%g}R+KJO(#6V{`Ule#~Vv8b)m zw{=ClS7n(fN>t0hm%y;Uv`4xD=bi`lRtF`1t3NS?lq5Sl*_*(wl#8|wbkq9#f#JZg zqq++)_puAlEAoc02Z5aiE$E`~{7u~e``r_*h9G&J6WXQ6Aq)evwrZS`Yk5s;D-CG; zvr+Vep{wEJNi=-xPQcXh_(qOa02|MF?nM5Qz6tBGHukW!K-kGUs->QOS<{`OZOI0y zh3kI8|KBGQ|GuTjgU7|);Aeb(a;0Ywnuqz)gUr!X(@94M;KlR_Fzxm?W{`YKG*xv~ JDwJRm{|DkQwW$CA diff --git a/crates/gpui/src/platform/linux/blade_belt.rs b/crates/gpui/src/platform/linux/blade_belt.rs index b0e4be589398e..2562097cbb2a4 100644 --- a/crates/gpui/src/platform/linux/blade_belt.rs +++ b/crates/gpui/src/platform/linux/blade_belt.rs @@ -75,11 +75,11 @@ impl BladeBelt { chunk.into() } - //Note: assuming T: bytemuck::Zeroable + //todo!(linux): enforce T: bytemuck::Zeroable pub fn alloc_data(&mut self, data: &[T], gpu: &gpu::Context) -> gpu::BufferPiece { assert!(!data.is_empty()); let type_alignment = mem::align_of::() as u64; - assert_eq!( + debug_assert_eq!( self.desc.alignment % type_alignment, 0, "Type alignment {} is too big", diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 492b135cde0da..128b918ef7811 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -411,7 +411,7 @@ impl BladeRenderer { } PrimitiveBatch::Paths(paths) => { let mut encoder = pass.with(&self.pipelines.paths); - //TODO: group by texture ID + //todo!(linux): group by texture ID for path in paths { let tile = &self.path_tiles[&path.id]; let tex_info = self.atlas.get_texture_info(tile.texture_id); diff --git a/crates/gpui/src/platform/linux/dispatcher.rs b/crates/gpui/src/platform/linux/dispatcher.rs index 28526d360a428..108b8ed354e4b 100644 --- a/crates/gpui/src/platform/linux/dispatcher.rs +++ b/crates/gpui/src/platform/linux/dispatcher.rs @@ -90,7 +90,7 @@ impl PlatformDispatcher for LinuxDispatcher { fn dispatch_on_main_thread(&self, runnable: Runnable) { self.main_sender.send(runnable).unwrap(); // Send a message to the invisible window, forcing - // tha main loop to wake up and dispatch the runnable. + // the main loop to wake up and dispatch the runnable. self.xcb_connection.send_request(&x::SendEvent { propagate: false, destination: x::SendEventDest::Window(self.x_listener_window), diff --git a/crates/gpui/src/platform/linux/platform.rs b/crates/gpui/src/platform/linux/platform.rs index eae7893054b09..30d2b66bf013c 100644 --- a/crates/gpui/src/platform/linux/platform.rs +++ b/crates/gpui/src/platform/linux/platform.rs @@ -170,14 +170,19 @@ impl Platform for LinuxPlatform { self.state.lock().quit_requested = true; } + //todo!(linux) fn restart(&self) {} + //todo!(linux) fn activate(&self, ignoring_other_apps: bool) {} + //todo!(linux) fn hide(&self) {} + //todo!(linux) fn hide_other_apps(&self) {} + //todo!(linux) fn unhide_other_apps(&self) {} fn displays(&self) -> Vec> { @@ -199,6 +204,7 @@ impl Platform for LinuxPlatform { ))) } + //todo!(linux) fn active_window(&self) -> Option { None } @@ -324,6 +330,7 @@ impl Platform for LinuxPlatform { unimplemented!() } + //todo!(linux) fn set_menus(&self, menus: Vec, keymap: &Keymap) {} fn local_timezone(&self) -> UtcOffset { @@ -334,14 +341,16 @@ impl Platform for LinuxPlatform { unimplemented!() } + //todo!(linux) fn set_cursor_style(&self, style: CursorStyle) {} - fn should_auto_hide_scrollbars(&self) -> bool { - false - } + //todo!(linux) + fn should_auto_hide_scrollbars(&self) -> bool {} + //todo!(linux) fn write_to_clipboard(&self, item: ClipboardItem) {} + //todo!(linux) fn read_from_clipboard(&self) -> Option { None } diff --git a/crates/gpui/src/platform/linux/text_system.rs b/crates/gpui/src/platform/linux/text_system.rs index 187ffb526abc0..53faa936bc29b 100644 --- a/crates/gpui/src/platform/linux/text_system.rs +++ b/crates/gpui/src/platform/linux/text_system.rs @@ -29,6 +29,7 @@ struct LinuxTextSystemState { postscript_names_by_font_id: HashMap, } +// todo!(linux): Double check this unsafe impl Send for LinuxTextSystemState {} unsafe impl Sync for LinuxTextSystemState {} @@ -54,33 +55,52 @@ impl Default for LinuxTextSystem { #[allow(unused)] impl PlatformTextSystem for LinuxTextSystem { + // todo!(linux) fn add_fonts(&self, fonts: Vec>) -> Result<()> { - Ok(()) //TODO + Ok(()) } + + // todo!(linux) fn all_font_names(&self) -> Vec { Vec::new() } + + // todo!(linux) fn all_font_families(&self) -> Vec { Vec::new() } + + // todo!(linux) fn font_id(&self, descriptor: &Font) -> Result { - Ok(FontId(0)) //TODO + Ok(FontId(0)) } + + // todo!(linux) fn font_metrics(&self, font_id: FontId) -> FontMetrics { unimplemented!() } + + // todo!(linux) fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { unimplemented!() } + + // todo!(linux) fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result> { unimplemented!() } + + // todo!(linux) fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option { None } + + // todo!(linux) fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result> { unimplemented!() } + + // todo!(linux) fn rasterize_glyph( &self, params: &RenderGlyphParams, @@ -88,9 +108,13 @@ impl PlatformTextSystem for LinuxTextSystem { ) -> Result<(Size, Vec)> { unimplemented!() } + + // todo!(linux) fn layout_line(&self, text: &str, font_size: Pixels, runs: &[FontRun]) -> LineLayout { LineLayout::default() //TODO } + + // todo!(linux) fn wrap_line( &self, text: &str, diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 7ac7ebf9b4b7f..9ad02039e8a9e 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -77,6 +77,7 @@ pub(crate) struct LinuxWindowState { #[derive(Clone)] pub(crate) struct LinuxWindow(pub(crate) Arc); +//todo!(linux): Remove other RawWindowHandle implementation unsafe impl blade_rwh::HasRawWindowHandle for RawWindow { fn raw_window_handle(&self) -> blade_rwh::RawWindowHandle { let mut wh = blade_rwh::XcbWindowHandle::empty(); @@ -287,10 +288,12 @@ impl PlatformWindow for LinuxWindow { self.0.inner.lock().scale_factor } + //todo!(linux) fn titlebar_height(&self) -> Pixels { unimplemented!() } + //todo!(linux) fn appearance(&self) -> WindowAppearance { unimplemented!() } @@ -299,10 +302,12 @@ impl PlatformWindow for LinuxWindow { Rc::clone(&self.0.display) } + //todo!(linux) fn mouse_position(&self) -> Point { Point::default() } + //todo!(linux) fn modifiers(&self) -> crate::Modifiers { crate::Modifiers::default() } @@ -311,12 +316,15 @@ impl PlatformWindow for LinuxWindow { self } + //todo!(linux) fn set_input_handler(&mut self, input_handler: PlatformInputHandler) {} + //todo!(linux) fn take_input_handler(&mut self) -> Option { None } + //todo!(linux) fn prompt( &self, _level: crate::PromptLevel, @@ -327,24 +335,36 @@ impl PlatformWindow for LinuxWindow { unimplemented!() } + //todo!(linux) fn activate(&self) {} + //todo!(linux) fn set_title(&mut self, title: &str) {} + //todo!(linux) fn set_edited(&mut self, edited: bool) {} + //todo!(linux), this corresponds to `orderFrontCharacterPalette` on macOS, + // but it looks like the equivalent for Linux is GTK specific: + // + // https://docs.gtk.org/gtk3/signal.Entry.insert-emoji.html + // + // This API might need to change, or we might need to build an emoji picker into GPUI fn show_character_palette(&self) { unimplemented!() } + //todo!(linux) fn minimize(&self) { unimplemented!() } + //todo!(linux) fn zoom(&self) { unimplemented!() } + //todo!(linux) fn toggle_full_screen(&self) { unimplemented!() } @@ -385,10 +405,12 @@ impl PlatformWindow for LinuxWindow { self.0.callbacks.lock().appearance_changed = Some(callback); } + //todo!(linux) fn is_topmost_for_position(&self, _position: crate::Point) -> bool { unimplemented!() } + //todo!(linux) fn invalidate(&self) {} fn draw(&self, scene: &crate::Scene) { diff --git a/crates/gpui/src/platform/test/platform.rs b/crates/gpui/src/platform/test/platform.rs index 464e38b0c405d..7052377a00cae 100644 --- a/crates/gpui/src/platform/test/platform.rs +++ b/crates/gpui/src/platform/test/platform.rs @@ -118,7 +118,11 @@ impl Platform for TestPlatform { } fn text_system(&self) -> Arc { - Arc::new(TestTextSystem {}) + #[cfg(target_os = "linux")] + return Arc::new(TestTextSystem {}); + + #[cfg(target_os = "macos")] + return Arc::new(crate::platform::mac::MacTextSystem::new()); } fn run(&self, _on_finish_launching: Box) { diff --git a/crates/gpui/src/platform/test/text_system.rs b/crates/gpui/src/platform/test/text_system.rs index d7847f1012878..0e877aabbdb80 100644 --- a/crates/gpui/src/platform/test/text_system.rs +++ b/crates/gpui/src/platform/test/text_system.rs @@ -7,6 +7,7 @@ use std::borrow::Cow; pub(crate) struct TestTextSystem {} +//todo!(linux) #[allow(unused)] impl PlatformTextSystem for TestTextSystem { fn add_fonts(&self, fonts: Vec>) -> Result<()> { diff --git a/crates/project/src/terminals.rs b/crates/project/src/terminals.rs index 8c5156f3cbeb2..e125af052d790 100644 --- a/crates/project/src/terminals.rs +++ b/crates/project/src/terminals.rs @@ -7,8 +7,8 @@ use terminal::{ Terminal, TerminalBuilder, }; -#[cfg(target_os = "macos")] -use std::os::unix::ffi::OsStrExt; +// #[cfg(target_os = "macos")] +// use std::os::unix::ffi::OsStrExt; pub struct Terminals { pub(crate) local_handles: Vec>, diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 0448cb8ec6c61..e191ff1b660de 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -280,7 +280,8 @@ pub fn init( ], ); - /// Produces a link error on linux due to duplicated `state_new` symbol + // Produces a link error on linux due to duplicated `state_new` symbol + // todo!(linux): Restore purescript #[cfg(not(target_os = "linux"))] language( "purescript", diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 73acfdef343de..c291773e4a156 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -257,7 +257,7 @@ fn main() { initialize_workspace(app_state.clone(), cx); if stdout_is_a_pty() { - //TODO: unblock this + //todo!(linux): unblock this #[cfg(not(target_os = "linux"))] upload_panics_and_crashes(http.clone(), cx); cx.activate(true); @@ -919,6 +919,7 @@ fn load_user_themes_in_background(fs: Arc, cx: &mut AppContext) { .detach(); } +//todo!(linux): Port fsevents to linux /// Spawns a background task to watch the themes directory for changes. #[cfg(target_os = "macos")] fn watch_themes(fs: Arc, cx: &mut AppContext) { From f507698c62422752a56dd2b75d07b961ee98088a Mon Sep 17 00:00:00 2001 From: Mikayla Date: Wed, 7 Feb 2024 11:59:52 -0800 Subject: [PATCH 50/51] Fix a few out of date warnings --- crates/gpui/src/platform/test.rs | 1 + crates/gpui/src/platform/test/platform.rs | 6 +++--- crates/live_kit_client/src/test.rs | 3 +-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index 6cd833b681b9d..3b08985737801 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -7,5 +7,6 @@ mod window; pub(crate) use dispatcher::*; pub(crate) use display::*; pub(crate) use platform::*; +#[cfg(not(target_os = "macos"))] pub(crate) use text_system::*; pub(crate) use window::*; diff --git a/crates/gpui/src/platform/test/platform.rs b/crates/gpui/src/platform/test/platform.rs index 942211d577748..6a17c731d6df4 100644 --- a/crates/gpui/src/platform/test/platform.rs +++ b/crates/gpui/src/platform/test/platform.rs @@ -1,7 +1,7 @@ use crate::{ AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId, ForegroundExecutor, - Keymap, Platform, PlatformDisplay, PlatformTextSystem, Task, TestDisplay, TestTextSystem, - TestWindow, WindowOptions, + Keymap, Platform, PlatformDisplay, PlatformTextSystem, Task, TestDisplay, TestWindow, + WindowAppearance, WindowOptions, }; use anyhow::{anyhow, Result}; use collections::VecDeque; @@ -121,7 +121,7 @@ impl Platform for TestPlatform { fn text_system(&self) -> Arc { #[cfg(target_os = "linux")] - return Arc::new(TestTextSystem {}); + return Arc::new(crate::platform::test::TestTextSystem {}); #[cfg(target_os = "macos")] return Arc::new(crate::platform::mac::MacTextSystem::new()); diff --git a/crates/live_kit_client/src/test.rs b/crates/live_kit_client/src/test.rs index aa2c91febb9c1..677370c0b8321 100644 --- a/crates/live_kit_client/src/test.rs +++ b/crates/live_kit_client/src/test.rs @@ -5,8 +5,7 @@ use collections::{BTreeMap, HashMap, HashSet}; use futures::Stream; use gpui::{BackgroundExecutor, ImageSource}; use live_kit_server::{proto, token}; -#[cfg(target_os = "macos")] -use media::core_video::CVImageBuffer; + use parking_lot::Mutex; use postage::watch; use std::{ From be455f7f283d16e89ab559407c087c4ffa820578 Mon Sep 17 00:00:00 2001 From: Mikayla Date: Wed, 7 Feb 2024 12:04:29 -0800 Subject: [PATCH 51/51] Restore nanoid dependency --- crates/live_kit_client/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/live_kit_client/Cargo.toml b/crates/live_kit_client/Cargo.toml index 956240215cf56..f6ae36c93547b 100644 --- a/crates/live_kit_client/Cargo.toml +++ b/crates/live_kit_client/Cargo.toml @@ -19,7 +19,7 @@ test-support = [ "collections/test-support", "gpui/test-support", "live_kit_server", - #"nanoid", + "nanoid", ] [dependencies]