Start on using CVMetalTextureCache

Nathan Sobo created

Change summary

Cargo.lock                               |   1 
crates/gpui/src/platform/mac/renderer.rs |  30 ++++++-
crates/media/Cargo.toml                  |   3 
crates/media/src/media.rs                | 100 ++++++++++++++++++++++++++
4 files changed, 129 insertions(+), 5 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -3036,6 +3036,7 @@ version = "0.1.0"
 dependencies = [
  "block",
  "core-foundation",
+ "metal",
  "objc",
 ]
 

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

@@ -9,10 +9,13 @@ use crate::{
     scene::{Glyph, Icon, Image, ImageGlyph, Layer, Quad, Scene, Shadow, Underline},
 };
 use cocoa::foundation::NSUInteger;
+use core_foundation::base::TCFType;
+use foreign_types::ForeignTypeRef;
 use log::warn;
+use media::core_video::{self, CVMetalTextureCache};
 use metal::{MTLPixelFormat, MTLResourceOptions, NSRange};
 use shaders::ToFloat2 as _;
-use std::{collections::HashMap, ffi::c_void, iter::Peekable, mem, sync::Arc, vec};
+use std::{collections::HashMap, ffi::c_void, iter::Peekable, mem, ptr, sync::Arc, vec};
 
 const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
 const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value.
@@ -29,6 +32,7 @@ pub struct Renderer {
     underline_pipeline_state: metal::RenderPipelineState,
     unit_vertices: metal::Buffer,
     instances: metal::Buffer,
+    cv_texture_cache: core_video::CVMetalTextureCache,
 }
 
 struct PathSprite {
@@ -39,7 +43,7 @@ struct PathSprite {
 
 pub struct Surface {
     pub bounds: RectF,
-    pub image_buffer: media::core_video::CVImageBuffer,
+    pub image_buffer: core_video::CVImageBuffer,
 }
 
 impl Renderer {
@@ -128,6 +132,7 @@ impl Renderer {
             "underline_fragment",
             pixel_format,
         );
+        let cv_texture_cache = CVMetalTextureCache::new(device.as_ptr());
         Self {
             sprite_cache,
             image_cache,
@@ -140,6 +145,7 @@ impl Renderer {
             underline_pipeline_state,
             unit_vertices,
             instances,
+            cv_texture_cache,
         }
     }
 
@@ -786,10 +792,26 @@ impl Renderer {
         }
 
         for surface in surfaces {
-            let origin = surface.bounds.origin() * scale_factor;
-            let target_size = surface.bounds.size() * scale_factor;
+            // let origin = surface.bounds.origin() * scale_factor;
+            // let target_size = surface.bounds.size() * scale_factor;
             // let corner_radius = surface.corner_radius * scale_factor;
             // let border_width = surface.border.width * scale_factor;
+
+            let width = surface.image_buffer.width();
+            let height = surface.image_buffer.height();
+
+            // We should add this method, but this return CVPixelFormatType and we need MTLPixelFormat
+            // I found at least one code example that manually maps them. Not sure what other options we have.
+            let pixel_format = surface.image_buffer.pixel_format_type();
+
+            let texture = self.cv_texture_cache.create_texture_from_image(
+                surface.image_buffer.as_concrete_TypeRef(),
+                ptr::null(),
+                pixel_format,
+                width,
+                height,
+                0,
+            );
         }
 
         // command_encoder.set_render_pipeline_state(&self.image_pipeline_state);

crates/media/Cargo.toml 🔗

@@ -10,4 +10,5 @@ doctest = false
 [dependencies]
 block = "0.1"
 core-foundation = "0.9.3"
-objc = "0.2"
+metal = "0.21.0"
+objc = "0.2"

crates/media/src/media.rs 🔗

@@ -1,11 +1,15 @@
 #![allow(non_snake_case)]
+#![allow(non_camel_case_types)]
 
 use core_foundation::{
     base::{CFTypeID, TCFType},
     declare_TCFType, impl_CFTypeDescription, impl_TCFType,
 };
+use objc::runtime;
 use std::ffi::c_void;
 
+pub type id = *mut runtime::Object;
+
 pub mod io_surface {
     use super::*;
 
@@ -27,8 +31,14 @@ pub mod io_surface {
 pub mod core_video {
     #![allow(non_snake_case)]
 
+    use std::ptr;
+
     use super::*;
+    use core_foundation::{
+        base::kCFAllocatorDefault, dictionary::CFDictionaryRef, mach_port::CFAllocatorRef,
+    };
     use io_surface::{IOSurface, IOSurfaceRef};
+    use metal::{MTLDevice, MTLPixelFormat};
 
     #[repr(C)]
     pub struct __CVImageBuffer(c_void);
@@ -63,4 +73,94 @@ pub mod core_video {
         fn CVPixelBufferGetWidth(buffer: CVImageBufferRef) -> usize;
         fn CVPixelBufferGetHeight(buffer: CVImageBufferRef) -> usize;
     }
+
+    #[repr(C)]
+    pub struct __CVMetalTextureCache(c_void);
+    pub type CVMetalTextureCacheRef = *const __CVMetalTextureCache;
+
+    declare_TCFType!(CVMetalTextureCache, CVMetalTextureCacheRef);
+    impl_TCFType!(
+        CVMetalTextureCache,
+        CVMetalTextureCacheRef,
+        CVMetalTextureCacheGetTypeID
+    );
+    impl_CFTypeDescription!(CVMetalTextureCache);
+
+    impl CVMetalTextureCache {
+        pub fn new(metal_device: *mut MTLDevice) -> Self {
+            unsafe {
+                let mut this = ptr::null();
+                let result = CVMetalTextureCacheCreate(
+                    kCFAllocatorDefault,
+                    ptr::null_mut(),
+                    metal_device,
+                    ptr::null_mut(),
+                    &mut this,
+                );
+                // TODO: Check result
+                CVMetalTextureCache::wrap_under_create_rule(this)
+            }
+        }
+
+        pub fn create_texture_from_image(
+            &self,
+            source: CVImageBufferRef,
+            texture_attributes: CFDictionaryRef,
+            pixel_format: MTLPixelFormat,
+            width: usize,
+            height: usize,
+            plane_index: usize,
+        ) -> CVMetalTexture {
+            unsafe {
+                let mut this = ptr::null();
+                let result = CVMetalTextureCacheCreateTextureFromImage(
+                    kCFAllocatorDefault,
+                    self.as_concrete_TypeRef(),
+                    source,
+                    texture_attributes,
+                    pixel_format,
+                    width,
+                    height,
+                    plane_index,
+                    &mut this,
+                );
+                // TODO: Check result
+                CVMetalTexture::wrap_under_create_rule(this)
+            }
+        }
+    }
+
+    extern "C" {
+        fn CVMetalTextureCacheGetTypeID() -> CFTypeID;
+        fn CVMetalTextureCacheCreate(
+            allocator: CFAllocatorRef,
+            cache_attributes: CFDictionaryRef,
+            metal_device: *const MTLDevice,
+            texture_attributes: CFDictionaryRef,
+            cache_out: *mut CVMetalTextureCacheRef,
+        ) -> i32; // TODO: This should be a CVReturn enum
+        fn CVMetalTextureCacheCreateTextureFromImage(
+            allocator: CFAllocatorRef,
+            texture_cache: CVMetalTextureCacheRef,
+            source_image: CVImageBufferRef,
+            texture_attributes: CFDictionaryRef,
+            pixel_format: MTLPixelFormat,
+            width: usize,
+            height: usize,
+            plane_index: usize,
+            texture_out: *mut CVMetalTextureRef,
+        ) -> i32;
+    }
+
+    #[repr(C)]
+    pub struct __CVMetalTexture(c_void);
+    pub type CVMetalTextureRef = *const __CVMetalTexture;
+
+    declare_TCFType!(CVMetalTexture, CVMetalTextureRef);
+    impl_TCFType!(CVMetalTexture, CVMetalTextureRef, CVMetalTextureGetTypeID);
+    impl_CFTypeDescription!(CVMetalTexture);
+
+    extern "C" {
+        fn CVMetalTextureGetTypeID() -> CFTypeID;
+    }
 }