diff --git a/gpui/src/platform/mac/renderer.rs b/gpui/src/platform/mac/renderer.rs index cfd42228d9..6b3801d649 100644 --- a/gpui/src/platform/mac/renderer.rs +++ b/gpui/src/platform/mac/renderer.rs @@ -65,12 +65,12 @@ impl Renderer { MTLResourceOptions::StorageModeManaged, ); - let path_stencil_pixel_format = metal::MTLPixelFormat::Stencil8; let path_stencil_descriptor = metal::TextureDescriptor::new(); - path_stencil_descriptor.set_width(2048); - path_stencil_descriptor.set_height(2048); - path_stencil_descriptor.set_pixel_format(path_stencil_pixel_format); - path_stencil_descriptor.set_usage(metal::MTLTextureUsage::RenderTarget); + path_stencil_descriptor.set_width(1024); + path_stencil_descriptor.set_height(768); + path_stencil_descriptor.set_pixel_format(pixel_format); + path_stencil_descriptor + .set_usage(metal::MTLTextureUsage::RenderTarget | metal::MTLTextureUsage::ShaderRead); path_stencil_descriptor.set_storage_mode(metal::MTLStorageMode::Private); let sprite_cache = SpriteCache::new(device.clone(), vec2i(1024, 768), fonts); @@ -105,7 +105,7 @@ impl Renderer { "path_winding", "path_winding_vertex", "path_winding_fragment", - path_stencil_pixel_format, + pixel_format, )?; Ok(Self { device, @@ -128,15 +128,21 @@ impl Renderer { output: &metal::TextureRef, ) { let mut offset = 0; - self.render_path_stencils(scene, &mut offset, drawable_size, command_buffer); - self.render_layers(scene, &mut offset, drawable_size, command_buffer, output); + let stencils = self.render_path_stencils(scene, &mut offset, command_buffer); + self.render_layers( + scene, + stencils, + &mut offset, + drawable_size, + command_buffer, + output, + ); } fn render_path_stencils( &mut self, scene: &Scene, offset: &mut usize, - drawable_size: Vector2F, command_buffer: &metal::CommandBufferRef, ) -> Vec { let mut stencils = Vec::new(); @@ -145,16 +151,20 @@ impl Renderer { for (layer_id, layer) in scene.layers().iter().enumerate() { for path in layer.paths() { // Push a PathStencil struct for use later when sampling from the atlas as we draw the content of the layers - let size = path.bounds.size().ceil().to_i32(); - let (atlas_id, atlas_origin) = self.path_stencils.allocate(size).unwrap(); + let origin = path.bounds.origin() * scene.scale_factor(); + let size = (path.bounds.size() * scene.scale_factor()).ceil(); + let (atlas_id, atlas_origin) = + self.path_stencils.allocate(size.ceil().to_i32()).unwrap(); + let atlas_origin = atlas_origin.to_f32(); stencils.push(PathSprite { layer_id, atlas_id, sprite: shaders::GPUISprite { - origin: path.bounds.origin().to_float2(), + origin: origin.to_float2(), size: size.to_float2(), atlas_origin: atlas_origin.to_float2(), color: path.color.to_uchar4(), + compute_winding: 1, }, }); @@ -172,11 +182,10 @@ impl Renderer { // Populate the vertices by translating them to their appropriate location in the atlas. for vertex in &path.vertices { - let xy_position = (vertex.xy_position - path.bounds.origin()) - * scene.scale_factor() - + atlas_origin.to_f32(); + let xy_position = + (vertex.xy_position - path.bounds.origin()) * scene.scale_factor(); vertices.push(shaders::GPUIPathVertex { - xy_position: xy_position.to_float2(), + xy_position: (atlas_origin + xy_position).to_float2(), st_position: vertex.st_position.to_float2(), }); } @@ -205,25 +214,32 @@ impl Renderer { ); let render_pass_descriptor = metal::RenderPassDescriptor::new(); - - let stencil_attachment = render_pass_descriptor.stencil_attachment().unwrap(); + let color_attachment = render_pass_descriptor + .color_attachments() + .object_at(0) + .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); + color_attachment.set_texture(Some(stencil_texture)); + 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 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 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, @@ -264,6 +280,7 @@ impl Renderer { fn render_layers( &mut self, scene: &Scene, + path_sprites: Vec, offset: &mut usize, drawable_size: Vector2F, command_buffer: &metal::CommandBufferRef, @@ -289,11 +306,20 @@ impl Renderer { zfar: 1.0, }); - for layer in scene.layers() { + for (layer_id, layer) in scene.layers().iter().enumerate() { self.clip(scene, layer, 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); + // TODO: Pass sprites relevant to this layer in a more efficient manner. + self.render_path_sprites( + scene, + layer, + path_sprites.iter().filter(|s| s.layer_id == layer_id), + offset, + drawable_size, + command_encoder, + ); + self.render_glyph_sprites(scene, layer, offset, drawable_size, command_encoder); } command_encoder.end_encoding(); @@ -471,7 +497,7 @@ impl Renderer { ); } - fn render_sprites( + fn render_glyph_sprites( &mut self, scene: &Scene, layer: &Layer, @@ -502,6 +528,7 @@ impl Renderer { size: sprite.size.to_float2(), atlas_origin: sprite.atlas_origin.to_float2(), color: glyph.color.to_uchar4(), + compute_winding: 0, }); } } @@ -563,6 +590,87 @@ impl Renderer { ); } } + + fn render_path_sprites<'a>( + &mut self, + scene: &Scene, + layer: &Layer, + sprites: impl Iterator, + offset: &mut usize, + drawable_size: Vector2F, + command_encoder: &metal::RenderCommandEncoderRef, + ) { + let mut sprites = sprites.peekable(); + if sprites.peek().is_none() { + return; + } + + let mut sprites_by_atlas = HashMap::new(); + for sprite in sprites { + sprites_by_atlas + .entry(sprite.atlas_id) + .or_insert_with(Vec::new) + .push(sprite.sprite); + } + + command_encoder.set_render_pipeline_state(&self.sprite_pipeline_state); + command_encoder.set_vertex_buffer( + shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexVertices as u64, + Some(&self.unit_vertices), + 0, + ); + command_encoder.set_vertex_bytes( + shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexViewportSize as u64, + mem::size_of::() as u64, + [drawable_size.to_float2()].as_ptr() as *const c_void, + ); + + for (atlas_id, sprites) in sprites_by_atlas { + align_offset(offset); + let next_offset = *offset + sprites.len() * mem::size_of::(); + assert!( + next_offset <= INSTANCE_BUFFER_SIZE, + "instance buffer exhausted" + ); + + command_encoder.set_vertex_buffer( + shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexSprites as u64, + Some(&self.instances), + *offset as u64, + ); + + let texture = self.path_stencils.texture(atlas_id).unwrap(); + command_encoder.set_vertex_bytes( + shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexAtlasSize as u64, + mem::size_of::() as u64, + [vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr() + as *const c_void, + ); + command_encoder.set_fragment_texture( + shaders::GPUISpriteFragmentInputIndex_GPUISpriteFragmentInputIndexAtlas as u64, + Some(texture), + ); + + unsafe { + let buffer_contents = (self.instances.contents() as *mut u8) + .offset(*offset as isize) + as *mut shaders::GPUISprite; + std::ptr::copy_nonoverlapping(sprites.as_ptr(), buffer_contents, sprites.len()); + } + self.instances.did_modify_range(NSRange { + location: *offset as u64, + length: (next_offset - *offset) as u64, + }); + *offset = next_offset; + + command_encoder.draw_primitives_instanced( + metal::MTLPrimitiveType::Triangle, + 0, + 6, + sprites.len() as u64, + ); + } + } } fn align_offset(offset: &mut usize) { @@ -625,13 +733,47 @@ fn build_stencil_pipeline_state( descriptor.set_label(label); descriptor.set_vertex_function(Some(vertex_fn.as_ref())); descriptor.set_fragment_function(Some(fragment_fn.as_ref())); - descriptor.set_stencil_attachment_pixel_format(pixel_format); + let color_attachment = descriptor.color_attachments().object_at(0).unwrap(); + color_attachment.set_pixel_format(pixel_format); + color_attachment.set_blending_enabled(true); + color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add); + color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add); + color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::One); + color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One); + color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::One); + color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One); device .new_render_pipeline_state(&descriptor) .map_err(|message| anyhow!("could not create render pipeline state: {}", message)) } +// fn build_stencil_pipeline_state( +// device: &metal::DeviceRef, +// library: &metal::LibraryRef, +// label: &str, +// vertex_fn_name: &str, +// fragment_fn_name: &str, +// pixel_format: metal::MTLPixelFormat, +// ) -> Result { +// let vertex_fn = library +// .get_function(vertex_fn_name, None) +// .map_err(|message| anyhow!("error locating vertex function: {}", message))?; +// let fragment_fn = library +// .get_function(fragment_fn_name, None) +// .map_err(|message| anyhow!("error locating fragment function: {}", message))?; + +// let descriptor = metal::RenderPipelineDescriptor::new(); +// descriptor.set_label(label); +// descriptor.set_vertex_function(Some(vertex_fn.as_ref())); +// descriptor.set_fragment_function(Some(fragment_fn.as_ref())); +// descriptor.set_stencil_attachment_pixel_format(pixel_format); + +// device +// .new_render_pipeline_state(&descriptor) +// .map_err(|message| anyhow!("could not create render pipeline state: {}", message)) +// } + mod shaders { #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] diff --git a/gpui/src/platform/mac/shaders/shaders.h b/gpui/src/platform/mac/shaders/shaders.h index dd0ae6b2e8..01b42d6e4b 100644 --- a/gpui/src/platform/mac/shaders/shaders.h +++ b/gpui/src/platform/mac/shaders/shaders.h @@ -52,6 +52,7 @@ typedef struct { vector_float2 size; vector_float2 atlas_origin; vector_uchar4 color; + uint8_t compute_winding; } GPUISprite; typedef enum { diff --git a/gpui/src/platform/mac/shaders/shaders.metal b/gpui/src/platform/mac/shaders/shaders.metal index b79cccb949..cd4bcb2efb 100644 --- a/gpui/src/platform/mac/shaders/shaders.metal +++ b/gpui/src/platform/mac/shaders/shaders.metal @@ -168,6 +168,7 @@ struct SpriteFragmentInput { float4 position [[position]]; float2 atlas_position; float4 color [[flat]]; + uchar compute_winding [[flat]]; }; vertex SpriteFragmentInput sprite_vertex( @@ -188,6 +189,7 @@ vertex SpriteFragmentInput sprite_vertex( device_position, atlas_position, coloru_to_colorf(sprite.color), + sprite.compute_winding }; } @@ -197,8 +199,14 @@ fragment float4 sprite_fragment( ) { constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear); float4 color = input.color; - float4 mask = atlas.sample(atlas_sampler, input.atlas_position); - color.a *= mask.a; + float4 sample = atlas.sample(atlas_sampler, input.atlas_position); + float mask; + if (input.compute_winding) { + mask = fmod(sample.r * 255., 2.); + } else { + mask = sample.a; + } + color.a *= mask; return color; } @@ -223,9 +231,14 @@ 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.) { - return float4(0.); - } else { - return float4(1.); - } + float2 dx = dfdx(input.st_position); + float2 dy = dfdy(input.st_position); + float2 gradient = float2( + (2. * input.st_position.x) * dx.x - dx.y, + (2. * input.st_position.x) * dy.x - dy.y + ); + float f = (input.st_position.x * input.st_position.x) - input.st_position.y; + float distance = f / length(gradient); + float alpha = saturate(0.5 - distance) / 255.; + return float4(alpha, 0., 0., 1.); } diff --git a/zed/src/editor/buffer_element.rs b/zed/src/editor/buffer_element.rs index 506695f4b7..2b124899a2 100644 --- a/zed/src/editor/buffer_element.rs +++ b/zed/src/editor/buffer_element.rs @@ -230,25 +230,29 @@ impl BufferElement { let selection = Selection { line_height, - start_y: row_range.start as f32 * line_height - scroll_top, + start_y: bounds.origin_y() + row_range.start as f32 * line_height - scroll_top, lines: row_range .into_iter() .map(|row| { let line_layout = &layout.line_layouts[(row - start_row) as usize]; SelectionLine { start_x: if row == range_start.row() { - line_layout.x_for_index(range_start.column() as usize) + bounds.origin_x() + + line_layout.x_for_index(range_start.column() as usize) - scroll_left - descent } else { -scroll_left }, end_x: if row == range_end.row() { - line_layout.x_for_index(range_end.column() as usize) + bounds.origin_x() + + line_layout.x_for_index(range_end.column() as usize) - scroll_left - descent } else { - line_layout.width + corner_radius * 2.0 - scroll_left - descent + bounds.origin_x() + line_layout.width + corner_radius * 2.0 + - scroll_left + - descent }, } })