From 52594fe250cc98bde8c05bccaece8a345e14cee0 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 26 Jan 2022 13:07:39 +0100 Subject: [PATCH] WIP: Start on squiggly underlines --- crates/gpui/src/platform/mac/renderer.rs | 79 ++++++++++++++++++- .../gpui/src/platform/mac/shaders/shaders.h | 15 ++++ .../src/platform/mac/shaders/shaders.metal | 46 +++++++++++ crates/gpui/src/text_layout.rs | 4 +- 4 files changed, 141 insertions(+), 3 deletions(-) diff --git a/crates/gpui/src/platform/mac/renderer.rs b/crates/gpui/src/platform/mac/renderer.rs index 2a97f4820c..b5c563c9e4 100644 --- a/crates/gpui/src/platform/mac/renderer.rs +++ b/crates/gpui/src/platform/mac/renderer.rs @@ -26,6 +26,7 @@ pub struct Renderer { sprite_pipeline_state: metal::RenderPipelineState, image_pipeline_state: metal::RenderPipelineState, path_atlas_pipeline_state: metal::RenderPipelineState, + underline_pipeline_state: metal::RenderPipelineState, unit_vertices: metal::Buffer, instances: metal::Buffer, } @@ -109,6 +110,14 @@ impl Renderer { "path_atlas_fragment", MTLPixelFormat::R16Float, ); + let underline_pipeline_state = build_pipeline_state( + &device, + &library, + "underline", + "underline_vertex", + "underline_fragment", + pixel_format, + ); Self { sprite_cache, image_cache, @@ -118,6 +127,7 @@ impl Renderer { sprite_pipeline_state, image_pipeline_state, path_atlas_pipeline_state, + underline_pipeline_state, unit_vertices, instances, } @@ -339,7 +349,7 @@ impl Renderer { drawable_size, command_encoder, ); - self.render_quads( + self.render_underlines( layer.underlines(), scale_factor, offset, @@ -821,6 +831,73 @@ impl Renderer { ); *offset = next_offset; } + + fn render_underlines( + &mut self, + underlines: &[Quad], + scale_factor: f32, + offset: &mut usize, + drawable_size: Vector2F, + command_encoder: &metal::RenderCommandEncoderRef, + ) { + if underlines.is_empty() { + return; + } + align_offset(offset); + let next_offset = *offset + underlines.len() * mem::size_of::(); + assert!( + next_offset <= INSTANCE_BUFFER_SIZE, + "instance buffer exhausted" + ); + + command_encoder.set_render_pipeline_state(&self.underline_pipeline_state); + command_encoder.set_vertex_buffer( + shaders::GPUIUnderlineInputIndex_GPUIUnderlineInputIndexVertices as u64, + Some(&self.unit_vertices), + 0, + ); + command_encoder.set_vertex_buffer( + shaders::GPUIUnderlineInputIndex_GPUIUnderlineInputIndexUnderlines as u64, + Some(&self.instances), + *offset as u64, + ); + command_encoder.set_vertex_bytes( + shaders::GPUIUnderlineInputIndex_GPUIUnderlineInputIndexUniforms as u64, + mem::size_of::() as u64, + [shaders::GPUIUniforms { + viewport_size: 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::GPUIUnderline + }; + for (ix, quad) in underlines.iter().enumerate() { + let bounds = quad.bounds * scale_factor; + let shader_quad = shaders::GPUIUnderline { + origin: bounds.origin().round().to_float2(), + size: bounds.size().round().to_float2(), + thickness: 1. * scale_factor, + color: quad + .background + .unwrap_or(Color::transparent_black()) + .to_uchar4(), + }; + unsafe { + *(buffer_contents.offset(ix as isize)) = shader_quad; + } + } + + command_encoder.draw_primitives_instanced( + metal::MTLPrimitiveType::Triangle, + 0, + 6, + underlines.len() as u64, + ); + *offset = next_offset; + } } fn build_path_atlas_texture_descriptor() -> metal::TextureDescriptor { diff --git a/crates/gpui/src/platform/mac/shaders/shaders.h b/crates/gpui/src/platform/mac/shaders/shaders.h index 1b6ad3f26f..2e6133fdd5 100644 --- a/crates/gpui/src/platform/mac/shaders/shaders.h +++ b/crates/gpui/src/platform/mac/shaders/shaders.h @@ -104,3 +104,18 @@ typedef struct vector_uchar4 border_color; float corner_radius; } GPUIImage; + +typedef enum +{ + GPUIUnderlineInputIndexVertices = 0, + GPUIUnderlineInputIndexUnderlines = 1, + GPUIUnderlineInputIndexUniforms = 2, +} GPUIUnderlineInputIndex; + +typedef struct +{ + vector_float2 origin; + vector_float2 size; + float thickness; + vector_uchar4 color; +} GPUIUnderline; diff --git a/crates/gpui/src/platform/mac/shaders/shaders.metal b/crates/gpui/src/platform/mac/shaders/shaders.metal index 0cf7d290f2..413440a24e 100644 --- a/crates/gpui/src/platform/mac/shaders/shaders.metal +++ b/crates/gpui/src/platform/mac/shaders/shaders.metal @@ -304,3 +304,49 @@ fragment float4 path_atlas_fragment( float alpha = saturate(0.5 - distance); return float4(alpha, 0., 0., 1.); } + +struct UnderlineFragmentInput { + float4 position [[position]]; + float2 origin; + float2 size; + float thickness; + float4 color; +}; + +vertex UnderlineFragmentInput underline_vertex( + uint unit_vertex_id [[vertex_id]], + uint underline_id [[instance_id]], + constant float2 *unit_vertices [[buffer(GPUIUnderlineInputIndexVertices)]], + constant GPUIUnderline *underlines [[buffer(GPUIUnderlineInputIndexUnderlines)]], + constant GPUIUniforms *uniforms [[buffer(GPUIUnderlineInputIndexUniforms)]] +) { + float2 unit_vertex = unit_vertices[unit_vertex_id]; + GPUIUnderline underline = underlines[underline_id]; + float2 position = unit_vertex * underline.size + underline.origin; + float4 device_position = to_device_position(position, uniforms->viewport_size); + + return UnderlineFragmentInput { + device_position, + underline.origin, + underline.size, + underline.thickness, + coloru_to_colorf(underline.color), + }; +} + +fragment float4 underline_fragment( + UnderlineFragmentInput input [[stage_in]] +) { + float half_thickness = input.thickness * 0.5; + float2 st = ((input.position.xy - input.origin) / input.size.y) - float2(0., 0.5); + float frequency = M_PI_F * 0.75; + float amplitude = 0.3; + float sine = sin(st.x * frequency) * amplitude; + float dSine = cos(st.x * frequency) * amplitude * frequency; + float distance = (st.y - sine) / sqrt(1. + dSine * dSine); + float distance_in_pixels = distance * input.size.y; + float distance_from_top_border = distance_in_pixels - half_thickness; + float distance_from_bottom_border = distance_in_pixels + half_thickness; + float alpha = saturate(0.5 - max(-distance_from_bottom_border, distance_from_top_border)); + return input.color * float4(1., 1., 1., alpha); +} diff --git a/crates/gpui/src/text_layout.rs b/crates/gpui/src/text_layout.rs index 9c975ea491..f2fca6d0a6 100644 --- a/crates/gpui/src/text_layout.rs +++ b/crates/gpui/src/text_layout.rs @@ -290,7 +290,7 @@ impl Line { if let Some((underline_origin, underline_color)) = finished_underline { cx.scene.push_underline(scene::Quad { - bounds: RectF::from_points(underline_origin, glyph_origin + vec2f(0., 1.)), + bounds: RectF::from_points(underline_origin, glyph_origin + vec2f(0., 3.)), background: Some(underline_color), border: Default::default(), corner_radius: 0., @@ -311,7 +311,7 @@ impl Line { let line_end = origin + baseline_offset + vec2f(self.layout.width, 0.); cx.scene.push_underline(scene::Quad { - bounds: RectF::from_points(underline_start, line_end + vec2f(0., 1.)), + bounds: RectF::from_points(underline_start, line_end + vec2f(0., 3.)), background: Some(underline_color), border: Default::default(), corner_radius: 0.,