Construct Metal command encoder inside of `Renderer`

Antonio Scandurra and Max Brunsfeld created

Co-Authored-By: Max Brunsfeld <max@zed.dev>

Change summary

gpui/src/platform/mac/renderer.rs | 50 ++++++++++++++++++++++++++------
gpui/src/platform/mac/window.rs   | 29 ++----------------
2 files changed, 44 insertions(+), 35 deletions(-)

Detailed changes

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

@@ -1,9 +1,9 @@
-use super::{sprite_cache::SpriteCache, window::RenderContext};
+use super::sprite_cache::SpriteCache;
 use crate::{
     color::ColorU,
     geometry::{
         rect::RectF,
-        vector::{vec2f, vec2i, Vector2I},
+        vector::{vec2f, vec2i, Vector2F, Vector2I},
     },
     platform,
     scene::Layer,
@@ -19,6 +19,11 @@ const SHADERS_METALLIB: &'static [u8] =
     include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
 const INSTANCE_BUFFER_SIZE: usize = 1024 * 1024; // This is an arbitrary decision. There's probably a more optimal value.
 
+struct RenderContext<'a> {
+    drawable_size: Vector2F,
+    command_encoder: &'a metal::RenderCommandEncoderRef,
+}
+
 pub struct Renderer {
     sprite_cache: SpriteCache,
     quad_pipeline_state: metal::RenderPipelineState,
@@ -85,26 +90,51 @@ impl Renderer {
             )?,
             unit_vertices,
             instances,
+            paths_texture,
         })
     }
 
-    pub fn render(&mut self, scene: &Scene, ctx: &RenderContext) {
-        ctx.command_encoder.set_viewport(metal::MTLViewport {
+    pub fn render(
+        &mut self,
+        scene: &Scene,
+        drawable_size: Vector2F,
+        device: &metal::DeviceRef,
+        command_buffer: &metal::CommandBufferRef,
+        output: &metal::TextureRef,
+    ) {
+        let render_pass_descriptor = metal::RenderPassDescriptor::new();
+        let color_attachment = render_pass_descriptor
+            .color_attachments()
+            .object_at(0)
+            .unwrap();
+        color_attachment.set_texture(Some(output));
+        color_attachment.set_load_action(metal::MTLLoadAction::Clear);
+        color_attachment.set_store_action(metal::MTLStoreAction::Store);
+        color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., 1.));
+        let command_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);
+
+        command_encoder.set_viewport(metal::MTLViewport {
             originX: 0.0,
             originY: 0.0,
-            width: ctx.drawable_size.x() as f64,
-            height: ctx.drawable_size.y() as f64,
+            width: drawable_size.x() as f64,
+            height: drawable_size.y() as f64,
             znear: 0.0,
             zfar: 1.0,
         });
 
+        let ctx = RenderContext {
+            drawable_size,
+            command_encoder,
+        };
         let mut offset = 0;
         for layer in scene.layers() {
-            self.clip(scene, layer, ctx);
-            self.render_shadows(scene, layer, &mut offset, ctx);
-            self.render_quads(scene, layer, &mut offset, ctx);
-            self.render_sprites(scene, layer, &mut offset, ctx);
+            self.clip(scene, layer, &ctx);
+            self.render_shadows(scene, layer, &mut offset, &ctx);
+            self.render_quads(scene, layer, &mut offset, &ctx);
+            self.render_sprites(scene, layer, &mut offset, &ctx);
         }
+
+        command_encoder.end_encoding();
     }
 
     fn clip(&mut self, scene: &Scene, layer: &Layer, ctx: &RenderContext) {

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

@@ -16,7 +16,6 @@ use cocoa::{
 };
 use ctor::ctor;
 use foreign_types::ForeignType as _;
-use metal::{MTLClearColor, MTLLoadAction, MTLStoreAction};
 use objc::{
     class,
     declare::ClassDecl,
@@ -131,12 +130,6 @@ struct WindowState {
     layer: id,
 }
 
-pub struct RenderContext<'a> {
-    pub drawable_size: Vector2F,
-    pub device: &'a metal::Device,
-    pub command_encoder: &'a metal::RenderCommandEncoderRef,
-}
-
 impl Window {
     pub fn open(
         options: platform::WindowOptions,
@@ -428,20 +421,8 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
 
         if let Some(scene) = window_state.scene_to_render.take() {
             let drawable: &metal::MetalDrawableRef = msg_send![window_state.layer, nextDrawable];
-
-            let render_pass_descriptor = metal::RenderPassDescriptor::new();
-            let color_attachment = render_pass_descriptor
-                .color_attachments()
-                .object_at(0)
-                .unwrap();
-            color_attachment.set_texture(Some(drawable.texture()));
-            color_attachment.set_load_action(MTLLoadAction::Clear);
-            color_attachment.set_store_action(MTLStoreAction::Store);
-            color_attachment.set_clear_color(MTLClearColor::new(0., 0., 0., 1.));
-
             let command_queue = window_state.command_queue.clone();
             let command_buffer = command_queue.new_command_buffer();
-            let command_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);
 
             let size = window_state.size();
             let scale_factor = window_state.scale_factor();
@@ -449,14 +430,12 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
 
             window_state.renderer.render(
                 &scene,
-                &RenderContext {
-                    drawable_size: size * scale_factor,
-                    device: &device,
-                    command_encoder,
-                },
+                size * scale_factor,
+                &device,
+                command_buffer,
+                drawable.texture(),
             );
 
-            command_encoder.end_encoding();
             command_buffer.commit();
             command_buffer.wait_until_completed();
             drawable.present();