From d889cdecdecf459befc746de0a5fdf1eb680e7bf Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 9 Oct 2023 18:50:42 +0200 Subject: [PATCH] Checkpoint --- crates/gpui3/build.rs | 2 +- crates/gpui3/src/platform.rs | 32 +- crates/gpui3/src/platform/mac/metal_atlas.rs | 118 +++--- .../gpui3/src/platform/mac/metal_renderer.rs | 26 +- crates/gpui3/src/platform/mac/window.rs | 6 +- crates/gpui3/src/scene.rs | 363 ++++++++++++------ crates/gpui3/src/style.rs | 57 +-- crates/gpui3/src/window.rs | 159 +++++--- 8 files changed, 484 insertions(+), 279 deletions(-) diff --git a/crates/gpui3/build.rs b/crates/gpui3/build.rs index d0d4a5c74b..0131d9f148 100644 --- a/crates/gpui3/build.rs +++ b/crates/gpui3/build.rs @@ -47,7 +47,7 @@ fn generate_shader_bindings() -> PathBuf { "Pixels".into(), "PointF".into(), "Hsla".into(), - "ScaledContentMask".into(), + "ContentMask".into(), "Uniforms".into(), "AtlasTile".into(), "ShadowInputIndex".into(), diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 53a35fadcc..1589a26ee9 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -40,7 +40,7 @@ pub(crate) fn current_platform() -> Arc { Arc::new(MacPlatform::new()) } -pub trait Platform: 'static { +pub(crate) trait Platform: 'static { fn executor(&self) -> Executor; fn display_linker(&self) -> Arc; fn text_system(&self) -> Arc; @@ -113,7 +113,7 @@ impl Debug for DisplayId { unsafe impl Send for DisplayId {} -pub trait PlatformWindow { +pub(crate) trait PlatformWindow { fn bounds(&self) -> WindowBounds; fn content_size(&self) -> Size; fn scale_factor(&self) -> f32; @@ -194,11 +194,17 @@ pub enum AtlasKey { } impl AtlasKey { - pub fn is_monochrome(&self) -> bool { + pub(crate) fn texture_kind(&self) -> AtlasTextureKind { match self { - AtlasKey::Glyph(params) => !params.is_emoji, - AtlasKey::Svg(_) => true, - AtlasKey::Image(_) => false, + AtlasKey::Glyph(params) => { + if params.is_emoji { + AtlasTextureKind::Polychrome + } else { + AtlasTextureKind::Monochrome + } + } + AtlasKey::Svg(_) => AtlasTextureKind::Monochrome, + AtlasKey::Image(_) => AtlasTextureKind::Polychrome, } } } @@ -241,7 +247,19 @@ pub struct AtlasTile { #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(C)] -pub(crate) struct AtlasTextureId(pub(crate) u32); // We use u32 instead of usize for Metal Shader Language compatibility +pub(crate) struct AtlasTextureId { + // We use u32 instead of usize for Metal Shader Language compatibility + pub(crate) index: u32, + pub(crate) kind: AtlasTextureKind, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(C)] +pub(crate) enum AtlasTextureKind { + Monochrome = 0, + Polychrome = 1, + Path = 2, +} #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] #[repr(C)] diff --git a/crates/gpui3/src/platform/mac/metal_atlas.rs b/crates/gpui3/src/platform/mac/metal_atlas.rs index 05ec7ab71c..f6392d2fc0 100644 --- a/crates/gpui3/src/platform/mac/metal_atlas.rs +++ b/crates/gpui3/src/platform/mac/metal_atlas.rs @@ -1,14 +1,14 @@ -use std::borrow::Cow; - use crate::{ - AtlasKey, AtlasTextureId, AtlasTile, Bounds, DevicePixels, PlatformAtlas, Point, Size, + AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas, + Point, Size, }; -use anyhow::{anyhow, Result}; +use anyhow::Result; use collections::HashMap; use derive_more::{Deref, DerefMut}; use etagere::BucketedAtlasAllocator; use metal::Device; use parking_lot::Mutex; +use std::borrow::Cow; pub struct MetalAtlas(Mutex); @@ -16,19 +16,31 @@ impl MetalAtlas { pub fn new(device: Device) -> Self { MetalAtlas(Mutex::new(MetalAtlasState { device: AssertSend(device), - textures: Default::default(), + monochrome_textures: Default::default(), + polychrome_textures: Default::default(), + path_textures: Default::default(), tiles_by_key: Default::default(), })) } - pub(crate) fn texture(&self, id: AtlasTextureId) -> metal::Texture { - self.0.lock().textures[id.0 as usize].metal_texture.clone() + pub(crate) fn metal_texture(&self, id: AtlasTextureId) -> metal::Texture { + self.0.lock().texture(id).metal_texture.clone() + } + + pub(crate) fn allocate( + &self, + size: Size, + texture_kind: AtlasTextureKind, + ) -> AtlasTile { + self.0.lock().allocate(size, texture_kind) } } struct MetalAtlasState { device: AssertSend, - textures: Vec, + monochrome_textures: Vec, + polychrome_textures: Vec, + path_textures: Vec, tiles_by_key: HashMap, } @@ -43,23 +55,9 @@ impl PlatformAtlas for MetalAtlas { return Ok(tile.clone()); } else { let (size, bytes) = build()?; - let tile = lock - .textures - .iter_mut() - .rev() - .find_map(|texture| { - if texture.monochrome == key.is_monochrome() { - texture.upload(size, &bytes) - } else { - None - } - }) - .or_else(|| { - let texture = lock.push_texture(size, key.is_monochrome()); - texture.upload(size, &bytes) - }) - .ok_or_else(|| anyhow!("could not allocate in new texture"))?; - lock.tiles_by_key.insert(key.clone(), tile.clone()); + let tile = lock.allocate(size, key.texture_kind()); + let texture = lock.texture(tile.texture_id); + texture.upload(tile.bounds, &bytes); Ok(tile) } } @@ -70,10 +68,26 @@ impl PlatformAtlas for MetalAtlas { } impl MetalAtlasState { + fn allocate(&mut self, size: Size, texture_kind: AtlasTextureKind) -> AtlasTile { + let textures = match texture_kind { + AtlasTextureKind::Monochrome => &mut self.monochrome_textures, + AtlasTextureKind::Polychrome => &mut self.polychrome_textures, + AtlasTextureKind::Path => &mut self.path_textures, + }; + textures + .iter_mut() + .rev() + .find_map(|texture| texture.allocate(size)) + .unwrap_or_else(|| { + let texture = self.push_texture(size, texture_kind); + texture.allocate(size).unwrap() + }) + } + fn push_texture( &mut self, min_size: Size, - monochrome: bool, + kind: AtlasTextureKind, ) -> &mut MetalAtlasTexture { const DEFAULT_ATLAS_SIZE: Size = Size { width: DevicePixels(1024), @@ -84,21 +98,38 @@ impl MetalAtlasState { let texture_descriptor = metal::TextureDescriptor::new(); texture_descriptor.set_width(size.width.into()); texture_descriptor.set_height(size.height.into()); - if monochrome { - texture_descriptor.set_pixel_format(metal::MTLPixelFormat::A8Unorm); - } else { - texture_descriptor.set_pixel_format(metal::MTLPixelFormat::BGRA8Unorm); - } + let pixel_format = match kind { + AtlasTextureKind::Monochrome => metal::MTLPixelFormat::A8Unorm, + AtlasTextureKind::Polychrome => metal::MTLPixelFormat::BGRA8Unorm, + AtlasTextureKind::Path => metal::MTLPixelFormat::R16Float, + }; + texture_descriptor.set_pixel_format(pixel_format); let metal_texture = self.device.new_texture(&texture_descriptor); + let textures = match kind { + AtlasTextureKind::Monochrome => &mut self.monochrome_textures, + AtlasTextureKind::Polychrome => &mut self.polychrome_textures, + AtlasTextureKind::Path => &mut self.path_textures, + }; let atlas_texture = MetalAtlasTexture { - id: AtlasTextureId(self.textures.len() as u32), + id: AtlasTextureId { + index: textures.len() as u32, + kind, + }, allocator: etagere::BucketedAtlasAllocator::new(size.into()), metal_texture: AssertSend(metal_texture), - monochrome, }; - self.textures.push(atlas_texture); - self.textures.last_mut().unwrap() + textures.push(atlas_texture); + textures.last_mut().unwrap() + } + + fn texture(&self, id: AtlasTextureId) -> &MetalAtlasTexture { + let textures = match id.kind { + crate::AtlasTextureKind::Monochrome => &self.monochrome_textures, + crate::AtlasTextureKind::Polychrome => &self.polychrome_textures, + crate::AtlasTextureKind::Path => &self.path_textures, + }; + &textures[id.index as usize] } } @@ -106,11 +137,10 @@ struct MetalAtlasTexture { id: AtlasTextureId, allocator: BucketedAtlasAllocator, metal_texture: AssertSend, - monochrome: bool, } impl MetalAtlasTexture { - fn upload(&mut self, size: Size, bytes: &[u8]) -> Option { + fn allocate(&mut self, size: Size) -> Option { let allocation = self.allocator.allocate(size.into())?; let tile = AtlasTile { texture_id: self.id, @@ -120,20 +150,22 @@ impl MetalAtlasTexture { size, }, }; + Some(tile) + } + fn upload(&self, bounds: Bounds, bytes: &[u8]) { let region = metal::MTLRegion::new_2d( - tile.bounds.origin.x.into(), - tile.bounds.origin.y.into(), - tile.bounds.size.width.into(), - tile.bounds.size.height.into(), + bounds.origin.x.into(), + bounds.origin.y.into(), + bounds.size.width.into(), + bounds.size.height.into(), ); self.metal_texture.replace_region( region, 0, bytes.as_ptr() as *const _, - u32::from(tile.bounds.size.width.to_bytes(self.bytes_per_pixel())) as u64, + u32::from(bounds.size.width.to_bytes(self.bytes_per_pixel())) as u64, ); - Some(tile) } fn bytes_per_pixel(&self) -> u8 { diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index 9b93a8a561..f4c735c606 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -1,12 +1,14 @@ use crate::{ - point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, PolychromeSprite, - PrimitiveBatch, Quad, Scene, Shadow, Size, Underline, + point, size, AtlasTextureId, AtlasTextureKind, AtlasTile, DevicePixels, MetalAtlas, + MonochromeSprite, PathId, PolychromeSprite, PrimitiveBatch, Quad, Scene, Shadow, Size, + Underline, }; use cocoa::{ base::{NO, YES}, foundation::NSUInteger, quartzcore::AutoresizingMask, }; +use collections::HashMap; use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange}; use objc::{self, msg_send, sel, sel_impl}; use std::{ffi::c_void, mem, ptr, sync::Arc}; @@ -14,7 +16,7 @@ use std::{ffi::c_void, mem, ptr, sync::Arc}; const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib")); const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value. -pub struct MetalRenderer { +pub(crate) struct MetalRenderer { layer: metal::MetalLayer, command_queue: CommandQueue, shadows_pipeline_state: metal::RenderPipelineState, @@ -150,7 +152,7 @@ impl MetalRenderer { &self.sprite_atlas } - pub fn draw(&mut self, scene: &mut Scene) { + pub fn draw(&mut self, scene: &Scene) { let layer = self.layer.clone(); let viewport_size = layer.drawable_size(); let viewport_size: Size = size( @@ -192,6 +194,15 @@ impl MetalRenderer { }); let mut instance_offset = 0; + + let mut path_tiles: HashMap = HashMap::default(); + for path in scene.paths() { + let tile = self + .sprite_atlas + .allocate(path.bounds.size.map(Into::into), AtlasTextureKind::Path); + path_tiles.insert(path.id, tile); + } + for batch in scene.batches() { match batch { PrimitiveBatch::Shadows(shadows) => { @@ -205,6 +216,9 @@ impl MetalRenderer { PrimitiveBatch::Quads(quads) => { self.draw_quads(quads, &mut instance_offset, viewport_size, command_encoder); } + PrimitiveBatch::Paths(paths) => { + // self.draw_paths(paths, &mut instance_offset, viewport_size, command_encoder); + } PrimitiveBatch::Underlines(underlines) => { self.draw_underlines( underlines, @@ -441,7 +455,7 @@ impl MetalRenderer { } align_offset(offset); - let texture = self.sprite_atlas.texture(texture_id); + let texture = self.sprite_atlas.metal_texture(texture_id); let texture_size = size( DevicePixels(texture.width() as i32), DevicePixels(texture.height() as i32), @@ -512,7 +526,7 @@ impl MetalRenderer { } align_offset(offset); - let texture = self.sprite_atlas.texture(texture_id); + let texture = self.sprite_atlas.metal_texture(texture_id); let texture_size = size( DevicePixels(texture.width() as i32), DevicePixels(texture.height() as i32), diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index b16e85f08d..d994942002 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -911,7 +911,7 @@ impl PlatformWindow for MacWindow { } } - fn draw(&self, scene: crate::Scene) { + fn draw(&self, scene: Scene) { let mut this = self.0.lock(); this.scene_to_render = Some(scene); unsafe { @@ -1395,8 +1395,8 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) { unsafe { let window_state = get_window_state(this); let mut window_state = window_state.as_ref().lock(); - if let Some(mut scene) = window_state.scene_to_render.take() { - window_state.renderer.draw(&mut scene); + if let Some(scene) = window_state.scene_to_render.take() { + window_state.renderer.draw(&scene); } } } diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index 9ddb69ccae..11e121413e 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -1,16 +1,12 @@ use crate::{ - point, px, AtlasTextureId, AtlasTile, Bounds, Corners, Edges, Hsla, Pixels, Point, - ScaledContentMask, ScaledPixels, + point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, Hsla, Pixels, Point, + ScaledPixels, }; use collections::BTreeMap; use etagere::euclid::{Point3D, Vector3D}; use plane_split::{BspSplitter, Polygon as BspPolygon}; use smallvec::SmallVec; -use std::{ - iter::Peekable, - mem, slice, - sync::atomic::{AtomicU32, Ordering::SeqCst}, -}; +use std::{fmt::Debug, iter::Peekable, mem, slice}; // Exported to metal pub type PointF = Point; @@ -18,45 +14,131 @@ pub type StackingOrder = SmallVec<[u32; 16]>; pub type LayerId = u32; pub type DrawOrder = u32; -#[derive(Debug)] -pub struct Scene { - pub(crate) scale_factor: f32, - pub(crate) layers: BTreeMap, - pub shadows: Vec, - pub quads: Vec, - pub underlines: Vec, - pub monochrome_sprites: Vec, - pub polychrome_sprites: Vec, +pub(crate) struct SceneBuilder { + layers_by_order: BTreeMap, + splitter: BspSplitter<(PrimitiveKind, usize)>, + shadows: Vec, + quads: Vec, + paths: Vec>, + underlines: Vec, + monochrome_sprites: Vec, + polychrome_sprites: Vec, } -impl Scene { - pub fn new(scale_factor: f32) -> Scene { - Scene { - scale_factor, - layers: BTreeMap::new(), +impl SceneBuilder { + pub fn new() -> SceneBuilder { + SceneBuilder { + layers_by_order: BTreeMap::new(), + splitter: BspSplitter::new(), shadows: Vec::new(), quads: Vec::new(), + paths: Vec::new(), underlines: Vec::new(), monochrome_sprites: Vec::new(), polychrome_sprites: Vec::new(), } } - pub fn take(&mut self) -> Scene { + pub fn build(&mut self) -> Scene { + // Map each layer id to a float between 0. and 1., with 1. closer to the viewer. + let mut layer_z_values = vec![0.; self.layers_by_order.len()]; + for (ix, layer_id) in self.layers_by_order.values().enumerate() { + layer_z_values[*layer_id as usize] = ix as f32 / self.layers_by_order.len() as f32; + } + self.layers_by_order.clear(); + + // Add all primitives to the BSP splitter to determine draw order + self.splitter.reset(); + + for (ix, shadow) in self.shadows.iter().enumerate() { + let z = layer_z_values[shadow.order as LayerId as usize]; + self.splitter + .add(shadow.bounds.to_bsp_polygon(z, (PrimitiveKind::Shadow, ix))); + } + + for (ix, quad) in self.quads.iter().enumerate() { + let z = layer_z_values[quad.order as LayerId as usize]; + self.splitter + .add(quad.bounds.to_bsp_polygon(z, (PrimitiveKind::Quad, ix))); + } + + for (ix, underline) in self.underlines.iter().enumerate() { + let z = layer_z_values[underline.order as LayerId as usize]; + self.splitter.add( + underline + .bounds + .to_bsp_polygon(z, (PrimitiveKind::Underline, ix)), + ); + } + + for (ix, monochrome_sprite) in self.monochrome_sprites.iter().enumerate() { + let z = layer_z_values[monochrome_sprite.order as LayerId as usize]; + self.splitter.add( + monochrome_sprite + .bounds + .to_bsp_polygon(z, (PrimitiveKind::MonochromeSprite, ix)), + ); + } + + for (ix, polychrome_sprite) in self.polychrome_sprites.iter().enumerate() { + let z = layer_z_values[polychrome_sprite.order as LayerId as usize]; + self.splitter.add( + polychrome_sprite + .bounds + .to_bsp_polygon(z, (PrimitiveKind::PolychromeSprite, ix)), + ); + } + + // Sort all polygons, then reassign the order field of each primitive to `draw_order` + // We need primitives to be repr(C), hence the weird reuse of the order field for two different types. + for (draw_order, polygon) in self + .splitter + .sort(Vector3D::new(0., 0., 1.)) + .iter() + .enumerate() + { + match polygon.anchor { + (PrimitiveKind::Shadow, ix) => self.shadows[ix].order = draw_order as DrawOrder, + (PrimitiveKind::Quad, ix) => self.quads[ix].order = draw_order as DrawOrder, + (PrimitiveKind::Path, ix) => self.paths[ix].order = draw_order as DrawOrder, + (PrimitiveKind::Underline, ix) => { + self.underlines[ix].order = draw_order as DrawOrder + } + (PrimitiveKind::MonochromeSprite, ix) => { + self.monochrome_sprites[ix].order = draw_order as DrawOrder + } + (PrimitiveKind::PolychromeSprite, ix) => { + self.polychrome_sprites[ix].order = draw_order as DrawOrder + } + } + } + + self.shadows.sort_unstable(); + self.quads.sort_unstable(); + self.paths.sort_unstable(); + self.underlines.sort_unstable(); + self.monochrome_sprites.sort_unstable(); + self.polychrome_sprites.sort_unstable(); + Scene { - scale_factor: self.scale_factor, - layers: mem::take(&mut self.layers), shadows: mem::take(&mut self.shadows), quads: mem::take(&mut self.quads), + paths: mem::take(&mut self.paths), underlines: mem::take(&mut self.underlines), monochrome_sprites: mem::take(&mut self.monochrome_sprites), polychrome_sprites: mem::take(&mut self.polychrome_sprites), } } - pub fn insert(&mut self, layer_id: StackingOrder, primitive: impl Into) { - let next_id = self.layers.len() as LayerId; - let layer_id = *self.layers.entry(layer_id).or_insert(next_id); + pub fn insert(&mut self, order: &StackingOrder, primitive: impl Into) { + let layer_id = if let Some(layer_id) = self.layers_by_order.get(order) { + *layer_id + } else { + let next_id = self.layers_by_order.len() as LayerId; + self.layers_by_order.insert(order.clone(), next_id); + next_id + }; + let primitive = primitive.into(); match primitive { Primitive::Shadow(mut shadow) => { @@ -67,6 +149,11 @@ impl Scene { quad.order = layer_id; self.quads.push(quad); } + Primitive::Path(mut path) => { + path.order = layer_id; + path.id = PathId(self.paths.len()); + self.paths.push(path); + } Primitive::Underline(mut underline) => { underline.order = layer_id; self.underlines.push(underline); @@ -81,80 +168,23 @@ impl Scene { } } } +} - pub(crate) fn batches(&mut self) -> impl Iterator { - // Map each layer id to a float between 0. and 1., with 1. closer to the viewer. - let mut layer_z_values = vec![0.; self.layers.len()]; - for (ix, layer_id) in self.layers.values().enumerate() { - layer_z_values[*layer_id as usize] = ix as f32 / self.layers.len() as f32; - } +pub(crate) struct Scene { + pub shadows: Vec, + pub quads: Vec, + pub paths: Vec>, + pub underlines: Vec, + pub monochrome_sprites: Vec, + pub polychrome_sprites: Vec, +} - // Add all primitives to the BSP splitter to determine draw order - // todo!("reuse the same splitter") - let mut splitter = BspSplitter::new(); - - for (ix, shadow) in self.shadows.iter().enumerate() { - let z = layer_z_values[shadow.order as LayerId as usize]; - splitter.add(shadow.bounds.to_bsp_polygon(z, (PrimitiveKind::Shadow, ix))); - } - - for (ix, quad) in self.quads.iter().enumerate() { - let z = layer_z_values[quad.order as LayerId as usize]; - splitter.add(quad.bounds.to_bsp_polygon(z, (PrimitiveKind::Quad, ix))); - } - - for (ix, underline) in self.underlines.iter().enumerate() { - let z = layer_z_values[underline.order as LayerId as usize]; - splitter.add( - underline - .bounds - .to_bsp_polygon(z, (PrimitiveKind::Underline, ix)), - ); - } - - for (ix, monochrome_sprite) in self.monochrome_sprites.iter().enumerate() { - let z = layer_z_values[monochrome_sprite.order as LayerId as usize]; - splitter.add( - monochrome_sprite - .bounds - .to_bsp_polygon(z, (PrimitiveKind::MonochromeSprite, ix)), - ); - } - - for (ix, polychrome_sprite) in self.polychrome_sprites.iter().enumerate() { - let z = layer_z_values[polychrome_sprite.order as LayerId as usize]; - splitter.add( - polychrome_sprite - .bounds - .to_bsp_polygon(z, (PrimitiveKind::PolychromeSprite, ix)), - ); - } - - // Sort all polygons, then reassign the order field of each primitive to `draw_order` - // We need primitives to be repr(C), hence the weird reuse of the order field for two different types. - for (draw_order, polygon) in splitter.sort(Vector3D::new(0., 0., 1.)).iter().enumerate() { - match polygon.anchor { - (PrimitiveKind::Shadow, ix) => self.shadows[ix].order = draw_order as DrawOrder, - (PrimitiveKind::Quad, ix) => self.quads[ix].order = draw_order as DrawOrder, - (PrimitiveKind::Underline, ix) => { - self.underlines[ix].order = draw_order as DrawOrder - } - (PrimitiveKind::MonochromeSprite, ix) => { - self.monochrome_sprites[ix].order = draw_order as DrawOrder - } - (PrimitiveKind::PolychromeSprite, ix) => { - self.polychrome_sprites[ix].order = draw_order as DrawOrder - } - } - } - - // Sort the primitives - self.shadows.sort_unstable(); - self.quads.sort_unstable(); - self.underlines.sort_unstable(); - self.monochrome_sprites.sort_unstable(); - self.polychrome_sprites.sort_unstable(); +impl Scene { + pub fn paths(&self) -> impl Iterator> { + self.paths.iter() + } + pub fn batches(&self) -> impl Iterator { BatchIterator { shadows: &self.shadows, shadows_start: 0, @@ -162,6 +192,9 @@ impl Scene { quads: &self.quads, quads_start: 0, quads_iter: self.quads.iter().peekable(), + paths: &self.paths, + paths_start: 0, + paths_iter: self.paths.iter().peekable(), underlines: &self.underlines, underlines_start: 0, underlines_iter: self.underlines.iter().peekable(), @@ -176,12 +209,15 @@ impl Scene { } struct BatchIterator<'a> { - quads: &'a [Quad], - quads_start: usize, - quads_iter: Peekable>, shadows: &'a [Shadow], shadows_start: usize, shadows_iter: Peekable>, + quads: &'a [Quad], + quads_start: usize, + quads_iter: Peekable>, + paths: &'a [Path], + paths_start: usize, + paths_iter: Peekable>>, underlines: &'a [Underline], underlines_start: usize, underlines_iter: Peekable>, @@ -255,6 +291,19 @@ impl<'a> Iterator for BatchIterator<'a> { self.quads_start = quads_end; Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end])) } + PrimitiveKind::Path => { + let paths_start = self.paths_start; + let mut paths_end = paths_start; + while self + .paths_iter + .next_if(|path| path.order <= max_order) + .is_some() + { + paths_end += 1; + } + self.paths_start = paths_end; + Some(PrimitiveBatch::Paths(&self.paths[paths_start..paths_end])) + } PrimitiveKind::Underline => { let underlines_start = self.underlines_start; let mut underlines_end = underlines_start; @@ -317,15 +366,16 @@ pub enum PrimitiveKind { Shadow, #[default] Quad, + Path, Underline, MonochromeSprite, PolychromeSprite, } -#[derive(Clone, Debug)] pub enum Primitive { Shadow(Shadow), Quad(Quad), + Path(Path), Underline(Underline), MonochromeSprite(MonochromeSprite), PolychromeSprite(PolychromeSprite), @@ -335,6 +385,7 @@ pub enum Primitive { pub(crate) enum PrimitiveBatch<'a> { Shadows(&'a [Shadow]), Quads(&'a [Quad]), + Paths(&'a [Path]), Underlines(&'a [Underline]), MonochromeSprites { texture_id: AtlasTextureId, @@ -351,7 +402,7 @@ pub(crate) enum PrimitiveBatch<'a> { pub struct Quad { pub order: u32, // Initially a LayerId, then a DrawOrder. pub bounds: Bounds, - pub content_mask: ScaledContentMask, + pub content_mask: ContentMask, pub background: Hsla, pub border_color: Hsla, pub corner_radii: Corners, @@ -381,7 +432,7 @@ impl From for Primitive { pub struct Underline { pub order: u32, pub bounds: Bounds, - pub content_mask: ScaledContentMask, + pub content_mask: ContentMask, pub thickness: ScaledPixels, pub color: Hsla, pub wavy: bool, @@ -411,7 +462,7 @@ pub struct Shadow { pub order: u32, pub bounds: Bounds, pub corner_radii: Corners, - pub content_mask: ScaledContentMask, + pub content_mask: ContentMask, pub color: Hsla, pub blur_radius: ScaledPixels, } @@ -439,7 +490,7 @@ impl From for Primitive { pub struct MonochromeSprite { pub order: u32, pub bounds: Bounds, - pub content_mask: ScaledContentMask, + pub content_mask: ContentMask, pub color: Hsla, pub tile: AtlasTile, } @@ -470,7 +521,7 @@ impl From for Primitive { pub struct PolychromeSprite { pub order: u32, pub bounds: Bounds, - pub content_mask: ScaledContentMask, + pub content_mask: ContentMask, pub corner_radii: Corners, pub tile: AtlasTile, pub grayscale: bool, @@ -497,21 +548,24 @@ impl From for Primitive { } } -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct PathId(u32); +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub(crate) struct PathId(pub(crate) usize); -pub struct PathBuilder { - bounds: Bounds, - vertices: Vec, - start: Option>, - current: Point, +#[derive(Debug)] +pub struct Path { + pub(crate) id: PathId, + order: u32, + pub(crate) bounds: Bounds

, + pub(crate) vertices: Vec>, + start: Option>, + current: Point

, } -impl PathBuilder { +impl Path { pub fn new() -> Self { - const NEXT_PATH_ID: AtomicU32 = AtomicU32::new(0); - Self { + id: PathId(0), + order: 0, vertices: Vec::new(), start: Default::default(), current: Default::default(), @@ -519,6 +573,21 @@ impl PathBuilder { } } + pub fn scale(&self, factor: f32) -> Path { + Path { + id: self.id, + order: self.order, + bounds: self.bounds.scale(factor), + vertices: self + .vertices + .iter() + .map(|vertex| vertex.scale(factor)) + .collect(), + start: self.start.map(|start| start.scale(factor)), + current: self.current.scale(factor), + } + } + pub fn line_to(&mut self, to: Point) { if let Some(start) = self.start { self.push_triangle( @@ -568,23 +637,63 @@ impl PathBuilder { self.vertices.push(PathVertex { xy_position: xy.0, st_position: st.0, + content_mask: Default::default(), }); self.vertices.push(PathVertex { xy_position: xy.1, st_position: st.1, + content_mask: Default::default(), }); self.vertices.push(PathVertex { xy_position: xy.2, st_position: st.2, + content_mask: Default::default(), }); } } -#[derive(Clone, Debug)] +impl Eq for Path {} + +impl PartialEq for Path { + fn eq(&self, other: &Self) -> bool { + self.order == other.order + } +} + +impl Ord for Path { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.order.cmp(&other.order) + } +} + +impl PartialOrd for Path { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl From> for Primitive { + fn from(path: Path) -> Self { + Primitive::Path(path) + } +} + +#[derive(Debug)] #[repr(C)] -pub struct PathVertex { - pub xy_position: Point, - pub st_position: Point, +pub struct PathVertex { + pub(crate) xy_position: Point

, + pub(crate) st_position: Point, + pub(crate) content_mask: ContentMask

, +} + +impl PathVertex { + pub fn scale(&self, factor: f32) -> PathVertex { + PathVertex { + xy_position: self.xy_position.scale(factor), + st_position: self.st_position, + content_mask: self.content_mask.scale(factor), + } + } } #[derive(Copy, Clone, Debug)] @@ -619,15 +728,15 @@ mod tests { #[test] fn test_scene() { - let mut scene = Scene::new(1.0); - assert_eq!(scene.layers.len(), 0); + let mut scene = SceneBuilder::new(); + assert_eq!(scene.layers_by_order.len(), 0); - scene.insert(smallvec![1], quad()); - scene.insert(smallvec![2], shadow()); - scene.insert(smallvec![3], quad()); + scene.insert(&smallvec![1], quad()); + scene.insert(&smallvec![2], shadow()); + scene.insert(&smallvec![3], quad()); let mut batches_count = 0; - for _ in scene.batches() { + for _ in scene.build().batches() { batches_count += 1; } assert_eq!(batches_count, 3); diff --git a/crates/gpui3/src/style.rs b/crates/gpui3/src/style.rs index af2b87d0bb..3a7131ab9e 100644 --- a/crates/gpui3/src/style.rs +++ b/crates/gpui3/src/style.rs @@ -1,8 +1,8 @@ use crate::{ phi, point, rems, AbsoluteLength, BorrowAppContext, BorrowWindow, Bounds, ContentMask, Corners, CornersRefinement, DefiniteLength, Edges, EdgesRefinement, Font, FontFeatures, FontStyle, - FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Quad, Rems, Result, RunStyle, Shadow, - SharedString, Size, SizeRefinement, ViewContext, WindowContext, + FontWeight, Hsla, Length, Pixels, Point, PointRefinement, Rems, Result, RunStyle, SharedString, + Size, SizeRefinement, ViewContext, WindowContext, }; use refineable::Refineable; use smallvec::SmallVec; @@ -243,51 +243,24 @@ impl Style { /// Paints the background of an element styled with this style. pub fn paint(&self, bounds: Bounds, cx: &mut ViewContext) { let rem_size = cx.rem_size(); - let scale = cx.scale_factor(); - for shadow in &self.box_shadow { - let content_mask = cx.content_mask(); - let mut shadow_bounds = bounds; - shadow_bounds.origin += shadow.offset; - shadow_bounds.dilate(shadow.spread_radius); - cx.stack(0, |cx| { - let layer_id = cx.current_stacking_order(); - cx.scene().insert( - layer_id, - Shadow { - order: 0, - bounds: shadow_bounds.scale(scale), - content_mask: content_mask.scale(scale), - corner_radii: self - .corner_radii - .to_pixels(shadow_bounds.size, rem_size) - .scale(scale), - color: shadow.color, - blur_radius: shadow.blur_radius.scale(scale), - }, - ); - }) - } + cx.stack(0, |cx| { + cx.paint_shadows( + bounds, + self.corner_radii.to_pixels(bounds.size, rem_size), + &self.box_shadow, + ); + }); let background_color = self.fill.as_ref().and_then(Fill::color); if background_color.is_some() || self.is_border_visible() { - let content_mask = cx.content_mask(); cx.stack(1, |cx| { - let order = cx.current_stacking_order(); - cx.scene().insert( - order, - Quad { - order: 0, - bounds: bounds.scale(scale), - content_mask: content_mask.scale(scale), - background: background_color.unwrap_or_default(), - border_color: self.border_color.unwrap_or_default(), - corner_radii: self - .corner_radii - .to_pixels(bounds.size, rem_size) - .scale(scale), - border_widths: self.border_widths.to_pixels(rem_size).scale(scale), - }, + cx.paint_quad( + bounds, + self.corner_radii.to_pixels(bounds.size, rem_size), + background_color.unwrap_or_default(), + self.border_widths.to_pixels(rem_size), + self.border_color.unwrap_or_default(), ); }); } diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 123ec128ac..96b97427af 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,15 +1,17 @@ use crate::{ image_cache::RenderImageParams, px, size, AnyView, AppContext, AsyncWindowContext, - AvailableSpace, BorrowAppContext, Bounds, Context, Corners, DevicePixels, DisplayId, Effect, - Element, EntityId, FontId, GlyphId, Handle, Hsla, ImageData, IsZero, LayoutId, MainThread, - MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, - PolychromeSprite, Reference, RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene, - SharedString, Size, StackingOrder, Style, TaffyLayoutEngine, Task, Underline, UnderlineStyle, - WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, + AvailableSpace, BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DisplayId, + Edges, Effect, Element, EntityId, FontId, GlyphId, Handle, Hsla, ImageData, IsZero, LayoutId, + MainThread, MainThreadOnly, MonochromeSprite, Path, Pixels, PlatformAtlas, PlatformWindow, + Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderSvgParams, ScaledPixels, + SceneBuilder, Shadow, SharedString, Size, StackingOrder, Style, TaffyLayoutEngine, Task, + Underline, UnderlineStyle, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; use smallvec::SmallVec; -use std::{any::TypeId, borrow::Cow, future::Future, marker::PhantomData, mem, sync::Arc}; +use std::{ + any::TypeId, borrow::Cow, fmt::Debug, future::Future, marker::PhantomData, mem, sync::Arc, +}; use util::ResultExt; pub struct AnyWindow {} @@ -25,8 +27,9 @@ pub struct Window { pub(crate) root_view: Option>, mouse_position: Point, current_stacking_order: StackingOrder, - content_mask_stack: Vec, - pub(crate) scene: Scene, + content_mask_stack: Vec>, + scale_factor: f32, + pub(crate) scene_builder: SceneBuilder, pub(crate) dirty: bool, } @@ -47,7 +50,8 @@ impl Window { let cx = cx.to_async(); move |content_size, scale_factor| { cx.update_window(handle, |cx| { - cx.window.scene = Scene::new(scale_factor); + cx.window.scale_factor = scale_factor; + cx.window.scene_builder = SceneBuilder::new(); cx.window.content_size = content_size; cx.window.display_id = cx .window @@ -75,20 +79,22 @@ impl Window { mouse_position, current_stacking_order: SmallVec::new(), content_mask_stack: Vec::new(), - scene: Scene::new(scale_factor), + scale_factor, + scene_builder: SceneBuilder::new(), dirty: true, } } } -#[derive(Clone, Debug)] -pub struct ContentMask { - pub bounds: Bounds, +#[derive(Clone, Debug, Default, PartialEq, Eq)] +#[repr(C)] +pub struct ContentMask { + pub bounds: Bounds

, } -impl ContentMask { - pub fn scale(&self, factor: f32) -> ScaledContentMask { - ScaledContentMask { +impl ContentMask { + pub fn scale(&self, factor: f32) -> ContentMask { + ContentMask { bounds: self.bounds.scale(factor), } } @@ -99,12 +105,6 @@ impl ContentMask { } } -#[derive(Default, Clone, Debug, PartialEq, Eq)] -#[repr(C)] -pub struct ScaledContentMask { - bounds: Bounds, -} - pub struct WindowContext<'a, 'w> { app: Reference<'a, AppContext>, window: Reference<'w, Window>, @@ -234,7 +234,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { } pub fn scale_factor(&self) -> f32 { - self.window.scene.scale_factor + self.window.scale_factor } pub fn rem_size(&self) -> Pixels { @@ -245,10 +245,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { self.window.mouse_position } - pub fn scene(&mut self) -> &mut Scene { - &mut self.window.scene - } - pub fn stack(&mut self, order: u32, f: impl FnOnce(&mut Self) -> R) -> R { self.window.current_stacking_order.push(order); let result = f(self); @@ -256,8 +252,69 @@ impl<'a, 'w> WindowContext<'a, 'w> { result } - pub fn current_stacking_order(&self) -> StackingOrder { - self.window.current_stacking_order.clone() + pub fn paint_shadows( + &mut self, + bounds: Bounds, + corner_radii: Corners, + shadows: &[BoxShadow], + ) { + let scale_factor = self.scale_factor(); + let content_mask = self.content_mask(); + let window = &mut *self.window; + for shadow in shadows { + let mut shadow_bounds = bounds; + shadow_bounds.origin += shadow.offset; + shadow_bounds.dilate(shadow.spread_radius); + window.scene_builder.insert( + &window.current_stacking_order, + Shadow { + order: 0, + bounds: shadow_bounds.scale(scale_factor), + content_mask: content_mask.scale(scale_factor), + corner_radii: corner_radii.scale(scale_factor), + color: shadow.color, + blur_radius: shadow.blur_radius.scale(scale_factor), + }, + ); + } + } + + pub fn paint_quad( + &mut self, + bounds: Bounds, + corner_radii: Corners, + background: impl Into, + border_widths: Edges, + border_color: impl Into, + ) { + let scale_factor = self.scale_factor(); + let content_mask = self.content_mask(); + + let window = &mut *self.window; + window.scene_builder.insert( + &window.current_stacking_order, + Quad { + order: 0, + bounds: bounds.scale(scale_factor), + content_mask: content_mask.scale(scale_factor), + background: background.into(), + border_color: border_color.into(), + corner_radii: corner_radii.scale(scale_factor), + border_widths: border_widths.scale(scale_factor), + }, + ); + } + + pub fn paint_path(&mut self, mut path: Path) { + let scale_factor = self.scale_factor(); + let content_mask = self.content_mask(); + for vertex in &mut path.vertices { + vertex.content_mask = content_mask.clone(); + } + let window = &mut *self.window; + window + .scene_builder + .insert(&window.current_stacking_order, path.scale(scale_factor)); } pub fn paint_underline( @@ -277,9 +334,9 @@ impl<'a, 'w> WindowContext<'a, 'w> { size: size(width, height), }; let content_mask = self.content_mask(); - let layer_id = self.current_stacking_order(); - self.window.scene.insert( - layer_id, + let window = &mut *self.window; + window.scene_builder.insert( + &window.current_stacking_order, Underline { order: 0, bounds: bounds.scale(scale_factor), @@ -317,7 +374,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { let raster_bounds = self.text_system().raster_bounds(¶ms)?; if !raster_bounds.is_zero() { - let layer_id = self.current_stacking_order(); let tile = self.window .sprite_atlas @@ -330,9 +386,9 @@ impl<'a, 'w> WindowContext<'a, 'w> { size: tile.bounds.size.map(Into::into), }; let content_mask = self.content_mask().scale(scale_factor); - - self.window.scene.insert( - layer_id, + let window = &mut *self.window; + window.scene_builder.insert( + &window.current_stacking_order, MonochromeSprite { order: 0, bounds, @@ -366,7 +422,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { let raster_bounds = self.text_system().raster_bounds(¶ms)?; if !raster_bounds.is_zero() { - let layer_id = self.current_stacking_order(); let tile = self.window .sprite_atlas @@ -379,9 +434,10 @@ impl<'a, 'w> WindowContext<'a, 'w> { size: tile.bounds.size.map(Into::into), }; let content_mask = self.content_mask().scale(scale_factor); + let window = &mut *self.window; - self.window.scene.insert( - layer_id, + window.scene_builder.insert( + &window.current_stacking_order, PolychromeSprite { order: 0, bounds, @@ -411,7 +467,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { .map(|pixels| DevicePixels::from((pixels.0 * 2.).ceil() as i32)), }; - let layer_id = self.current_stacking_order(); let tile = self.window .sprite_atlas @@ -421,8 +476,9 @@ impl<'a, 'w> WindowContext<'a, 'w> { })?; let content_mask = self.content_mask().scale(scale_factor); - self.window.scene.insert( - layer_id, + let window = &mut *self.window; + window.scene_builder.insert( + &window.current_stacking_order, MonochromeSprite { order: 0, bounds, @@ -446,7 +502,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { let bounds = bounds.scale(scale_factor); let params = RenderImageParams { image_id: data.id }; - let order = self.current_stacking_order(); let tile = self .window .sprite_atlas @@ -456,8 +511,9 @@ impl<'a, 'w> WindowContext<'a, 'w> { let content_mask = self.content_mask().scale(scale_factor); let corner_radii = corner_radii.scale(scale_factor); - self.window.scene.insert( - order, + let window = &mut *self.window; + window.scene_builder.insert( + &window.current_stacking_order, PolychromeSprite { order: 0, bounds, @@ -467,7 +523,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { grayscale, }, ); - Ok(()) } @@ -485,7 +540,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { root_view.paint(layout, &mut (), &mut frame_state, cx)?; cx.window.root_view = Some(root_view); - let scene = cx.window.scene.take(); + let scene = cx.window.scene_builder.build(); cx.run_on_main(view, |_, cx| { cx.window @@ -557,7 +612,11 @@ pub trait BorrowWindow: BorrowAppContext { fn window(&self) -> &Window; fn window_mut(&mut self) -> &mut Window; - fn with_content_mask(&mut self, mask: ContentMask, f: impl FnOnce(&mut Self) -> R) -> R { + fn with_content_mask( + &mut self, + mask: ContentMask, + f: impl FnOnce(&mut Self) -> R, + ) -> R { let mask = mask.intersect(&self.content_mask()); self.window_mut().content_mask_stack.push(mask); let result = f(self); @@ -565,7 +624,7 @@ pub trait BorrowWindow: BorrowAppContext { result } - fn content_mask(&self) -> ContentMask { + fn content_mask(&self) -> ContentMask { self.window() .content_mask_stack .last()