diff --git a/crates/gpui/src/platform/linux/blade_atlas.rs b/crates/gpui/src/platform/linux/blade_atlas.rs index 56bb745764..8de5a562ea 100644 --- a/crates/gpui/src/platform/linux/blade_atlas.rs +++ b/crates/gpui/src/platform/linux/blade_atlas.rs @@ -10,6 +10,8 @@ use etagere::BucketedAtlasAllocator; use parking_lot::Mutex; use std::{borrow::Cow, sync::Arc}; +pub(crate) const PATH_TEXTURE_FORMAT: gpu::TextureFormat = gpu::TextureFormat::R16Float; + pub(crate) struct BladeAtlas(Mutex); struct BladeAtlasState { @@ -32,6 +34,7 @@ impl BladeAtlasState { } for texture in self.path_textures.drain(..) { self.gpu.destroy_texture(texture.raw); + self.gpu.destroy_texture_view(texture.raw_view.unwrap()); } self.gpu.destroy_command_encoder(&mut self.gpu_encoder); self.upload_belt.destroy(&self.gpu); @@ -78,6 +81,11 @@ impl BladeAtlas { lock.gpu_encoder.start(); } + pub fn allocate(&self, size: Size, texture_kind: AtlasTextureKind) -> AtlasTile { + let mut lock = self.0.lock(); + lock.allocate(size, texture_kind) + } + pub fn finish_frame(&self) -> gpu::SyncPoint { let mut lock = self.0.lock(); let gpu = lock.gpu.clone(); @@ -85,6 +93,16 @@ impl BladeAtlas { lock.upload_belt.flush(&sync_point); sync_point } + + pub fn get_texture_view(&self, id: AtlasTextureId) -> gpu::TextureView { + 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() + } } impl PlatformAtlas for BladeAtlas { @@ -146,7 +164,7 @@ impl BladeAtlasState { usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE; } AtlasTextureKind::Path => { - format = gpu::TextureFormat::R16Float; + format = PATH_TEXTURE_FORMAT; usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE | gpu::TextureUsage::TARGET; @@ -166,6 +184,17 @@ impl BladeAtlasState { dimension: gpu::TextureDimension::D2, usage, }); + let raw_view = if usage.contains(gpu::TextureUsage::TARGET) { + Some(self.gpu.create_texture_view(gpu::TextureViewDesc { + name: "", + texture: raw, + format, + dimension: gpu::ViewDimension::D2, + subresources: &Default::default(), + })) + } else { + None + }; let textures = match kind { AtlasTextureKind::Monochrome => &mut self.monochrome_textures, @@ -180,6 +209,7 @@ impl BladeAtlasState { allocator: etagere::BucketedAtlasAllocator::new(size.into()), format, raw, + raw_view, }; textures.push(atlas_texture); textures.last_mut().unwrap() @@ -218,6 +248,7 @@ struct BladeAtlasTexture { id: AtlasTextureId, allocator: BucketedAtlasAllocator, raw: gpu::Texture, + raw_view: Option, format: gpu::TextureFormat, } diff --git a/crates/gpui/src/platform/linux/blade_renderer.rs b/crates/gpui/src/platform/linux/blade_renderer.rs index 4d58cbd1e6..0ade2b5df3 100644 --- a/crates/gpui/src/platform/linux/blade_renderer.rs +++ b/crates/gpui/src/platform/linux/blade_renderer.rs @@ -2,8 +2,12 @@ #![allow(irrefutable_let_patterns)] use super::{BladeBelt, BladeBeltDescriptor}; -use crate::{PrimitiveBatch, Quad, Scene, Shadow}; +use crate::{ + AtlasTextureKind, AtlasTile, BladeAtlas, ContentMask, Path, PathId, PathVertex, PrimitiveBatch, + Quad, ScaledPixels, Scene, Shadow, PATH_TEXTURE_FORMAT, +}; use bytemuck::{Pod, Zeroable}; +use collections::HashMap; use blade_graphics as gpu; use std::sync::Arc; @@ -33,6 +37,7 @@ struct ShaderShadowsData { struct BladePipelines { quads: gpu::RenderPipeline, shadows: gpu::RenderPipeline, + path_rasterization: gpu::RenderPipeline, } impl BladePipelines { @@ -77,6 +82,22 @@ impl BladePipelines { write_mask: gpu::ColorWrites::default(), }], }), + path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc { + name: "path_rasterization", + data_layouts: &[&shadows_layout], + vertex: shader.at("vs_path_rasterization"), + primitive: gpu::PrimitiveState { + topology: gpu::PrimitiveTopology::TriangleStrip, + ..Default::default() + }, + depth_stencil: None, + fragment: shader.at("fs_path_rasterization"), + color_targets: &[gpu::ColorTargetState { + format: PATH_TEXTURE_FORMAT, + blend: Some(gpu::BlendState::ALPHA_BLENDING), + write_mask: gpu::ColorWrites::default(), + }], + }), } } } @@ -88,6 +109,8 @@ pub struct BladeRenderer { pipelines: BladePipelines, instance_belt: BladeBelt, viewport_size: gpu::Extent, + path_tiles: HashMap, + atlas: Arc, } impl BladeRenderer { @@ -106,6 +129,8 @@ impl BladeRenderer { memory: gpu::Memory::Shared, min_chunk_size: 0x1000, }); + let atlas = Arc::new(BladeAtlas::new(&gpu)); + Self { gpu, command_encoder, @@ -113,6 +138,8 @@ impl BladeRenderer { pipelines, instance_belt, viewport_size: size, + path_tiles: HashMap::default(), + atlas, } } @@ -126,6 +153,7 @@ impl BladeRenderer { pub fn destroy(&mut self) { self.wait_for_gpu(); + self.atlas.destroy(); self.instance_belt.destroy(&self.gpu); self.gpu.destroy_command_encoder(&mut self.command_encoder); } @@ -140,11 +168,56 @@ impl BladeRenderer { self.viewport_size = size; } + pub fn atlas(&self) -> &Arc { + &self.atlas + } + + fn rasterize_paths(&mut self, paths: &[Path]) { + self.path_tiles.clear(); + let mut vertices_by_texture_id = HashMap::default(); + + for path in paths { + let clipped_bounds = path.bounds.intersect(&path.content_mask.bounds); + let tile = self + .atlas + .allocate(clipped_bounds.size.map(Into::into), AtlasTextureKind::Path); + vertices_by_texture_id + .entry(tile.texture_id) + .or_insert(Vec::new()) + .extend(path.vertices.iter().map(|vertex| PathVertex { + xy_position: vertex.xy_position - clipped_bounds.origin + + tile.bounds.origin.map(Into::into), + st_position: vertex.st_position, + content_mask: ContentMask { + bounds: tile.bounds.map(Into::into), + }, + })); + self.path_tiles.insert(path.id, tile); + } + + for (texture_id, vertices) in vertices_by_texture_id { + let instances = 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), + init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack), + finish_op: gpu::FinishOp::Store, + }], + depth_stencil: None, + }); + + let mut encoder = pass.with(&self.pipelines.path_rasterization); + encoder.draw(0, vertices.len() as u32, 0, 1); + } + } + pub fn draw(&mut self, scene: &Scene) { let frame = self.gpu.acquire_frame(); self.command_encoder.start(); self.command_encoder.init_texture(frame.texture()); + self.rasterize_paths(scene.paths()); + let globals = GlobalParams { viewport_size: [ self.viewport_size.width as f32, @@ -187,6 +260,7 @@ impl BladeRenderer { ); encoder.draw(0, 4, 0, shadows.len() as u32); } + PrimitiveBatch::Paths(paths) => {} _ => continue, } } diff --git a/crates/gpui/src/platform/linux/shaders.wgsl b/crates/gpui/src/platform/linux/shaders.wgsl index c25456ff3f..4b943fcd55 100644 --- a/crates/gpui/src/platform/linux/shaders.wgsl +++ b/crates/gpui/src/platform/linux/shaders.wgsl @@ -287,3 +287,5 @@ fn fs_shadow(input: ShadowVarying) -> @location(0) vec4 { return input.color * vec4(1.0, 1.0, 1.0, alpha); } + +// --- path rasterization --- // diff --git a/crates/gpui/src/platform/linux/window.rs b/crates/gpui/src/platform/linux/window.rs index 71c80df1ba..87d4d01387 100644 --- a/crates/gpui/src/platform/linux/window.rs +++ b/crates/gpui/src/platform/linux/window.rs @@ -1,6 +1,6 @@ use super::BladeRenderer; use crate::{ - BladeAtlas, Bounds, GlobalPixels, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler, + Bounds, GlobalPixels, LinuxDisplay, Pixels, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point, Size, WindowAppearance, WindowBounds, WindowOptions, XcbAtoms, }; use blade_graphics as gpu; @@ -57,7 +57,6 @@ pub(crate) struct LinuxWindowState { x_window: x::Window, callbacks: Mutex, inner: Mutex, - sprite_atlas: Arc, } #[derive(Clone)] @@ -186,7 +185,6 @@ impl LinuxWindowState { height: bounds.size.height as u32, depth: 1, }; - let sprite_atlas = Arc::new(BladeAtlas::new(&gpu)); Self { xcb_connection: Arc::clone(xcb_connection), @@ -200,16 +198,11 @@ impl LinuxWindowState { scale_factor: 1.0, renderer: BladeRenderer::new(gpu, gpu_extent), }), - sprite_atlas, } } pub fn destroy(&self) { - self.sprite_atlas.destroy(); - { - let mut inner = self.inner.lock(); - inner.renderer.destroy(); - } + self.inner.lock().renderer.destroy(); self.xcb_connection.send_request(&x::UnmapWindow { window: self.x_window, }); @@ -379,6 +372,7 @@ impl PlatformWindow for LinuxWindow { } fn sprite_atlas(&self) -> sync::Arc { - self.0.sprite_atlas.clone() + let mut inner = self.0.inner.lock(); + inner.renderer.atlas().clone() } }