Enable Blade on MacOS via "macos-blade" feature (#7669)

Dzmitry Malyshau and Mikayla created

Depends on https://github.com/zed-industries/font-kit/pull/2 and
https://github.com/kvark/blade/pull/77

This change enables Blade to be also used on MacOS. It will also make it
easier to use it on Windows.

What works: most of the things. Zed loads as fast and appears equally
responsive to the current renderer.
<img width="306" alt="Screenshot 2024-02-11 at 12 09 15 AM"
src="https://github.com/zed-industries/zed/assets/107301/66d82f45-5ea2-4e2b-86c6-5b3ed333c827">

Things missing:
- [x] video streaming. ~~Requires a bit of plumbing on both Blade and
Zed sides, but all fairly straightforward.~~
  -  verified with a local setup
- [x] resize. ~~Not sure where exactly to hook up the reaction on the
window size change. Once we know where, the fix is one line.~~
- [ ] fine-tune CA Layer
- this isn't a blocker for merging the PR, but it would be a blocker if
we wanted to switch to the new path by default
- [ ] rebase on latest, get the dependency merged (need review/merge of
https://github.com/zed-industries/font-kit/pull/2!)

Update: I implemented resize support as well as "surface" rendering on
the Blade path (which will be useful on Linux/Windows later on). I
haven't tested the latter though - not sure how to get something
streaming. Would appreciate some help! I don't think this should be a
blocker to this PR, anyway.

The only little piece that's missing for the Blade on MacOS path to be
full-featured is fine-tuning the CALayer configuration. Zed does a lot
of careful logic in configuring the layer, such as switching the
"present with transaction" on/off intermittently, which Blade path
doesn't have yet.

Release Notes:
- N/A

---------

Co-authored-by: Mikayla <mikayla@zed.dev>

Change summary

.github/workflows/ci.yml                         |   4 
Cargo.lock                                       |  42 +--
Cargo.toml                                       |   3 
crates/gpui/Cargo.toml                           |  21 
crates/gpui/build.rs                             |  11 
crates/gpui/src/gpui.rs                          |   5 
crates/gpui/src/platform.rs                      |   6 
crates/gpui/src/platform/blade.rs                |   8 
crates/gpui/src/platform/blade/blade_atlas.rs    |   0 
crates/gpui/src/platform/blade/blade_belt.rs     |   0 
crates/gpui/src/platform/blade/blade_renderer.rs | 245 ++++++++++++++++-
crates/gpui/src/platform/blade/shaders.wgsl      |  57 +++
crates/gpui/src/platform/linux.rs                |   6 
crates/gpui/src/platform/linux/wayland/window.rs |  14 
crates/gpui/src/platform/linux/x11/window.rs     |  33 +
crates/gpui/src/platform/mac.rs                  |  15 
crates/gpui/src/platform/mac/display_link.rs     |  10 
crates/gpui/src/platform/mac/events.rs           |   2 
crates/gpui/src/platform/mac/metal_renderer.rs   |  59 ++-
crates/gpui/src/platform/mac/platform.rs         |   9 
crates/gpui/src/platform/mac/window.rs           |  85 ++++-
crates/media/Cargo.toml                          |   4 
script/bundle                                    |  16 
23 files changed, 492 insertions(+), 163 deletions(-)

Detailed changes

.github/workflows/ci.yml 🔗

@@ -91,8 +91,8 @@ jobs:
       - name: Build collab
         run: cargo build -p collab
 
-      - name: Build other binaries
-        run: cargo build --workspace --bins --all-features
+      - name: Build other binaries and features
+        run: cargo build --workspace --bins --all-features; cargo check -p gpui --features "macos-blade"
 
   # todo!(linux): Actually run the tests
   linux_tests:

Cargo.lock 🔗

@@ -1396,7 +1396,7 @@ dependencies = [
 [[package]]
 name = "blade-graphics"
 version = "0.3.0"
-source = "git+https://github.com/kvark/blade?rev=c4f951a88b345724cb952e920ad30e39851f7760#c4f951a88b345724cb952e920ad30e39851f7760"
+source = "git+https://github.com/kvark/blade?rev=e9d93a4d41f3946a03ffb76136290d6ccf7f2b80#e9d93a4d41f3946a03ffb76136290d6ccf7f2b80"
 dependencies = [
  "ash",
  "ash-window",
@@ -1413,7 +1413,7 @@ dependencies = [
  "khronos-egl",
  "libloading 0.8.0",
  "log",
- "metal 0.25.0",
+ "metal",
  "mint",
  "naga",
  "objc",
@@ -1426,7 +1426,7 @@ dependencies = [
 [[package]]
 name = "blade-macros"
 version = "0.2.1"
-source = "git+https://github.com/kvark/blade?rev=c4f951a88b345724cb952e920ad30e39851f7760#c4f951a88b345724cb952e920ad30e39851f7760"
+source = "git+https://github.com/kvark/blade?rev=e9d93a4d41f3946a03ffb76136290d6ccf7f2b80#e9d93a4d41f3946a03ffb76136290d6ccf7f2b80"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2359,13 +2359,13 @@ dependencies = [
 
 [[package]]
 name = "core-text"
-version = "19.2.0"
+version = "20.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25"
+checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5"
 dependencies = [
  "core-foundation",
- "core-graphics 0.22.3",
- "foreign-types 0.3.2",
+ "core-graphics 0.23.1",
+ "foreign-types 0.5.0",
  "libc",
 ]
 
@@ -3511,12 +3511,12 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 [[package]]
 name = "font-kit"
 version = "0.11.0"
-source = "git+https://github.com/zed-industries/font-kit?rev=d97147f#d97147ff11a9024b9707d9c9c7e3a0bdaba048ac"
+source = "git+https://github.com/zed-industries/font-kit?rev=5a5c4d4#5a5c4d4ca395c74eb0abde38508e170ce0fd761a"
 dependencies = [
  "bitflags 1.3.2",
  "byteorder",
  "core-foundation",
- "core-graphics 0.22.3",
+ "core-graphics 0.23.1",
  "core-text",
  "dirs-next",
  "dwrote",
@@ -4085,7 +4085,7 @@ dependencies = [
  "cocoa",
  "collections",
  "core-foundation",
- "core-graphics 0.22.3",
+ "core-graphics 0.23.1",
  "core-text",
  "cosmic-text",
  "ctor",
@@ -4095,7 +4095,7 @@ dependencies = [
  "etagere",
  "flume",
  "font-kit",
- "foreign-types 0.3.2",
+ "foreign-types 0.5.0",
  "futures 0.3.28",
  "gpui_macros",
  "image",
@@ -4104,7 +4104,7 @@ dependencies = [
  "linkme",
  "log",
  "media",
- "metal 0.21.0",
+ "metal",
  "num_cpus",
  "objc",
  "open",
@@ -5350,8 +5350,8 @@ dependencies = [
  "block",
  "bytes 1.5.0",
  "core-foundation",
- "foreign-types 0.3.2",
- "metal 0.21.0",
+ "foreign-types 0.5.0",
+ "metal",
  "objc",
 ]
 
@@ -5423,20 +5423,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "metal"
-version = "0.21.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4598d719460ade24c7d91f335daf055bf2a7eec030728ce751814c50cdd6a26c"
-dependencies = [
- "bitflags 1.3.2",
- "block",
- "cocoa-foundation",
- "foreign-types 0.3.2",
- "log",
- "objc",
-]
-
 [[package]]
 name = "metal"
 version = "0.25.0"

Cargo.toml 🔗

@@ -181,6 +181,9 @@ anyhow = "1.0.57"
 async-compression = { version = "0.4", features = ["gzip", "futures-io"] }
 async-tar = "0.4.2"
 async-trait = "0.1"
+blade-graphics = { git = "https://github.com/kvark/blade", rev = "e9d93a4d41f3946a03ffb76136290d6ccf7f2b80" }
+blade-macros = { git = "https://github.com/kvark/blade", rev = "e9d93a4d41f3946a03ffb76136290d6ccf7f2b80" }
+blade-rwh = { package = "raw-window-handle", version = "0.5" }
 chrono = { version = "0.4", features = ["serde"] }
 ctor = "0.2.6"
 derive_more = "0.99.17"

crates/gpui/Cargo.toml 🔗

@@ -16,6 +16,7 @@ test-support = [
     "util/test-support",
 ]
 runtime_shaders = []
+macos-blade = ["blade-graphics", "blade-macros", "blade-rwh", "bytemuck"]
 
 [lib]
 path = "src/gpui.rs"
@@ -26,6 +27,10 @@ anyhow.workspace = true
 async-task = "4.7"
 backtrace = { version = "0.3", optional = true }
 bitflags = "2.4.0"
+blade-graphics = { workspace = true, optional = true }
+blade-macros = { workspace = true, optional = true }
+blade-rwh = { workspace = true, optional = true }
+bytemuck = { version = "1", optional = true }
 collections.workspace = true
 ctor.workspace = true
 derive_more.workspace = true
@@ -33,7 +38,7 @@ dhat = { version = "0.3", optional = true }
 env_logger = { version = "0.9", optional = true }
 etagere = "0.2"
 futures.workspace = true
-font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "d97147f" }
+font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "5a5c4d4" }
 gpui_macros.workspace = true
 image = "0.23"
 itertools = "0.10"
@@ -48,7 +53,6 @@ pathfinder_geometry = "0.5"
 postage.workspace = true
 rand.workspace = true
 raw-window-handle = "0.6"
-blade-rwh = { package = "raw-window-handle", version = "0.5" }
 refineable.workspace = true
 resvg = "0.14"
 schemars.workspace = true
@@ -86,12 +90,12 @@ cbindgen = "0.26.0"
 block = "0.1"
 cocoa = "0.25"
 core-foundation = { version = "0.9.3", features = ["with-uuid"] }
-core-graphics = "0.22.3"
-core-text = "19.2"
-foreign-types = "0.3"
+core-graphics = "0.23"
+core-text = "20.1"
+foreign-types = "0.5"
 log.workspace = true
 media.workspace = true
-metal = "0.21.0"
+metal = "0.25"
 objc = "0.2"
 
 [target.'cfg(target_os = "linux")'.dependencies]
@@ -105,8 +109,9 @@ wayland-protocols = { version = "0.31.2", features = ["client"] }
 wayland-backend = { version = "0.3.3", features = ["client_system"] }
 as-raw-xcb-connection = "1"
 #TODO: use these on all platforms
-blade-graphics = { git = "https://github.com/kvark/blade", rev = "c4f951a88b345724cb952e920ad30e39851f7760" }
-blade-macros = { git = "https://github.com/kvark/blade", rev = "c4f951a88b345724cb952e920ad30e39851f7760" }
+blade-graphics.workspace = true
+blade-macros.workspace = true
+blade-rwh.workspace = true
 bytemuck = "1"
 cosmic-text = "0.10.0"
 xkbcommon = { version = "0.7", features = ["x11"] }

crates/gpui/build.rs 🔗

@@ -1,4 +1,4 @@
-#![cfg_attr(not(target_os = "macos"), allow(unused))]
+#![cfg_attr(any(not(target_os = "macos"), feature = "macos-blade"), allow(unused))]
 
 use std::{
     env,
@@ -7,15 +7,18 @@ use std::{
 
 use cbindgen::Config;
 
+//TODO: consider generating shader code for WGSL
+//TODO: deprecate "runtime-shaders" and "macos-blade"
+
 fn main() {
     #[cfg(target_os = "macos")]
     generate_dispatch_bindings();
-    #[cfg(target_os = "macos")]
+    #[cfg(all(target_os = "macos", not(feature = "macos-blade")))]
     let header_path = generate_shader_bindings();
-    #[cfg(target_os = "macos")]
+    #[cfg(all(target_os = "macos", not(feature = "macos-blade")))]
     #[cfg(feature = "runtime_shaders")]
     emit_stitched_shaders(&header_path);
-    #[cfg(target_os = "macos")]
+    #[cfg(all(target_os = "macos", not(feature = "macos-blade")))]
     #[cfg(not(feature = "runtime_shaders"))]
     compile_metal_shaders(&header_path);
 }

crates/gpui/src/gpui.rs 🔗

@@ -60,8 +60,9 @@
 //! and will be publishing more guides to GPUI on our [blog](https://zed.dev/blog).
 
 #![deny(missing_docs)]
-#![allow(clippy::type_complexity)]
-#![allow(clippy::collapsible_else_if)]
+#![allow(clippy::type_complexity)] // Not useful, GPUI makes heavy use of callbacks
+#![allow(clippy::collapsible_else_if)] // False positives in platform specific code
+#![allow(unused_mut)] // False positives in platform specific code
 
 #[macro_use]
 mod action;

crates/gpui/src/platform.rs 🔗

@@ -3,10 +3,16 @@
 
 mod app_menu;
 mod keystroke;
+
 #[cfg(target_os = "linux")]
 mod linux;
+
 #[cfg(target_os = "macos")]
 mod mac;
+
+#[cfg(any(target_os = "linux", feature = "macos-blade"))]
+mod blade;
+
 #[cfg(any(test, feature = "test-support"))]
 mod test;
 

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

@@ -0,0 +1,8 @@
+mod blade_atlas;
+mod blade_belt;
+mod blade_renderer;
+
+pub(crate) use blade_atlas::*;
+pub(crate) use blade_renderer::*;
+
+use blade_belt::*;

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

@@ -1,14 +1,18 @@
 // Doing `if let` gives you nice scoping with passes/encoders
 #![allow(irrefutable_let_patterns)]
 
-use super::{BladeBelt, BladeBeltDescriptor};
+use super::{BladeAtlas, BladeBelt, BladeBeltDescriptor, PATH_TEXTURE_FORMAT};
 use crate::{
-    AtlasTextureKind, AtlasTile, BladeAtlas, Bounds, ContentMask, Hsla, MonochromeSprite, Path,
-    PathId, PathVertex, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow,
-    Underline, PATH_TEXTURE_FORMAT,
+    AtlasTextureKind, AtlasTile, Bounds, ContentMask, Hsla, MonochromeSprite, Path, PathId,
+    PathVertex, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size,
+    Underline,
 };
 use bytemuck::{Pod, Zeroable};
 use collections::HashMap;
+#[cfg(target_os = "macos")]
+use media::core_video::CVMetalTextureCache;
+#[cfg(target_os = "macos")]
+use std::ffi::c_void;
 
 use blade_graphics as gpu;
 use std::{mem, sync::Arc};
@@ -16,6 +20,61 @@ use std::{mem, sync::Arc};
 const SURFACE_FRAME_COUNT: u32 = 3;
 const MAX_FRAME_TIME_MS: u32 = 1000;
 
+pub type Context = ();
+pub type Renderer = BladeRenderer;
+
+#[cfg(target_os = "macos")]
+pub unsafe fn new_renderer(
+    _context: self::Context,
+    native_window: *mut c_void,
+    native_view: *mut c_void,
+    bounds: crate::Size<f32>,
+) -> Renderer {
+    struct RawWindow {
+        window: *mut c_void,
+        view: *mut c_void,
+    }
+
+    unsafe impl blade_rwh::HasRawWindowHandle for RawWindow {
+        fn raw_window_handle(&self) -> blade_rwh::RawWindowHandle {
+            let mut wh = blade_rwh::AppKitWindowHandle::empty();
+            wh.ns_window = self.window;
+            wh.ns_view = self.view;
+            wh.into()
+        }
+    }
+
+    unsafe impl blade_rwh::HasRawDisplayHandle for RawWindow {
+        fn raw_display_handle(&self) -> blade_rwh::RawDisplayHandle {
+            let dh = blade_rwh::AppKitDisplayHandle::empty();
+            dh.into()
+        }
+    }
+
+    let gpu = Arc::new(
+        gpu::Context::init_windowed(
+            &RawWindow {
+                window: native_window as *mut _,
+                view: native_view as *mut _,
+            },
+            gpu::ContextDesc {
+                validation: cfg!(debug_assertions),
+                capture: false,
+            },
+        )
+        .unwrap(),
+    );
+
+    BladeRenderer::new(
+        gpu,
+        gpu::Extent {
+            width: bounds.width as u32,
+            height: bounds.height as u32,
+            depth: 1,
+        },
+    )
+}
+
 #[repr(C)]
 #[derive(Clone, Copy, Pod, Zeroable)]
 struct GlobalParams {
@@ -23,6 +82,31 @@ struct GlobalParams {
     pad: [u32; 2],
 }
 
+//Note: we can't use `Bounds` directly here because
+// it doesn't implement Pod + Zeroable
+#[repr(C)]
+#[derive(Clone, Copy, Pod, Zeroable)]
+struct PodBounds {
+    origin: [f32; 2],
+    size: [f32; 2],
+}
+
+impl From<Bounds<ScaledPixels>> for PodBounds {
+    fn from(bounds: Bounds<ScaledPixels>) -> Self {
+        Self {
+            origin: [bounds.origin.x.0, bounds.origin.y.0],
+            size: [bounds.size.width.0, bounds.size.height.0],
+        }
+    }
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Pod, Zeroable)]
+struct SurfaceParams {
+    bounds: PodBounds,
+    content_mask: PodBounds,
+}
+
 #[derive(blade_macros::ShaderData)]
 struct ShaderQuadsData {
     globals: GlobalParams,
@@ -71,6 +155,15 @@ struct ShaderPolySpritesData {
     b_poly_sprites: gpu::BufferPiece,
 }
 
+#[derive(blade_macros::ShaderData)]
+struct ShaderSurfacesData {
+    globals: GlobalParams,
+    surface_locals: SurfaceParams,
+    t_y: gpu::TextureView,
+    t_cb_cr: gpu::TextureView,
+    s_surface: gpu::Sampler,
+}
+
 #[derive(Clone, Debug, Eq, PartialEq)]
 #[repr(C)]
 struct PathSprite {
@@ -87,6 +180,7 @@ struct BladePipelines {
     underlines: gpu::RenderPipeline,
     mono_sprites: gpu::RenderPipeline,
     poly_sprites: gpu::RenderPipeline,
+    surfaces: gpu::RenderPipeline,
 }
 
 impl BladePipelines {
@@ -96,6 +190,8 @@ impl BladePipelines {
         let shader = gpu.create_shader(gpu::ShaderDesc {
             source: include_str!("shaders.wgsl"),
         });
+        shader.check_struct_size::<GlobalParams>();
+        shader.check_struct_size::<SurfaceParams>();
         shader.check_struct_size::<Quad>();
         shader.check_struct_size::<Shadow>();
         assert_eq!(
@@ -220,6 +316,22 @@ impl BladePipelines {
                     write_mask: gpu::ColorWrites::default(),
                 }],
             }),
+            surfaces: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
+                name: "surfaces",
+                data_layouts: &[&ShaderSurfacesData::layout()],
+                vertex: shader.at("vs_surface"),
+                primitive: gpu::PrimitiveState {
+                    topology: gpu::PrimitiveTopology::TriangleStrip,
+                    ..Default::default()
+                },
+                depth_stencil: None,
+                fragment: shader.at("fs_surface"),
+                color_targets: &[gpu::ColorTargetState {
+                    format: surface_format,
+                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
+                    write_mask: gpu::ColorWrites::default(),
+                }],
+            }),
         }
     }
 }
@@ -234,6 +346,8 @@ pub struct BladeRenderer {
     path_tiles: HashMap<PathId, AtlasTile>,
     atlas: Arc<BladeAtlas>,
     atlas_sampler: gpu::Sampler,
+    #[cfg(target_os = "macos")]
+    core_video_texture_cache: CVMetalTextureCache,
 }
 
 impl BladeRenderer {
@@ -268,6 +382,12 @@ impl BladeRenderer {
             ..Default::default()
         });
 
+        #[cfg(target_os = "macos")]
+        let core_video_texture_cache = unsafe {
+            use foreign_types::ForeignType as _;
+            CVMetalTextureCache::new(gpu.metal_device().as_ptr()).unwrap()
+        };
+
         Self {
             gpu,
             command_encoder,
@@ -278,6 +398,8 @@ impl BladeRenderer {
             path_tiles: HashMap::default(),
             atlas,
             atlas_sampler,
+            #[cfg(target_os = "macos")]
+            core_video_texture_cache,
         }
     }
 
@@ -289,27 +411,39 @@ 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);
-    }
+    pub fn update_drawable_size(&mut self, size: Size<f64>) {
+        let gpu_size = gpu::Extent {
+            width: size.width as u32,
+            height: size.height as u32,
+            depth: 1,
+        };
 
-    pub fn resize(&mut self, size: gpu::Extent) {
-        self.wait_for_gpu();
-        self.gpu.resize(Self::make_surface_config(size));
-        self.viewport_size = size;
+        if gpu_size != self.viewport_size() {
+            self.wait_for_gpu();
+            self.gpu.resize(Self::make_surface_config(gpu_size));
+            self.viewport_size = gpu_size;
+        }
     }
 
     pub fn viewport_size(&self) -> gpu::Extent {
         self.viewport_size
     }
 
-    pub fn atlas(&self) -> &Arc<BladeAtlas> {
+    pub fn sprite_atlas(&self) -> &Arc<BladeAtlas> {
         &self.atlas
     }
 
+    #[cfg(target_os = "macos")]
+    pub fn layer(&self) -> metal::MetalLayer {
+        self.gpu.metal_layer().unwrap()
+    }
+
+    #[cfg(target_os = "macos")]
+    pub fn layer_ptr(&self) -> *mut metal::CAMetalLayer {
+        use metal::foreign_types::ForeignType as _;
+        self.gpu.metal_layer().unwrap().as_ptr()
+    }
+
     fn rasterize_paths(&mut self, paths: &[Path<ScaledPixels>]) {
         self.path_tiles.clear();
         let mut vertices_by_texture_id = HashMap::default();
@@ -362,6 +496,13 @@ 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);
+    }
+
     pub fn draw(&mut self, scene: &Scene) {
         let frame = self.gpu.acquire_frame();
         self.command_encoder.start();
@@ -495,8 +636,78 @@ impl BladeRenderer {
                         );
                         encoder.draw(0, 4, 0, sprites.len() as u32);
                     }
-                    PrimitiveBatch::Surfaces { .. } => {
-                        unimplemented!()
+                    PrimitiveBatch::Surfaces(surfaces) => {
+                        let mut _encoder = pass.with(&self.pipelines.surfaces);
+
+                        for surface in surfaces {
+                            #[cfg(not(target_os = "macos"))]
+                            {
+                                let _ = surface;
+                                continue;
+                            };
+
+                            #[cfg(target_os = "macos")]
+                            {
+                                let (t_y, t_cb_cr) = {
+                                    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()
+                                    };
+                                    (
+                                        gpu::TextureView::from_metal_texture(
+                                            y_texture.as_texture_ref(),
+                                        ),
+                                        gpu::TextureView::from_metal_texture(
+                                            cb_cr_texture.as_texture_ref(),
+                                        ),
+                                    )
+                                };
+
+                                _encoder.bind(
+                                    0,
+                                    &ShaderSurfacesData {
+                                        globals,
+                                        surface_locals: SurfaceParams {
+                                            bounds: surface.bounds.into(),
+                                            content_mask: surface.content_mask.bounds.into(),
+                                        },
+                                        t_y,
+                                        t_cb_cr,
+                                        s_surface: self.atlas_sampler,
+                                    },
+                                );
+
+                                _encoder.draw(0, 4, 0, 1);
+                            }
+                        }
                     }
                 }
             }

crates/gpui/src/platform/linux/shaders.wgsl → crates/gpui/src/platform/blade/shaders.wgsl 🔗

@@ -1,9 +1,9 @@
-struct Globals {
+struct GlobalParams {
     viewport_size: vec2<f32>,
     pad: vec2<u32>,
 }
 
-var<uniform> globals: Globals;
+var<uniform> globals: GlobalParams;
 var t_sprite: texture_2d<f32>;
 var s_sprite: sampler;
 
@@ -563,7 +563,56 @@ fn fs_poly_sprite(input: PolySpriteVarying) -> @location(0) vec4<f32> {
         color = vec4<f32>(vec3<f32>(grayscale), sample.a);
     }
     color.a *= saturate(0.5 - distance);
-    return color;;
+    return color;
+}
+
+// --- surfaces --- //
+
+struct SurfaceParams {
+    bounds: Bounds,
+    content_mask: Bounds,
+}
+
+var<uniform> surface_locals: SurfaceParams;
+var t_y: texture_2d<f32>;
+var t_cb_cr: texture_2d<f32>;
+var s_surface: sampler;
+
+const ycbcr_to_RGB = mat4x4<f32>(
+    vec4<f32>( 1.0000f,  1.0000f,  1.0000f, 0.0),
+    vec4<f32>( 0.0000f, -0.3441f,  1.7720f, 0.0),
+    vec4<f32>( 1.4020f, -0.7141f,  0.0000f, 0.0),
+    vec4<f32>(-0.7010f,  0.5291f, -0.8860f, 1.0),
+);
+
+struct SurfaceVarying {
+    @builtin(position) position: vec4<f32>,
+    @location(0) texture_position: vec2<f32>,
+    @location(3) clip_distances: vec4<f32>,
+}
+
+@vertex
+fn vs_surface(@builtin(vertex_index) vertex_id: u32) -> SurfaceVarying {
+    let unit_vertex = vec2<f32>(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u));
+
+    var out = SurfaceVarying();
+    out.position = to_device_position(unit_vertex, surface_locals.bounds);
+    out.texture_position = unit_vertex;
+    out.clip_distances = distance_from_clip_rect(unit_vertex, surface_locals.bounds, surface_locals.content_mask);
+    return out;
 }
 
-// --- surface sprites --- //
+@fragment
+fn fs_surface(input: SurfaceVarying) -> @location(0) vec4<f32> {
+    // Alpha clip after using the derivatives.
+    if (any(input.clip_distances < vec4<f32>(0.0))) {
+        return vec4<f32>(0.0);
+    }
+
+    let y_cb_cr = vec4<f32>(
+        textureSampleLevel(t_y, s_surface, input.texture_position, 0.0).r,
+        textureSampleLevel(t_cb_cr, s_surface, input.texture_position, 0.0).rg,
+        1.0);
+
+    return ycbcr_to_RGB * y_cb_cr;
+}

crates/gpui/src/platform/linux.rs 🔗

@@ -1,6 +1,3 @@
-mod blade_atlas;
-mod blade_belt;
-mod blade_renderer;
 mod client;
 mod client_dispatcher;
 mod dispatcher;
@@ -9,10 +6,7 @@ mod text_system;
 mod wayland;
 mod x11;
 
-pub(crate) use blade_atlas::*;
 pub(crate) use dispatcher::*;
 pub(crate) use platform::*;
 pub(crate) use text_system::*;
 pub(crate) use x11::*;
-
-use blade_belt::*;

crates/gpui/src/platform/linux/wayland/window.rs 🔗

@@ -13,12 +13,12 @@ use raw_window_handle::{
 use wayland_client::{protocol::wl_surface, Proxy};
 use wayland_protocols::xdg::shell::client::xdg_toplevel;
 
-use crate::platform::linux::blade_renderer::BladeRenderer;
+use crate::platform::blade::BladeRenderer;
 use crate::platform::linux::wayland::display::WaylandDisplay;
 use crate::platform::{PlatformAtlas, PlatformInputHandler, PlatformWindow};
 use crate::scene::Scene;
 use crate::{
-    px, Bounds, Modifiers, Pixels, PlatformDisplay, PlatformInput, Point, PromptLevel, Size,
+    px, size, Bounds, Modifiers, Pixels, PlatformDisplay, PlatformInput, Point, PromptLevel, Size,
     WindowAppearance, WindowBounds, WindowOptions,
 };
 
@@ -150,11 +150,9 @@ impl WaylandWindowState {
             let mut inner = self.inner.lock();
             inner.bounds.size.width = width;
             inner.bounds.size.height = height;
-            inner.renderer.resize(gpu::Extent {
-                width: width as u32,
-                height: height as u32,
-                depth: 1,
-            });
+            inner
+                .renderer
+                .update_drawable_size(size(width as f64, height as f64));
         }
         let mut callbacks = self.callbacks.lock();
         if let Some(ref mut fun) = callbacks.resize {
@@ -341,7 +339,7 @@ impl PlatformWindow for WaylandWindow {
 
     fn sprite_atlas(&self) -> Arc<dyn PlatformAtlas> {
         let inner = self.0.inner.lock();
-        inner.renderer.atlas().clone()
+        inner.renderer.sprite_atlas().clone()
     }
 
     fn set_graphics_profiler_enabled(&self, enabled: bool) {

crates/gpui/src/platform/linux/x11/window.rs 🔗

@@ -1,6 +1,20 @@
 //todo!(linux): remove
 #![allow(unused)]
 
+use crate::{
+    platform::blade::BladeRenderer, size, Bounds, GlobalPixels, Pixels, PlatformDisplay,
+    PlatformInput, PlatformInputHandler, PlatformWindow, Point, Size, WindowAppearance,
+    WindowBounds, WindowOptions, X11Display,
+};
+use blade_graphics as gpu;
+use parking_lot::Mutex;
+use raw_window_handle as rwh;
+
+use xcb::{
+    x::{self, StackMode},
+    Xid as _,
+};
+
 use std::{
     ffi::c_void,
     mem,
@@ -10,17 +24,6 @@ use std::{
     sync::{self, Arc},
 };
 
-use blade_graphics as gpu;
-use parking_lot::Mutex;
-use raw_window_handle as rwh;
-use xcb::{x, Xid as _};
-
-use crate::platform::linux::blade_renderer::BladeRenderer;
-use crate::{
-    Bounds, GlobalPixels, Pixels, PlatformDisplay, PlatformInput, PlatformInputHandler,
-    PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, X11Display,
-};
-
 #[derive(Default)]
 struct Callbacks {
     request_frame: Option<Box<dyn FnMut()>>,
@@ -293,9 +296,13 @@ impl X11WindowState {
             let mut inner = self.inner.lock();
             let old_bounds = mem::replace(&mut inner.bounds, bounds);
             do_move = old_bounds.origin != bounds.origin;
+            //todo!(linux): use normal GPUI types here, refactor out the double
+            // viewport check and extra casts ( )
             let gpu_size = query_render_extent(&self.xcb_connection, self.x_window);
             if inner.renderer.viewport_size() != gpu_size {
-                inner.renderer.resize(gpu_size);
+                inner
+                    .renderer
+                    .update_drawable_size(size(gpu_size.width as f64, gpu_size.height as f64));
                 resize_args = Some((inner.content_size(), inner.scale_factor));
             }
         }
@@ -493,7 +500,7 @@ impl PlatformWindow for X11Window {
 
     fn sprite_atlas(&self) -> sync::Arc<dyn crate::PlatformAtlas> {
         let inner = self.0.inner.lock();
-        inner.renderer.atlas().clone()
+        inner.renderer.sprite_atlas().clone()
     }
 
     fn set_graphics_profiler_enabled(&self, enabled: bool) {

crates/gpui/src/platform/mac.rs 🔗

@@ -4,8 +4,18 @@ mod dispatcher;
 mod display;
 mod display_link;
 mod events;
+
+#[cfg(not(feature = "macos-blade"))]
 mod metal_atlas;
-mod metal_renderer;
+#[cfg(not(feature = "macos-blade"))]
+pub mod metal_renderer;
+
+#[cfg(not(feature = "macos-blade"))]
+use metal_renderer as renderer;
+
+#[cfg(feature = "macos-blade")]
+use crate::platform::blade as renderer;
+
 mod open_type;
 mod platform;
 mod text_system;
@@ -17,14 +27,13 @@ use cocoa::{
     base::{id, nil},
     foundation::{NSAutoreleasePool, NSNotFound, NSRect, NSSize, NSString, NSUInteger},
 };
-use metal_renderer::*;
+
 use objc::runtime::{BOOL, NO, YES};
 use std::ops::Range;
 
 pub(crate) use dispatcher::*;
 pub(crate) use display::*;
 pub(crate) use display_link::*;
-pub(crate) use metal_atlas::*;
 pub(crate) use platform::*;
 pub(crate) use text_system::*;
 pub(crate) use window::*;

crates/gpui/src/platform/mac/display_link.rs 🔗

@@ -111,11 +111,11 @@ mod sys {
     pub enum CVDisplayLink {}
 
     foreign_type! {
-        type CType = CVDisplayLink;
-        fn drop = CVDisplayLinkRelease;
-        fn clone = CVDisplayLinkRetain;
-        pub struct DisplayLink;
-        pub struct DisplayLinkRef;
+        pub unsafe type DisplayLink {
+            type CType = CVDisplayLink;
+            fn drop = CVDisplayLinkRelease;
+            fn clone = CVDisplayLinkRetain;
+        }
     }
 
     impl Debug for DisplayLink {

crates/gpui/src/platform/mac/events.rs 🔗

@@ -13,7 +13,7 @@ use core_graphics::{
     event_source::{CGEventSource, CGEventSourceStateID},
 };
 use ctor::ctor;
-use foreign_types::ForeignType;
+use metal::foreign_types::ForeignType as _;
 use objc::{class, msg_send, sel, sel_impl};
 use std::{borrow::Cow, ffi::CStr, mem, os::raw::c_char, ptr};
 

crates/gpui/src/platform/mac/metal_renderer.rs 🔗

@@ -1,19 +1,20 @@
+use super::metal_atlas::MetalAtlas;
 use crate::{
-    platform::mac::ns_string, point, size, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds,
-    ContentMask, DevicePixels, Hsla, MetalAtlas, MonochromeSprite, Path, PathId, PathVertex,
-    PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size, Surface, Underline,
+    point, size, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, ContentMask, DevicePixels,
+    Hsla, MonochromeSprite, Path, PathId, PathVertex, PolychromeSprite, PrimitiveBatch, Quad,
+    ScaledPixels, Scene, Shadow, Size, Surface, Underline,
 };
 use block::ConcreteBlock;
 use cocoa::{
-    base::{nil, NO, YES},
-    foundation::{NSDictionary, NSUInteger},
+    base::{NO, YES},
+    foundation::NSUInteger,
     quartzcore::AutoresizingMask,
 };
 use collections::HashMap;
 use core_foundation::base::TCFType;
 use foreign_types::ForeignType;
 use media::core_video::CVMetalTextureCache;
-use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange};
+use metal::{CAMetalLayer, CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange};
 use objc::{self, msg_send, sel, sel_impl};
 use parking_lot::Mutex;
 use smallvec::SmallVec;
@@ -29,6 +30,18 @@ const SHADERS_SOURCE_FILE: &'static str =
     include_str!(concat!(env!("OUT_DIR"), "/stitched_shaders.metal"));
 const INSTANCE_BUFFER_SIZE: usize = 2 * 1024 * 1024; // This is an arbitrary decision. There's probably a more optimal value (maybe even we could adjust dynamically...)
 
+pub type Context = Arc<Mutex<Vec<metal::Buffer>>>;
+pub type Renderer = MetalRenderer;
+
+pub unsafe fn new_renderer(
+    context: self::Context,
+    _native_window: *mut c_void,
+    _native_view: *mut c_void,
+    _bounds: crate::Size<f32>,
+) -> Renderer {
+    MetalRenderer::new(context)
+}
+
 pub(crate) struct MetalRenderer {
     device: metal::Device,
     layer: metal::MetalLayer,
@@ -196,25 +209,12 @@ impl MetalRenderer {
         &self.layer
     }
 
-    pub fn sprite_atlas(&self) -> &Arc<MetalAtlas> {
-        &self.sprite_atlas
+    pub fn layer_ptr(&self) -> *mut CAMetalLayer {
+        self.layer.as_ptr()
     }
 
-    /// Enables or disables the Metal HUD for debugging purposes. Note that this only works
-    /// when the app is bundled and it has the `MetalHudEnabled` key set to true in Info.plist.
-    pub fn set_hud_enabled(&mut self, enabled: bool) {
-        unsafe {
-            if enabled {
-                let hud_properties = NSDictionary::dictionaryWithObject_forKey_(
-                    nil,
-                    ns_string("default"),
-                    ns_string("mode"),
-                );
-                let _: () = msg_send![&*self.layer, setDeveloperHUDProperties: hud_properties];
-            } else {
-                let _: () = msg_send![&*self.layer, setDeveloperHUDProperties: NSDictionary::dictionary(nil)];
-            }
-        }
+    pub fn sprite_atlas(&self) -> &Arc<MetalAtlas> {
+        &self.sprite_atlas
     }
 
     pub fn set_presents_with_transaction(&mut self, presents_with_transaction: bool) {
@@ -223,6 +223,19 @@ impl MetalRenderer {
             .set_presents_with_transaction(presents_with_transaction);
     }
 
+    pub fn update_drawable_size(&mut self, size: Size<f64>) {
+        unsafe {
+            let _: () = msg_send![
+                self.layer(),
+                setDrawableSize: size
+            ];
+        }
+    }
+
+    pub fn destroy(&mut self) {
+        // nothing to do
+    }
+
     pub fn draw(&mut self, scene: &Scene) {
         let layer = self.layer.clone();
         let viewport_size = layer.drawable_size();

crates/gpui/src/platform/mac/platform.rs 🔗

@@ -52,6 +52,8 @@ use std::{
 };
 use time::UtcOffset;
 
+use super::renderer;
+
 #[allow(non_upper_case_globals)]
 const NSUTF8StringEncoding: NSUInteger = 4;
 
@@ -145,7 +147,7 @@ pub(crate) struct MacPlatformState {
     background_executor: BackgroundExecutor,
     foreground_executor: ForegroundExecutor,
     text_system: Arc<MacTextSystem>,
-    instance_buffer_pool: Arc<Mutex<Vec<metal::Buffer>>>,
+    renderer_context: renderer::Context,
     pasteboard: id,
     text_hash_pasteboard_type: id,
     metadata_pasteboard_type: id,
@@ -175,7 +177,7 @@ impl MacPlatform {
             background_executor: BackgroundExecutor::new(dispatcher.clone()),
             foreground_executor: ForegroundExecutor::new(dispatcher),
             text_system: Arc::new(MacTextSystem::new()),
-            instance_buffer_pool: Arc::default(),
+            renderer_context: renderer::Context::default(),
             pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) },
             text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") },
             metadata_pasteboard_type: unsafe { ns_string("zed-metadata") },
@@ -494,12 +496,11 @@ impl Platform for MacPlatform {
         handle: AnyWindowHandle,
         options: WindowOptions,
     ) -> Box<dyn PlatformWindow> {
-        let instance_buffer_pool = self.0.lock().instance_buffer_pool.clone();
         Box::new(MacWindow::open(
             handle,
             options,
             self.foreground_executor(),
-            instance_buffer_pool,
+            self.0.lock().renderer_context.clone(),
         ))
     }
 

crates/gpui/src/platform/mac/window.rs 🔗

@@ -1,4 +1,4 @@
-use super::{global_bounds_from_ns_rect, ns_string, MacDisplay, MetalRenderer, NSRange};
+use super::{global_bounds_from_ns_rect, ns_string, renderer, MacDisplay, NSRange};
 use crate::{
     global_bounds_to_ns_rect, platform::PlatformInputHandler, point, px, size, AnyWindowHandle,
     Bounds, DisplayLink, ExternalPaths, FileDropEvent, ForegroundExecutor, GlobalPixels,
@@ -23,7 +23,6 @@ use cocoa::{
 };
 use core_graphics::display::{CGDirectDisplayID, CGRect};
 use ctor::ctor;
-use foreign_types::ForeignTypeRef;
 use futures::channel::oneshot;
 use objc::{
     class,
@@ -322,7 +321,7 @@ struct MacWindowState {
     native_window: id,
     native_view: NonNull<Object>,
     display_link: Option<DisplayLink>,
-    renderer: MetalRenderer,
+    renderer: renderer::Renderer,
     kind: WindowKind,
     request_frame_callback: Option<Box<dyn FnMut()>>,
     event_callback: Option<Box<dyn FnMut(PlatformInput) -> bool>>,
@@ -450,6 +449,13 @@ impl MacWindowState {
         get_scale_factor(self.native_window)
     }
 
+    fn update_drawable_size(&mut self, drawable_size: NSSize) {
+        self.renderer.update_drawable_size(Size {
+            width: drawable_size.width,
+            height: drawable_size.height,
+        })
+    }
+
     fn titlebar_height(&self) -> Pixels {
         unsafe {
             let frame = NSWindow::frame(self.native_window);
@@ -478,7 +484,7 @@ impl MacWindow {
         handle: AnyWindowHandle,
         options: WindowOptions,
         executor: ForegroundExecutor,
-        instance_buffer_pool: Arc<Mutex<Vec<metal::Buffer>>>,
+        renderer_context: renderer::Context,
     ) -> Self {
         unsafe {
             let pool = NSAutoreleasePool::new(nil);
@@ -541,13 +547,32 @@ impl MacWindow {
             let native_view = NSView::init(native_view);
             assert!(!native_view.is_null());
 
+            let window_size = {
+                let bounds = match options.bounds {
+                    WindowBounds::Fullscreen | WindowBounds::Maximized => {
+                        native_window.screen().visibleFrame()
+                    }
+                    WindowBounds::Fixed(bounds) => global_bounds_to_ns_rect(bounds),
+                };
+                let scale = get_scale_factor(native_window);
+                size(
+                    bounds.size.width as f32 * scale,
+                    bounds.size.height as f32 * scale,
+                )
+            };
+
             let window = Self(Arc::new(Mutex::new(MacWindowState {
                 handle,
                 executor,
                 native_window,
                 native_view: NonNull::new_unchecked(native_view),
                 display_link: None,
-                renderer: MetalRenderer::new(instance_buffer_pool),
+                renderer: renderer::new_renderer(
+                    renderer_context,
+                    native_window as *mut _,
+                    native_view as *mut _,
+                    window_size,
+                ),
                 kind: options.kind,
                 request_frame_callback: None,
                 event_callback: None,
@@ -704,6 +729,7 @@ impl MacWindow {
 impl Drop for MacWindow {
     fn drop(&mut self) {
         let mut this = self.0.lock();
+        this.renderer.destroy();
         let window = this.native_window;
         this.display_link.take();
         this.executor
@@ -1031,7 +1057,22 @@ impl PlatformWindow for MacWindow {
     /// Enables or disables the Metal HUD for debugging purposes. Note that this only works
     /// when the app is bundled and it has the `MetalHudEnabled` key set to true in Info.plist.
     fn set_graphics_profiler_enabled(&self, enabled: bool) {
-        self.0.lock().renderer.set_hud_enabled(enabled);
+        let this_lock = self.0.lock();
+        let layer = this_lock.renderer.layer();
+
+        unsafe {
+            if enabled {
+                let hud_properties = NSDictionary::dictionaryWithObject_forKey_(
+                    nil,
+                    ns_string("default"),
+                    ns_string("mode"),
+                );
+                let _: () = msg_send![layer, setDeveloperHUDProperties: hud_properties];
+            } else {
+                let _: () =
+                    msg_send![layer, setDeveloperHUDProperties: NSDictionary::dictionary(nil)];
+            }
+        }
     }
 }
 
@@ -1487,31 +1528,28 @@ extern "C" fn close_window(this: &Object, _: Sel) {
 extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
     let window_state = unsafe { get_window_state(this) };
     let window_state = window_state.as_ref().lock();
-    window_state.renderer.layer().as_ptr() as id
+    window_state.renderer.layer_ptr() as id
 }
 
 extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
     let window_state = unsafe { get_window_state(this) };
     let mut lock = window_state.as_ref().lock();
 
+    let scale_factor = lock.scale_factor() as f64;
+    let size = lock.content_size();
+    let drawable_size: NSSize = NSSize {
+        width: f64::from(size.width) * scale_factor,
+        height: f64::from(size.height) * scale_factor,
+    };
     unsafe {
-        let scale_factor = lock.scale_factor() as f64;
-        let size = lock.content_size();
-        let drawable_size: NSSize = NSSize {
-            width: f64::from(size.width) * scale_factor,
-            height: f64::from(size.height) * scale_factor,
-        };
-
         let _: () = msg_send![
             lock.renderer.layer(),
             setContentsScale: scale_factor
         ];
-        let _: () = msg_send![
-            lock.renderer.layer(),
-            setDrawableSize: drawable_size
-        ];
     }
 
+    lock.update_drawable_size(drawable_size);
+
     if let Some(mut callback) = lock.resize_callback.take() {
         let content_size = lock.content_size();
         let scale_factor = lock.scale_factor();
@@ -1523,7 +1561,7 @@ extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
 
 extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
     let window_state = unsafe { get_window_state(this) };
-    let lock = window_state.as_ref().lock();
+    let mut lock = window_state.as_ref().lock();
 
     if lock.content_size() == size.into() {
         return;
@@ -1539,12 +1577,7 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
         height: size.height * scale_factor,
     };
 
-    unsafe {
-        let _: () = msg_send![
-            lock.renderer.layer(),
-            setDrawableSize: drawable_size
-        ];
-    }
+    lock.update_drawable_size(drawable_size);
 
     drop(lock);
     let mut lock = window_state.lock();
@@ -1561,6 +1594,7 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
     let window_state = unsafe { get_window_state(this) };
     let mut lock = window_state.lock();
     if let Some(mut callback) = lock.request_frame_callback.take() {
+        #[cfg(not(feature = "macos-blade"))]
         lock.renderer.set_presents_with_transaction(true);
         lock.stop_display_link();
         drop(lock);
@@ -1568,6 +1602,7 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
 
         let mut lock = window_state.lock();
         lock.request_frame_callback = Some(callback);
+        #[cfg(not(feature = "macos-blade"))]
         lock.renderer.set_presents_with_transaction(false);
         lock.start_display_link();
     }

crates/media/Cargo.toml 🔗

@@ -16,8 +16,8 @@ bytes = "1.2"
 
 [target.'cfg(target_os = "macos")'.dependencies]
 core-foundation = "0.9.3"
-foreign-types = "0.3"
-metal = "0.21.0"
+foreign-types = "0.5"
+metal = "0.25"
 objc = "0.2"
 
 [build-dependencies]

script/bundle 🔗

@@ -135,24 +135,24 @@ echo "Bundled ${app_path}"
 
 if [ "$local_arch" = false ]; then
     echo "Uploading dSYMs"
-    dsymutil --flat target/aarch64-apple-darwin/release/Zed
-    dsymutil --flat target/x86_64-apple-darwin/release/Zed
+    dsymutil --flat target/aarch64-apple-darwin/${target_dir}/Zed
+    dsymutil --flat target/x86_64-apple-darwin/${target_dir}/Zed
     version="$(cargo metadata --no-deps --manifest-path crates/zed/Cargo.toml --offline --format-version=1 | jq -r '.packages | map(select(.name == "zed"))[0].version')"
     if [ "$channel" == "nightly" ]; then
         version="$version-$(git rev-parse --short HEAD)"
     fi
 
     echo "Removing existing gzipped dSYMs"
-    rm -f target/aarch64-apple-darwin/release/Zed.dwarf.gz
-    rm -f target/x86_64-apple-darwin/release/Zed.dwarf.gz
+    rm -f target/aarch64-apple-darwin/${target_dir}/Zed.dwarf.gz
+    rm -f target/x86_64-apple-darwin/${target_dir}/Zed.dwarf.gz
 
     echo "Gzipping dSYMs"
-    gzip target/aarch64-apple-darwin/release/Zed.dwarf
-    gzip target/x86_64-apple-darwin/release/Zed.dwarf
+    gzip target/aarch64-apple-darwin/${target_dir}/Zed.dwarf
+    gzip target/x86_64-apple-darwin/${target_dir}/Zed.dwarf
 
     echo "Uploading dSYMs"
-    uploadDsym target/aarch64-apple-darwin/release/Zed.dwarf.gz "$channel/Zed-$version-aarch64-apple-darwin.dwarf.gz"
-    uploadDsym target/x86_64-apple-darwin/release/Zed.dwarf.gz "$channel/Zed-$version-x86_64-apple-darwin.dwarf.gz"
+    uploadDsym target/aarch64-apple-darwin/${target_dir}/Zed.dwarf.gz "$channel/Zed-$version-aarch64-apple-darwin.dwarf.gz"
+    uploadDsym target/x86_64-apple-darwin/${target_dir}/Zed.dwarf.gz "$channel/Zed-$version-x86_64-apple-darwin.dwarf.gz"
 
     echo "Creating fat binaries"
     lipo \