Update Blade with acquire fixes, multisampling (#22409)

Dzmitry Malyshau created

Fixes #22406

Picks up:
- https://github.com/kvark/blade/pull/234 and
https://github.com/kvark/blade/pull/237 (`OutOfDate` workaround)
- https://github.com/kvark/blade/pull/229 ("metal-rs" to "objc2"
migration)

Release Notes:

- Improved handling of resizing and multiple monitors in Linux/Windows

Change summary

Cargo.lock                                       | 241 +++++++++++++++++
Cargo.toml                                       |  13 
crates/gpui/Cargo.toml                           |   7 
crates/gpui/src/platform/blade/blade_atlas.rs    |   1 
crates/gpui/src/platform/blade/blade_renderer.rs | 111 +++++---
5 files changed, 306 insertions(+), 67 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -1805,16 +1805,14 @@ dependencies = [
 
 [[package]]
 name = "blade-graphics"
-version = "0.5.0"
-source = "git+https://github.com/kvark/blade?rev=099555282605c7c4cca9e66a8f40148298347f80#099555282605c7c4cca9e66a8f40148298347f80"
+version = "0.6.0"
+source = "git+https://github.com/kvark/blade?rev=091a8401033847bb9b6ace3fcf70448d069621c5#091a8401033847bb9b6ace3fcf70448d069621c5"
 dependencies = [
  "ash",
  "ash-window",
  "bitflags 2.6.0",
- "block",
  "bytemuck",
  "codespan-reporting",
- "core-graphics-types 0.1.3",
  "glow",
  "gpu-alloc",
  "gpu-alloc-ash",
@@ -1823,10 +1821,14 @@ dependencies = [
  "khronos-egl",
  "libloading",
  "log",
- "metal",
  "mint",
  "naga",
- "objc",
+ "objc2",
+ "objc2-app-kit",
+ "objc2-foundation",
+ "objc2-metal",
+ "objc2-quartz-core",
+ "objc2-ui-kit",
  "raw-window-handle",
  "slab",
  "wasm-bindgen",
@@ -1836,7 +1838,7 @@ dependencies = [
 [[package]]
 name = "blade-macros"
 version = "0.3.0"
-source = "git+https://github.com/kvark/blade?rev=099555282605c7c4cca9e66a8f40148298347f80#099555282605c7c4cca9e66a8f40148298347f80"
+source = "git+https://github.com/kvark/blade?rev=091a8401033847bb9b6ace3fcf70448d069621c5#091a8401033847bb9b6ace3fcf70448d069621c5"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1845,8 +1847,8 @@ dependencies = [
 
 [[package]]
 name = "blade-util"
-version = "0.1.0"
-source = "git+https://github.com/kvark/blade?rev=099555282605c7c4cca9e66a8f40148298347f80#099555282605c7c4cca9e66a8f40148298347f80"
+version = "0.2.0"
+source = "git+https://github.com/kvark/blade?rev=091a8401033847bb9b6ace3fcf70448d069621c5#091a8401033847bb9b6ace3fcf70448d069621c5"
 dependencies = [
  "blade-graphics",
  "bytemuck",
@@ -1891,6 +1893,15 @@ dependencies = [
  "generic-array",
 ]
 
+[[package]]
+name = "block2"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
+dependencies = [
+ "objc2",
+]
+
 [[package]]
 name = "blocking"
 version = "1.6.1"
@@ -5342,6 +5353,8 @@ dependencies = [
  "metal",
  "num_cpus",
  "objc",
+ "objc2",
+ "objc2-metal",
  "oo7",
  "open",
  "parking",
@@ -7578,7 +7591,8 @@ dependencies = [
 [[package]]
 name = "metal"
 version = "0.30.0"
-source = "git+https://github.com/gfx-rs/metal-rs?rev=ef768ff9d742ae6a0f4e83ddc8031264e7d460c4#ef768ff9d742ae6a0f4e83ddc8031264e7d460c4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c3572083504c43e14aec05447f8a3d57cce0f66d7a3c1b9058572eca4d70ab9"
 dependencies = [
  "bitflags 2.6.0",
  "block",
@@ -7718,8 +7732,9 @@ checksum = "defc4c55412d89136f966bbb339008b474350e5e6e78d2714439c386b3137a03"
 
 [[package]]
 name = "naga"
-version = "23.0.0"
-source = "git+https://github.com/gfx-rs/wgpu?rev=1a643291c2e8854ba7e4f5445a4388202731bfa1#1a643291c2e8854ba7e4f5445a4388202731bfa1"
+version = "23.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "364f94bc34f61332abebe8cad6f6cd82a5b65cff22c828d05d0968911462ca4f"
 dependencies = [
  "arrayvec",
  "bit-set 0.8.0",
@@ -8149,6 +8164,208 @@ dependencies = [
  "malloc_buf",
 ]
 
+[[package]]
+name = "objc-sys"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
+
+[[package]]
+name = "objc2"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
+dependencies = [
+ "objc-sys",
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2-app-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
+dependencies = [
+ "bitflags 2.6.0",
+ "block2",
+ "libc",
+ "objc2",
+ "objc2-core-data",
+ "objc2-core-image",
+ "objc2-foundation",
+ "objc2-quartz-core",
+]
+
+[[package]]
+name = "objc2-cloud-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
+dependencies = [
+ "bitflags 2.6.0",
+ "block2",
+ "objc2",
+ "objc2-core-location",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-contacts"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889"
+dependencies = [
+ "block2",
+ "objc2",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-core-data"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
+dependencies = [
+ "bitflags 2.6.0",
+ "block2",
+ "objc2",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-core-image"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
+dependencies = [
+ "block2",
+ "objc2",
+ "objc2-foundation",
+ "objc2-metal",
+]
+
+[[package]]
+name = "objc2-core-location"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781"
+dependencies = [
+ "block2",
+ "objc2",
+ "objc2-contacts",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "4.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8"
+
+[[package]]
+name = "objc2-foundation"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
+dependencies = [
+ "bitflags 2.6.0",
+ "block2",
+ "libc",
+ "objc2",
+]
+
+[[package]]
+name = "objc2-link-presentation"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
+dependencies = [
+ "block2",
+ "objc2",
+ "objc2-app-kit",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-metal"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
+dependencies = [
+ "bitflags 2.6.0",
+ "block2",
+ "objc2",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-quartz-core"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
+dependencies = [
+ "bitflags 2.6.0",
+ "block2",
+ "objc2",
+ "objc2-foundation",
+ "objc2-metal",
+]
+
+[[package]]
+name = "objc2-symbols"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc"
+dependencies = [
+ "objc2",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-ui-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
+dependencies = [
+ "bitflags 2.6.0",
+ "block2",
+ "objc2",
+ "objc2-cloud-kit",
+ "objc2-core-data",
+ "objc2-core-image",
+ "objc2-core-location",
+ "objc2-foundation",
+ "objc2-link-presentation",
+ "objc2-quartz-core",
+ "objc2-symbols",
+ "objc2-uniform-type-identifiers",
+ "objc2-user-notifications",
+]
+
+[[package]]
+name = "objc2-uniform-type-identifiers"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe"
+dependencies = [
+ "block2",
+ "objc2",
+ "objc2-foundation",
+]
+
+[[package]]
+name = "objc2-user-notifications"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
+dependencies = [
+ "bitflags 2.6.0",
+ "block2",
+ "objc2",
+ "objc2-core-location",
+ "objc2-foundation",
+]
+
 [[package]]
 name = "object"
 version = "0.36.5"

Cargo.toml 🔗

@@ -355,9 +355,9 @@ async-watch = "0.3.1"
 async_zip = { version = "0.0.17", features = ["deflate", "deflate64"] }
 base64 = "0.22"
 bitflags = "2.6.0"
-blade-graphics = { git = "https://github.com/kvark/blade", rev = "099555282605c7c4cca9e66a8f40148298347f80" }
-blade-macros = { git = "https://github.com/kvark/blade", rev = "099555282605c7c4cca9e66a8f40148298347f80" }
-blade-util = { git = "https://github.com/kvark/blade", rev = "099555282605c7c4cca9e66a8f40148298347f80" }
+blade-graphics = { git = "https://github.com/kvark/blade", rev = "091a8401033847bb9b6ace3fcf70448d069621c5" }
+blade-macros = { git = "https://github.com/kvark/blade", rev = "091a8401033847bb9b6ace3fcf70448d069621c5" }
+blade-util = { git = "https://github.com/kvark/blade", rev = "091a8401033847bb9b6ace3fcf70448d069621c5" }
 blake3 = "1.5.3"
 bytes = "1.0"
 cargo_metadata = "0.19"
@@ -524,12 +524,7 @@ wasmtime-wasi = "24"
 which = "6.0.0"
 wit-component = "0.201"
 zstd = "0.11"
-# Custom metal-rs is only needed for "macos-blade" feature of GPUI
-#TODO: switch to crates once these are published:
-# - https://github.com/gfx-rs/metal-rs/pull/335
-# - https://github.com/gfx-rs/metal-rs/pull/336
-# - https://github.com/gfx-rs/metal-rs/pull/337
-metal = { git = "https://github.com/gfx-rs/metal-rs", rev = "ef768ff9d742ae6a0f4e83ddc8031264e7d460c4" }
+metal = "0.30"
 
 [workspace.dependencies.async-stripe]
 git = "https://github.com/zed-industries/async-stripe"

crates/gpui/Cargo.toml 🔗

@@ -22,7 +22,7 @@ test-support = [
     "x11",
 ]
 runtime_shaders = []
-macos-blade = ["blade-graphics", "blade-macros", "blade-util", "bytemuck"]
+macos-blade = ["blade-graphics", "blade-macros", "blade-util", "bytemuck", "objc2", "objc2-metal"]
 wayland = [
     "blade-graphics",
     "blade-macros",
@@ -132,11 +132,14 @@ core-foundation.workspace = true
 core-foundation-sys = "0.8"
 core-graphics = "0.23"
 core-text = "20.1"
-font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "40391b7", optional = true}
+font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "40391b7", optional = true }
 foreign-types = "0.5"
 log.workspace = true
 media.workspace = true
 objc = "0.2"
+objc2 = { version = "0.5", optional = true }
+objc2-metal = { version = "0.2", optional = true }
+#TODO: replace with "objc2"
 metal.workspace = true
 
 [target.'cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))'.dependencies]

crates/gpui/src/platform/blade/blade_renderer.rs 🔗

@@ -174,8 +174,9 @@ impl BladePipelines {
                     ..Default::default()
                 },
                 depth_stencil: None,
-                fragment: shader.at("fs_quad"),
+                fragment: Some(shader.at("fs_quad")),
                 color_targets,
+                multisample_state: gpu::MultisampleState::default(),
             }),
             shadows: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
                 name: "shadows",
@@ -187,8 +188,9 @@ impl BladePipelines {
                     ..Default::default()
                 },
                 depth_stencil: None,
-                fragment: shader.at("fs_shadow"),
+                fragment: Some(shader.at("fs_shadow")),
                 color_targets,
+                multisample_state: gpu::MultisampleState::default(),
             }),
             path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
                 name: "path_rasterization",
@@ -200,12 +202,13 @@ impl BladePipelines {
                     ..Default::default()
                 },
                 depth_stencil: None,
-                fragment: shader.at("fs_path_rasterization"),
+                fragment: Some(shader.at("fs_path_rasterization")),
                 color_targets: &[gpu::ColorTargetState {
                     format: PATH_TEXTURE_FORMAT,
                     blend: Some(gpu::BlendState::ADDITIVE),
                     write_mask: gpu::ColorWrites::default(),
                 }],
+                multisample_state: gpu::MultisampleState::default(),
             }),
             paths: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
                 name: "paths",
@@ -217,8 +220,9 @@ impl BladePipelines {
                     ..Default::default()
                 },
                 depth_stencil: None,
-                fragment: shader.at("fs_path"),
+                fragment: Some(shader.at("fs_path")),
                 color_targets,
+                multisample_state: gpu::MultisampleState::default(),
             }),
             underlines: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
                 name: "underlines",
@@ -230,8 +234,9 @@ impl BladePipelines {
                     ..Default::default()
                 },
                 depth_stencil: None,
-                fragment: shader.at("fs_underline"),
+                fragment: Some(shader.at("fs_underline")),
                 color_targets,
+                multisample_state: gpu::MultisampleState::default(),
             }),
             mono_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
                 name: "mono-sprites",
@@ -243,8 +248,9 @@ impl BladePipelines {
                     ..Default::default()
                 },
                 depth_stencil: None,
-                fragment: shader.at("fs_mono_sprite"),
+                fragment: Some(shader.at("fs_mono_sprite")),
                 color_targets,
+                multisample_state: gpu::MultisampleState::default(),
             }),
             poly_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
                 name: "poly-sprites",
@@ -256,8 +262,9 @@ impl BladePipelines {
                     ..Default::default()
                 },
                 depth_stencil: None,
-                fragment: shader.at("fs_poly_sprite"),
+                fragment: Some(shader.at("fs_poly_sprite")),
                 color_targets,
+                multisample_state: gpu::MultisampleState::default(),
             }),
             surfaces: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
                 name: "surfaces",
@@ -269,8 +276,9 @@ impl BladePipelines {
                     ..Default::default()
                 },
                 depth_stencil: None,
-                fragment: shader.at("fs_surface"),
+                fragment: Some(shader.at("fs_surface")),
                 color_targets,
+                multisample_state: gpu::MultisampleState::default(),
             }),
         }
     }
@@ -350,8 +358,10 @@ impl BladeRenderer {
 
         #[cfg(target_os = "macos")]
         let core_video_texture_cache = unsafe {
-            use foreign_types::ForeignType as _;
-            CVMetalTextureCache::new(context.gpu.metal_device().as_ptr()).unwrap()
+            CVMetalTextureCache::new(
+                objc2::rc::Retained::as_ptr(&context.gpu.metal_device()) as *mut _
+            )
+            .unwrap()
         };
 
         Ok(Self {
@@ -440,13 +450,12 @@ impl BladeRenderer {
 
     #[cfg(target_os = "macos")]
     pub fn layer(&self) -> metal::MetalLayer {
-        self.surface.metal_layer()
+        unsafe { foreign_types::ForeignType::from_ptr(self.layer_ptr()) }
     }
 
     #[cfg(target_os = "macos")]
     pub fn layer_ptr(&self) -> *mut metal::CAMetalLayer {
-        use metal::foreign_types::ForeignType as _;
-        self.surface.metal_layer().as_ptr()
+        objc2::rc::Retained::as_ptr(&self.surface.metal_layer()) as *mut _
     }
 
     #[profiling::function]
@@ -678,45 +687,59 @@ impl BladeRenderer {
 
                             #[cfg(target_os = "macos")]
                             {
-                                let (t_y, t_cb_cr) = {
+                                let (t_y, t_cb_cr) = unsafe {
                                     use core_foundation::base::TCFType as _;
                                     use std::ptr;
 
                                     assert_eq!(
-                                    surface.image_buffer.pixel_format_type(),
-                                    media::core_video::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
-                                );
-
-                                    let y_texture = unsafe {
-                                        self.core_video_texture_cache
-                                            .create_texture_from_image(
-                                                surface.image_buffer.as_concrete_TypeRef(),
-                                                ptr::null(),
-                                                metal::MTLPixelFormat::R8Unorm,
-                                                surface.image_buffer.plane_width(0),
-                                                surface.image_buffer.plane_height(0),
-                                                0,
-                                            )
-                                            .unwrap()
-                                    };
-                                    let cb_cr_texture = unsafe {
-                                        self.core_video_texture_cache
-                                            .create_texture_from_image(
-                                                surface.image_buffer.as_concrete_TypeRef(),
-                                                ptr::null(),
-                                                metal::MTLPixelFormat::RG8Unorm,
-                                                surface.image_buffer.plane_width(1),
-                                                surface.image_buffer.plane_height(1),
-                                                1,
-                                            )
-                                            .unwrap()
-                                    };
+                                        surface.image_buffer.pixel_format_type(),
+                                        media::core_video::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
+                                    );
+
+                                    let y_texture = self
+                                        .core_video_texture_cache
+                                        .create_texture_from_image(
+                                            surface.image_buffer.as_concrete_TypeRef(),
+                                            ptr::null(),
+                                            metal::MTLPixelFormat::R8Unorm,
+                                            surface.image_buffer.plane_width(0),
+                                            surface.image_buffer.plane_height(0),
+                                            0,
+                                        )
+                                        .unwrap();
+                                    let cb_cr_texture = self
+                                        .core_video_texture_cache
+                                        .create_texture_from_image(
+                                            surface.image_buffer.as_concrete_TypeRef(),
+                                            ptr::null(),
+                                            metal::MTLPixelFormat::RG8Unorm,
+                                            surface.image_buffer.plane_width(1),
+                                            surface.image_buffer.plane_height(1),
+                                            1,
+                                        )
+                                        .unwrap();
                                     (
                                         gpu::TextureView::from_metal_texture(
-                                            y_texture.as_texture_ref(),
+                                            &objc2::rc::Retained::retain(
+                                                foreign_types::ForeignTypeRef::as_ptr(
+                                                    y_texture.as_texture_ref(),
+                                                )
+                                                    as *mut objc2::runtime::ProtocolObject<
+                                                        dyn objc2_metal::MTLTexture,
+                                                    >,
+                                            )
+                                            .unwrap(),
                                         ),
                                         gpu::TextureView::from_metal_texture(
-                                            cb_cr_texture.as_texture_ref(),
+                                            &objc2::rc::Retained::retain(
+                                                foreign_types::ForeignTypeRef::as_ptr(
+                                                    cb_cr_texture.as_texture_ref(),
+                                                )
+                                                    as *mut objc2::runtime::ProtocolObject<
+                                                        dyn objc2_metal::MTLTexture,
+                                                    >,
+                                            )
+                                            .unwrap(),
                                         ),
                                     )
                                 };