diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 0a143f7976..7ac3acda19 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, Underline, PATH_TEXTURE_FORMAT, + PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Underline, MonochromeSprite, PolychromeSprite, PATH_TEXTURE_FORMAT, }; use bytemuck::{Pod, Zeroable}; use collections::HashMap; @@ -43,8 +43,8 @@ struct ShaderPathRasterizationData { #[derive(blade_macros::ShaderData)] struct ShaderPathsData { globals: GlobalParams, - t_tile: gpu::TextureView, - s_tile: gpu::Sampler, + t_sprite: gpu::TextureView, + s_sprite: gpu::Sampler, b_path_sprites: gpu::BufferPiece, } @@ -54,6 +54,22 @@ struct ShaderUnderlinesData { b_underlines: gpu::BufferPiece, } +#[derive(blade_macros::ShaderData)] +struct ShaderMonoSpritesData { + globals: GlobalParams, + t_sprite: gpu::TextureView, + s_sprite: gpu::Sampler, + b_mono_sprites: gpu::BufferPiece, +} + +#[derive(blade_macros::ShaderData)] +struct ShaderPolySpritesData { + globals: GlobalParams, + t_sprite: gpu::TextureView, + s_sprite: gpu::Sampler, + b_poly_sprites: gpu::BufferPiece, +} + #[derive(Clone, Debug, Eq, PartialEq)] #[repr(C)] struct PathSprite { @@ -68,10 +84,14 @@ struct BladePipelines { path_rasterization: gpu::RenderPipeline, paths: gpu::RenderPipeline, underlines: gpu::RenderPipeline, + mono_sprites: gpu::RenderPipeline, + poly_sprites: gpu::RenderPipeline, } impl BladePipelines { fn new(gpu: &gpu::Context, surface_format: gpu::TextureFormat) -> Self { + use gpu::ShaderData as _; + let shader = gpu.create_shader(gpu::ShaderDesc { source: include_str!("shaders.wgsl"), }); @@ -83,17 +103,13 @@ impl BladePipelines { ); 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(); + shader.check_struct_size::(); + shader.check_struct_size::(); Self { quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc { name: "quads", - data_layouts: &[&quads_layout], + data_layouts: &[&ShaderQuadsData::layout()], vertex: shader.at("vs_quad"), primitive: gpu::PrimitiveState { topology: gpu::PrimitiveTopology::TriangleStrip, @@ -109,7 +125,7 @@ impl BladePipelines { }), shadows: gpu.create_render_pipeline(gpu::RenderPipelineDesc { name: "shadows", - data_layouts: &[&shadows_layout], + data_layouts: &[&ShaderShadowsData::layout()], vertex: shader.at("vs_shadow"), primitive: gpu::PrimitiveState { topology: gpu::PrimitiveTopology::TriangleStrip, @@ -125,7 +141,7 @@ impl BladePipelines { }), path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc { name: "path_rasterization", - data_layouts: &[&path_rasterization_layout], + data_layouts: &[&ShaderPathRasterizationData::layout()], vertex: shader.at("vs_path_rasterization"), primitive: gpu::PrimitiveState { topology: gpu::PrimitiveTopology::TriangleStrip, @@ -141,7 +157,7 @@ impl BladePipelines { }), paths: gpu.create_render_pipeline(gpu::RenderPipelineDesc { name: "paths", - data_layouts: &[&paths_layout], + data_layouts: &[&ShaderPathsData::layout()], vertex: shader.at("vs_path"), primitive: gpu::PrimitiveState { topology: gpu::PrimitiveTopology::TriangleStrip, @@ -157,7 +173,7 @@ impl BladePipelines { }), underlines: gpu.create_render_pipeline(gpu::RenderPipelineDesc { name: "underlines", - data_layouts: &[&underlines_layout], + data_layouts: &[&ShaderUnderlinesData::layout()], vertex: shader.at("vs_underline"), primitive: gpu::PrimitiveState { topology: gpu::PrimitiveTopology::TriangleStrip, @@ -171,6 +187,38 @@ impl BladePipelines { write_mask: gpu::ColorWrites::default(), }], }), + mono_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc { + name: "mono-sprites", + data_layouts: &[&ShaderMonoSpritesData::layout()], + vertex: shader.at("vs_mono_sprite"), + primitive: gpu::PrimitiveState { + topology: gpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + fragment: shader.at("fs_mono_sprite"), + color_targets: &[gpu::ColorTargetState { + format: surface_format, + blend: Some(gpu::BlendState::ALPHA_BLENDING), + write_mask: gpu::ColorWrites::default(), + }], + }), + poly_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc { + name: "poly-sprites", + data_layouts: &[&ShaderPolySpritesData::layout()], + vertex: shader.at("vs_poly_sprite"), + primitive: gpu::PrimitiveState { + topology: gpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + fragment: shader.at("fs_poly_sprite"), + color_targets: &[gpu::ColorTargetState { + format: surface_format, + blend: Some(gpu::BlendState::ALPHA_BLENDING), + write_mask: gpu::ColorWrites::default(), + }], + }), } } } @@ -376,8 +424,8 @@ impl BladeRenderer { 0, &ShaderPathsData { globals, - t_tile: tex_info.raw_view.unwrap(), - s_tile: self.atlas_sampler, + t_sprite: tex_info.raw_view.unwrap(), + s_sprite: self.atlas_sampler, b_path_sprites: instance_buf, }, ); @@ -396,7 +444,39 @@ impl BladeRenderer { ); encoder.draw(0, 4, 0, underlines.len() as u32); } - _ => continue, + PrimitiveBatch::MonochromeSprites { texture_id, sprites } => { + let tex_info = self.atlas.get_texture_info(texture_id); + let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu); + let mut encoder = pass.with(&self.pipelines.mono_sprites); + encoder.bind( + 0, + &ShaderMonoSpritesData { + globals, + t_sprite: tex_info.raw_view.unwrap(), + s_sprite: self.atlas_sampler, + b_mono_sprites: instance_buf, + }, + ); + encoder.draw(0, 4, 0, sprites.len() as u32); + } + PrimitiveBatch::PolychromeSprites { texture_id, sprites } => { + let tex_info = self.atlas.get_texture_info(texture_id); + let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu); + let mut encoder = pass.with(&self.pipelines.poly_sprites); + encoder.bind( + 0, + &ShaderPolySpritesData { + globals, + t_sprite: tex_info.raw_view.unwrap(), + s_sprite: self.atlas_sampler, + b_poly_sprites: instance_buf, + }, + ); + encoder.draw(0, 4, 0, sprites.len() as u32); + } + PrimitiveBatch::Surfaces {..} => { + unimplemented!() + } } } } diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl index e0990c6c1b..7d0c9d53ae 100644 --- a/crates/gpui/src/platform/linux/shaders.wgsl +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -4,10 +4,11 @@ struct Globals { } var globals: Globals; -var t_tile: texture_2d; -var s_tile: sampler; +var t_sprite: texture_2d; +var s_sprite: sampler; const M_PI_F: f32 = 3.1415926; +const GRAYSCALE_FACTORS: vec3 = vec3(0.2126, 0.7152, 0.0722); struct ViewId { lo: u32, @@ -60,7 +61,7 @@ fn to_device_position(unit_vertex: vec2, bounds: Bounds) -> vec4 { } fn to_tile_position(unit_vertex: vec2, tile: AtlasTile) -> vec2 { - let atlas_size = vec2(textureDimensions(t_tile, 0)); + let atlas_size = vec2(textureDimensions(t_sprite, 0)); return (tile.bounds.origin + unit_vertex * tile.bounds.size) / atlas_size; } @@ -153,6 +154,17 @@ fn pick_corner_radius(point: vec2, radii: Corners) -> f32 { } } +fn quad_sdf(point: vec2, bounds: Bounds, corner_radii: Corners) -> f32 { + let half_size = bounds.size / 2.0; + let center = bounds.origin + half_size; + let center_to_point = point - center; + let corner_radius = pick_corner_radius(center_to_point, corner_radii); + let rounded_edge_to_point = abs(center_to_point) - half_size + corner_radius; + return length(max(vec2(0.0), rounded_edge_to_point)) + + min(0.0, max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - + corner_radius; +} + // --- quads --- // struct Quad { @@ -386,7 +398,7 @@ 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 sample = textureSample(t_sprite, s_sprite, input.tile_position).r; let mask = 1.0 - abs(1.0 - sample % 2.0); return input.color * mask; } @@ -434,7 +446,7 @@ fn fs_underline(input: UnderlineVarying) -> @location(0) vec4 { } let underline = b_underlines[input.underline_id]; - if (underline.wavy == 0u) + if ((underline.wavy & 0xFFu) == 0u) { return vec4(0.0); } @@ -452,3 +464,94 @@ fn fs_underline(input: UnderlineVarying) -> @location(0) vec4 { 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); } + +// --- monochrome sprites --- // + +struct MonochromeSprite { + view_id: ViewId, + layer_id: u32, + order: u32, + bounds: Bounds, + content_mask: Bounds, + color: Hsla, + tile: AtlasTile, +} +var b_mono_sprites: array; + +struct MonoSpriteVarying { + @builtin(position) position: vec4, + @location(0) tile_position: vec2, + @location(1) @interpolate(flat) color: vec4, + @location(3) clip_distances: vec4, +} + +@vertex +fn vs_mono_sprite(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> MonoSpriteVarying { + let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); + let sprite = b_mono_sprites[instance_id]; + + var out = MonoSpriteVarying(); + out.position = to_device_position(unit_vertex, sprite.bounds); + out.tile_position = to_tile_position(unit_vertex, sprite.tile); + out.color = hsla_to_rgba(sprite.color); + out.clip_distances = distance_from_clip_rect(unit_vertex, sprite.bounds, sprite.content_mask); + return out; +} + +@fragment +fn fs_mono_sprite(input: MonoSpriteVarying) -> @location(0) vec4 { + let sample = textureSample(t_sprite, s_sprite, input.tile_position).r; + return input.color * vec4(1.0, 1.0, 1.0, sample); +} + +// --- polychrome sprites --- // + +struct PolychromeSprite { + view_id: ViewId, + layer_id: u32, + order: u32, + bounds: Bounds, + content_mask: Bounds, + corner_radii: Corners, + tile: AtlasTile, + grayscale: u32, + pad: u32, +} +var b_poly_sprites: array; + +struct PolySpriteVarying { + @builtin(position) position: vec4, + @location(0) tile_position: vec2, + @location(1) @interpolate(flat) sprite_id: u32, + @location(3) clip_distances: vec4, +} + +@vertex +fn vs_poly_sprite(@builtin(vertex_index) vertex_id: u32, @builtin(instance_index) instance_id: u32) -> PolySpriteVarying { + let unit_vertex = vec2(f32(vertex_id & 1u), 0.5 * f32(vertex_id & 2u)); + let sprite = b_poly_sprites[instance_id]; + + var out = PolySpriteVarying(); + out.position = to_device_position(unit_vertex, sprite.bounds); + out.tile_position = to_tile_position(unit_vertex, sprite.tile); + out.sprite_id = instance_id; + out.clip_distances = distance_from_clip_rect(unit_vertex, sprite.bounds, sprite.content_mask); + return out; +} + +@fragment +fn fs_poly_sprite(input: PolySpriteVarying) -> @location(0) vec4 { + let sample = textureSample(t_sprite, s_sprite, input.tile_position); + let sprite = b_poly_sprites[input.sprite_id]; + let distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii); + + var color = sample; + if ((sprite.grayscale & 0xFFu) != 0u) { + let grayscale = dot(color.rgb, GRAYSCALE_FACTORS); + color = vec4(vec3(grayscale), sample.a); + } + color.a *= saturate(0.5 - distance); + return color;; +} + +// --- surface sprites --- // diff --git a/crates/gpui/src/scene.rs b/crates/gpui/src/scene.rs index dcf3f2cbea..4b16e2075f 100644 --- a/crates/gpui/src/scene.rs +++ b/crates/gpui/src/scene.rs @@ -642,6 +642,7 @@ pub(crate) struct PolychromeSprite { pub corner_radii: Corners, pub tile: AtlasTile, pub grayscale: bool, + pub pad: u32, // align to 8 bytes } impl Ord for PolychromeSprite { diff --git a/crates/gpui/src/window/element_cx.rs b/crates/gpui/src/window/element_cx.rs index bc38db3ab5..8b321b3377 100644 --- a/crates/gpui/src/window/element_cx.rs +++ b/crates/gpui/src/window/element_cx.rs @@ -874,6 +874,7 @@ impl<'a> ElementContext<'a> { content_mask, tile, grayscale: false, + pad: 0, }, ); } @@ -958,6 +959,7 @@ impl<'a> ElementContext<'a> { corner_radii, tile, grayscale, + pad: 0, }, ); Ok(())