Render stencils to atlas

Antonio Scandurra created

Change summary

gpui/src/platform/mac/atlas.rs              |  11 
gpui/src/platform/mac/renderer.rs           | 245 ++++++++++++----------
gpui/src/platform/mac/shaders/shaders.h     |   2 
gpui/src/platform/mac/shaders/shaders.metal |  12 
gpui/src/platform/mac/window.rs             |   1 
zed/src/editor/buffer_element.rs            |   2 
6 files changed, 146 insertions(+), 127 deletions(-)

Detailed changes

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

@@ -13,17 +13,18 @@ pub struct AtlasAllocator {
 
 impl AtlasAllocator {
     pub fn new(device: Device, texture_descriptor: TextureDescriptor) -> Self {
-        let me = Self {
+        let mut me = Self {
             device,
             texture_descriptor,
             atlasses: Vec::new(),
             free_atlasses: Vec::new(),
         };
-        me.atlasses.push(me.new_atlas());
+        let atlas = me.new_atlas();
+        me.atlasses.push(atlas);
         me
     }
 
-    fn atlas_size(&self) -> Vector2I {
+    pub fn atlas_size(&self) -> Vector2I {
         vec2i(
             self.texture_descriptor.width() as i32,
             self.texture_descriptor.height() as i32,
@@ -62,6 +63,10 @@ impl AtlasAllocator {
         self.free_atlasses.extend(self.atlasses.drain(1..));
     }
 
+    pub fn texture(&self, atlas_id: usize) -> Option<&metal::TextureRef> {
+        self.atlasses.get(atlas_id).map(|a| a.texture.as_ref())
+    }
+
     fn new_atlas(&mut self) -> Atlas {
         self.free_atlasses.pop().unwrap_or_else(|| {
             Atlas::new(

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

@@ -21,7 +21,6 @@ const INSTANCE_BUFFER_SIZE: usize = 1024 * 1024; // This is an arbitrary decisio
 
 pub struct Renderer {
     device: metal::Device,
-    command_buffer: metal::CommandBuffer,
     sprite_cache: SpriteCache,
     path_stencils: AtlasAllocator,
     quad_pipeline_state: metal::RenderPipelineState,
@@ -41,7 +40,6 @@ struct PathSprite {
 impl Renderer {
     pub fn new(
         device: metal::Device,
-        command_buffer: metal::CommandBuffer,
         pixel_format: metal::MTLPixelFormat,
         fonts: Arc<dyn platform::FontSystem>,
     ) -> Result<Self> {
@@ -75,52 +73,63 @@ impl Renderer {
         path_stencil_descriptor.set_usage(metal::MTLTextureUsage::RenderTarget);
         path_stencil_descriptor.set_storage_mode(metal::MTLStorageMode::Private);
 
+        let sprite_cache = SpriteCache::new(device.clone(), vec2i(1024, 768), fonts);
+        let path_stencils = AtlasAllocator::new(device.clone(), path_stencil_descriptor);
+        let quad_pipeline_state = build_pipeline_state(
+            &device,
+            &library,
+            "quad",
+            "quad_vertex",
+            "quad_fragment",
+            pixel_format,
+        )?;
+        let shadow_pipeline_state = build_pipeline_state(
+            &device,
+            &library,
+            "shadow",
+            "shadow_vertex",
+            "shadow_fragment",
+            pixel_format,
+        )?;
+        let sprite_pipeline_state = build_pipeline_state(
+            &device,
+            &library,
+            "sprite",
+            "sprite_vertex",
+            "sprite_fragment",
+            pixel_format,
+        )?;
+        let path_stencil_pipeline_state = build_stencil_pipeline_state(
+            &device,
+            &library,
+            "path_winding",
+            "path_winding_vertex",
+            "path_winding_fragment",
+            path_stencil_pixel_format,
+        )?;
         Ok(Self {
             device,
-            command_buffer,
-            sprite_cache: SpriteCache::new(device.clone(), vec2i(1024, 768), fonts),
-            path_stencils: AtlasAllocator::new(device.clone(), path_stencil_descriptor),
-            quad_pipeline_state: build_pipeline_state(
-                &device,
-                &library,
-                "quad",
-                "quad_vertex",
-                "quad_fragment",
-                pixel_format,
-            )?,
-            shadow_pipeline_state: build_pipeline_state(
-                &device,
-                &library,
-                "shadow",
-                "shadow_vertex",
-                "shadow_fragment",
-                pixel_format,
-            )?,
-            sprite_pipeline_state: build_pipeline_state(
-                &device,
-                &library,
-                "sprite",
-                "sprite_vertex",
-                "sprite_fragment",
-                pixel_format,
-            )?,
-            path_stencil_pipeline_state: build_stencil_pipeline_state(
-                &device,
-                &library,
-                "path_winding",
-                "path_winding_vertex",
-                "path_winding_fragment",
-                path_stencil_pixel_format,
-            )?,
+            sprite_cache,
+            path_stencils,
+            quad_pipeline_state,
+            shadow_pipeline_state,
+            sprite_pipeline_state,
+            path_stencil_pipeline_state,
             unit_vertices,
             instances,
         })
     }
 
-    pub fn render(&mut self, scene: &Scene, drawable_size: Vector2F, output: &metal::TextureRef) {
+    pub fn render(
+        &mut self,
+        scene: &Scene,
+        drawable_size: Vector2F,
+        command_buffer: &metal::CommandBufferRef,
+        output: &metal::TextureRef,
+    ) {
         let mut offset = 0;
-        self.render_path_stencils(scene, &mut offset, drawable_size);
-        self.render_layers(scene, &mut offset, drawable_size, output);
+        self.render_path_stencils(scene, &mut offset, drawable_size, command_buffer);
+        self.render_layers(scene, &mut offset, drawable_size, command_buffer, output);
     }
 
     fn render_path_stencils(
@@ -128,6 +137,7 @@ impl Renderer {
         scene: &Scene,
         offset: &mut usize,
         drawable_size: Vector2F,
+        command_buffer: &metal::CommandBufferRef,
     ) -> Vec<PathSprite> {
         let mut stencils = Vec::new();
         let mut vertices = Vec::<shaders::GPUIPathVertex>::new();
@@ -150,11 +160,10 @@ impl Renderer {
 
                 if current_atlas_id.map_or(false, |current_atlas_id| atlas_id != current_atlas_id) {
                     self.render_path_stencils_for_atlas(
-                        scene,
                         offset,
-                        drawable_size,
-                        vertices.as_slice(),
-                        self.path_stencils.texture(atlas_id).unwrap(),
+                        &vertices,
+                        atlas_id,
+                        command_buffer,
                     );
                     vertices.clear();
                 }
@@ -163,19 +172,19 @@ impl Renderer {
 
                 // Populate the vertices by translating them to their appropriate location in the atlas.
                 for vertex in &path.vertices {
-                    vertices.push(todo!());
+                    let xy_position = (vertex.xy_position - path.bounds.origin())
+                        * scene.scale_factor()
+                        + atlas_origin.to_f32();
+                    vertices.push(shaders::GPUIPathVertex {
+                        xy_position: xy_position.to_float2(),
+                        st_position: vertex.st_position.to_float2(),
+                    });
                 }
             }
         }
 
         if let Some(atlas_id) = current_atlas_id {
-            self.render_path_stencils_for_atlas(
-                scene,
-                offset,
-                drawable_size,
-                vertices.as_slice(),
-                self.path_stencils.texture(atlas_id).unwrap(),
-            );
+            self.render_path_stencils_for_atlas(offset, &vertices, atlas_id, command_buffer);
         }
 
         stencils
@@ -183,66 +192,73 @@ impl Renderer {
 
     fn render_path_stencils_for_atlas(
         &mut self,
-        scene: &Scene,
         offset: &mut usize,
-        drawable_size: Vector2F,
         vertices: &[shaders::GPUIPathVertex],
-        texture: &metal::TextureRef,
+        atlas_id: usize,
+        command_buffer: &metal::CommandBufferRef,
     ) {
-        // let render_pass_descriptor = metal::RenderPassDescriptor::new();
-        // let stencil_attachment = render_pass_descriptor.stencil_attachment().unwrap();
-        // stencil_attachment.set_texture(Some(&self.path_winding_texture));
-        // stencil_attachment.set_load_action(metal::MTLLoadAction::Clear);
-        // stencil_attachment.set_store_action(metal::MTLStoreAction::Store);
-        // let winding_command_encoder = self
-        //     .command_buffer
-        //     .new_render_command_encoder(render_pass_descriptor);
-
-        // Dubious shit that may be valuable:
-
-        // for path in scene.paths() {
-        //     winding_command_encoder.set_render_pipeline_state(&self.path_stencil_pipeline_state);
-        //     winding_command_encoder.set_vertex_buffer(
-        //         shaders::GPUIPathWindingVertexInputIndex_GPUIPathWindingVertexInputIndexVertices
-        //             as u64,
-        //         Some(&self.instances),
-        //         *offset as u64,
-        //     );
-        //     winding_command_encoder.set_vertex_bytes(
-        //         shaders::GPUIPathWindingVertexInputIndex_GPUIPathWindingVertexInputIndexViewportSize
-        //             as u64,
-        //         mem::size_of::<shaders::vector_float2>() as u64,
-        //         [drawable_size.to_float2()].as_ptr() as *const c_void,
-        //     );
-
-        //     let buffer_contents = unsafe {
-        //         (self.instances.contents() as *mut u8).offset(*offset as isize)
-        //             as *mut shaders::GPUIPathVertex
-        //     };
-
-        //     for (ix, vertex) in paths.iter().flat_map(|p| &p.vertices).enumerate() {
-        //         let shader_vertex = shaders::GPUIPathVertex {
-        //             xy_position: vertex.xy_position.to_float2(),
-        //             st_position: vertex.st_position.to_float2(),
-        //         };
-        //         unsafe {
-        //             *(buffer_contents.offset(ix as isize)) = shader_vertex;
-        //         }
-        //     }
-
-        //     self.instances.did_modify_range(NSRange {
-        //         location: *offset as u64,
-        //         length: (next_offset - *offset) as u64,
-        //     });
-        //     *offset = next_offset;
-
-        //     winding_command_encoder.draw_primitives(
-        //         metal::MTLPrimitiveType::Triangle,
-        //         0,
-        //         vertex_count as u64,
-        //     );
-        //     winding_command_encoder.end_encoding();
-        // }
+        align_offset(offset);
+        let next_offset = *offset + vertices.len() * mem::size_of::<shaders::GPUIPathVertex>();
+        assert!(
+            next_offset <= INSTANCE_BUFFER_SIZE,
+            "instance buffer exhausted"
+        );
+
+        let render_pass_descriptor = metal::RenderPassDescriptor::new();
+
+        let stencil_attachment = render_pass_descriptor.stencil_attachment().unwrap();
+        let stencil_texture = self.path_stencils.texture(atlas_id).unwrap();
+        stencil_attachment.set_texture(Some(stencil_texture));
+        stencil_attachment.set_load_action(metal::MTLLoadAction::Clear);
+        stencil_attachment.set_store_action(metal::MTLStoreAction::Store);
+
+        let stencil_descriptor = metal::DepthStencilDescriptor::new();
+        let front_face_stencil = stencil_descriptor.front_face_stencil().unwrap();
+        front_face_stencil.set_depth_stencil_pass_operation(metal::MTLStencilOperation::Invert);
+        front_face_stencil.set_depth_failure_operation(metal::MTLStencilOperation::Keep);
+        front_face_stencil.set_stencil_compare_function(metal::MTLCompareFunction::Always);
+        front_face_stencil.set_read_mask(0x1);
+        front_face_stencil.set_write_mask(0x1);
+        let depth_stencil_state = self.device.new_depth_stencil_state(&stencil_descriptor);
+
+        let winding_command_encoder =
+            command_buffer.new_render_command_encoder(render_pass_descriptor);
+        winding_command_encoder.set_depth_stencil_state(&depth_stencil_state);
+        winding_command_encoder.set_render_pipeline_state(&self.path_stencil_pipeline_state);
+        winding_command_encoder.set_vertex_buffer(
+            shaders::GPUIPathWindingVertexInputIndex_GPUIPathWindingVertexInputIndexVertices as u64,
+            Some(&self.instances),
+            *offset as u64,
+        );
+        winding_command_encoder.set_vertex_bytes(
+            shaders::GPUIPathWindingVertexInputIndex_GPUIPathWindingVertexInputIndexAtlasSize
+                as u64,
+            mem::size_of::<shaders::vector_float2>() as u64,
+            [self.path_stencils.atlas_size().to_float2()].as_ptr() as *const c_void,
+        );
+
+        let buffer_contents = unsafe {
+            (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUIPathVertex
+        };
+
+        for (ix, vertex) in vertices.iter().enumerate() {
+            unsafe {
+                *buffer_contents.add(ix) = *vertex;
+            }
+        }
+
+        self.instances.did_modify_range(NSRange {
+            location: *offset as u64,
+            length: (next_offset - *offset) as u64,
+        });
+        *offset = next_offset;
+
+        winding_command_encoder.draw_primitives(
+            metal::MTLPrimitiveType::Triangle,
+            0,
+            vertices.len() as u64,
+        );
+        winding_command_encoder.end_encoding();
     }
 
     fn render_layers(
@@ -250,6 +266,7 @@ impl Renderer {
         scene: &Scene,
         offset: &mut usize,
         drawable_size: Vector2F,
+        command_buffer: &metal::CommandBufferRef,
         output: &metal::TextureRef,
     ) {
         let render_pass_descriptor = metal::RenderPassDescriptor::new();
@@ -261,9 +278,7 @@ impl Renderer {
         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 = self
-            .command_buffer
-            .new_render_command_encoder(render_pass_descriptor);
+        let command_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);
 
         command_encoder.set_viewport(metal::MTLViewport {
             originX: 0.0,
@@ -276,9 +291,9 @@ impl Renderer {
 
         for layer in scene.layers() {
             self.clip(scene, layer, drawable_size, command_encoder);
-            self.render_shadows(scene, layer, &mut offset, drawable_size, command_encoder);
-            self.render_quads(scene, layer, &mut offset, drawable_size, command_encoder);
-            self.render_sprites(scene, layer, &mut offset, drawable_size, command_encoder);
+            self.render_shadows(scene, layer, offset, drawable_size, command_encoder);
+            self.render_quads(scene, layer, offset, drawable_size, command_encoder);
+            self.render_sprites(scene, layer, offset, drawable_size, command_encoder);
         }
 
         command_encoder.end_encoding();

gpui/src/platform/mac/shaders/shaders.h 🔗

@@ -56,7 +56,7 @@ typedef struct {
 
 typedef enum {
     GPUIPathWindingVertexInputIndexVertices = 0,
-    GPUIPathWindingVertexInputIndexViewportSize = 1,
+    GPUIPathWindingVertexInputIndexAtlasSize = 1,
 } GPUIPathWindingVertexInputIndex;
 
 typedef struct {

gpui/src/platform/mac/shaders/shaders.metal 🔗

@@ -210,10 +210,10 @@ struct PathWindingFragmentInput {
 vertex PathWindingFragmentInput path_winding_vertex(
     uint vertex_id [[vertex_id]],
     constant GPUIPathVertex *vertices [[buffer(GPUIPathWindingVertexInputIndexVertices)]],
-    constant float2 *viewport_size [[buffer(GPUIPathWindingVertexInputIndexViewportSize)]]
+    constant float2 *atlas_size [[buffer(GPUIPathWindingVertexInputIndexAtlasSize)]]
 ) {
     GPUIPathVertex v = vertices[vertex_id];
-    float4 device_position = to_device_position(v.xy_position, *viewport_size);
+    float4 device_position = to_device_position(v.xy_position, *atlas_size);
     return PathWindingFragmentInput {
         device_position,
         v.st_position,
@@ -223,9 +223,9 @@ vertex PathWindingFragmentInput path_winding_vertex(
 fragment float4 path_winding_fragment(
     PathWindingFragmentInput input [[stage_in]]
 ) {
-    if (input.st_position.x * input.st_position.x - input.st_position.y > 0.0) {
-        return float4(0.0);
+    if (input.st_position.x * input.st_position.x - input.st_position.y > 0.) {
+        return float4(0.);
     } else {
-        return float4(float3(0.0), 1.0 / 255.0);
+        return float4(1.);
     }
-}
+}

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

@@ -431,7 +431,6 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
             window_state.renderer.render(
                 &scene,
                 size * scale_factor,
-                &device,
                 command_buffer,
                 drawable.texture(),
             );

zed/src/editor/buffer_element.rs 🔗

@@ -637,7 +637,7 @@ impl Selection {
         );
         path.line_to(vec2f(first_line.end_x - corner_radius, start_y));
 
-        scene.push_path(ColorU::from_u32(0xff0000ff), path.build());
+        scene.push_path(path.build(ColorU::from_u32(0xff0000ff)));
 
         // rounded_corner(&mut path, corner, corner_radius, Right, Down);