From fdaffdbfff0140251e4c51d64cf394f1f85360c2 Mon Sep 17 00:00:00 2001 From: Dzmitry Malyshau Date: Thu, 1 Feb 2024 22:56:50 -0800 Subject: [PATCH] linux: path rasterization shader --- crates/gpui/src/platform/linux/blade_atlas.rs | 18 ++++++- .../gpui/src/platform/linux/blade_renderer.rs | 41 +++++++++++--- crates/gpui/src/platform/linux/shaders.wgsl | 53 +++++++++++++++++-- crates/gpui/src/platform/linux/text_system.rs | 2 +- 4 files changed, 100 insertions(+), 14 deletions(-) diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index 8de5a562ea..3c661c2191 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -41,6 +41,11 @@ impl BladeAtlasState { } } +pub struct BladeTextureInfo { + pub size: gpu::Extent, + pub raw_view: Option, +} + impl BladeAtlas { pub(crate) fn new(gpu: &Arc) -> Self { BladeAtlas(Mutex::new(BladeAtlasState { @@ -94,14 +99,23 @@ impl BladeAtlas { sync_point } - pub fn get_texture_view(&self, id: AtlasTextureId) -> gpu::TextureView { + pub fn get_texture_info(&self, id: AtlasTextureId) -> BladeTextureInfo { let lock = self.0.lock(); let textures = match id.kind { crate::AtlasTextureKind::Monochrome => &lock.monochrome_textures, crate::AtlasTextureKind::Polychrome => &lock.polychrome_textures, crate::AtlasTextureKind::Path => &lock.path_textures, }; - textures[id.index as usize].raw_view.unwrap() + let texture = &textures[id.index as usize]; + let size = texture.allocator.size(); + BladeTextureInfo { + size: gpu::Extent { + width: size.width as u32, + height: size.height as u32, + depth: 1, + }, + raw_view: texture.raw_view, + } } } diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 0ade2b5df3..ef2a9fead8 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -10,7 +10,7 @@ use bytemuck::{Pod, Zeroable}; use collections::HashMap; use blade_graphics as gpu; -use std::sync::Arc; +use std::{mem, sync::Arc}; const SURFACE_FRAME_COUNT: u32 = 3; const MAX_FRAME_TIME_MS: u32 = 1000; @@ -34,6 +34,12 @@ struct ShaderShadowsData { b_shadows: gpu::BufferPiece, } +#[derive(blade_macros::ShaderData)] +struct ShaderPathRasterizationData { + globals: GlobalParams, + b_path_vertices: gpu::BufferPiece, +} + struct BladePipelines { quads: gpu::RenderPipeline, shadows: gpu::RenderPipeline, @@ -47,8 +53,14 @@ impl BladePipelines { }); shader.check_struct_size::(); shader.check_struct_size::(); + assert_eq!( + mem::size_of::>(), + shader.get_struct_size("PathVertex") as usize, + ); let quads_layout = ::layout(); let shadows_layout = ::layout(); + let path_rasterization_layout = ::layout(); + Self { quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc { name: "quads", @@ -84,7 +96,7 @@ impl BladePipelines { }), path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc { name: "path_rasterization", - data_layouts: &[&shadows_layout], + data_layouts: &[&path_rasterization_layout], vertex: shader.at("vs_path_rasterization"), primitive: gpu::PrimitiveState { topology: gpu::PrimitiveTopology::TriangleStrip, @@ -196,10 +208,16 @@ impl BladeRenderer { } for (texture_id, vertices) in vertices_by_texture_id { - let instances = self.instance_belt.alloc_data(&vertices, &self.gpu); + let tex_info = self.atlas.get_texture_info(texture_id); + let globals = GlobalParams { + viewport_size: [tex_info.size.width as f32, tex_info.size.height as f32], + pad: [0; 2], + }; + + let vertex_buf = self.instance_belt.alloc_data(&vertices, &self.gpu); let mut pass = self.command_encoder.render(gpu::RenderTargetSet { colors: &[gpu::RenderTarget { - view: self.atlas.get_texture_view(texture_id), + view: tex_info.raw_view.unwrap(), init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack), finish_op: gpu::FinishOp::Store, }], @@ -207,6 +225,13 @@ impl BladeRenderer { }); let mut encoder = pass.with(&self.pipelines.path_rasterization); + encoder.bind( + 0, + &ShaderPathRasterizationData { + globals, + b_path_vertices: vertex_buf, + }, + ); encoder.draw(0, vertices.len() as u32, 0, 1); } } @@ -237,25 +262,25 @@ impl BladeRenderer { for batch in scene.batches() { match batch { PrimitiveBatch::Quads(quads) => { - let instances = self.instance_belt.alloc_data(quads, &self.gpu); + let instance_buf = self.instance_belt.alloc_data(quads, &self.gpu); let mut encoder = pass.with(&self.pipelines.quads); encoder.bind( 0, &ShaderQuadsData { globals, - b_quads: instances, + b_quads: instance_buf, }, ); encoder.draw(0, 4, 0, quads.len() as u32); } PrimitiveBatch::Shadows(shadows) => { - let instances = self.instance_belt.alloc_data(shadows, &self.gpu); + let instance_buf = self.instance_belt.alloc_data(shadows, &self.gpu); let mut encoder = pass.with(&self.pipelines.shadows); encoder.bind( 0, &ShaderShadowsData { globals, - b_shadows: instances, + b_shadows: instance_buf, }, ); encoder.draw(0, 4, 0, shadows.len() as u32); diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl index 4b943fcd55..ddba4be846 100644 --- a/crates/gpui/src/platform/linux/shaders.wgsl +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -35,19 +35,27 @@ struct Hsla { a: f32, } -fn to_device_position(unit_vertex: vec2, bounds: Bounds) -> vec4 { - let position = unit_vertex * vec2(bounds.size) + bounds.origin; +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); } -fn distance_from_clip_rect(unit_vertex: vec2, bounds: Bounds, clip_bounds: Bounds) -> vec4 { +fn to_device_position(unit_vertex: vec2, bounds: Bounds) -> vec4 { let position = unit_vertex * vec2(bounds.size) + bounds.origin; + return to_device_position_impl(position); +} + +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; return vec4(tl.x, br.x, tl.y, br.y); } +fn distance_from_clip_rect(unit_vertex: vec2, bounds: Bounds, clip_bounds: Bounds) -> vec4 { + let position = unit_vertex * vec2(bounds.size) + bounds.origin; + return distance_from_clip_rect_impl(position, clip_bounds); +} + fn hsla_to_rgba(hsla: Hsla) -> vec4 { let h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range let s = hsla.s; @@ -289,3 +297,42 @@ fn fs_shadow(input: ShadowVarying) -> @location(0) vec4 { } // --- path rasterization --- // + +struct PathVertex { + xy_position: vec2, + st_position: vec2, + content_mask: Bounds, +} +var b_path_vertices: array; + +struct PathRasterizationVarying { + @builtin(position) position: vec4, + @location(0) st_position: vec2, + //TODO: use `clip_distance` once Naga supports it + @location(3) clip_distances: vec4, +} + +@vertex +fn vs_path_rasterization(@builtin(vertex_index) vertex_id: u32) -> PathRasterizationVarying { + let v = b_path_vertices[vertex_id]; + + var out = PathRasterizationVarying(); + out.position = to_device_position_impl(v.xy_position); + out.st_position = v.st_position; + out.clip_distances = distance_from_clip_rect_impl(v.xy_position, v.content_mask); + return out; +} + +@fragment +fn fs_path_rasterization(input: PathRasterizationVarying) -> @location(0) f32 { + 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); +} diff --git a/crates/gpui/src/platform/linux/text_system.rs b/crates/gpui/src/platform/linux/text_system.rs index 36ed155f6a..62935fc232 100644 --- a/crates/gpui/src/platform/linux/text_system.rs +++ b/crates/gpui/src/platform/linux/text_system.rs @@ -15,7 +15,7 @@ use font_kit::{ }; use parking_lot::RwLock; use smallvec::SmallVec; -use std::{borrow::Cow}; +use std::borrow::Cow; pub(crate) struct LinuxTextSystem(RwLock);