diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 184b607ca6..0a143f7976 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -4,7 +4,7 @@ use super::{BladeBelt, BladeBeltDescriptor}; use crate::{ AtlasTextureKind, AtlasTile, BladeAtlas, Bounds, ContentMask, Hsla, Path, PathId, PathVertex, - PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, PATH_TEXTURE_FORMAT, + PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Underline, PATH_TEXTURE_FORMAT, }; use bytemuck::{Pod, Zeroable}; use collections::HashMap; @@ -48,6 +48,12 @@ struct ShaderPathsData { b_path_sprites: gpu::BufferPiece, } +#[derive(blade_macros::ShaderData)] +struct ShaderUnderlinesData { + globals: GlobalParams, + b_underlines: gpu::BufferPiece, +} + #[derive(Clone, Debug, Eq, PartialEq)] #[repr(C)] struct PathSprite { @@ -61,6 +67,7 @@ struct BladePipelines { shadows: gpu::RenderPipeline, path_rasterization: gpu::RenderPipeline, paths: gpu::RenderPipeline, + underlines: gpu::RenderPipeline, } impl BladePipelines { @@ -75,11 +82,13 @@ impl BladePipelines { shader.get_struct_size("PathVertex") as usize, ); shader.check_struct_size::(); + shader.check_struct_size::(); let quads_layout = ::layout(); let shadows_layout = ::layout(); let path_rasterization_layout = ::layout(); let paths_layout = ::layout(); + let underlines_layout = ::layout(); Self { quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc { @@ -146,6 +155,22 @@ impl BladePipelines { write_mask: gpu::ColorWrites::default(), }], }), + underlines: gpu.create_render_pipeline(gpu::RenderPipelineDesc { + name: "underlines", + data_layouts: &[&underlines_layout], + vertex: shader.at("vs_underline"), + primitive: gpu::PrimitiveState { + topology: gpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + fragment: shader.at("fs_underline"), + color_targets: &[gpu::ColorTargetState { + format: surface_format, + blend: Some(gpu::BlendState::ALPHA_BLENDING), + write_mask: gpu::ColorWrites::default(), + }], + }), } } } @@ -359,6 +384,18 @@ impl BladeRenderer { encoder.draw(0, 4, 0, sprites.len() as u32); } } + PrimitiveBatch::Underlines(underlines) => { + let instance_buf = self.instance_belt.alloc_data(underlines, &self.gpu); + let mut encoder = pass.with(&self.pipelines.underlines); + encoder.bind( + 0, + &ShaderUnderlinesData { + globals, + b_underlines: instance_buf, + }, + ); + encoder.draw(0, 4, 0, underlines.len() as u32); + } _ => continue, } } diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl index 3a7853142a..e0990c6c1b 100644 --- a/crates/gpui/src/platform/linux/shaders.wgsl +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -373,7 +373,7 @@ struct PathVarying { @vertex fn vs_path(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> PathVarying { - let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); + let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); let sprite = b_path_sprites[instance_id]; // Don't apply content mask because it was already accounted for when rasterizing the path. @@ -386,7 +386,69 @@ fn vs_path(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) insta @fragment fn fs_path(input: PathVarying) -> @location(0) vec4 { - let sample = textureSample(t_tile, s_tile, input.tile_position).r; - let mask = 1.0 - abs(1.0 - sample % 2.0); - return input.color * mask; + let sample = textureSample(t_tile, s_tile, input.tile_position).r; + let mask = 1.0 - abs(1.0 - sample % 2.0); + return input.color * mask; +} + +// --- underlines --- // + +struct Underline { + view_id: ViewId, + layer_id: u32, + order: u32, + bounds: Bounds, + content_mask: Bounds, + color: Hsla, + thickness: f32, + wavy: u32, +} +var b_underlines: array; + +struct UnderlineVarying { + @builtin(position) position: vec4, + @location(0) @interpolate(flat) color: vec4, + @location(1) @interpolate(flat) underline_id: u32, + //TODO: use `clip_distance` once Naga supports it + @location(3) clip_distances: vec4, +} + +@vertex +fn vs_underline(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> UnderlineVarying { + let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); + let underline = b_underlines[instance_id]; + + var out = UnderlineVarying(); + out.position = to_device_position(unit_vertex, underline.bounds); + out.color = hsla_to_rgba(underline.color); + out.underline_id = instance_id; + out.clip_distances = distance_from_clip_rect(unit_vertex, underline.bounds, underline.content_mask); + return out; +} + +@fragment +fn fs_underline(input: UnderlineVarying) -> @location(0) vec4 { + // Alpha clip first, since we don't have `clip_distance`. + if (any(input.clip_distances < vec4(0.0))) { + return vec4(0.0); + } + + let underline = b_underlines[input.underline_id]; + if (underline.wavy == 0u) + { + return vec4(0.0); + } + + let half_thickness = underline.thickness * 0.5; + let st = (input.position.xy - underline.bounds.origin) / underline.bounds.size.y - vec2(0.0, 0.5); + let frequency = M_PI_F * 3.0 * underline.thickness / 8.0; + let amplitude = 1.0 / (2.0 * underline.thickness); + let sine = sin(st.x * frequency) * amplitude; + let dSine = cos(st.x * frequency) * amplitude * frequency; + let distance = (st.y - sine) / sqrt(1.0 + dSine * dSine); + let distance_in_pixels = distance * underline.bounds.size.y; + let distance_from_top_border = distance_in_pixels - half_thickness; + let distance_from_bottom_border = distance_in_pixels + half_thickness; + let alpha = saturate(0.5 - max(-distance_from_bottom_border, distance_from_top_border)); + return input.color * vec4(1.0, 1.0, 1.0, alpha); } diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index f5fcade711..dcf3f2cbea 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -543,8 +543,8 @@ pub(crate) struct Underline { pub order: DrawOrder, pub bounds: Bounds, pub content_mask: ContentMask, - pub thickness: ScaledPixels, pub color: Hsla, + pub thickness: ScaledPixels, pub wavy: bool, } diff --git a/crates/gpui/src/window/element_cx.rs b/crates/gpui/src/window/element_cx.rs index cde5f17b05..bc38db3ab5 100644 --- a/crates/gpui/src/window/element_cx.rs +++ b/crates/gpui/src/window/element_cx.rs @@ -753,8 +753,8 @@ impl<'a> ElementContext<'a> { order: 0, bounds: bounds.scale(scale_factor), content_mask: content_mask.scale(scale_factor), - thickness: style.thickness.scale(scale_factor), color: style.color.unwrap_or_default(), + thickness: style.thickness.scale(scale_factor), wavy: style.wavy, }, );