diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 008baea333..5bd985a74b 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -304,6 +304,7 @@ pub(crate) trait PlatformAtlas: Send + Sync { pub(crate) struct AtlasTile { pub(crate) texture_id: AtlasTextureId, pub(crate) tile_id: TileId, + pub(crate) padding: u32, pub(crate) bounds: Bounds, } diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index 3c661c2191..7f27bf68a4 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -276,6 +276,7 @@ impl BladeAtlasTexture { let tile = AtlasTile { texture_id: self.id, tile_id: allocation.id.into(), + padding: 0, bounds: Bounds { origin: allocation.rectangle.min.into(), size, diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index ef2a9fead8..4d64b184f0 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -3,8 +3,8 @@ use super::{BladeBelt, BladeBeltDescriptor}; use crate::{ - AtlasTextureKind, AtlasTile, BladeAtlas, ContentMask, Path, PathId, PathVertex, PrimitiveBatch, - Quad, ScaledPixels, Scene, Shadow, PATH_TEXTURE_FORMAT, + AtlasTextureKind, AtlasTile, BladeAtlas, Bounds, ContentMask, Hsla, Path, PathId, PathVertex, + PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, PATH_TEXTURE_FORMAT, }; use bytemuck::{Pod, Zeroable}; use collections::HashMap; @@ -40,10 +40,27 @@ struct ShaderPathRasterizationData { b_path_vertices: gpu::BufferPiece, } +#[derive(blade_macros::ShaderData)] +struct ShaderPathsData { + globals: GlobalParams, + t_tile: gpu::TextureView, + s_tile: gpu::Sampler, + b_path_sprites: gpu::BufferPiece, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +#[repr(C)] +struct PathSprite { + bounds: Bounds, + color: Hsla, + tile: AtlasTile, +} + struct BladePipelines { quads: gpu::RenderPipeline, shadows: gpu::RenderPipeline, path_rasterization: gpu::RenderPipeline, + paths: gpu::RenderPipeline, } impl BladePipelines { @@ -57,9 +74,12 @@ impl BladePipelines { mem::size_of::>(), shader.get_struct_size("PathVertex") as usize, ); + shader.check_struct_size::(); + let quads_layout = ::layout(); let shadows_layout = ::layout(); let path_rasterization_layout = ::layout(); + let paths_layout = ::layout(); Self { quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc { @@ -110,6 +130,22 @@ impl BladePipelines { write_mask: gpu::ColorWrites::default(), }], }), + paths: gpu.create_render_pipeline(gpu::RenderPipelineDesc { + name: "paths", + data_layouts: &[&paths_layout], + vertex: shader.at("vs_path"), + primitive: gpu::PrimitiveState { + topology: gpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + fragment: shader.at("fs_path"), + color_targets: &[gpu::ColorTargetState { + format: surface_format, + blend: Some(gpu::BlendState::ALPHA_BLENDING), + write_mask: gpu::ColorWrites::default(), + }], + }), } } } @@ -123,6 +159,7 @@ pub struct BladeRenderer { viewport_size: gpu::Extent, path_tiles: HashMap, atlas: Arc, + atlas_sampler: gpu::Sampler, } impl BladeRenderer { @@ -142,6 +179,12 @@ impl BladeRenderer { min_chunk_size: 0x1000, }); let atlas = Arc::new(BladeAtlas::new(&gpu)); + let atlas_sampler = gpu.create_sampler(gpu::SamplerDesc { + name: "atlas", + mag_filter: gpu::FilterMode::Linear, + min_filter: gpu::FilterMode::Linear, + ..Default::default() + }); Self { gpu, @@ -152,6 +195,7 @@ impl BladeRenderer { viewport_size: size, path_tiles: HashMap::default(), atlas, + atlas_sampler, } } @@ -285,7 +329,35 @@ impl BladeRenderer { ); encoder.draw(0, 4, 0, shadows.len() as u32); } - PrimitiveBatch::Paths(paths) => {} + PrimitiveBatch::Paths(paths) => { + let mut encoder = pass.with(&self.pipelines.paths); + //TODO: group by texture ID + for path in paths { + let tile = &self.path_tiles[&path.id]; + let tex_info = self.atlas.get_texture_info(tile.texture_id); + let origin = path.bounds.intersect(&path.content_mask.bounds).origin; + let sprites = [PathSprite { + bounds: Bounds { + origin: origin.map(|p| p.floor()), + size: tile.bounds.size.map(Into::into), + }, + color: path.color, + tile: (*tile).clone(), + }]; + + let instance_buf = self.instance_belt.alloc_data(&sprites, &self.gpu); + encoder.bind( + 0, + &ShaderPathsData { + globals, + t_tile: tex_info.raw_view.unwrap(), + s_tile: self.atlas_sampler, + b_path_sprites: instance_buf, + }, + ); + encoder.draw(0, 4, 0, sprites.len() as u32); + } + } _ => continue, } } diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl index ddba4be846..3a7853142a 100644 --- a/crates/gpui/src/platform/linux/shaders.wgsl +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -4,6 +4,8 @@ struct Globals { } var globals: Globals; +var t_tile: texture_2d; +var s_tile: sampler; const M_PI_F: f32 = 3.1415926; @@ -35,6 +37,18 @@ struct Hsla { a: f32, } +struct AtlasTextureId { + index: u32, + kind: u32, +} + +struct AtlasTile { + texture_id: AtlasTextureId, + tile_id: u32, + padding: u32, + bounds: Bounds, +} + fn to_device_position_impl(position: vec2) -> vec4 { let device_position = position / globals.viewport_size * vec2(2.0, -2.0) + vec2(-1.0, 1.0); return vec4(device_position, 0.0, 1.0); @@ -45,6 +59,11 @@ fn to_device_position(unit_vertex: vec2, bounds: Bounds) -> vec4 { return to_device_position_impl(position); } +fn to_tile_position(unit_vertex: vec2, tile: AtlasTile) -> vec2 { + let atlas_size = vec2(textureDimensions(t_tile, 0)); + return (tile.bounds.origin + unit_vertex * tile.bounds.size) / atlas_size; +} + fn distance_from_clip_rect_impl(position: vec2, clip_bounds: Bounds) -> vec4 { let tl = position - clip_bounds.origin; let br = clip_bounds.origin + clip_bounds.size - position; @@ -325,14 +344,49 @@ fn vs_path_rasterization(@builtin(vertex_index) vertex_id: u32) -> PathRasteriza @fragment fn fs_path_rasterization(input: PathRasterizationVarying) -> @location(0) f32 { - let dx = dpdx(input.st_position); - let dy = dpdy(input.st_position); + let dx = dpdx(input.st_position); + let dy = dpdy(input.st_position); if (any(input.clip_distances < vec4(0.0))) { return 0.0; } - let gradient = 2.0 * input.st_position * vec2(dx.x, dy.x) - vec2(dx.y, dy.y); - let f = input.st_position.x * input.st_position.x - input.st_position.y; - let distance = f / length(gradient); - return saturate(0.5 - distance); + let gradient = 2.0 * input.st_position * vec2(dx.x, dy.x) - vec2(dx.y, dy.y); + let f = input.st_position.x * input.st_position.x - input.st_position.y; + let distance = f / length(gradient); + return saturate(0.5 - distance); +} + +// --- paths --- // + +struct PathSprite { + bounds: Bounds, + color: Hsla, + tile: AtlasTile, +} +var b_path_sprites: array; + +struct PathVarying { + @builtin(position) position: vec4, + @location(0) tile_position: vec2, + @location(1) color: vec4, +} + +@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 sprite = b_path_sprites[instance_id]; + // Don't apply content mask because it was already accounted for when rasterizing the path. + + var out = PathVarying(); + 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); + return out; +} + +@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; } diff --git a/crates/gpui/src/platform/test/window.rs b/crates/gpui/src/platform/test/window.rs index b310084fc4..f7e52f8c16 100644 --- a/crates/gpui/src/platform/test/window.rs +++ b/crates/gpui/src/platform/test/window.rs @@ -344,6 +344,7 @@ impl PlatformAtlas for TestAtlas { kind: crate::AtlasTextureKind::Path, }, tile_id: TileId(tile_id), + padding: 0, bounds: crate::Bounds { origin: Point::default(), size,