From 6046ed4f5c33cb58d61c7f2ac36d5440c04abbe6 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 2 Oct 2023 15:43:03 -0600 Subject: [PATCH 01/28] Checkpoint --- crates/gpui3/src/elements/text.rs | 8 +- crates/gpui3/src/geometry.rs | 13 +- crates/gpui3/src/platform/mac/shaders.metal | 314 +++++++++-------- crates/gpui3/src/scene.rs | 41 ++- crates/gpui3/src/text_system.rs | 21 +- .../src/text_system/text_layout_cache.rs | 327 +----------------- crates/gpui3/src/window.rs | 10 + 7 files changed, 229 insertions(+), 505 deletions(-) diff --git a/crates/gpui3/src/elements/text.rs b/crates/gpui3/src/elements/text.rs index b05a3c6fae9b76fd51a43f3c31a59b275e88384a..8044e1ab9ee3c0ffd03846fc0549a2c7d3878e62 100644 --- a/crates/gpui3/src/elements/text.rs +++ b/crates/gpui3/src/elements/text.rs @@ -3,7 +3,7 @@ use crate::{ }; use parking_lot::Mutex; use std::{marker::PhantomData, sync::Arc}; -use util::{arc_cow::ArcCow, ResultExt}; +use util::arc_cow::ArcCow; impl IntoAnyElement for ArcCow<'static, str> { fn into_any(self) -> AnyElement { @@ -92,8 +92,6 @@ impl Element for Text { paint_state: &mut Self::FrameState, cx: &mut ViewContext, ) -> Result<()> { - let bounds = layout.bounds; - let line; let line_height; { @@ -108,8 +106,8 @@ impl Element for Text { let _text_style = cx.text_style(); // todo!("We haven't added visible bounds to the new element system yet, so this is a placeholder."); - let visible_bounds = bounds; - line.paint(bounds.origin, visible_bounds, line_height, cx)?; + let visible_bounds = layout.bounds; + line.paint(&layout, visible_bounds, line_height, cx)?; Ok(()) } diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index 4f187f3252a02d39e12ae752b883d7a67f3addf2..ec5788be25aef116cca6f75c89ab0d156e107d95 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -425,7 +425,9 @@ unsafe impl Zeroable for Corners {} unsafe impl Pod for Corners {} -#[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd)] +#[derive( + Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd, Zeroable, Pod, +)] #[repr(transparent)] pub struct Pixels(pub(crate) f32); @@ -445,6 +447,12 @@ impl Mul for f32 { } } +impl MulAssign for Pixels { + fn mul_assign(&mut self, other: f32) { + self.0 *= other; + } +} + impl Pixels { pub fn round(&self) -> Self { Self(self.0.round()) @@ -489,9 +497,6 @@ impl From for Pixels { } } -unsafe impl bytemuck::Pod for Pixels {} -unsafe impl bytemuck::Zeroable for Pixels {} - impl Debug for Pixels { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{} px", self.0) diff --git a/crates/gpui3/src/platform/mac/shaders.metal b/crates/gpui3/src/platform/mac/shaders.metal index e77f6085bac97d308bb94436c783e5fc272bc215..798ef3d66c1f32914b25253ec93af347846e06ab 100644 --- a/crates/gpui3/src/platform/mac/shaders.metal +++ b/crates/gpui3/src/platform/mac/shaders.metal @@ -7,174 +7,194 @@ float4 hsla_to_rgba(Hsla hsla); float4 to_device_position(float2 pixel_position, float2 viewport_size); struct QuadVertexOutput { - float4 position [[position]]; - float4 background_color; - float4 border_color; - uint quad_id; + float4 position [[position]]; + float4 background_color; + float4 border_color; + uint quad_id; }; vertex QuadVertexOutput quad_vertex( - uint unit_vertex_id [[vertex_id]], - uint quad_id [[instance_id]], + uint unit_vertex_id [[vertex_id]], uint quad_id [[instance_id]], constant float2 *unit_vertices [[buffer(QuadInputIndex_Vertices)]], constant Quad *quads [[buffer(QuadInputIndex_Quads)]], - constant QuadUniforms *uniforms [[buffer(QuadInputIndex_Uniforms)]] -) { - float2 unit_vertex = unit_vertices[unit_vertex_id]; - Quad quad = quads[quad_id]; - float2 position_2d = unit_vertex * float2(quad.bounds.size.width, quad.bounds.size.height) + float2(quad.bounds.origin.x, quad.bounds.origin.y); - position_2d.x = max(quad.clip_bounds.origin.x, position_2d.x); - position_2d.x = min(quad.clip_bounds.origin.x + quad.clip_bounds.size.width, position_2d.x); - position_2d.y = max(quad.clip_bounds.origin.y, position_2d.y); - position_2d.y = min(quad.clip_bounds.origin.y + quad.clip_bounds.size.height, position_2d.y); - - float2 viewport_size = float2((float)uniforms->viewport_size.width, (float)uniforms->viewport_size.height); - float4 device_position = to_device_position(position_2d, viewport_size); - float4 background_color = hsla_to_rgba(quad.background); - float4 border_color = hsla_to_rgba(quad.border_color); - return QuadVertexOutput { - device_position, - background_color, - border_color, - quad_id - }; + constant QuadUniforms *uniforms [[buffer(QuadInputIndex_Uniforms)]]) { + float2 unit_vertex = unit_vertices[unit_vertex_id]; + Quad quad = quads[quad_id]; + float2 position_2d = + unit_vertex * float2(quad.bounds.size.width, quad.bounds.size.height) + + float2(quad.bounds.origin.x, quad.bounds.origin.y); + position_2d.x = max(quad.clip_bounds.origin.x, position_2d.x); + position_2d.x = min(quad.clip_bounds.origin.x + quad.clip_bounds.size.width, + position_2d.x); + position_2d.y = max(quad.clip_bounds.origin.y, position_2d.y); + position_2d.y = min(quad.clip_bounds.origin.y + quad.clip_bounds.size.height, + position_2d.y); + + float2 viewport_size = float2((float)uniforms->viewport_size.width, + (float)uniforms->viewport_size.height); + float4 device_position = to_device_position(position_2d, viewport_size); + float4 background_color = hsla_to_rgba(quad.background); + float4 border_color = hsla_to_rgba(quad.border_color); + return QuadVertexOutput{device_position, background_color, border_color, + quad_id}; } -float quad_sdf(float2 point, Bounds_Pixels bounds, Corners_Pixels corner_radii) { - float2 half_size = float2(bounds.size.width, bounds.size.height) / 2.; - float2 center = float2(bounds.origin.x, bounds.origin.y) + half_size; - float2 center_to_point = point - center; - float corner_radius; - if (center_to_point.x < 0.) { - if (center_to_point.y < 0.) { - corner_radius = corner_radii.top_left; - } else { - corner_radius = corner_radii.bottom_left; - } +float quad_sdf(float2 point, Bounds_Pixels bounds, + Corners_Pixels corner_radii) { + float2 half_size = float2(bounds.size.width, bounds.size.height) / 2.; + float2 center = float2(bounds.origin.x, bounds.origin.y) + half_size; + float2 center_to_point = point - center; + float corner_radius; + if (center_to_point.x < 0.) { + if (center_to_point.y < 0.) { + corner_radius = corner_radii.top_left; } else { - if (center_to_point.y < 0.) { - corner_radius = corner_radii.top_right; - } else { - corner_radius = corner_radii.bottom_right; - } + corner_radius = corner_radii.bottom_left; } + } else { + if (center_to_point.y < 0.) { + corner_radius = corner_radii.top_right; + } else { + corner_radius = corner_radii.bottom_right; + } + } - float2 rounded_edge_to_point = abs(center_to_point) - half_size + corner_radius; - float distance = length(max(0., rounded_edge_to_point)) - + min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - - corner_radius; + float2 rounded_edge_to_point = + abs(center_to_point) - half_size + corner_radius; + float distance = + length(max(0., rounded_edge_to_point)) + + min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - + corner_radius; - return distance; + return distance; } -fragment float4 quad_fragment( - QuadVertexOutput input [[stage_in]], - constant Quad *quads [[buffer(QuadInputIndex_Quads)]] -) { - Quad quad = quads[input.quad_id]; - float2 half_size = float2(quad.bounds.size.width, quad.bounds.size.height) / 2.; - float2 center = float2(quad.bounds.origin.x, quad.bounds.origin.y) + half_size; - float2 center_to_point = input.position.xy - center; - float corner_radius; - if (center_to_point.x < 0.) { - if (center_to_point.y < 0.) { - corner_radius = quad.corner_radii.top_left; - } else { - corner_radius = quad.corner_radii.bottom_left; - } - } else { - if (center_to_point.y < 0.) { - corner_radius = quad.corner_radii.top_right; - } else { - corner_radius = quad.corner_radii.bottom_right; - } - } - - float2 rounded_edge_to_point = fabs(center_to_point) - half_size + corner_radius; - float distance = length(max(0., rounded_edge_to_point)) + min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - corner_radius; - - float vertical_border = center_to_point.x <= 0. ? quad.border_widths.left : quad.border_widths.right; - float horizontal_border = center_to_point.y <= 0. ? quad.border_widths.top : quad.border_widths.bottom; - float2 inset_size = half_size - corner_radius - float2(vertical_border, horizontal_border); - float2 point_to_inset_corner = fabs(center_to_point) - inset_size; - float border_width; - if (point_to_inset_corner.x < 0. && point_to_inset_corner.y < 0.) { - border_width = 0.; - } else if (point_to_inset_corner.y > point_to_inset_corner.x) { - border_width = horizontal_border; +fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]], + constant Quad *quads + [[buffer(QuadInputIndex_Quads)]]) { + Quad quad = quads[input.quad_id]; + float2 half_size = + float2(quad.bounds.size.width, quad.bounds.size.height) / 2.; + float2 center = + float2(quad.bounds.origin.x, quad.bounds.origin.y) + half_size; + float2 center_to_point = input.position.xy - center; + float corner_radius; + if (center_to_point.x < 0.) { + if (center_to_point.y < 0.) { + corner_radius = quad.corner_radii.top_left; } else { - border_width = vertical_border; + corner_radius = quad.corner_radii.bottom_left; } - - float4 color; - if (border_width == 0.) { - color = input.background_color; + } else { + if (center_to_point.y < 0.) { + corner_radius = quad.corner_radii.top_right; } else { - float inset_distance = distance + border_width; - - // Decrease border's opacity as we move inside the background. - input.border_color.a *= 1. - saturate(0.5 - inset_distance); - - // Alpha-blend the border and the background. - float output_alpha = quad.border_color.a + quad.background.a * (1. - quad.border_color.a); - float3 premultiplied_border_rgb = input.border_color.rgb * quad.border_color.a; - float3 premultiplied_background_rgb = input.background_color.rgb * input.background_color.a; - float3 premultiplied_output_rgb = premultiplied_border_rgb + premultiplied_background_rgb * (1. - input.border_color.a); - color = float4(premultiplied_output_rgb, output_alpha); + corner_radius = quad.corner_radii.bottom_right; } - - float clip_distance = quad_sdf(input.position.xy, quad.clip_bounds, quad.clip_corner_radii); - return color * float4(1., 1., 1., saturate(0.5 - distance) * saturate(0.5 - clip_distance)); + } + + float2 rounded_edge_to_point = + fabs(center_to_point) - half_size + corner_radius; + float distance = + length(max(0., rounded_edge_to_point)) + + min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - + corner_radius; + + float vertical_border = center_to_point.x <= 0. ? quad.border_widths.left + : quad.border_widths.right; + float horizontal_border = center_to_point.y <= 0. ? quad.border_widths.top + : quad.border_widths.bottom; + float2 inset_size = + half_size - corner_radius - float2(vertical_border, horizontal_border); + float2 point_to_inset_corner = fabs(center_to_point) - inset_size; + float border_width; + if (point_to_inset_corner.x < 0. && point_to_inset_corner.y < 0.) { + border_width = 0.; + } else if (point_to_inset_corner.y > point_to_inset_corner.x) { + border_width = horizontal_border; + } else { + border_width = vertical_border; + } + + float4 color; + if (border_width == 0.) { + color = input.background_color; + } else { + float inset_distance = distance + border_width; + + // Decrease border's opacity as we move inside the background. + input.border_color.a *= 1. - saturate(0.5 - inset_distance); + + // Alpha-blend the border and the background. + float output_alpha = + quad.border_color.a + quad.background.a * (1. - quad.border_color.a); + float3 premultiplied_border_rgb = + input.border_color.rgb * quad.border_color.a; + float3 premultiplied_background_rgb = + input.background_color.rgb * input.background_color.a; + float3 premultiplied_output_rgb = + premultiplied_border_rgb + + premultiplied_background_rgb * (1. - input.border_color.a); + color = float4(premultiplied_output_rgb, output_alpha); + } + + float clip_distance = + quad_sdf(input.position.xy, quad.clip_bounds, quad.clip_corner_radii); + return color * + float4(1., 1., 1., + saturate(0.5 - distance) * saturate(0.5 - clip_distance)); } float4 hsla_to_rgba(Hsla hsla) { - float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range - float s = hsla.s; - float l = hsla.l; - float a = hsla.a; - - float c = (1.0 - fabs(2.0*l - 1.0)) * s; - float x = c * (1.0 - fabs(fmod(h, 2.0) - 1.0)); - float m = l - c/2.0; - - float r = 0.0; - float g = 0.0; - float b = 0.0; - - if (h >= 0.0 && h < 1.0) { - r = c; - g = x; - b = 0.0; - } else if (h >= 1.0 && h < 2.0) { - r = x; - g = c; - b = 0.0; - } else if (h >= 2.0 && h < 3.0) { - r = 0.0; - g = c; - b = x; - } else if (h >= 3.0 && h < 4.0) { - r = 0.0; - g = x; - b = c; - } else if (h >= 4.0 && h < 5.0) { - r = x; - g = 0.0; - b = c; - } else { - r = c; - g = 0.0; - b = x; - } - - float4 rgba; - rgba.x = (r + m); - rgba.y = (g + m); - rgba.z = (b + m); - rgba.w = a; - return rgba; + float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range + float s = hsla.s; + float l = hsla.l; + float a = hsla.a; + + float c = (1.0 - fabs(2.0 * l - 1.0)) * s; + float x = c * (1.0 - fabs(fmod(h, 2.0) - 1.0)); + float m = l - c / 2.0; + + float r = 0.0; + float g = 0.0; + float b = 0.0; + + if (h >= 0.0 && h < 1.0) { + r = c; + g = x; + b = 0.0; + } else if (h >= 1.0 && h < 2.0) { + r = x; + g = c; + b = 0.0; + } else if (h >= 2.0 && h < 3.0) { + r = 0.0; + g = c; + b = x; + } else if (h >= 3.0 && h < 4.0) { + r = 0.0; + g = x; + b = c; + } else if (h >= 4.0 && h < 5.0) { + r = x; + g = 0.0; + b = c; + } else { + r = c; + g = 0.0; + b = x; + } + + float4 rgba; + rgba.x = (r + m); + rgba.y = (g + m); + rgba.z = (b + m); + rgba.w = a; + return rgba; } float4 to_device_position(float2 pixel_position, float2 viewport_size) { - return float4(pixel_position / viewport_size * float2(2., -2.) + float2(-1., 1.), 0., 1.); + return float4(pixel_position / viewport_size * float2(2., -2.) + + float2(-1., 1.), + 0., 1.); } diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index 78c3d87246aae0e2c39f86831d3d144449b86510..6e7ee82a4186554ac96b3fa1a596484e382f78eb 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -1,7 +1,7 @@ use std::mem; use super::{Bounds, Hsla, Pixels, Point}; -use crate::{Corners, Edges}; +use crate::{Corners, Edges, FontId, GlyphId}; use bytemuck::{Pod, Zeroable}; use collections::BTreeMap; @@ -17,6 +17,7 @@ pub struct Scene { #[derive(Default, Debug)] pub struct SceneLayer { pub quads: Vec, + pub symbol: Vec, } impl Scene { @@ -40,6 +41,7 @@ impl Scene { let layer = self.layers.entry(primitive.order()).or_default(); match primitive { Primitive::Quad(quad) => layer.quads.push(quad), + Primitive::MonochromeGlyph(glyph) => layer.symbol.push(glyph), } } @@ -51,20 +53,14 @@ impl Scene { #[derive(Clone, Debug)] pub enum Primitive { Quad(Quad), + MonochromeGlyph(Symbol), } impl Primitive { pub fn order(&self) -> u32 { match self { Primitive::Quad(quad) => quad.order, - } - } - - pub fn is_transparent(&self) -> bool { - match self { - Primitive::Quad(quad) => { - quad.background.is_transparent() && quad.border_color.is_transparent() - } + Primitive::MonochromeGlyph(glyph) => glyph.order, } } @@ -73,6 +69,9 @@ impl Primitive { Primitive::Quad(quad) => { quad.scale(factor); } + Primitive::MonochromeGlyph(glyph) => { + glyph.scale(factor); + } } } } @@ -119,3 +118,27 @@ impl From for Primitive { Primitive::Quad(quad) } } + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Symbol { + pub order: u32, + pub origin: Point, + pub font_id: FontId, + pub font_size: Pixels, + pub id: GlyphId, + pub color: Hsla, +} + +impl Symbol { + pub fn scale(&mut self, factor: f32) { + self.font_size *= factor; + self.origin *= factor; + } +} + +impl From for Primitive { + fn from(glyph: Symbol) -> Self { + Primitive::MonochromeGlyph(glyph) + } +} diff --git a/crates/gpui3/src/text_system.rs b/crates/gpui3/src/text_system.rs index c7e4d763162b6a8e884386ee02f3cca470eab7db..38bb1fded4a3f1936ecdd5a45127fbced8857f17 100644 --- a/crates/gpui3/src/text_system.rs +++ b/crates/gpui3/src/text_system.rs @@ -1,9 +1,12 @@ mod font_features; +mod line; mod line_wrapper; mod text_layout_cache; use anyhow::anyhow; +use bytemuck::{Pod, Zeroable}; pub use font_features::*; +pub use line::*; use line_wrapper::*; pub use text_layout_cache::*; @@ -20,7 +23,8 @@ use std::{ sync::Arc, }; -#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)] +#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, Zeroable, Pod)] +#[repr(C)] pub struct FontId(pub usize); #[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)] @@ -51,7 +55,6 @@ impl TextSystem { pub fn font_id(&self, font: &Font) -> Result { let font_id = self.font_ids_by_font.read().get(font).copied(); - if let Some(font_id) = font_id { Ok(font_id) } else { @@ -160,29 +163,18 @@ impl TextSystem { ) -> Result { let mut font_runs = self.font_runs_pool.lock().pop().unwrap_or_default(); - dbg!("got font runs from pool"); let mut last_font: Option<&Font> = None; for (len, style) in runs { - dbg!(len); if let Some(last_font) = last_font.as_ref() { - dbg!("a"); if **last_font == style.font { - dbg!("b"); font_runs.last_mut().unwrap().0 += len; - dbg!("c"); continue; } - dbg!("d"); } - dbg!("e"); last_font = Some(&style.font); - dbg!("f"); font_runs.push((*len, self.font_id(&style.font)?)); - dbg!("g"); } - dbg!("built font runs"); - let layout = self .text_layout_cache .layout_line(text, font_size, &font_runs); @@ -332,7 +324,8 @@ pub struct RunStyle { pub underline: Option, } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Zeroable, Pod)] +#[repr(C)] pub struct GlyphId(u32); impl From for u32 { diff --git a/crates/gpui3/src/text_system/text_layout_cache.rs b/crates/gpui3/src/text_system/text_layout_cache.rs index 0f0d54c37e53165d7d93416707d92c1df8965b14..59cfd300eea82b102e21d8c4714aeaf00c01be9f 100644 --- a/crates/gpui3/src/text_system/text_layout_cache.rs +++ b/crates/gpui3/src/text_system/text_layout_cache.rs @@ -1,8 +1,4 @@ -use crate::{ - black, point, px, Bounds, FontId, Glyph, Hsla, LineLayout, Pixels, PlatformTextSystem, Point, - Run, RunStyle, UnderlineStyle, WindowContext, -}; -use anyhow::Result; +use crate::{FontId, Glyph, LineLayout, Pixels, PlatformTextSystem, Run}; use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; use smallvec::SmallVec; use std::{ @@ -40,7 +36,6 @@ impl TextLayoutCache { font_size: Pixels, runs: &[(usize, FontId)], ) -> Arc { - dbg!("layout line"); let key = &CacheKeyRef { text, font_size, @@ -145,332 +140,12 @@ impl<'a> Hash for CacheKeyRef<'a> { } } -#[derive(Default, Debug, Clone)] -pub struct Line { - layout: Arc, - style_runs: SmallVec<[StyleRun; 32]>, -} - -#[derive(Debug, Clone)] -struct StyleRun { - len: u32, - color: Hsla, - underline: UnderlineStyle, -} - #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct ShapedBoundary { pub run_ix: usize, pub glyph_ix: usize, } -impl Line { - pub fn new(layout: Arc, runs: &[(usize, RunStyle)]) -> Self { - let mut style_runs = SmallVec::new(); - for (len, style) in runs { - style_runs.push(StyleRun { - len: *len as u32, - color: style.color, - underline: style.underline.clone().unwrap_or_default(), - }); - } - Self { layout, style_runs } - } - - pub fn runs(&self) -> &[Run] { - &self.layout.runs - } - - pub fn width(&self) -> Pixels { - self.layout.width - } - - pub fn font_size(&self) -> Pixels { - self.layout.font_size - } - - pub fn x_for_index(&self, index: usize) -> Pixels { - for run in &self.layout.runs { - for glyph in &run.glyphs { - if glyph.index >= index { - return glyph.position.x; - } - } - } - self.layout.width - } - - pub fn font_for_index(&self, index: usize) -> Option { - for run in &self.layout.runs { - for glyph in &run.glyphs { - if glyph.index >= index { - return Some(run.font_id); - } - } - } - - None - } - - pub fn len(&self) -> usize { - self.layout.len - } - - pub fn is_empty(&self) -> bool { - self.layout.len == 0 - } - - pub fn index_for_x(&self, x: Pixels) -> Option { - if x >= self.layout.width { - None - } else { - for run in self.layout.runs.iter().rev() { - for glyph in run.glyphs.iter().rev() { - if glyph.position.x <= x { - return Some(glyph.index); - } - } - } - Some(0) - } - } - - // todo! - pub fn paint( - &self, - origin: Point, - visible_bounds: Bounds, - line_height: Pixels, - cx: &mut WindowContext, - ) -> Result<()> { - let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.; - let baseline_offset = point(px(0.), padding_top + self.layout.ascent); - - let mut style_runs = self.style_runs.iter(); - let mut run_end = 0; - let mut color = black(); - let mut underline = None; - - for run in &self.layout.runs { - cx.text_system().with_font(run.font_id, |system, font| { - let max_glyph_width = system.bounding_box(font, self.layout.font_size)?.size.width; - - for glyph in &run.glyphs { - let glyph_origin = origin + baseline_offset + glyph.position; - if glyph_origin.x > visible_bounds.upper_right().x { - break; - } - - let mut finished_underline: Option<(Point, UnderlineStyle)> = None; - if glyph.index >= run_end { - if let Some(style_run) = style_runs.next() { - if let Some((_, underline_style)) = &mut underline { - if style_run.underline != *underline_style { - finished_underline = underline.take(); - } - } - if style_run.underline.thickness > px(0.) { - underline.get_or_insert(( - point( - glyph_origin.x, - origin.y - + baseline_offset.y - + (self.layout.descent * 0.618), - ), - UnderlineStyle { - color: style_run.underline.color, - thickness: style_run.underline.thickness, - squiggly: style_run.underline.squiggly, - }, - )); - } - - run_end += style_run.len as usize; - color = style_run.color; - } else { - run_end = self.layout.len; - finished_underline = underline.take(); - } - } - - if glyph_origin.x + max_glyph_width < visible_bounds.origin.x { - continue; - } - - if let Some((_underline_origin, _underline_style)) = finished_underline { - // cx.scene().insert(Underline { - // origin: underline_origin, - // width: glyph_origin.x - underline_origin.x, - // thickness: underline_style.thickness.into(), - // color: underline_style.color.unwrap(), - // squiggly: underline_style.squiggly, - // }); - } - - // if glyph.is_emoji { - // cx.scene().push_image_glyph(scene::ImageGlyph { - // font_id: run.font_id, - // font_size: self.layout.font_size, - // id: glyph.id, - // origin: glyph_origin, - // }); - // } else { - // cx.scene().push_glyph(scene::Glyph { - // font_id: run.font_id, - // font_size: self.layout.font_size, - // id: glyph.id, - // origin: glyph_origin, - // color, - // }); - // } - } - - anyhow::Ok(()) - })??; - } - - if let Some((_underline_start, _underline_style)) = underline.take() { - let _line_end_x = origin.x + self.layout.width; - // cx.scene().push_underline(Underline { - // origin: underline_start, - // width: line_end_x - underline_start.x, - // color: underline_style.color, - // thickness: underline_style.thickness.into(), - // squiggly: underline_style.squiggly, - // }); - } - - Ok(()) - } - - pub fn paint_wrapped( - &self, - origin: Point, - _visible_bounds: Bounds, - line_height: Pixels, - boundaries: &[ShapedBoundary], - cx: &mut WindowContext, - ) -> Result<()> { - let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.; - let baseline_offset = point(px(0.), padding_top + self.layout.ascent); - - let mut boundaries = boundaries.into_iter().peekable(); - let mut color_runs = self.style_runs.iter(); - let mut style_run_end = 0; - let mut _color = black(); // todo! - let mut underline: Option<(Point, UnderlineStyle)> = None; - - let mut glyph_origin = origin; - let mut prev_position = px(0.); - for (run_ix, run) in self.layout.runs.iter().enumerate() { - for (glyph_ix, glyph) in run.glyphs.iter().enumerate() { - glyph_origin.x += glyph.position.x - prev_position; - - if boundaries - .peek() - .map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix) - { - boundaries.next(); - if let Some((_underline_origin, _underline_style)) = underline.take() { - // cx.scene().push_underline(Underline { - // origin: underline_origin, - // width: glyph_origin.x - underline_origin.x, - // thickness: underline_style.thickness.into(), - // color: underline_style.color.unwrap(), - // squiggly: underline_style.squiggly, - // }); - } - - glyph_origin = point(origin.x, glyph_origin.y + line_height); - } - prev_position = glyph.position.x; - - let mut finished_underline = None; - if glyph.index >= style_run_end { - if let Some(style_run) = color_runs.next() { - style_run_end += style_run.len as usize; - _color = style_run.color; - if let Some((_, underline_style)) = &mut underline { - if style_run.underline != *underline_style { - finished_underline = underline.take(); - } - } - if style_run.underline.thickness > px(0.) { - underline.get_or_insert(( - glyph_origin - + point( - px(0.), - baseline_offset.y + (self.layout.descent * 0.618), - ), - UnderlineStyle { - color: Some( - style_run.underline.color.unwrap_or(style_run.color), - ), - thickness: style_run.underline.thickness, - squiggly: style_run.underline.squiggly, - }, - )); - } - } else { - style_run_end = self.layout.len; - _color = black(); - finished_underline = underline.take(); - } - } - - if let Some((_underline_origin, _underline_style)) = finished_underline { - // cx.scene().push_underline(Underline { - // origin: underline_origin, - // width: glyph_origin.x - underline_origin.x, - // thickness: underline_style.thickness.into(), - // color: underline_style.color.unwrap(), - // squiggly: underline_style.squiggly, - // }); - } - - cx.text_system().with_font(run.font_id, |system, font| { - let _glyph_bounds = Bounds { - origin: glyph_origin, - size: system.bounding_box(font, self.layout.font_size)?.size, - }; - // if glyph_bounds.intersects(visible_bounds) { - // if glyph.is_emoji { - // cx.scene().push_image_glyph(scene::ImageGlyph { - // font_id: run.font_id, - // font_size: self.layout.font_size, - // id: glyph.id, - // origin: glyph_bounds.origin() + baseline_offset, - // }); - // } else { - // cx.scene().push_glyph(scene::Glyph { - // font_id: run.font_id, - // font_size: self.layout.font_size, - // id: glyph.id, - // origin: glyph_bounds.origin() + baseline_offset, - // color, - // }); - // } - // } - anyhow::Ok(()) - })??; - } - } - - if let Some((_underline_origin, _underline_style)) = underline.take() { - // let line_end_x = glyph_origin.x + self.layout.width - prev_position; - // cx.scene().push_underline(Underline { - // origin: underline_origin, - // width: line_end_x - underline_origin.x, - // thickness: underline_style.thickness.into(), - // color: underline_style.color, - // squiggly: underline_style.squiggly, - // }); - } - - Ok(()) - } -} - impl Run { pub fn glyphs(&self) -> &[Glyph] { &self.glyphs diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 0066adfef1350f432e675d5701461c12a4f94d0f..8ec4063a275a52308c9d6f8689ec2d9246f62940 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -5,6 +5,7 @@ use crate::{ }; use anyhow::Result; use futures::Future; +use smallvec::SmallVec; use std::{any::TypeId, marker::PhantomData, mem, sync::Arc}; use util::ResultExt; @@ -18,6 +19,7 @@ pub struct Window { layout_engine: TaffyLayoutEngine, pub(crate) root_view: Option>, mouse_position: Point, + z_index_stack: SmallVec<[u32; 8]>, pub(crate) scene: Scene, pub(crate) dirty: bool, } @@ -56,6 +58,7 @@ impl Window { layout_engine: TaffyLayoutEngine::new(), root_view: None, mouse_position, + z_index_stack: SmallVec::new(), scene: Scene::new(scale_factor), dirty: true, } @@ -126,6 +129,13 @@ impl<'a, 'w> WindowContext<'a, 'w> { &mut self.window.scene } + pub fn with_z_index(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R { + self.window.z_index_stack.push(z_index); + let result = f(self); + self.window.z_index_stack.pop(); + result + } + pub fn run_on_main( &self, f: impl FnOnce(&mut MainThread) -> R + Send + 'static, From dcaf4c905f112f64916c0f80c1992efc498af4af Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 2 Oct 2023 22:14:45 -0600 Subject: [PATCH 02/28] Checkpoint --- crates/gpui3/src/elements/text.rs | 2 +- crates/gpui3/src/geometry.rs | 59 +++- crates/gpui3/src/gpui3.rs | 1 + crates/gpui3/src/platform.rs | 16 +- crates/gpui3/src/platform/mac.rs | 2 + .../gpui3/src/platform/mac/metal_renderer.rs | 46 ++- crates/gpui3/src/platform/mac/shaders.metal | 49 +++ crates/gpui3/src/platform/mac/sprite.rs | 1 + crates/gpui3/src/platform/mac/window.rs | 17 +- crates/gpui3/src/scene.rs | 225 +++++++++--- crates/gpui3/src/style.rs | 23 +- crates/gpui3/src/text_system/line.rs | 333 ++++++++++++++++++ crates/gpui3/src/window.rs | 16 +- 13 files changed, 694 insertions(+), 96 deletions(-) create mode 100644 crates/gpui3/src/platform/mac/sprite.rs create mode 100644 crates/gpui3/src/text_system/line.rs diff --git a/crates/gpui3/src/elements/text.rs b/crates/gpui3/src/elements/text.rs index 8044e1ab9ee3c0ffd03846fc0549a2c7d3878e62..1c1bd89c0e996d4df53cee355181d2a5035a220d 100644 --- a/crates/gpui3/src/elements/text.rs +++ b/crates/gpui3/src/elements/text.rs @@ -3,7 +3,7 @@ use crate::{ }; use parking_lot::Mutex; use std::{marker::PhantomData, sync::Arc}; -use util::arc_cow::ArcCow; +use util::{arc_cow::ArcCow, ResultExt}; impl IntoAnyElement for ArcCow<'static, str> { fn into_any(self) -> AnyElement { diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index ec5788be25aef116cca6f75c89ab0d156e107d95..7cffe4b59bac1881954ce359424efc3b4bfea730 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -31,10 +31,10 @@ impl Point { impl Mul for Point where - T: Mul + Clone + Debug, + T: Mul + Clone + Debug, Rhs: Clone + Debug, { - type Output = Point; + type Output = Point; fn mul(self, rhs: Rhs) -> Self::Output { Point { @@ -105,7 +105,7 @@ impl Clone for Point { unsafe impl Zeroable for Point {} unsafe impl Pod for Point {} -#[derive(Refineable, Default, Clone, Copy, Debug, PartialEq, Div)] +#[derive(Refineable, Default, Clone, Copy, Debug, PartialEq, Div, Hash)] #[refineable(debug)] #[repr(C)] pub struct Size { @@ -129,6 +129,23 @@ impl Size { } } +impl Size { + pub fn max(&self, other: &Self) -> Self { + Size { + width: if self.width >= other.width { + self.width.clone() + } else { + other.width.clone() + }, + height: if self.height >= other.height { + self.height.clone() + } else { + other.height.clone() + }, + } + } +} + impl Mul for Size where T: Mul + Debug + Clone, @@ -151,6 +168,8 @@ impl, S: Clone> MulAssign for Size { } } +impl Eq for Size {} + impl From>> for Size> { fn from(val: Size>) -> Self { Size { @@ -202,6 +221,7 @@ unsafe impl Pod for Bounds {} impl Mul for Bounds where T: Mul + Clone + Debug, + Point: Mul>, Rhs: Clone + Debug, { type Output = Bounds; @@ -522,11 +542,30 @@ impl From for f64 { } #[derive( - Clone, Copy, Debug, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd, + Add, + AddAssign, + Clone, + Copy, + Debug, + Default, + Div, + Eq, + Hash, + Ord, + PartialEq, + PartialOrd, + Sub, + SubAssign, )] #[repr(transparent)] pub struct DevicePixels(pub(crate) u32); +impl DevicePixels { + pub fn to_bytes(&self, bytes_per_pixel: u8) -> u32 { + self.0 * bytes_per_pixel as u32 + } +} + unsafe impl bytemuck::Pod for DevicePixels {} unsafe impl bytemuck::Zeroable for DevicePixels {} @@ -542,6 +581,18 @@ impl From for DevicePixels { } } +impl From for u64 { + fn from(device_pixels: DevicePixels) -> Self { + device_pixels.0 as u64 + } +} + +impl From for DevicePixels { + fn from(val: u64) -> Self { + DevicePixels(val as u32) + } +} + #[derive(Clone, Copy, Default, Add, Sub, Mul, Div)] pub struct Rems(f32); diff --git a/crates/gpui3/src/gpui3.rs b/crates/gpui3/src/gpui3.rs index d62b42ed747edc31dcddffec669cf5fd30ea239b..d143c979df061083ddd5b1de7553940aaa815a41 100644 --- a/crates/gpui3/src/gpui3.rs +++ b/crates/gpui3/src/gpui3.rs @@ -23,6 +23,7 @@ pub use elements::*; pub use executor::*; pub use geometry::*; pub use gpui3_macros::*; + pub use platform::*; pub use refineable::*; pub use scene::*; diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index be407702fd65f0cf5fc2c661da53f62befea9595..81d02af0839aa706eae73a97f4025c19debbc03e 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -6,8 +6,8 @@ mod mac; mod test; use crate::{ - AnyWindowHandle, Bounds, Font, FontId, FontMetrics, GlyphId, LineLayout, Pixels, Point, Result, - Scene, SharedString, Size, + AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, LineLayout, + MonochromeSprite, Pixels, Point, Result, Scene, SharedString, Size, }; use anyhow::anyhow; use async_task::Runnable; @@ -122,7 +122,7 @@ pub trait PlatformWindow { fn screen(&self) -> Rc; fn mouse_position(&self) -> Point; fn as_any_mut(&mut self) -> &mut dyn Any; - fn set_input_handler(&mut self, input_handler: Box); + fn set_input_handler(&mut self, input_handler: Box); fn prompt( &self, level: WindowPromptLevel, @@ -180,7 +180,15 @@ pub trait PlatformTextSystem: Send + Sync { ) -> Vec; } -pub trait InputHandler { +pub trait PlatformSpriteSystem { + fn get_or_insert_with( + &self, + key: Key, + build: impl FnOnce() -> (Size, Vec), + ) -> MonochromeSprite; +} + +pub trait PlatformInputHandler { fn selected_text_range(&self) -> Option>; fn marked_text_range(&self) -> Option>; fn text_for_range(&self, range_utf16: Range) -> Option; diff --git a/crates/gpui3/src/platform/mac.rs b/crates/gpui3/src/platform/mac.rs index 8ad9385c114cc73e019857bb0524ae535d802a6b..fec4274168af5af1f66990bfb28e1de5ff2cb388 100644 --- a/crates/gpui3/src/platform/mac.rs +++ b/crates/gpui3/src/platform/mac.rs @@ -6,6 +6,7 @@ mod metal_renderer; mod open_type; mod platform; mod screen; +mod sprite; mod text_system; mod window; mod window_appearence; @@ -32,6 +33,7 @@ use std::{ pub use dispatcher::*; pub use platform::*; pub use screen::*; +pub use sprite::*; pub use text_system::*; pub use window::*; diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index e19f130f96c53d36d4f42684356c7254f1fe6e7a..d49b0d94f4dd82b99469ddaed12729d40070042c 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -1,4 +1,4 @@ -use crate::{point, size, DevicePixels, Quad, Scene, Size}; +use crate::{point, size, DevicePixels, MonochromeSprite, Quad, Scene, Size}; use bytemuck::{Pod, Zeroable}; use cocoa::{ base::{NO, YES}, @@ -102,9 +102,7 @@ impl MetalRenderer { &*self.layer } - pub fn draw(&mut self, scene: &Scene) { - dbg!(scene); - + pub fn draw(&mut self, scene: &mut Scene) { let layer = self.layer.clone(); let viewport_size = layer.drawable_size(); let viewport_size: Size = size( @@ -159,21 +157,35 @@ impl MetalRenderer { zfar: 1.0, }); - let mut buffer_offset = 0; + let mut instance_offset = 0; for layer in scene.layers() { - self.draw_quads( - &layer.quads, - &mut buffer_offset, - viewport_size, - command_encoder, - ); + for batch in layer.batches() { + match batch { + crate::PrimitiveBatch::Quads(quads) => { + self.draw_quads( + quads, + &mut instance_offset, + viewport_size, + command_encoder, + ); + } + crate::PrimitiveBatch::Sprites(sprites) => { + self.draw_monochrome_sprites( + sprites, + &mut instance_offset, + viewport_size, + command_encoder, + ); + } + } + } } command_encoder.end_encoding(); self.instances.did_modify_range(NSRange { location: 0, - length: buffer_offset as NSUInteger, + length: instance_offset as NSUInteger, }); command_buffer.commit(); @@ -238,6 +250,16 @@ impl MetalRenderer { ); *offset = next_offset; } + + fn draw_monochrome_sprites( + &mut self, + monochrome: &[MonochromeSprite], + offset: &mut usize, + viewport_size: Size, + command_encoder: &metal::RenderCommandEncoderRef, + ) { + todo!() + } } fn build_pipeline_state( diff --git a/crates/gpui3/src/platform/mac/shaders.metal b/crates/gpui3/src/platform/mac/shaders.metal index 798ef3d66c1f32914b25253ec93af347846e06ab..76fb0e2d1c9ae67f5c1c4ba4423b29e401de9637 100644 --- a/crates/gpui3/src/platform/mac/shaders.metal +++ b/crates/gpui3/src/platform/mac/shaders.metal @@ -198,3 +198,52 @@ float4 to_device_position(float2 pixel_position, float2 viewport_size) { float2(-1., 1.), 0., 1.); } + +// struct SpriteFragmentInput { +// float4 position [[position]]; +// float2 atlas_position; +// float4 color [[flat]]; +// uchar compute_winding [[flat]]; +// }; + +// vertex SpriteFragmentInput sprite_vertex( +// uint unit_vertex_id [[vertex_id]], +// uint sprite_id [[instance_id]], +// constant float2 *unit_vertices +// [[buffer(GPUISpriteVertexInputIndexVertices)]], constant GPUISprite +// *sprites [[buffer(GPUISpriteVertexInputIndexSprites)]], constant float2 +// *viewport_size [[buffer(GPUISpriteVertexInputIndexViewportSize)]], +// constant float2 *atlas_size +// [[buffer(GPUISpriteVertexInputIndexAtlasSize)]] +// ) { +// float2 unit_vertex = unit_vertices[unit_vertex_id]; +// GPUISprite sprite = sprites[sprite_id]; +// float2 position = unit_vertex * sprite.target_size + sprite.origin; +// float4 device_position = to_device_position(position, *viewport_size); +// float2 atlas_position = (unit_vertex * sprite.source_size + +// sprite.atlas_origin) / *atlas_size; + +// return SpriteFragmentInput { +// device_position, +// atlas_position, +// coloru_to_colorf(sprite.color), +// sprite.compute_winding +// }; +// } + +// fragment float4 sprite_fragment( +// SpriteFragmentInput input [[stage_in]], +// texture2d atlas [[ texture(GPUISpriteFragmentInputIndexAtlas) ]] +// ) { +// constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear); +// float4 color = input.color; +// float4 sample = atlas.sample(atlas_sampler, input.atlas_position); +// float mask; +// if (input.compute_winding) { +// mask = 1. - abs(1. - fmod(sample.r, 2.)); +// } else { +// mask = sample.a; +// } +// color.a *= mask; +// return color; +// } diff --git a/crates/gpui3/src/platform/mac/sprite.rs b/crates/gpui3/src/platform/mac/sprite.rs new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/crates/gpui3/src/platform/mac/sprite.rs @@ -0,0 +1 @@ + diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index 5c0355ddacd11cc57707bcaae7bd9719858c38b8..3cf471b45ae9f27ccfd00e66b58c619ab6388027 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -1,8 +1,8 @@ use super::{ns_string, MetalRenderer, NSRange}; use crate::{ - point, px, size, AnyWindowHandle, Bounds, Event, InputHandler, KeyDownEvent, Keystroke, - MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, - MouseUpEvent, NSRectExt, Pixels, Platform, PlatformDispatcher, PlatformScreen, PlatformWindow, + point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers, + ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt, + Pixels, Platform, PlatformDispatcher, PlatformInputHandler, PlatformScreen, PlatformWindow, Point, Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel, }; @@ -292,7 +292,7 @@ struct MacWindowState { should_close_callback: Option bool>>, close_callback: Option>, appearance_changed_callback: Option>, - input_handler: Option>, + input_handler: Option>, pending_key_down: Option<(KeyDownEvent, Option)>, last_key_equivalent: Option, synthetic_drag_counter: usize, @@ -671,7 +671,7 @@ impl PlatformWindow for MacWindow { self } - fn set_input_handler(&mut self, input_handler: Box) { + fn set_input_handler(&mut self, input_handler: Box) { self.0.as_ref().lock().input_handler = Some(input_handler); } @@ -1357,9 +1357,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(scene) = window_state.scene_to_render.take() { - dbg!("render", &scene); - window_state.renderer.draw(&scene); + if let Some(mut scene) = window_state.scene_to_render.take() { + window_state.renderer.draw(&mut scene); } } } @@ -1580,7 +1579,7 @@ async fn synthetic_drag( fn with_input_handler(window: &Object, f: F) -> Option where - F: FnOnce(&mut dyn InputHandler) -> R, + F: FnOnce(&mut dyn PlatformInputHandler) -> R, { let window_state = unsafe { get_window_state(window) }; let mut lock = window_state.as_ref().lock(); diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index 6e7ee82a4186554ac96b3fa1a596484e382f78eb..7d29c9f7025de1a99f0a7f85726aa5f73c341aa9 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -1,82 +1,201 @@ -use std::mem; +use std::{iter::Peekable, mem}; use super::{Bounds, Hsla, Pixels, Point}; -use crate::{Corners, Edges, FontId, GlyphId}; +use crate::{Corners, DevicePixels, Edges}; use bytemuck::{Pod, Zeroable}; -use collections::BTreeMap; // Exported to metal pub type PointF = Point; +pub type StackingOrder = SmallVec<[u32; 16]>; #[derive(Debug)] pub struct Scene { - layers: BTreeMap, - pub(crate) scale_factor: f32, -} - -#[derive(Default, Debug)] -pub struct SceneLayer { - pub quads: Vec, - pub symbol: Vec, + scale_factor: f32, + pub(crate) layers: BTreeMap, } impl Scene { pub fn new(scale_factor: f32) -> Scene { Scene { - layers: Default::default(), scale_factor, + layers: BTreeMap::new(), } } pub fn take(&mut self) -> Scene { Scene { - layers: mem::take(&mut self.layers), scale_factor: self.scale_factor, + layers: mem::take(&mut self.layers), } } - pub fn insert(&mut self, primitive: impl Into) { - let mut primitive = primitive.into(); - primitive.scale(self.scale_factor); - let layer = self.layers.entry(primitive.order()).or_default(); + pub fn insert(&mut self, order: StackingOrder, primitive: impl Into) { + let layer = self.layers.entry(order).or_default(); + + let primitive = primitive.into(); match primitive { - Primitive::Quad(quad) => layer.quads.push(quad), - Primitive::MonochromeGlyph(glyph) => layer.symbol.push(glyph), + Primitive::Quad(mut quad) => { + quad.scale(self.scale_factor); + layer.quads.push(quad); + } + Primitive::Sprite(mut sprite) => { + sprite.scale(self.scale_factor); + layer.sprites.push(sprite); + } } } - pub fn layers(&self) -> impl Iterator { - self.layers.values() + pub(crate) fn layers(&mut self) -> impl Iterator { + self.layers.values_mut() } } -#[derive(Clone, Debug)] -pub enum Primitive { - Quad(Quad), - MonochromeGlyph(Symbol), +#[derive(Debug, Default)] +pub(crate) struct SceneLayer { + pub quads: Vec, + pub sprites: Vec, +} + +impl SceneLayer { + pub fn batches(&mut self) -> impl Iterator { + self.quads.sort_unstable_by(|a, b| a.order.cmp(&b.order)); + self.sprites.sort_unstable_by(|a, b| a.order.cmp(&b.order)); + + BatchIterator::new( + &self.quads, + self.quads.iter().peekable(), + &self.sprites, + self.sprites.iter().peekable(), + ) + } } -impl Primitive { - pub fn order(&self) -> u32 { - match self { - Primitive::Quad(quad) => quad.order, - Primitive::MonochromeGlyph(glyph) => glyph.order, +struct BatchIterator<'a, Q, S> +where + Q: Iterator, + S: Iterator, +{ + next_batch_kind: Option, + quads: &'a [Quad], + sprites: &'a [MonochromeSprite], + quads_start: usize, + sprites_start: usize, + quads_iter: Peekable, + sprites_iter: Peekable, +} + +impl<'a, Q: 'a, S: 'a> Iterator for BatchIterator<'a, Q, S> +where + Q: Iterator, + S: Iterator, +{ + type Item = PrimitiveBatch<'a>; + + fn next(&mut self) -> Option { + if let Some(batch_kind) = self.next_batch_kind.take() { + match batch_kind { + PrimitiveKind::Quad => { + let max_order = self + .next_order(Some(PrimitiveKind::Quad)) + .unwrap_or(u32::MAX); + let quads_start = self.quads_start; + let quads_end = quads_start + + self + .quads_iter + .by_ref() + .take_while(|quad| quad.order <= max_order) + .count(); + self.quads_start = quads_end; + Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end])) + } + PrimitiveKind::Sprite => { + let max_order = self + .next_order(Some(PrimitiveKind::Sprite)) + .unwrap_or(u32::MAX); + let sprites_start = self.sprites_start; + let sprites_end = sprites_start + + self + .sprites_iter + .by_ref() + .take_while(|sprite| sprite.order <= max_order) + .count(); + self.sprites_start = sprites_end; + Some(PrimitiveBatch::Sprites( + &self.sprites[sprites_start..sprites_end], + )) + } + } + } else { + None } } +} + +impl<'a, Q: 'a, S: 'a> BatchIterator<'a, Q, S> +where + Q: Iterator, + S: Iterator, +{ + fn new( + quads: &'a [Quad], + quads_iter: Peekable, + sprites: &'a [MonochromeSprite], + sprites_iter: Peekable, + ) -> Self { + let mut this = Self { + quads, + quads_start: 0, + quads_iter, + sprites, + sprites_start: 0, + sprites_iter, + next_batch_kind: None, + }; + this.next_order(None); // Called for its side effect of setting this.next_batch_kind + this + } - pub fn scale(&mut self, factor: f32) { - match self { - Primitive::Quad(quad) => { - quad.scale(factor); + fn next_order(&mut self, exclude_kind: Option) -> Option { + let mut next_order = u32::MAX; + + if exclude_kind != Some(PrimitiveKind::Quad) { + if let Some(next_quad) = self.quads_iter.peek() { + self.next_batch_kind = Some(PrimitiveKind::Quad); + next_order = next_quad.order; } - Primitive::MonochromeGlyph(glyph) => { - glyph.scale(factor); + } + + if exclude_kind != Some(PrimitiveKind::Sprite) { + if let Some(next_sprite) = self.sprites_iter.peek() { + if next_sprite.order < next_order { + self.next_batch_kind = Some(PrimitiveKind::Sprite); + next_order = next_sprite.order; + } } } + + (next_order < u32::MAX).then_some(next_order) } } -#[derive(Debug, Clone, Copy, Zeroable, Pod)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum PrimitiveKind { + Quad, + Sprite, +} + +#[derive(Clone, Debug)] +pub enum Primitive { + Quad(Quad), + Sprite(MonochromeSprite), +} + +pub enum PrimitiveBatch<'a> { + Quads(&'a [Quad]), + Sprites(&'a [MonochromeSprite]), +} + +#[derive(Debug, Copy, Clone, Zeroable, Pod)] #[repr(C)] pub struct Quad { pub order: u32, @@ -119,26 +238,32 @@ impl From for Primitive { } } -#[derive(Debug, Clone, Copy)] +#[derive(Clone, Debug)] #[repr(C)] -pub struct Symbol { +pub struct MonochromeSprite { pub order: u32, - pub origin: Point, - pub font_id: FontId, - pub font_size: Pixels, - pub id: GlyphId, - pub color: Hsla, + pub bounds: Bounds, + pub atlas_id: AtlasId, + pub tile_id: TileId, + pub bounds_in_atlas: Bounds, + pub color: Option, } -impl Symbol { +impl MonochromeSprite { pub fn scale(&mut self, factor: f32) { - self.font_size *= factor; - self.origin *= factor; + self.bounds *= factor; } } -impl From for Primitive { - fn from(glyph: Symbol) -> Self { - Primitive::MonochromeGlyph(glyph) +impl From for Primitive { + fn from(sprite: MonochromeSprite) -> Self { + Primitive::Sprite(sprite) } } + +#[derive(Copy, Clone, Debug)] +pub struct AtlasId(pub(crate) usize); + +use collections::BTreeMap; +use etagere::AllocId as TileId; +use smallvec::SmallVec; diff --git a/crates/gpui3/src/style.rs b/crates/gpui3/src/style.rs index 863736e4a3f7eb614276834b96e796f0cfaa815f..f972b2ead3a436504962930b666ae0375466ea10 100644 --- a/crates/gpui3/src/style.rs +++ b/crates/gpui3/src/style.rs @@ -185,16 +185,19 @@ impl Style { let background_color = self.fill.as_ref().and_then(Fill::color); if background_color.is_some() || self.is_border_visible() { - cx.scene().insert(Quad { - order, - bounds, - clip_bounds: bounds, // todo! - clip_corner_radii: self.corner_radii.map(|length| length.to_pixels(rem_size)), - background: background_color.unwrap_or_default(), - border_color: self.border_color.unwrap_or_default(), - corner_radii: self.corner_radii.map(|length| length.to_pixels(rem_size)), - border_widths: self.border_widths.map(|length| length.to_pixels(rem_size)), - }); + cx.scene().insert( + todo!(), + Quad { + order, + bounds, + clip_bounds: bounds, // todo! + clip_corner_radii: self.corner_radii.map(|length| length.to_pixels(rem_size)), + background: background_color.unwrap_or_default(), + border_color: self.border_color.unwrap_or_default(), + corner_radii: self.corner_radii.map(|length| length.to_pixels(rem_size)), + border_widths: self.border_widths.map(|length| length.to_pixels(rem_size)), + }, + ); } } diff --git a/crates/gpui3/src/text_system/line.rs b/crates/gpui3/src/text_system/line.rs new file mode 100644 index 0000000000000000000000000000000000000000..14f1d96f54994a4a24f53f6042081aa35d453726 --- /dev/null +++ b/crates/gpui3/src/text_system/line.rs @@ -0,0 +1,333 @@ +use crate::{ + black, point, px, Bounds, FontId, Hsla, Layout, LineLayout, Pixels, Point, Run, RunStyle, + ShapedBoundary, UnderlineStyle, WindowContext, +}; +use anyhow::Result; +use smallvec::SmallVec; +use std::sync::Arc; + +#[derive(Default, Debug, Clone)] +pub struct Line { + layout: Arc, + style_runs: SmallVec<[StyleRun; 32]>, +} + +#[derive(Debug, Clone)] +struct StyleRun { + len: u32, + color: Hsla, + underline: UnderlineStyle, +} + +impl Line { + pub fn new(layout: Arc, runs: &[(usize, RunStyle)]) -> Self { + let mut style_runs = SmallVec::new(); + for (len, style) in runs { + style_runs.push(StyleRun { + len: *len as u32, + color: style.color, + underline: style.underline.clone().unwrap_or_default(), + }); + } + Self { layout, style_runs } + } + + pub fn runs(&self) -> &[Run] { + &self.layout.runs + } + + pub fn width(&self) -> Pixels { + self.layout.width + } + + pub fn font_size(&self) -> Pixels { + self.layout.font_size + } + + pub fn x_for_index(&self, index: usize) -> Pixels { + for run in &self.layout.runs { + for glyph in &run.glyphs { + if glyph.index >= index { + return glyph.position.x; + } + } + } + self.layout.width + } + + pub fn font_for_index(&self, index: usize) -> Option { + for run in &self.layout.runs { + for glyph in &run.glyphs { + if glyph.index >= index { + return Some(run.font_id); + } + } + } + + None + } + + pub fn len(&self) -> usize { + self.layout.len + } + + pub fn is_empty(&self) -> bool { + self.layout.len == 0 + } + + pub fn index_for_x(&self, x: Pixels) -> Option { + if x >= self.layout.width { + None + } else { + for run in self.layout.runs.iter().rev() { + for glyph in run.glyphs.iter().rev() { + if glyph.position.x <= x { + return Some(glyph.index); + } + } + } + Some(0) + } + } + + // todo! + pub fn paint( + &self, + layout: &Layout, + visible_bounds: Bounds, + line_height: Pixels, + cx: &mut WindowContext, + ) -> Result<()> { + let origin = layout.bounds.origin; + let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.; + let baseline_offset = point(px(0.), padding_top + self.layout.ascent); + + let mut style_runs = self.style_runs.iter(); + let mut run_end = 0; + let mut color = black(); + let mut underline = None; + let text_system = cx.text_system().clone(); + + for run in &self.layout.runs { + text_system.with_font(run.font_id, |system, font| { + let max_glyph_width = system.bounding_box(font, self.layout.font_size)?.size.width; + + for glyph in &run.glyphs { + let glyph_origin = origin + baseline_offset + glyph.position; + if glyph_origin.x > visible_bounds.upper_right().x { + break; + } + + let mut finished_underline: Option<(Point, UnderlineStyle)> = None; + if glyph.index >= run_end { + if let Some(style_run) = style_runs.next() { + if let Some((_, underline_style)) = &mut underline { + if style_run.underline != *underline_style { + finished_underline = underline.take(); + } + } + if style_run.underline.thickness > px(0.) { + underline.get_or_insert(( + point( + glyph_origin.x, + origin.y + + baseline_offset.y + + (self.layout.descent * 0.618), + ), + UnderlineStyle { + color: style_run.underline.color, + thickness: style_run.underline.thickness, + squiggly: style_run.underline.squiggly, + }, + )); + } + + run_end += style_run.len as usize; + color = style_run.color; + } else { + run_end = self.layout.len; + finished_underline = underline.take(); + } + } + + if glyph_origin.x + max_glyph_width < visible_bounds.origin.x { + continue; + } + + if let Some((_underline_origin, _underline_style)) = finished_underline { + todo!() + // cx.scene().insert(Underline { + // origin: underline_origin, + // width: glyph_origin.x - underline_origin.x, + // thickness: underline_style.thickness.into(), + // color: underline_style.color.unwrap(), + // squiggly: underline_style.squiggly, + // }); + } + + if glyph.is_emoji { + todo!() + // cx.scene().push_image_glyph(scene::ImageGlyph { + // font_id: run.font_id, + // font_size: self.layout.font_size, + // id: glyph.id, + // origin: glyph_origin, + // }); + } else { + todo!() + // cx.scene().insert(Symbol { + // order: layout.order, + // origin, + // font_id: run.font_id, + // font_size: self.layout.font_size, + // id: glyph.id, + // color, + // }); + } + } + + anyhow::Ok(()) + })??; + } + + if let Some((_underline_start, _underline_style)) = underline.take() { + let _line_end_x = origin.x + self.layout.width; + // cx.scene().push_underline(Underline { + // origin: underline_start, + // width: line_end_x - underline_start.x, + // color: underline_style.color, + // thickness: underline_style.thickness.into(), + // squiggly: underline_style.squiggly, + // }); + } + + Ok(()) + } + + pub fn paint_wrapped( + &self, + origin: Point, + _visible_bounds: Bounds, + line_height: Pixels, + boundaries: &[ShapedBoundary], + cx: &mut WindowContext, + ) -> Result<()> { + let padding_top = (line_height - self.layout.ascent - self.layout.descent) / 2.; + let baseline_offset = point(px(0.), padding_top + self.layout.ascent); + + let mut boundaries = boundaries.into_iter().peekable(); + let mut color_runs = self.style_runs.iter(); + let mut style_run_end = 0; + let mut _color = black(); // todo! + let mut underline: Option<(Point, UnderlineStyle)> = None; + + let mut glyph_origin = origin; + let mut prev_position = px(0.); + for (run_ix, run) in self.layout.runs.iter().enumerate() { + for (glyph_ix, glyph) in run.glyphs.iter().enumerate() { + glyph_origin.x += glyph.position.x - prev_position; + + if boundaries + .peek() + .map_or(false, |b| b.run_ix == run_ix && b.glyph_ix == glyph_ix) + { + boundaries.next(); + if let Some((_underline_origin, _underline_style)) = underline.take() { + // cx.scene().push_underline(Underline { + // origin: underline_origin, + // width: glyph_origin.x - underline_origin.x, + // thickness: underline_style.thickness.into(), + // color: underline_style.color.unwrap(), + // squiggly: underline_style.squiggly, + // }); + } + + glyph_origin = point(origin.x, glyph_origin.y + line_height); + } + prev_position = glyph.position.x; + + let mut finished_underline = None; + if glyph.index >= style_run_end { + if let Some(style_run) = color_runs.next() { + style_run_end += style_run.len as usize; + _color = style_run.color; + if let Some((_, underline_style)) = &mut underline { + if style_run.underline != *underline_style { + finished_underline = underline.take(); + } + } + if style_run.underline.thickness > px(0.) { + underline.get_or_insert(( + glyph_origin + + point( + px(0.), + baseline_offset.y + (self.layout.descent * 0.618), + ), + UnderlineStyle { + color: Some( + style_run.underline.color.unwrap_or(style_run.color), + ), + thickness: style_run.underline.thickness, + squiggly: style_run.underline.squiggly, + }, + )); + } + } else { + style_run_end = self.layout.len; + _color = black(); + finished_underline = underline.take(); + } + } + + if let Some((_underline_origin, _underline_style)) = finished_underline { + // cx.scene().push_underline(Underline { + // origin: underline_origin, + // width: glyph_origin.x - underline_origin.x, + // thickness: underline_style.thickness.into(), + // color: underline_style.color.unwrap(), + // squiggly: underline_style.squiggly, + // }); + } + + cx.text_system().with_font(run.font_id, |system, font| { + let _glyph_bounds = Bounds { + origin: glyph_origin, + size: system.bounding_box(font, self.layout.font_size)?.size, + }; + // if glyph_bounds.intersects(visible_bounds) { + // if glyph.is_emoji { + // cx.scene().push_image_glyph(scene::ImageGlyph { + // font_id: run.font_id, + // font_size: self.layout.font_size, + // id: glyph.id, + // origin: glyph_bounds.origin() + baseline_offset, + // }); + // } else { + // cx.scene().push_glyph(scene::Glyph { + // font_id: run.font_id, + // font_size: self.layout.font_size, + // id: glyph.id, + // origin: glyph_bounds.origin() + baseline_offset, + // color, + // }); + // } + // } + anyhow::Ok(()) + })??; + } + } + + if let Some((_underline_origin, _underline_style)) = underline.take() { + // let line_end_x = glyph_origin.x + self.layout.width - prev_position; + // cx.scene().push_underline(Underline { + // origin: underline_origin, + // width: line_end_x - underline_origin.x, + // thickness: underline_style.thickness.into(), + // color: underline_style.color, + // squiggly: underline_style.squiggly, + // }); + } + + Ok(()) + } +} diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 8ec4063a275a52308c9d6f8689ec2d9246f62940..b4a893cb5653752c9178dbf41c371a6017ef2e64 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,7 +1,7 @@ use crate::{ px, AnyView, AppContext, AvailableSpace, Bounds, Context, Effect, Element, EntityId, Handle, LayoutId, MainThread, MainThreadOnly, Pixels, PlatformWindow, Point, Reference, Scene, Size, - StackContext, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, + StackContext, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, }; use anyhow::Result; use futures::Future; @@ -19,7 +19,7 @@ pub struct Window { layout_engine: TaffyLayoutEngine, pub(crate) root_view: Option>, mouse_position: Point, - z_index_stack: SmallVec<[u32; 8]>, + current_stacking_order: StackingOrder, pub(crate) scene: Scene, pub(crate) dirty: bool, } @@ -58,7 +58,7 @@ impl Window { layout_engine: TaffyLayoutEngine::new(), root_view: None, mouse_position, - z_index_stack: SmallVec::new(), + current_stacking_order: SmallVec::new(), scene: Scene::new(scale_factor), dirty: true, } @@ -129,13 +129,17 @@ impl<'a, 'w> WindowContext<'a, 'w> { &mut self.window.scene } - pub fn with_z_index(&mut self, z_index: u32, f: impl FnOnce(&mut Self) -> R) -> R { - self.window.z_index_stack.push(z_index); + 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); - self.window.z_index_stack.pop(); + self.window.current_stacking_order.pop(); result } + pub fn current_stack_order(&self) -> StackingOrder { + self.window.current_stacking_order.clone() + } + pub fn run_on_main( &self, f: impl FnOnce(&mut MainThread) -> R + Send + 'static, From 12ba10bc2cc16d138834eca4594fea6b69df525d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 3 Oct 2023 14:48:08 +0200 Subject: [PATCH 03/28] Checkpoint --- crates/gpui3/src/elements/text.rs | 23 +-- crates/gpui3/src/platform.rs | 20 ++- crates/gpui3/src/platform/mac.rs | 4 +- crates/gpui3/src/platform/mac/metal_atlas.rs | 164 +++++++++++++++++++ crates/gpui3/src/platform/mac/sprite.rs | 1 - crates/gpui3/src/scene.rs | 8 +- 6 files changed, 196 insertions(+), 24 deletions(-) create mode 100644 crates/gpui3/src/platform/mac/metal_atlas.rs delete mode 100644 crates/gpui3/src/platform/mac/sprite.rs diff --git a/crates/gpui3/src/elements/text.rs b/crates/gpui3/src/elements/text.rs index 1c1bd89c0e996d4df53cee355181d2a5035a220d..db94b96b7747afdda0ff2cdcdcc12238af5e2e75 100644 --- a/crates/gpui3/src/elements/text.rs +++ b/crates/gpui3/src/elements/text.rs @@ -32,7 +32,7 @@ pub struct Text { impl Element for Text { type State = S; - type FrameState = Arc>>; + type FrameState = Arc>>; fn layout( &mut self, @@ -54,7 +54,6 @@ impl Element for Text { let layout_id = cx.request_measured_layout(Default::default(), rem_size, { let frame_state = paint_state.clone(); move |_, _| { - dbg!("starting measurement"); let Some(line_layout) = text_system .layout_line( text.as_ref(), @@ -65,23 +64,21 @@ impl Element for Text { else { return Size::default(); }; - dbg!("bbbb"); let size = Size { width: line_layout.width(), height: line_height, }; - frame_state.lock().replace(TextLayout { + frame_state.lock().replace(TextFrameState { line: Arc::new(line_layout), line_height, }); - dbg!(size) + size } }); - dbg!("got to end of text layout"); Ok((layout_id?, paint_state)) } @@ -89,22 +86,20 @@ impl Element for Text { &mut self, layout: Layout, _: &mut Self::State, - paint_state: &mut Self::FrameState, + frame_state: &mut Self::FrameState, cx: &mut ViewContext, ) -> Result<()> { let line; let line_height; { - let paint_state = paint_state.lock(); - let paint_state = paint_state + let frame_state = frame_state.lock(); + let frame_state = frame_state .as_ref() .expect("measurement has not been performed"); - line = paint_state.line.clone(); - line_height = paint_state.line_height; + line = frame_state.line.clone(); + line_height = frame_state.line_height; } - let _text_style = cx.text_style(); - // todo!("We haven't added visible bounds to the new element system yet, so this is a placeholder."); let visible_bounds = layout.bounds; line.paint(&layout, visible_bounds, line_height, cx)?; @@ -113,7 +108,7 @@ impl Element for Text { } } -pub struct TextLayout { +pub struct TextFrameState { line: Arc, line_height: Pixels, } diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 81d02af0839aa706eae73a97f4025c19debbc03e..2bd5b942e60b5e53d7306d5d392d980e44d0b99e 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -180,14 +180,30 @@ pub trait PlatformTextSystem: Send + Sync { ) -> Vec; } -pub trait PlatformSpriteSystem { +pub trait PlatformAtlas { fn get_or_insert_with( &self, key: Key, build: impl FnOnce() -> (Size, Vec), - ) -> MonochromeSprite; + ) -> AtlasTile; + + fn clear(&self); +} + +#[derive(Clone, Debug)] +#[repr(C)] +pub struct AtlasTile { + pub(crate) texture_id: AtlasTextureId, + pub(crate) tile_id: TileId, + pub(crate) bounds_in_atlas: Bounds, } +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub(crate) struct AtlasTextureId(pub(crate) usize); + +pub(crate) type TileId = etagere::AllocId; + pub trait PlatformInputHandler { fn selected_text_range(&self) -> Option>; fn marked_text_range(&self) -> Option>; diff --git a/crates/gpui3/src/platform/mac.rs b/crates/gpui3/src/platform/mac.rs index fec4274168af5af1f66990bfb28e1de5ff2cb388..dcbf8f887f2d5ee663ccb2d1383adaa1b55d5919 100644 --- a/crates/gpui3/src/platform/mac.rs +++ b/crates/gpui3/src/platform/mac.rs @@ -2,11 +2,11 @@ ///! an origin at the bottom left of the main display. mod dispatcher; mod events; +mod metal_atlas; mod metal_renderer; mod open_type; mod platform; mod screen; -mod sprite; mod text_system; mod window; mod window_appearence; @@ -31,9 +31,9 @@ use std::{ }; pub use dispatcher::*; +pub use metal_atlas::*; pub use platform::*; pub use screen::*; -pub use sprite::*; pub use text_system::*; pub use window::*; diff --git a/crates/gpui3/src/platform/mac/metal_atlas.rs b/crates/gpui3/src/platform/mac/metal_atlas.rs new file mode 100644 index 0000000000000000000000000000000000000000..926d7df8b67c064d619acfe5eb1adcd329b456b6 --- /dev/null +++ b/crates/gpui3/src/platform/mac/metal_atlas.rs @@ -0,0 +1,164 @@ +use crate::{AtlasTextureId, AtlasTile, Bounds, DevicePixels, PlatformAtlas, Point, Size}; +use collections::HashMap; +use etagere::BucketedAtlasAllocator; +use foreign_types::ForeignType; +use metal::{Device, TextureDescriptor, TextureDescriptorRef}; +use objc::{msg_send, sel, sel_impl}; +use parking_lot::{RwLock, RwLockUpgradableReadGuard}; +use std::hash::Hash; + +pub struct MetalAtlas(RwLock>); + +struct MetalAtlasState { + device: Device, + texture_descriptor: TextureDescriptor, + textures: Vec, + tiles_by_key: HashMap, +} + +impl PlatformAtlas for MetalAtlas +where + Key: Eq + Hash, +{ + fn get_or_insert_with( + &self, + key: Key, + build: impl FnOnce() -> (Size, Vec), + ) -> AtlasTile { + let lock = self.0.upgradable_read(); + if let Some(tile) = lock.tiles_by_key.get(&key) { + return tile.clone(); + } else { + let mut lock = RwLockUpgradableReadGuard::upgrade(lock); + let (size, bytes) = build(); + lock.textures + .iter_mut() + .rev() + .find_map(|texture| texture.allocate(size, &bytes)) + .unwrap_or_else(|| { + let texture = lock.push_texture(size); + texture + .allocate(size, &bytes) + .expect("could not allocate a tile in new texture") + }) + } + } + + fn clear(&self) { + self.0.write().tiles_by_key.clear(); + } +} + +impl MetalAtlasState { + fn push_texture(&mut self, min_size: Size) -> &mut MetalAtlasTexture { + let default_atlas_size = Size { + width: self.texture_descriptor.width().into(), + height: self.texture_descriptor.height().into(), + }; + let size; + let metal_texture; + + if min_size.width > default_atlas_size.width || min_size.height > default_atlas_size.height + { + let descriptor = unsafe { + let descriptor_ptr: *mut metal::MTLTextureDescriptor = + msg_send![self.texture_descriptor, copy]; + metal::TextureDescriptor::from_ptr(descriptor_ptr) + }; + descriptor.set_width(min_size.width.into()); + descriptor.set_height(min_size.height.into()); + + size = min_size; + metal_texture = self.device.new_texture(&descriptor); + } else { + size = default_atlas_size; + metal_texture = self.device.new_texture(&self.texture_descriptor); + } + + let atlas_texture = MetalAtlasTexture { + id: AtlasTextureId(self.textures.len()), + allocator: etagere::BucketedAtlasAllocator::new(size.into()), + metal_texture, + }; + self.textures.push(atlas_texture); + self.textures.last_mut().unwrap() + } +} + +struct MetalAtlasTexture { + id: AtlasTextureId, + allocator: BucketedAtlasAllocator, + metal_texture: metal::Texture, +} + +impl MetalAtlasTexture { + fn allocate(&mut self, size: Size, bytes: &[u8]) -> Option { + let size = size.into(); + let allocation = self.allocator.allocate(size)?; + let tile = AtlasTile { + texture_id: self.id, + tile_id: allocation.id, + bounds_in_atlas: allocation.rectangle.into(), + }; + let region = metal::MTLRegion::new_2d( + u32::from(tile.bounds_in_atlas.origin.x) as u64, + u32::from(tile.bounds_in_atlas.origin.y) as u64, + u32::from(tile.bounds_in_atlas.size.width) as u64, + u32::from(tile.bounds_in_atlas.size.height) as u64, + ); + self.metal_texture.replace_region( + region, + 0, + bytes.as_ptr() as *const _, + u32::from( + tile.bounds_in_atlas + .size + .width + .to_bytes(self.bytes_per_pixel()), + ) as u64, + ); + Some(tile) + } + + fn bytes_per_pixel(&self) -> u8 { + use metal::MTLPixelFormat::*; + match self.metal_texture.pixel_format() { + A8Unorm | R8Unorm => 1, + RGBA8Unorm | BGRA8Unorm => 4, + _ => unimplemented!(), + } + } +} + +impl From> for etagere::Size { + fn from(size: Size) -> Self { + etagere::Size::new(u32::from(size.width) as i32, u32::from(size.width) as i32) + } +} + +impl From for Point { + fn from(value: etagere::Point) -> Self { + Point { + x: DevicePixels::from(value.x as u32), + y: DevicePixels::from(value.y as u32), + } + } +} + +impl From for Size { + fn from(size: etagere::Size) -> Self { + Size { + width: DevicePixels::from(size.width as u32), + height: DevicePixels::from(size.height as u32), + } + } +} + +impl From for Bounds { + fn from(rectangle: etagere::Rectangle) -> Self { + Bounds { + origin: rectangle.min.into(), + size: rectangle.size().into(), + } + } +} diff --git a/crates/gpui3/src/platform/mac/sprite.rs b/crates/gpui3/src/platform/mac/sprite.rs deleted file mode 100644 index 8b137891791fe96927ad78e64b0aad7bded08bdc..0000000000000000000000000000000000000000 --- a/crates/gpui3/src/platform/mac/sprite.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index 7d29c9f7025de1a99f0a7f85726aa5f73c341aa9..f3e26f01973046d24a4c067ed42d4229f7f00dfb 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -1,7 +1,7 @@ use std::{iter::Peekable, mem}; use super::{Bounds, Hsla, Pixels, Point}; -use crate::{Corners, DevicePixels, Edges}; +use crate::{AtlasTile, Corners, DevicePixels, Edges}; use bytemuck::{Pod, Zeroable}; // Exported to metal @@ -243,10 +243,8 @@ impl From for Primitive { pub struct MonochromeSprite { pub order: u32, pub bounds: Bounds, - pub atlas_id: AtlasId, - pub tile_id: TileId, - pub bounds_in_atlas: Bounds, - pub color: Option, + pub color: Hsla, + pub tile: AtlasTile, } impl MonochromeSprite { From 08464ee26eada69ab9b642e78f2c1bd91205d04e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 3 Oct 2023 15:23:49 +0200 Subject: [PATCH 04/28] Checkpoint --- crates/gpui3/src/platform.rs | 12 +++-- crates/gpui3/src/platform/mac/metal_atlas.rs | 50 ++++++++++++++----- .../gpui3/src/platform/mac/metal_renderer.rs | 21 +++++++- crates/gpui3/src/platform/mac/text_system.rs | 16 +++--- crates/gpui3/src/platform/mac/window.rs | 15 ++++-- crates/gpui3/src/text_system.rs | 31 +++++++----- crates/gpui3/src/text_system/line.rs | 10 ++-- .../src/text_system/text_layout_cache.rs | 12 ++--- crates/gpui3/src/window.rs | 10 ++-- 9 files changed, 118 insertions(+), 59 deletions(-) diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 2bd5b942e60b5e53d7306d5d392d980e44d0b99e..1179a83618ab8420170490fe817d3f03da06333c 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -6,8 +6,8 @@ mod mac; mod test; use crate::{ - AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, LineLayout, - MonochromeSprite, Pixels, Point, Result, Scene, SharedString, Size, + AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, MonochromeSprite, + Pixels, Point, RasterizedGlyphId, Result, Scene, ShapedLine, SharedString, Size, }; use anyhow::anyhow; use async_task::Runnable; @@ -146,6 +146,8 @@ pub trait PlatformWindow { fn on_appearance_changed(&self, callback: Box); fn is_topmost_for_position(&self, position: Point) -> bool; fn draw(&self, scene: Scene); + + fn glyph_atlas(&self) -> Arc>; } pub trait PlatformDispatcher: Send + Sync { @@ -170,7 +172,7 @@ pub trait PlatformTextSystem: Send + Sync { scale_factor: f32, options: RasterizationOptions, ) -> Option<(Bounds, Vec)>; - fn layout_line(&self, text: &str, font_size: Pixels, runs: &[(usize, FontId)]) -> LineLayout; + fn layout_line(&self, text: &str, font_size: Pixels, runs: &[(usize, FontId)]) -> ShapedLine; fn wrap_line( &self, text: &str, @@ -180,11 +182,11 @@ pub trait PlatformTextSystem: Send + Sync { ) -> Vec; } -pub trait PlatformAtlas { +pub trait PlatformAtlas: Send + Sync { fn get_or_insert_with( &self, key: Key, - build: impl FnOnce() -> (Size, Vec), + build: &dyn Fn() -> (Size, Vec), ) -> AtlasTile; fn clear(&self); diff --git a/crates/gpui3/src/platform/mac/metal_atlas.rs b/crates/gpui3/src/platform/mac/metal_atlas.rs index 926d7df8b67c064d619acfe5eb1adcd329b456b6..96cb1a64ed5eb0b6d721d008e930598cd7ab89b2 100644 --- a/crates/gpui3/src/platform/mac/metal_atlas.rs +++ b/crates/gpui3/src/platform/mac/metal_atlas.rs @@ -1,35 +1,54 @@ use crate::{AtlasTextureId, AtlasTile, Bounds, DevicePixels, PlatformAtlas, Point, Size}; use collections::HashMap; +use derive_more::{Deref, DerefMut}; use etagere::BucketedAtlasAllocator; use foreign_types::ForeignType; -use metal::{Device, TextureDescriptor, TextureDescriptorRef}; +use metal::{Device, TextureDescriptor}; use objc::{msg_send, sel, sel_impl}; -use parking_lot::{RwLock, RwLockUpgradableReadGuard}; +use parking_lot::Mutex; use std::hash::Hash; -pub struct MetalAtlas(RwLock>); +pub struct MetalAtlas(Mutex>); + +impl MetalAtlas { + pub fn new( + size: Size, + pixel_format: metal::MTLPixelFormat, + device: Device, + ) -> Self { + let texture_descriptor = metal::TextureDescriptor::new(); + texture_descriptor.set_pixel_format(pixel_format); + texture_descriptor.set_width(size.width.into()); + texture_descriptor.set_height(size.height.into()); + MetalAtlas(Mutex::new(MetalAtlasState { + device: AssertSend(device), + texture_descriptor: AssertSend(texture_descriptor), + textures: Default::default(), + tiles_by_key: Default::default(), + })) + } +} struct MetalAtlasState { - device: Device, - texture_descriptor: TextureDescriptor, + device: AssertSend, + texture_descriptor: AssertSend, textures: Vec, tiles_by_key: HashMap, } impl PlatformAtlas for MetalAtlas where - Key: Eq + Hash, + Key: Eq + Hash + Send, { fn get_or_insert_with( &self, key: Key, - build: impl FnOnce() -> (Size, Vec), + build: &dyn Fn() -> (Size, Vec), ) -> AtlasTile { - let lock = self.0.upgradable_read(); + let mut lock = self.0.lock(); if let Some(tile) = lock.tiles_by_key.get(&key) { return tile.clone(); } else { - let mut lock = RwLockUpgradableReadGuard::upgrade(lock); let (size, bytes) = build(); lock.textures .iter_mut() @@ -45,7 +64,7 @@ where } fn clear(&self) { - self.0.write().tiles_by_key.clear(); + self.0.lock().tiles_by_key.clear(); } } @@ -62,7 +81,7 @@ impl MetalAtlasState { { let descriptor = unsafe { let descriptor_ptr: *mut metal::MTLTextureDescriptor = - msg_send![self.texture_descriptor, copy]; + msg_send![*self.texture_descriptor, copy]; metal::TextureDescriptor::from_ptr(descriptor_ptr) }; descriptor.set_width(min_size.width.into()); @@ -78,7 +97,7 @@ impl MetalAtlasState { let atlas_texture = MetalAtlasTexture { id: AtlasTextureId(self.textures.len()), allocator: etagere::BucketedAtlasAllocator::new(size.into()), - metal_texture, + metal_texture: AssertSend(metal_texture), }; self.textures.push(atlas_texture); self.textures.last_mut().unwrap() @@ -88,7 +107,7 @@ impl MetalAtlasState { struct MetalAtlasTexture { id: AtlasTextureId, allocator: BucketedAtlasAllocator, - metal_texture: metal::Texture, + metal_texture: AssertSend, } impl MetalAtlasTexture { @@ -162,3 +181,8 @@ impl From for Bounds { } } } + +#[derive(Deref, DerefMut)] +struct AssertSend(T); + +unsafe impl Send for AssertSend {} diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index d49b0d94f4dd82b99469ddaed12729d40070042c..56c0f130810f32f4eed3cb0605f3a9fb4737e718 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -1,4 +1,6 @@ -use crate::{point, size, DevicePixels, MonochromeSprite, Quad, Scene, Size}; +use crate::{ + point, size, DevicePixels, MetalAtlas, MonochromeSprite, Quad, RasterizedGlyphId, Scene, Size, +}; use bytemuck::{Pod, Zeroable}; use cocoa::{ base::{NO, YES}, @@ -7,7 +9,7 @@ use cocoa::{ }; use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange}; use objc::{self, msg_send, sel, sel_impl}; -use std::{ffi::c_void, mem, ptr}; +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. @@ -19,6 +21,7 @@ pub struct MetalRenderer { quad_pipeline_state: metal::RenderPipelineState, unit_vertices: metal::Buffer, instances: metal::Buffer, + glyph_atlas: Arc>, } impl MetalRenderer { @@ -88,6 +91,15 @@ impl MetalRenderer { ); let command_queue = device.new_command_queue(); + let glyph_atlas = Arc::new(MetalAtlas::new( + Size { + width: DevicePixels(1024), + height: DevicePixels(1024), + }, + MTLPixelFormat::A8Unorm, + device.clone(), + )); + Self { device, layer, @@ -95,6 +107,7 @@ impl MetalRenderer { quad_pipeline_state, unit_vertices, instances, + glyph_atlas, } } @@ -102,6 +115,10 @@ impl MetalRenderer { &*self.layer } + pub fn glyph_atlas(&self) -> &Arc> { + &self.glyph_atlas + } + pub fn draw(&mut self, scene: &mut Scene) { let layer = self.layer.clone(); let viewport_size = layer.drawable_size(); diff --git a/crates/gpui3/src/platform/mac/text_system.rs b/crates/gpui3/src/platform/mac/text_system.rs index 402a1db517900259b80623a9c433285c436c988d..9f1ea8a78f82303e0c52201c6e0195763641994b 100644 --- a/crates/gpui3/src/platform/mac/text_system.rs +++ b/crates/gpui3/src/platform/mac/text_system.rs @@ -1,7 +1,7 @@ use crate::{ - point, px, size, Bounds, Font, FontFeatures, FontId, FontMetrics, FontStyle, FontWeight, Glyph, - GlyphId, LineLayout, Pixels, PlatformTextSystem, Point, RasterizationOptions, Result, Run, - SharedString, Size, + point, px, size, Bounds, Font, FontFeatures, FontId, FontMetrics, FontStyle, FontWeight, + GlyphId, Pixels, PlatformTextSystem, Point, RasterizationOptions, Result, ShapedGlyph, + ShapedLine, ShapedRun, SharedString, Size, }; use cocoa::appkit::{CGFloat, CGPoint}; use collections::HashMap; @@ -161,7 +161,7 @@ impl PlatformTextSystem for MacTextSystem { text: &str, font_size: Pixels, font_runs: &[(usize, FontId)], - ) -> LineLayout { + ) -> ShapedLine { self.0.write().layout_line(text, font_size, font_runs) } @@ -348,7 +348,7 @@ impl MacTextSystemState { text: &str, font_size: Pixels, font_runs: &[(usize, FontId)], - ) -> LineLayout { + ) -> ShapedLine { // Construct the attributed string, converting UTF8 ranges to UTF16 ranges. let mut string = CFMutableAttributedString::new(); { @@ -409,7 +409,7 @@ impl MacTextSystemState { { let glyph_utf16_ix = usize::try_from(*glyph_utf16_ix).unwrap(); ix_converter.advance_to_utf16_ix(glyph_utf16_ix); - glyphs.push(Glyph { + glyphs.push(ShapedGlyph { id: (*glyph_id).into(), position: point(position.x as f32, position.y as f32).map(px), index: ix_converter.utf8_ix, @@ -417,11 +417,11 @@ impl MacTextSystemState { }); } - runs.push(Run { font_id, glyphs }) + runs.push(ShapedRun { font_id, glyphs }) } let typographic_bounds = line.get_typographic_bounds(); - LineLayout { + ShapedLine { width: typographic_bounds.width.into(), ascent: typographic_bounds.ascent.into(), descent: typographic_bounds.descent.into(), diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index 3cf471b45ae9f27ccfd00e66b58c619ab6388027..b6974ef17209429267115b26dfcc2d302cc1ba5c 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -1,10 +1,10 @@ use super::{ns_string, MetalRenderer, NSRange}; use crate::{ - point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers, - ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt, - Pixels, Platform, PlatformDispatcher, PlatformInputHandler, PlatformScreen, PlatformWindow, - Point, Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, - WindowPromptLevel, + point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, + MetalAtlas, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, + MouseUpEvent, NSRectExt, Pixels, Platform, PlatformAtlas, PlatformDispatcher, + PlatformInputHandler, PlatformScreen, PlatformWindow, Point, RasterizedGlyphId, Scene, Size, + Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel, }; use block::ConcreteBlock; use cocoa::{ @@ -20,6 +20,7 @@ use core_graphics::display::CGRect; use ctor::ctor; use foreign_types::ForeignTypeRef; use futures::channel::oneshot; +use metal::{MTLPixelFormat, TextureDescriptor}; use objc::{ class, declare::ClassDecl, @@ -885,6 +886,10 @@ impl PlatformWindow for MacWindow { let _: () = msg_send![this.native_window.contentView(), setNeedsDisplay: YES]; } } + + fn glyph_atlas(&self) -> Arc> { + self.0.lock().renderer.glyph_atlas().clone() + } } fn get_scale_factor(native_window: id) -> f32 { diff --git a/crates/gpui3/src/text_system.rs b/crates/gpui3/src/text_system.rs index 38bb1fded4a3f1936ecdd5a45127fbced8857f17..4d8a0e59fbc3d2d2e682ccd69065d8476bd47a34 100644 --- a/crates/gpui3/src/text_system.rs +++ b/crates/gpui3/src/text_system.rs @@ -346,28 +346,35 @@ impl From for GlyphId { } } -#[derive(Clone, Debug)] -pub struct Glyph { - pub id: GlyphId, - pub position: Point, - pub index: usize, - pub is_emoji: bool, -} - #[derive(Default, Debug)] -pub struct LineLayout { +pub struct ShapedLine { pub font_size: Pixels, pub width: Pixels, pub ascent: Pixels, pub descent: Pixels, - pub runs: Vec, + pub runs: Vec, pub len: usize, } #[derive(Debug)] -pub struct Run { +pub struct ShapedRun { pub font_id: FontId, - pub glyphs: Vec, + pub glyphs: Vec, +} + +#[derive(Clone, Debug)] +pub struct ShapedGlyph { + pub id: GlyphId, + pub position: Point, + pub index: usize, + pub is_emoji: bool, +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct RasterizedGlyphId { + font_id: FontId, + glyph_id: GlyphId, + font_size: Pixels, } #[derive(Clone, Debug, Eq, PartialEq, Hash)] diff --git a/crates/gpui3/src/text_system/line.rs b/crates/gpui3/src/text_system/line.rs index 14f1d96f54994a4a24f53f6042081aa35d453726..c7444192070c9ac286840082166fcc241fa2eca5 100644 --- a/crates/gpui3/src/text_system/line.rs +++ b/crates/gpui3/src/text_system/line.rs @@ -1,6 +1,6 @@ use crate::{ - black, point, px, Bounds, FontId, Hsla, Layout, LineLayout, Pixels, Point, Run, RunStyle, - ShapedBoundary, UnderlineStyle, WindowContext, + black, point, px, Bounds, FontId, Hsla, Layout, Pixels, Point, RunStyle, ShapedBoundary, + ShapedLine, ShapedRun, UnderlineStyle, WindowContext, }; use anyhow::Result; use smallvec::SmallVec; @@ -8,7 +8,7 @@ use std::sync::Arc; #[derive(Default, Debug, Clone)] pub struct Line { - layout: Arc, + layout: Arc, style_runs: SmallVec<[StyleRun; 32]>, } @@ -20,7 +20,7 @@ struct StyleRun { } impl Line { - pub fn new(layout: Arc, runs: &[(usize, RunStyle)]) -> Self { + pub fn new(layout: Arc, runs: &[(usize, RunStyle)]) -> Self { let mut style_runs = SmallVec::new(); for (len, style) in runs { style_runs.push(StyleRun { @@ -32,7 +32,7 @@ impl Line { Self { layout, style_runs } } - pub fn runs(&self) -> &[Run] { + pub fn runs(&self) -> &[ShapedRun] { &self.layout.runs } diff --git a/crates/gpui3/src/text_system/text_layout_cache.rs b/crates/gpui3/src/text_system/text_layout_cache.rs index 59cfd300eea82b102e21d8c4714aeaf00c01be9f..e02122478bc470504cb58cbcc89529c61bd88800 100644 --- a/crates/gpui3/src/text_system/text_layout_cache.rs +++ b/crates/gpui3/src/text_system/text_layout_cache.rs @@ -1,4 +1,4 @@ -use crate::{FontId, Glyph, LineLayout, Pixels, PlatformTextSystem, Run}; +use crate::{FontId, Pixels, PlatformTextSystem, ShapedGlyph, ShapedLine, ShapedRun}; use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard}; use smallvec::SmallVec; use std::{ @@ -9,8 +9,8 @@ use std::{ }; pub(crate) struct TextLayoutCache { - prev_frame: Mutex>>, - curr_frame: RwLock>>, + prev_frame: Mutex>>, + curr_frame: RwLock>>, platform_text_system: Arc, } @@ -35,7 +35,7 @@ impl TextLayoutCache { text: &'a str, font_size: Pixels, runs: &[(usize, FontId)], - ) -> Arc { + ) -> Arc { let key = &CacheKeyRef { text, font_size, @@ -146,8 +146,8 @@ pub struct ShapedBoundary { pub glyph_ix: usize, } -impl Run { - pub fn glyphs(&self) -> &[Glyph] { +impl ShapedRun { + pub fn glyphs(&self) -> &[ShapedGlyph] { &self.glyphs } } diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index b4a893cb5653752c9178dbf41c371a6017ef2e64..9fe5e3bc19bb80dd2623240e8f1d0fb72c934eb5 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,7 +1,8 @@ use crate::{ - px, AnyView, AppContext, AvailableSpace, Bounds, Context, Effect, Element, EntityId, Handle, - LayoutId, MainThread, MainThreadOnly, Pixels, PlatformWindow, Point, Reference, Scene, Size, - StackContext, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, + px, AnyView, AppContext, AvailableSpace, Bounds, Context, Effect, Element, EntityId, FontId, + GlyphId, Handle, LayoutId, MainThread, MainThreadOnly, Pixels, PlatformAtlas, PlatformWindow, + Point, RasterizedGlyphId, Reference, Scene, Size, StackContext, StackingOrder, Style, + TaffyLayoutEngine, WeakHandle, WindowOptions, }; use anyhow::Result; use futures::Future; @@ -14,6 +15,7 @@ pub struct AnyWindow {} pub struct Window { handle: AnyWindowHandle, platform_window: MainThreadOnly>, + glyph_atlas: Arc>, rem_size: Pixels, content_size: Size, layout_engine: TaffyLayoutEngine, @@ -31,6 +33,7 @@ impl Window { cx: &mut MainThread, ) -> Self { let platform_window = cx.platform().open_window(handle, options); + let glyph_atlas = platform_window.glyph_atlas(); let mouse_position = platform_window.mouse_position(); let content_size = platform_window.content_size(); let scale_factor = platform_window.scale_factor(); @@ -53,6 +56,7 @@ impl Window { Window { handle, platform_window, + glyph_atlas, rem_size: px(16.), content_size, layout_engine: TaffyLayoutEngine::new(), From e49b41120559ce8935fb3d384b66037273f79c3a Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 3 Oct 2023 16:30:41 +0200 Subject: [PATCH 05/28] Checkpoint --- crates/gpui3/src/platform.rs | 19 +-- crates/gpui3/src/platform/mac/metal_atlas.rs | 27 ++-- crates/gpui3/src/platform/mac/text_system.rs | 149 +++++++------------ crates/gpui3/src/text_system.rs | 34 ++++- crates/gpui3/src/text_system/line.rs | 4 +- crates/gpui3/src/window.rs | 42 +++++- 6 files changed, 148 insertions(+), 127 deletions(-) diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 1179a83618ab8420170490fe817d3f03da06333c..e7e7e4a4fb69c1893b68eb21615d8121343aafc2 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -6,8 +6,8 @@ mod mac; mod test; use crate::{ - AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, MonochromeSprite, - Pixels, Point, RasterizedGlyphId, Result, Scene, ShapedLine, SharedString, Size, + AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point, + RasterizedGlyphId, Result, Scene, ShapedLine, SharedString, Size, }; use anyhow::anyhow; use async_task::Runnable; @@ -165,13 +165,8 @@ pub trait PlatformTextSystem: Send + Sync { fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option; fn rasterize_glyph( &self, - font_id: FontId, - font_size: f32, - glyph_id: GlyphId, - subpixel_shift: Point, - scale_factor: f32, - options: RasterizationOptions, - ) -> Option<(Bounds, Vec)>; + glyph_id: &RasterizedGlyphId, + ) -> Result<(Bounds, Vec)>; fn layout_line(&self, text: &str, font_size: Pixels, runs: &[(usize, FontId)]) -> ShapedLine; fn wrap_line( &self, @@ -185,9 +180,9 @@ pub trait PlatformTextSystem: Send + Sync { pub trait PlatformAtlas: Send + Sync { fn get_or_insert_with( &self, - key: Key, - build: &dyn Fn() -> (Size, Vec), - ) -> AtlasTile; + key: &Key, + build: &mut dyn FnMut() -> Result<(Size, Vec)>, + ) -> Result; fn clear(&self); } diff --git a/crates/gpui3/src/platform/mac/metal_atlas.rs b/crates/gpui3/src/platform/mac/metal_atlas.rs index 96cb1a64ed5eb0b6d721d008e930598cd7ab89b2..4e3d661670bea7aeceefe1f844ef76c97e03a192 100644 --- a/crates/gpui3/src/platform/mac/metal_atlas.rs +++ b/crates/gpui3/src/platform/mac/metal_atlas.rs @@ -1,4 +1,5 @@ use crate::{AtlasTextureId, AtlasTile, Bounds, DevicePixels, PlatformAtlas, Point, Size}; +use anyhow::{anyhow, Result}; use collections::HashMap; use derive_more::{Deref, DerefMut}; use etagere::BucketedAtlasAllocator; @@ -38,28 +39,30 @@ struct MetalAtlasState { impl PlatformAtlas for MetalAtlas where - Key: Eq + Hash + Send, + Key: Clone + Eq + Hash + Send, { fn get_or_insert_with( &self, - key: Key, - build: &dyn Fn() -> (Size, Vec), - ) -> AtlasTile { + key: &Key, + build: &mut dyn FnMut() -> Result<(Size, Vec)>, + ) -> Result { let mut lock = self.0.lock(); - if let Some(tile) = lock.tiles_by_key.get(&key) { - return tile.clone(); + if let Some(tile) = lock.tiles_by_key.get(key) { + return Ok(tile.clone()); } else { - let (size, bytes) = build(); - lock.textures + let (size, bytes) = build()?; + let tile = lock + .textures .iter_mut() .rev() .find_map(|texture| texture.allocate(size, &bytes)) - .unwrap_or_else(|| { + .or_else(|| { let texture = lock.push_texture(size); - texture - .allocate(size, &bytes) - .expect("could not allocate a tile in new texture") + texture.allocate(size, &bytes) }) + .ok_or_else(|| anyhow!("could not allocate in new texture"))?; + lock.tiles_by_key.insert(key.clone(), tile.clone()); + Ok(tile) } } diff --git a/crates/gpui3/src/platform/mac/text_system.rs b/crates/gpui3/src/platform/mac/text_system.rs index 9f1ea8a78f82303e0c52201c6e0195763641994b..f28badbb629216f64a1109c7c28bc92bcd1a5e3a 100644 --- a/crates/gpui3/src/platform/mac/text_system.rs +++ b/crates/gpui3/src/platform/mac/text_system.rs @@ -1,8 +1,9 @@ use crate::{ - point, px, size, Bounds, Font, FontFeatures, FontId, FontMetrics, FontStyle, FontWeight, - GlyphId, Pixels, PlatformTextSystem, Point, RasterizationOptions, Result, ShapedGlyph, - ShapedLine, ShapedRun, SharedString, Size, + point, px, size, Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontStyle, + FontWeight, GlyphId, Pixels, PlatformTextSystem, Point, RasterizedGlyphId, Result, ShapedGlyph, + ShapedLine, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS, }; +use anyhow::anyhow; use cocoa::appkit::{CGFloat, CGPoint}; use collections::HashMap; use core_foundation::{ @@ -11,11 +12,7 @@ use core_foundation::{ base::{CFRange, TCFType}, string::CFString, }; -use core_graphics::{ - base::{kCGImageAlphaPremultipliedLast, CGGlyph}, - color_space::CGColorSpace, - context::CGContext, -}; +use core_graphics::{base::CGGlyph, color_space::CGColorSpace, context::CGContext}; use core_text::{font::CTFont, line::CTLine, string_attributes::kCTFontAttributeName}; use font_kit::{ font::Font as FontKitFont, @@ -139,21 +136,9 @@ impl PlatformTextSystem for MacTextSystem { fn rasterize_glyph( &self, - font_id: FontId, - font_size: f32, - glyph_id: GlyphId, - subpixel_shift: Point, - scale_factor: f32, - options: RasterizationOptions, - ) -> Option<(Bounds, Vec)> { - self.0.read().rasterize_glyph( - font_id, - font_size, - glyph_id, - subpixel_shift, - scale_factor, - options, - ) + glyph_id: &RasterizedGlyphId, + ) -> Result<(Bounds, Vec)> { + self.0.read().rasterize_glyph(glyph_id) } fn layout_line( @@ -247,63 +232,41 @@ impl MacTextSystemState { fn rasterize_glyph( &self, - font_id: FontId, - font_size: f32, - glyph_id: GlyphId, - subpixel_shift: Point, - scale_factor: f32, - options: RasterizationOptions, - ) -> Option<(Bounds, Vec)> { - let font = &self.fonts[font_id.0]; - let scale = Transform2F::from_scale(scale_factor); - let glyph_bounds = font - .raster_bounds( - glyph_id.into(), - font_size, - scale, - HintingOptions::None, - font_kit::canvas::RasterizationOptions::GrayscaleAa, - ) - .ok()?; + glyph_id: &RasterizedGlyphId, + ) -> Result<(Bounds, Vec)> { + let font = &self.fonts[glyph_id.font_id.0]; + let scale = Transform2F::from_scale(glyph_id.scale_factor); + let glyph_bounds = font.raster_bounds( + glyph_id.glyph_id.into(), + glyph_id.font_size.into(), + scale, + HintingOptions::None, + font_kit::canvas::RasterizationOptions::GrayscaleAa, + )?; if glyph_bounds.width() == 0 || glyph_bounds.height() == 0 { - None + Err(anyhow!("glyph bounds are empty")) } else { // Make room for subpixel variants. - let subpixel_padding = subpixel_shift.map(|v| f32::from(v).ceil() as u32); + let subpixel_padding = Vector2I::new( + glyph_id.subpixel_variant.x.min(1) as i32, + glyph_id.subpixel_variant.y.min(1) as i32, + ); let cx_bounds = RectI::new( glyph_bounds.origin(), - glyph_bounds.size() + Vector2I::from(subpixel_padding), + glyph_bounds.size() + subpixel_padding, ); - let mut bytes; - let cx; - match options { - RasterizationOptions::Alpha => { - bytes = vec![0; cx_bounds.width() as usize * cx_bounds.height() as usize]; - cx = CGContext::create_bitmap_context( - Some(bytes.as_mut_ptr() as *mut _), - cx_bounds.width() as usize, - cx_bounds.height() as usize, - 8, - cx_bounds.width() as usize, - &CGColorSpace::create_device_gray(), - kCGImageAlphaOnly, - ); - } - RasterizationOptions::Bgra => { - bytes = vec![0; cx_bounds.width() as usize * 4 * cx_bounds.height() as usize]; - cx = CGContext::create_bitmap_context( - Some(bytes.as_mut_ptr() as *mut _), - cx_bounds.width() as usize, - cx_bounds.height() as usize, - 8, - cx_bounds.width() as usize * 4, - &CGColorSpace::create_device_rgb(), - kCGImageAlphaPremultipliedLast, - ); - } - } + let mut bytes = vec![0; cx_bounds.width() as usize * cx_bounds.height() as usize]; + let cx = CGContext::create_bitmap_context( + Some(bytes.as_mut_ptr() as *mut _), + cx_bounds.width() as usize, + cx_bounds.height() as usize, + 8, + cx_bounds.width() as usize, + &CGColorSpace::create_device_gray(), + kCGImageAlphaOnly, + ); // Move the origin to bottom left and account for scaling, this // makes drawing text consistent with the font-kit's raster_bounds. @@ -311,35 +274,31 @@ impl MacTextSystemState { -glyph_bounds.origin_x() as CGFloat, (glyph_bounds.origin_y() + glyph_bounds.height()) as CGFloat, ); - cx.scale(scale_factor as CGFloat, scale_factor as CGFloat); + cx.scale( + glyph_id.scale_factor as CGFloat, + glyph_id.scale_factor as CGFloat, + ); + + let subpixel_shift = glyph_id + .subpixel_variant + .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32 / glyph_id.scale_factor); cx.set_allows_font_subpixel_positioning(true); cx.set_should_subpixel_position_fonts(true); cx.set_allows_font_subpixel_quantization(false); cx.set_should_subpixel_quantize_fonts(false); font.native_font() - .clone_with_font_size(font_size as CGFloat) + .clone_with_font_size(f32::from(glyph_id.font_size) as CGFloat) .draw_glyphs( - &[u32::from(glyph_id) as CGGlyph], + &[u32::from(glyph_id.glyph_id) as CGGlyph], &[CGPoint::new( - (f32::from(subpixel_shift.x) / scale_factor) as CGFloat, - (f32::from(subpixel_shift.y) / scale_factor) as CGFloat, + subpixel_shift.x as CGFloat, + subpixel_shift.y as CGFloat, )], cx, ); - if let RasterizationOptions::Bgra = options { - // Convert from RGBA with premultiplied alpha to BGRA with straight alpha. - for pixel in bytes.chunks_exact_mut(4) { - pixel.swap(0, 2); - let a = pixel[3] as f32 / 255.; - pixel[0] = (pixel[0] as f32 / a) as u8; - pixel[1] = (pixel[1] as f32 / a) as u8; - pixel[2] = (pixel[2] as f32 / a) as u8; - } - } - - Some((cx_bounds.into(), bytes)) + Ok((cx_bounds.into(), bytes)) } } @@ -549,11 +508,17 @@ impl From for Bounds { } } -impl From for Bounds { +impl From for Bounds { fn from(rect: RectI) -> Self { Bounds { - origin: point(rect.origin_x() as u32, rect.origin_y() as u32), - size: size(rect.width() as u32, rect.height() as u32), + origin: point( + DevicePixels(rect.origin_x() as u32), + DevicePixels(rect.origin_y() as u32), + ), + size: size( + DevicePixels(rect.width() as u32), + DevicePixels(rect.height() as u32), + ), } } } diff --git a/crates/gpui3/src/text_system.rs b/crates/gpui3/src/text_system.rs index 4d8a0e59fbc3d2d2e682ccd69065d8476bd47a34..84a6f54470537ee470ea0366d34cf681debf222f 100644 --- a/crates/gpui3/src/text_system.rs +++ b/crates/gpui3/src/text_system.rs @@ -11,7 +11,8 @@ use line_wrapper::*; pub use text_layout_cache::*; use crate::{ - px, Bounds, Hsla, Pixels, PlatformTextSystem, Point, Result, SharedString, Size, UnderlineStyle, + px, Bounds, DevicePixels, Hsla, Pixels, PlatformTextSystem, Point, RasterizationOptions, + Result, SharedString, Size, UnderlineStyle, }; use collections::HashMap; use core::fmt; @@ -30,6 +31,8 @@ pub struct FontId(pub usize); #[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)] pub struct FontFamilyId(pub usize); +pub const SUBPIXEL_VARIANTS: u8 = 4; + pub struct TextSystem { text_layout_cache: Arc, platform_text_system: Arc, @@ -212,6 +215,13 @@ impl TextSystem { text_system: self.clone(), }) } + + pub fn rasterize_glyph( + &self, + glyph_id: &RasterizedGlyphId, + ) -> Result<(Bounds, Vec)> { + self.platform_text_system.rasterize_glyph(glyph_id) + } } #[derive(Hash, Eq, PartialEq)] @@ -370,11 +380,25 @@ pub struct ShapedGlyph { pub is_emoji: bool, } -#[derive(Clone, Debug, Eq, PartialEq, Hash)] +#[derive(Clone, Debug, PartialEq)] pub struct RasterizedGlyphId { - font_id: FontId, - glyph_id: GlyphId, - font_size: Pixels, + pub(crate) font_id: FontId, + pub(crate) glyph_id: GlyphId, + pub(crate) font_size: Pixels, + pub(crate) subpixel_variant: Point, + pub(crate) scale_factor: f32, +} + +impl Eq for RasterizedGlyphId {} + +impl Hash for RasterizedGlyphId { + fn hash(&self, state: &mut H) { + self.font_id.0.hash(state); + self.glyph_id.0.hash(state); + self.font_size.0.to_bits().hash(state); + self.subpixel_variant.hash(state); + self.scale_factor.to_bits().hash(state); + } } #[derive(Clone, Debug, Eq, PartialEq, Hash)] diff --git a/crates/gpui3/src/text_system/line.rs b/crates/gpui3/src/text_system/line.rs index c7444192070c9ac286840082166fcc241fa2eca5..71ec13461f6d72966aea4b57f23c2b9e0b4d00c5 100644 --- a/crates/gpui3/src/text_system/line.rs +++ b/crates/gpui3/src/text_system/line.rs @@ -1,6 +1,6 @@ use crate::{ - black, point, px, Bounds, FontId, Hsla, Layout, Pixels, Point, RunStyle, ShapedBoundary, - ShapedLine, ShapedRun, UnderlineStyle, WindowContext, + black, point, px, Bounds, FontId, Hsla, Layout, Pixels, Point, RasterizedGlyphId, RunStyle, + ShapedBoundary, ShapedLine, ShapedRun, UnderlineStyle, WindowContext, }; use anyhow::Result; use smallvec::SmallVec; diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 9fe5e3bc19bb80dd2623240e8f1d0fb72c934eb5..97351e7869b617180e1ecb37b96a11e714826259 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,8 +1,9 @@ use crate::{ - px, AnyView, AppContext, AvailableSpace, Bounds, Context, Effect, Element, EntityId, FontId, - GlyphId, Handle, LayoutId, MainThread, MainThreadOnly, Pixels, PlatformAtlas, PlatformWindow, - Point, RasterizedGlyphId, Reference, Scene, Size, StackContext, StackingOrder, Style, - TaffyLayoutEngine, WeakHandle, WindowOptions, + px, AnyView, AppContext, AtlasTile, AvailableSpace, Bounds, Context, DevicePixels, Effect, + Element, EntityId, FontId, GlyphId, Handle, LayoutId, MainThread, MainThreadOnly, + MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, RasterizedGlyphId, Reference, + Scene, Size, StackContext, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, + SUBPIXEL_VARIANTS, }; use anyhow::Result; use futures::Future; @@ -161,6 +162,39 @@ impl<'a, 'w> WindowContext<'a, 'w> { }) } + pub fn rasterize_glyph( + &self, + font_id: FontId, + glyph_id: GlyphId, + font_size: Pixels, + scale_factor: f32, + target_position: Point, + ) -> Result<(AtlasTile, Point)> { + let target_position = target_position * scale_factor; + let subpixel_variant = Point { + x: (target_position.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, + y: (target_position.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, + }; + let rasterized_glyph_id = RasterizedGlyphId { + font_id, + glyph_id, + font_size, + subpixel_variant, + scale_factor, + }; + let mut offset = Default::default(); + let tile = self + .window + .glyph_atlas + .get_or_insert_with(&rasterized_glyph_id, &mut || { + let (bounds, pixels) = self.text_system().rasterize_glyph(&rasterized_glyph_id)?; + offset = bounds.origin; + Ok((bounds.size, pixels)) + })?; + + Ok((tile, offset)) + } + pub(crate) fn draw(&mut self) -> Result<()> { let unit_entity = self.unit_entity.clone(); self.update_entity(&unit_entity, |_, cx| { From 8a58733d9177b64939e2a7e42d06ffe826b7d6e2 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 3 Oct 2023 16:53:49 +0200 Subject: [PATCH 06/28] Checkpoint --- crates/gpui3/src/geometry.rs | 4 ++++ crates/gpui3/src/scene.rs | 6 +++--- crates/gpui3/src/text_system/line.rs | 29 +++++++++++++++++++++++++--- crates/gpui3/src/window.rs | 27 ++++++++++++++++++-------- 4 files changed, 52 insertions(+), 14 deletions(-) diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index 7cffe4b59bac1881954ce359424efc3b4bfea730..5f92c927bdeca0e57824a84c8c06532f140033bb 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -478,6 +478,10 @@ impl Pixels { Self(self.0.round()) } + pub fn floor(&self) -> Self { + Self(self.0.floor()) + } + pub fn to_device_pixels(&self, scale: f32) -> DevicePixels { DevicePixels((self.0 * scale).ceil() as u32) } diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index f3e26f01973046d24a4c067ed42d4229f7f00dfb..00882097916b096b3b9210f715cd01d8da9873d7 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -10,7 +10,7 @@ pub type StackingOrder = SmallVec<[u32; 16]>; #[derive(Debug)] pub struct Scene { - scale_factor: f32, + pub(crate) scale_factor: f32, pub(crate) layers: BTreeMap, } @@ -29,8 +29,8 @@ impl Scene { } } - pub fn insert(&mut self, order: StackingOrder, primitive: impl Into) { - let layer = self.layers.entry(order).or_default(); + pub fn insert(&mut self, stacking_order: StackingOrder, primitive: impl Into) { + let layer = self.layers.entry(stacking_order).or_default(); let primitive = primitive.into(); match primitive { diff --git a/crates/gpui3/src/text_system/line.rs b/crates/gpui3/src/text_system/line.rs index 71ec13461f6d72966aea4b57f23c2b9e0b4d00c5..98b0abcdbd774e51c1c0a3ea23360a55eca594e6 100644 --- a/crates/gpui3/src/text_system/line.rs +++ b/crates/gpui3/src/text_system/line.rs @@ -1,10 +1,12 @@ use crate::{ - black, point, px, Bounds, FontId, Hsla, Layout, Pixels, Point, RasterizedGlyphId, RunStyle, - ShapedBoundary, ShapedLine, ShapedRun, UnderlineStyle, WindowContext, + black, point, px, Bounds, FontId, Hsla, Layout, MonochromeSprite, Pixels, Point, + RasterizedGlyphId, RunStyle, ShapedBoundary, ShapedLine, ShapedRun, UnderlineStyle, + WindowContext, }; use anyhow::Result; use smallvec::SmallVec; use std::sync::Arc; +use util::ResultExt; #[derive(Default, Debug, Clone)] pub struct Line { @@ -174,7 +176,28 @@ impl Line { // origin: glyph_origin, // }); } else { - todo!() + if let Some((tile, bounds)) = cx + .rasterize_glyph( + run.font_id, + glyph.id, + self.layout.font_size, + cx.scale_factor(), + glyph_origin, + ) + .log_err() + { + let layer_id = cx.current_layer_id(); + cx.scene().insert( + layer_id, + MonochromeSprite { + order: layout.order, + bounds, + color, + tile, + }, + ); + } + // cx.scene().insert(Symbol { // order: layout.order, // origin, diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 97351e7869b617180e1ecb37b96a11e714826259..de518fb93fe25876bedac379e9fa37a7e0b70011 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -22,7 +22,7 @@ pub struct Window { layout_engine: TaffyLayoutEngine, pub(crate) root_view: Option>, mouse_position: Point, - current_stacking_order: StackingOrder, + current_layer_id: StackingOrder, pub(crate) scene: Scene, pub(crate) dirty: bool, } @@ -63,7 +63,7 @@ impl Window { layout_engine: TaffyLayoutEngine::new(), root_view: None, mouse_position, - current_stacking_order: SmallVec::new(), + current_layer_id: SmallVec::new(), scene: Scene::new(scale_factor), dirty: true, } @@ -122,6 +122,10 @@ impl<'a, 'w> WindowContext<'a, 'w> { .map(Into::into)?) } + pub fn scale_factor(&self) -> f32 { + self.window.scene.scale_factor + } + pub fn rem_size(&self) -> Pixels { self.window.rem_size } @@ -135,14 +139,14 @@ impl<'a, 'w> WindowContext<'a, 'w> { } pub fn stack(&mut self, order: u32, f: impl FnOnce(&mut Self) -> R) -> R { - self.window.current_stacking_order.push(order); + self.window.current_layer_id.push(order); let result = f(self); - self.window.current_stacking_order.pop(); + self.window.current_layer_id.pop(); result } - pub fn current_stack_order(&self) -> StackingOrder { - self.window.current_stacking_order.clone() + pub fn current_layer_id(&self) -> StackingOrder { + self.window.current_layer_id.clone() } pub fn run_on_main( @@ -169,7 +173,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { font_size: Pixels, scale_factor: f32, target_position: Point, - ) -> Result<(AtlasTile, Point)> { + ) -> Result<(AtlasTile, Bounds)> { let target_position = target_position * scale_factor; let subpixel_variant = Point { x: (target_position.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, @@ -192,7 +196,14 @@ impl<'a, 'w> WindowContext<'a, 'w> { Ok((bounds.size, pixels)) })?; - Ok((tile, offset)) + // Align bounding box surrounding glyph to pixel grid + let mut origin = (target_position * scale_factor).map(|p| p.floor()); + // Position glyph within bounding box + origin += offset.map(|o| px(u32::from(o) as f32)); + let size = tile.bounds_in_atlas.size.map(|b| px(b.0 as f32)); + let bounds = Bounds { origin, size }; + + Ok((tile, bounds)) } pub(crate) fn draw(&mut self) -> Result<()> { From 2b6d041cb62ebdb5a0f3ff0d5e7b223be731e917 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 3 Oct 2023 17:36:12 +0200 Subject: [PATCH 07/28] Checkpoint --- crates/gpui3/src/geometry.rs | 6 +- crates/gpui3/src/platform.rs | 27 ++- crates/gpui3/src/platform/mac/metal_atlas.rs | 2 +- .../gpui3/src/platform/mac/metal_renderer.rs | 10 +- crates/gpui3/src/scene.rs | 160 +++++++++--------- crates/gpui3/src/style.rs | 3 +- 6 files changed, 121 insertions(+), 87 deletions(-) diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index 5f92c927bdeca0e57824a84c8c06532f140033bb..87b11a6cb3984fa80bfaa13ea62b11ece61798cc 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -206,7 +206,7 @@ impl Size { } } -#[derive(Refineable, Clone, Default, Debug, PartialEq)] +#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)] #[refineable(debug)] #[repr(C)] pub struct Bounds { @@ -289,7 +289,7 @@ impl> Bounds { impl Copy for Bounds {} -#[derive(Refineable, Clone, Default, Debug)] +#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)] #[refineable(debug)] #[repr(C)] pub struct Edges { @@ -396,7 +396,7 @@ impl Edges { } } -#[derive(Refineable, Clone, Default, Debug)] +#[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)] #[refineable(debug)] #[repr(C)] pub struct Corners { diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index e7e7e4a4fb69c1893b68eb21615d8121343aafc2..2c81cb4fbba8cf94b9a4af203cc06aae5d130b46 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -14,6 +14,7 @@ use async_task::Runnable; use futures::channel::oneshot; use seahash::SeaHasher; use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; use std::ffi::c_void; use std::hash::{Hash, Hasher}; use std::{ @@ -187,7 +188,7 @@ pub trait PlatformAtlas: Send + Sync { fn clear(&self); } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] #[repr(C)] pub struct AtlasTile { pub(crate) texture_id: AtlasTextureId, @@ -195,11 +196,31 @@ pub struct AtlasTile { pub(crate) bounds_in_atlas: Bounds, } -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(C)] pub(crate) struct AtlasTextureId(pub(crate) usize); -pub(crate) type TileId = etagere::AllocId; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(C)] +pub(crate) struct TileId(pub(crate) etagere::AllocId); + +impl From for TileId { + fn from(id: etagere::AllocId) -> Self { + Self(id) + } +} + +impl Ord for TileId { + fn cmp(&self, other: &Self) -> Ordering { + self.0.serialize().cmp(&other.0.serialize()) + } +} + +impl PartialOrd for TileId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} pub trait PlatformInputHandler { fn selected_text_range(&self) -> Option>; diff --git a/crates/gpui3/src/platform/mac/metal_atlas.rs b/crates/gpui3/src/platform/mac/metal_atlas.rs index 4e3d661670bea7aeceefe1f844ef76c97e03a192..db1347e5572e3aa65cb944b15249ba80a29520dc 100644 --- a/crates/gpui3/src/platform/mac/metal_atlas.rs +++ b/crates/gpui3/src/platform/mac/metal_atlas.rs @@ -119,7 +119,7 @@ impl MetalAtlasTexture { let allocation = self.allocator.allocate(size)?; let tile = AtlasTile { texture_id: self.id, - tile_id: allocation.id, + tile_id: allocation.id.into(), bounds_in_atlas: allocation.rectangle.into(), }; let region = metal::MTLRegion::new_2d( diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index 56c0f130810f32f4eed3cb0605f3a9fb4737e718..a7d81e24afad850193bbf4f3dd4bb2471aebde97 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -1,5 +1,6 @@ use crate::{ - point, size, DevicePixels, MetalAtlas, MonochromeSprite, Quad, RasterizedGlyphId, Scene, Size, + point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, Quad, + RasterizedGlyphId, Scene, Size, }; use bytemuck::{Pod, Zeroable}; use cocoa::{ @@ -186,8 +187,12 @@ impl MetalRenderer { command_encoder, ); } - crate::PrimitiveBatch::Sprites(sprites) => { + crate::PrimitiveBatch::Sprites { + texture_id, + sprites, + } => { self.draw_monochrome_sprites( + texture_id, sprites, &mut instance_offset, viewport_size, @@ -270,6 +275,7 @@ impl MetalRenderer { fn draw_monochrome_sprites( &mut self, + texture_id: AtlasTextureId, monochrome: &[MonochromeSprite], offset: &mut usize, viewport_size: Size, diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index 00882097916b096b3b9210f715cd01d8da9873d7..8ccbd45ff350094dfe11b636abd67acef3837f3a 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -1,8 +1,10 @@ use std::{iter::Peekable, mem}; use super::{Bounds, Hsla, Pixels, Point}; -use crate::{AtlasTile, Corners, DevicePixels, Edges}; +use crate::{AtlasTextureId, AtlasTile, Corners, Edges}; use bytemuck::{Pod, Zeroable}; +use collections::BTreeMap; +use smallvec::SmallVec; // Exported to metal pub type PointF = Point; @@ -38,8 +40,7 @@ impl Scene { quad.scale(self.scale_factor); layer.quads.push(quad); } - Primitive::Sprite(mut sprite) => { - sprite.scale(self.scale_factor); + Primitive::Sprite(sprite) => { layer.sprites.push(sprite); } } @@ -58,8 +59,8 @@ pub(crate) struct SceneLayer { impl SceneLayer { pub fn batches(&mut self) -> impl Iterator { - self.quads.sort_unstable_by(|a, b| a.order.cmp(&b.order)); - self.sprites.sort_unstable_by(|a, b| a.order.cmp(&b.order)); + self.quads.sort_unstable(); + self.sprites.sort_unstable(); BatchIterator::new( &self.quads, @@ -75,7 +76,6 @@ where Q: Iterator, S: Iterator, { - next_batch_kind: Option, quads: &'a [Quad], sprites: &'a [MonochromeSprite], quads_start: usize, @@ -92,41 +92,52 @@ where type Item = PrimitiveBatch<'a>; fn next(&mut self) -> Option { - if let Some(batch_kind) = self.next_batch_kind.take() { - match batch_kind { - PrimitiveKind::Quad => { - let max_order = self - .next_order(Some(PrimitiveKind::Quad)) - .unwrap_or(u32::MAX); - let quads_start = self.quads_start; - let quads_end = quads_start - + self - .quads_iter - .by_ref() - .take_while(|quad| quad.order <= max_order) - .count(); - self.quads_start = quads_end; - Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end])) - } - PrimitiveKind::Sprite => { - let max_order = self - .next_order(Some(PrimitiveKind::Sprite)) - .unwrap_or(u32::MAX); - let sprites_start = self.sprites_start; - let sprites_end = sprites_start - + self - .sprites_iter - .by_ref() - .take_while(|sprite| sprite.order <= max_order) - .count(); - self.sprites_start = sprites_end; - Some(PrimitiveBatch::Sprites( - &self.sprites[sprites_start..sprites_end], - )) - } - } + let mut kinds_and_orders = [ + (PrimitiveKind::Quad, self.quads_iter.peek().map(|q| q.order)), + ( + PrimitiveKind::Sprite, + self.sprites_iter.peek().map(|s| s.order), + ), + ]; + kinds_and_orders.sort_by_key(|(_, order)| order.unwrap_or(u32::MAX)); + + let first = kinds_and_orders[0]; + let second = kinds_and_orders[1]; + let (batch_kind, max_order) = if first.1.is_some() { + (first.0, second.1.unwrap_or(u32::MAX)) } else { - None + return None; + }; + + match batch_kind { + PrimitiveKind::Quad => { + let quads_start = self.quads_start; + let quads_end = quads_start + + self + .quads_iter + .by_ref() + .take_while(|quad| quad.order <= max_order) + .count(); + self.quads_start = quads_end; + Some(PrimitiveBatch::Quads(&self.quads[quads_start..quads_end])) + } + PrimitiveKind::Sprite => { + let texture_id = self.sprites_iter.peek().unwrap().tile.texture_id; + let sprites_start = self.sprites_start; + let sprites_end = sprites_start + + self + .sprites_iter + .by_ref() + .take_while(|sprite| { + sprite.order <= max_order && sprite.tile.texture_id == texture_id + }) + .count(); + self.sprites_start = sprites_end; + Some(PrimitiveBatch::Sprites { + texture_id, + sprites: &self.sprites[sprites_start..sprites_end], + }) + } } } } @@ -142,39 +153,14 @@ where sprites: &'a [MonochromeSprite], sprites_iter: Peekable, ) -> Self { - let mut this = Self { + Self { quads, quads_start: 0, quads_iter, sprites, sprites_start: 0, sprites_iter, - next_batch_kind: None, - }; - this.next_order(None); // Called for its side effect of setting this.next_batch_kind - this - } - - fn next_order(&mut self, exclude_kind: Option) -> Option { - let mut next_order = u32::MAX; - - if exclude_kind != Some(PrimitiveKind::Quad) { - if let Some(next_quad) = self.quads_iter.peek() { - self.next_batch_kind = Some(PrimitiveKind::Quad); - next_order = next_quad.order; - } } - - if exclude_kind != Some(PrimitiveKind::Sprite) { - if let Some(next_sprite) = self.sprites_iter.peek() { - if next_sprite.order < next_order { - self.next_batch_kind = Some(PrimitiveKind::Sprite); - next_order = next_sprite.order; - } - } - } - - (next_order < u32::MAX).then_some(next_order) } } @@ -190,12 +176,15 @@ pub enum Primitive { Sprite(MonochromeSprite), } -pub enum PrimitiveBatch<'a> { +pub(crate) enum PrimitiveBatch<'a> { Quads(&'a [Quad]), - Sprites(&'a [MonochromeSprite]), + Sprites { + texture_id: AtlasTextureId, + sprites: &'a [MonochromeSprite], + }, } -#[derive(Debug, Copy, Clone, Zeroable, Pod)] +#[derive(Debug, Copy, Clone, Zeroable, Pod, Eq, PartialEq)] #[repr(C)] pub struct Quad { pub order: u32, @@ -232,13 +221,25 @@ impl Quad { } } +impl Ord for Quad { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.order.cmp(&other.order) + } +} + +impl PartialOrd for Quad { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + impl From for Primitive { fn from(quad: Quad) -> Self { Primitive::Quad(quad) } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] #[repr(C)] pub struct MonochromeSprite { pub order: u32, @@ -247,9 +248,18 @@ pub struct MonochromeSprite { pub tile: AtlasTile, } -impl MonochromeSprite { - pub fn scale(&mut self, factor: f32) { - self.bounds *= factor; +impl Ord for MonochromeSprite { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + match self.order.cmp(&other.order) { + std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id), + order => order, + } + } +} + +impl PartialOrd for MonochromeSprite { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) } } @@ -261,7 +271,3 @@ impl From for Primitive { #[derive(Copy, Clone, Debug)] pub struct AtlasId(pub(crate) usize); - -use collections::BTreeMap; -use etagere::AllocId as TileId; -use smallvec::SmallVec; diff --git a/crates/gpui3/src/style.rs b/crates/gpui3/src/style.rs index f972b2ead3a436504962930b666ae0375466ea10..aae76349c6e92811b59ff0caaae9ad28f7be43b9 100644 --- a/crates/gpui3/src/style.rs +++ b/crates/gpui3/src/style.rs @@ -185,8 +185,9 @@ impl Style { let background_color = self.fill.as_ref().and_then(Fill::color); if background_color.is_some() || self.is_border_visible() { + let layer_id = cx.current_layer_id(); cx.scene().insert( - todo!(), + layer_id, Quad { order, bounds, From dc40ac854a8d574aecbb90a888dbd126cc0a2fed Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 3 Oct 2023 17:53:08 +0200 Subject: [PATCH 08/28] WIP --- crates/gpui3/build.rs | 3 +++ crates/gpui3/src/platform.rs | 20 +++++++------------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/crates/gpui3/build.rs b/crates/gpui3/build.rs index af63512b8a2b6c8d619291796243b45ee0f57894..138cef6b83f2d0474c36290c00e662725b829163 100644 --- a/crates/gpui3/build.rs +++ b/crates/gpui3/build.rs @@ -48,6 +48,8 @@ fn generate_shader_bindings() -> PathBuf { "Quad".into(), "QuadInputIndex".into(), "QuadUniforms".into(), + "AtlasTile".into(), + "MonochromeSprite".into(), ]); config.no_includes = true; config.enumeration.prefix_with_name = true; @@ -55,6 +57,7 @@ fn generate_shader_bindings() -> PathBuf { .with_src(crate_dir.join("src/scene.rs")) .with_src(crate_dir.join("src/geometry.rs")) .with_src(crate_dir.join("src/color.rs")) + .with_src(crate_dir.join("src/platform.rs")) .with_src(crate_dir.join("src/platform/mac/metal_renderer.rs")) .with_config(config) .generate() diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 2c81cb4fbba8cf94b9a4af203cc06aae5d130b46..e27a222e38414da322ae25a1ef9965ad201638d5 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -196,29 +196,23 @@ pub struct AtlasTile { pub(crate) bounds_in_atlas: Bounds, } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Zeroable, Pod)] #[repr(C)] pub(crate) struct AtlasTextureId(pub(crate) usize); -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Zeroable, Pod)] #[repr(C)] -pub(crate) struct TileId(pub(crate) etagere::AllocId); +pub(crate) struct TileId(pub(crate) u32); impl From for TileId { fn from(id: etagere::AllocId) -> Self { - Self(id) + Self(id.serialize()) } } -impl Ord for TileId { - fn cmp(&self, other: &Self) -> Ordering { - self.0.serialize().cmp(&other.0.serialize()) - } -} - -impl PartialOrd for TileId { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) +impl From for etagere::AllocId { + fn from(id: TileId) -> Self { + Self::deserialize(id.0) } } From bfa211fb02614e2ce485151d97018c80b85c3d0c Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Tue, 3 Oct 2023 18:04:45 +0200 Subject: [PATCH 09/28] Checkpoint --- Cargo.lock | 15 ----------- crates/gpui3/Cargo.toml | 1 - crates/gpui3/src/color.rs | 3 +-- crates/gpui3/src/geometry.rs | 25 +------------------ crates/gpui3/src/platform.rs | 5 ++-- .../gpui3/src/platform/mac/metal_renderer.rs | 14 +++++------ crates/gpui3/src/scene.rs | 3 +-- crates/gpui3/src/text_system.rs | 5 ++-- 8 files changed, 13 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8aa299305f39c7b19e17d0ce2b788e6e0a81110..8062731144a9656c0fba03c045bcf7a41e90db87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1050,20 +1050,6 @@ name = "bytemuck" version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6" -dependencies = [ - "bytemuck_derive", -] - -[[package]] -name = "bytemuck_derive" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.29", -] [[package]] name = "byteorder" @@ -3256,7 +3242,6 @@ dependencies = [ "bindgen 0.65.1", "bitflags 2.4.0", "block", - "bytemuck", "cbindgen", "cocoa", "collections", diff --git a/crates/gpui3/Cargo.toml b/crates/gpui3/Cargo.toml index 9960e222c4071e715855b943155252a09d84cd73..855ce49d9d00f1b24e160aa637693a8b3c233a6e 100644 --- a/crates/gpui3/Cargo.toml +++ b/crates/gpui3/Cargo.toml @@ -55,7 +55,6 @@ usvg = { version = "0.14", features = [] } uuid = { version = "1.1.2", features = ["v4"] } waker-fn = "1.1.0" slotmap = "1.0.6" -bytemuck = { version = "1.14.0", features = ["derive"] } schemars.workspace = true plane-split = "0.18.0" bitflags = "2.4.0" diff --git a/crates/gpui3/src/color.rs b/crates/gpui3/src/color.rs index 052ff8a6217b4dfc670716b217961c187d999d3a..58bbcb5db2af506ae97bc196e685d859f1e37b14 100644 --- a/crates/gpui3/src/color.rs +++ b/crates/gpui3/src/color.rs @@ -1,6 +1,5 @@ #![allow(dead_code)] -use bytemuck::{Pod, Zeroable}; use serde::de::{self, Deserialize, Deserializer, Visitor}; use std::fmt; use std::num::ParseIntError; @@ -118,7 +117,7 @@ impl TryFrom<&'_ str> for Rgba { } } -#[derive(Default, Copy, Clone, Debug, PartialEq, Zeroable, Pod)] +#[derive(Default, Copy, Clone, Debug, PartialEq)] #[repr(C)] pub struct Hsla { pub h: f32, diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index 87b11a6cb3984fa80bfaa13ea62b11ece61798cc..951b1125cd964e6eda1d9330df3fba450c43733f 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -1,4 +1,3 @@ -use bytemuck::{Pod, Zeroable}; use core::fmt::Debug; use derive_more::{Add, AddAssign, Div, Mul, Sub, SubAssign}; use refineable::Refineable; @@ -102,9 +101,6 @@ impl Clone for Point { } } -unsafe impl Zeroable for Point {} -unsafe impl Pod for Point {} - #[derive(Refineable, Default, Clone, Copy, Debug, PartialEq, Div, Hash)] #[refineable(debug)] #[repr(C)] @@ -113,9 +109,6 @@ pub struct Size { pub height: T, } -unsafe impl Zeroable for Size {} -unsafe impl Pod for Size {} - pub fn size(width: T, height: T) -> Size { Size { width, height } } @@ -214,9 +207,6 @@ pub struct Bounds { pub size: Size, } -unsafe impl Zeroable for Bounds {} -unsafe impl Pod for Bounds {} - // Bounds * Pixels = Bounds impl Mul for Bounds where @@ -323,10 +313,6 @@ impl, S: Clone> MulAssign for Edges impl Copy for Edges {} -unsafe impl Zeroable for Edges {} - -unsafe impl Pod for Edges {} - impl Edges { pub fn map U>(&self, f: F) -> Edges { Edges { @@ -441,13 +427,7 @@ impl, S: Clone> MulAssign for Corners Copy for Corners {} -unsafe impl Zeroable for Corners {} - -unsafe impl Pod for Corners {} - -#[derive( - Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd, Zeroable, Pod, -)] +#[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd)] #[repr(transparent)] pub struct Pixels(pub(crate) f32); @@ -570,9 +550,6 @@ impl DevicePixels { } } -unsafe impl bytemuck::Pod for DevicePixels {} -unsafe impl bytemuck::Zeroable for DevicePixels {} - impl From for u32 { fn from(device_pixels: DevicePixels) -> Self { device_pixels.0 diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index e27a222e38414da322ae25a1ef9965ad201638d5..a86b2459e0c65e9e266f40f2f9c4eb662bbfee79 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -14,7 +14,6 @@ use async_task::Runnable; use futures::channel::oneshot; use seahash::SeaHasher; use serde::{Deserialize, Serialize}; -use std::cmp::Ordering; use std::ffi::c_void; use std::hash::{Hash, Hasher}; use std::{ @@ -196,11 +195,11 @@ pub struct AtlasTile { pub(crate) bounds_in_atlas: Bounds, } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Zeroable, Pod)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(C)] pub(crate) struct AtlasTextureId(pub(crate) usize); -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Zeroable, Pod)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] #[repr(C)] pub(crate) struct TileId(pub(crate) u32); diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index a7d81e24afad850193bbf4f3dd4bb2471aebde97..27d47054e9c265b8df8c90aac432e340fc697ca2 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -2,7 +2,6 @@ use crate::{ point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, Quad, RasterizedGlyphId, Scene, Size, }; -use bytemuck::{Pod, Zeroable}; use cocoa::{ base::{NO, YES}, foundation::NSUInteger, @@ -245,20 +244,19 @@ impl MetalRenderer { ); let quad_uniforms = QuadUniforms { viewport_size }; - let quad_uniform_bytes = bytemuck::bytes_of(&quad_uniforms); command_encoder.set_vertex_bytes( QuadInputIndex::Uniforms as u64, - quad_uniform_bytes.len() as u64, - quad_uniform_bytes.as_ptr() as *const c_void, + mem::size_of_val(&quad_uniforms) as u64, + &quad_uniforms as *const QuadUniforms as *const _, ); - let quad_bytes = bytemuck::cast_slice(quads); + let quad_bytes_len = mem::size_of::() * quads.len(); let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) }; unsafe { - ptr::copy_nonoverlapping(quad_bytes.as_ptr(), buffer_contents, quad_bytes.len()); + ptr::copy_nonoverlapping(quads.as_ptr() as *const u8, buffer_contents, quad_bytes_len); } - let next_offset = *offset + quad_bytes.len(); + let next_offset = *offset + quad_bytes_len; assert!( next_offset <= INSTANCE_BUFFER_SIZE, "instance buffer exhausted" @@ -332,7 +330,7 @@ enum QuadInputIndex { Uniforms = 2, } -#[derive(Debug, Clone, Copy, Zeroable, Pod)] +#[derive(Debug, Clone, Copy)] #[repr(C)] pub(crate) struct QuadUniforms { viewport_size: Size, diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index 8ccbd45ff350094dfe11b636abd67acef3837f3a..5ca3f14c0ae482f40df26cf9c174fe765dee2ee1 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -2,7 +2,6 @@ use std::{iter::Peekable, mem}; use super::{Bounds, Hsla, Pixels, Point}; use crate::{AtlasTextureId, AtlasTile, Corners, Edges}; -use bytemuck::{Pod, Zeroable}; use collections::BTreeMap; use smallvec::SmallVec; @@ -184,7 +183,7 @@ pub(crate) enum PrimitiveBatch<'a> { }, } -#[derive(Debug, Copy, Clone, Zeroable, Pod, Eq, PartialEq)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(C)] pub struct Quad { pub order: u32, diff --git a/crates/gpui3/src/text_system.rs b/crates/gpui3/src/text_system.rs index 84a6f54470537ee470ea0366d34cf681debf222f..ba5cadba984fd66d424ede5254b53226a5730f29 100644 --- a/crates/gpui3/src/text_system.rs +++ b/crates/gpui3/src/text_system.rs @@ -4,7 +4,6 @@ mod line_wrapper; mod text_layout_cache; use anyhow::anyhow; -use bytemuck::{Pod, Zeroable}; pub use font_features::*; pub use line::*; use line_wrapper::*; @@ -24,7 +23,7 @@ use std::{ sync::Arc, }; -#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug, Zeroable, Pod)] +#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)] #[repr(C)] pub struct FontId(pub usize); @@ -334,7 +333,7 @@ pub struct RunStyle { pub underline: Option, } -#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Zeroable, Pod)] +#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] #[repr(C)] pub struct GlyphId(u32); From 3698e89b88c68dfc96c7a155c7f9ae698196640d Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 3 Oct 2023 11:16:42 -0600 Subject: [PATCH 10/28] Checkpoint --- crates/gpui3/build.rs | 5 +- crates/gpui3/src/platform.rs | 4 +- crates/gpui3/src/platform/mac/metal_atlas.rs | 19 +- .../gpui3/src/platform/mac/metal_renderer.rs | 20 ++- crates/gpui3/src/platform/mac/shaders.metal | 170 ++++++++++++------ crates/gpui3/src/platform/mac/window.rs | 11 +- crates/gpui3/src/scene.rs | 2 + crates/gpui3/src/text_system.rs | 4 +- crates/gpui3/src/text_system/line.rs | 16 +- crates/gpui3/src/window.rs | 2 +- 10 files changed, 151 insertions(+), 102 deletions(-) diff --git a/crates/gpui3/build.rs b/crates/gpui3/build.rs index 138cef6b83f2d0474c36290c00e662725b829163..64d14ee28536c9e62e7ac9569563f401ad175f90 100644 --- a/crates/gpui3/build.rs +++ b/crates/gpui3/build.rs @@ -45,11 +45,12 @@ fn generate_shader_bindings() -> PathBuf { "Pixels".into(), "PointF".into(), "Hsla".into(), + "Uniforms".into(), + "AtlasTile".into(), "Quad".into(), "QuadInputIndex".into(), - "QuadUniforms".into(), - "AtlasTile".into(), "MonochromeSprite".into(), + "MonochromeSpriteInputIndex".into(), ]); config.no_includes = true; config.enumeration.prefix_with_name = true; diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index a86b2459e0c65e9e266f40f2f9c4eb662bbfee79..5e5127fcf15cd0bcd692b13803fbf930f59e08b7 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -192,12 +192,12 @@ pub trait PlatformAtlas: Send + Sync { pub struct AtlasTile { pub(crate) texture_id: AtlasTextureId, pub(crate) tile_id: TileId, - pub(crate) bounds_in_atlas: Bounds, + pub(crate) bounds: Bounds, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(C)] -pub(crate) struct AtlasTextureId(pub(crate) usize); +pub(crate) struct AtlasTextureId(pub(crate) u32); #[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 db1347e5572e3aa65cb944b15249ba80a29520dc..6924acf52ed175d62a222d773031b5c8a7a6be86 100644 --- a/crates/gpui3/src/platform/mac/metal_atlas.rs +++ b/crates/gpui3/src/platform/mac/metal_atlas.rs @@ -98,7 +98,7 @@ impl MetalAtlasState { } let atlas_texture = MetalAtlasTexture { - id: AtlasTextureId(self.textures.len()), + id: AtlasTextureId(self.textures.len() as u32), allocator: etagere::BucketedAtlasAllocator::new(size.into()), metal_texture: AssertSend(metal_texture), }; @@ -120,24 +120,19 @@ impl MetalAtlasTexture { let tile = AtlasTile { texture_id: self.id, tile_id: allocation.id.into(), - bounds_in_atlas: allocation.rectangle.into(), + bounds: allocation.rectangle.into(), }; let region = metal::MTLRegion::new_2d( - u32::from(tile.bounds_in_atlas.origin.x) as u64, - u32::from(tile.bounds_in_atlas.origin.y) as u64, - u32::from(tile.bounds_in_atlas.size.width) as u64, - u32::from(tile.bounds_in_atlas.size.height) as u64, + u32::from(tile.bounds.origin.x) as u64, + u32::from(tile.bounds.origin.y) as u64, + u32::from(tile.bounds.size.width) as u64, + u32::from(tile.bounds.size.height) as u64, ); self.metal_texture.replace_region( region, 0, bytes.as_ptr() as *const _, - u32::from( - tile.bounds_in_atlas - .size - .width - .to_bytes(self.bytes_per_pixel()), - ) as u64, + u32::from(tile.bounds.size.width.to_bytes(self.bytes_per_pixel())) as u64, ); Some(tile) } diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index 27d47054e9c265b8df8c90aac432e340fc697ca2..19b5374e585c53ed3736e91011090d6c8e28dae7 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -242,12 +242,11 @@ impl MetalRenderer { Some(&self.instances), *offset as u64, ); - let quad_uniforms = QuadUniforms { viewport_size }; command_encoder.set_vertex_bytes( - QuadInputIndex::Uniforms as u64, - mem::size_of_val(&quad_uniforms) as u64, - &quad_uniforms as *const QuadUniforms as *const _, + QuadInputIndex::ViewportSize as u64, + mem::size_of_val(&viewport_size) as u64, + &viewport_size as *const Size as *const _, ); let quad_bytes_len = mem::size_of::() * quads.len(); @@ -279,7 +278,7 @@ impl MetalRenderer { viewport_size: Size, command_encoder: &metal::RenderCommandEncoderRef, ) { - todo!() + // todo!() } } @@ -327,11 +326,14 @@ fn align_offset(offset: &mut usize) { enum QuadInputIndex { Vertices = 0, Quads = 1, - Uniforms = 2, + ViewportSize = 2, } -#[derive(Debug, Clone, Copy)] #[repr(C)] -pub(crate) struct QuadUniforms { - viewport_size: Size, +enum MonochromeSpriteInputIndex { + Vertices = 0, + Sprites = 1, + ViewportSize = 2, + AtlasSize = 3, + AtlasTexture = 4, } diff --git a/crates/gpui3/src/platform/mac/shaders.metal b/crates/gpui3/src/platform/mac/shaders.metal index 76fb0e2d1c9ae67f5c1c4ba4423b29e401de9637..3fd1f3748b4741490b27eee6db8c3490ff98800e 100644 --- a/crates/gpui3/src/platform/mac/shaders.metal +++ b/crates/gpui3/src/platform/mac/shaders.metal @@ -4,71 +4,36 @@ using namespace metal; float4 hsla_to_rgba(Hsla hsla); -float4 to_device_position(float2 pixel_position, float2 viewport_size); +float4 to_device_position(float2 unit_vertex, Bounds_Pixels bounds, + Bounds_Pixels clip_bounds, + constant Size_DevicePixels *viewport_size); +float quad_sdf(float2 point, Bounds_Pixels bounds, Corners_Pixels corner_radii); struct QuadVertexOutput { float4 position [[position]]; - float4 background_color; - float4 border_color; - uint quad_id; + float4 background_color [[flat]]; + float4 border_color [[flat]]; + uint quad_id [[flat]]; }; -vertex QuadVertexOutput quad_vertex( - uint unit_vertex_id [[vertex_id]], uint quad_id [[instance_id]], - constant float2 *unit_vertices [[buffer(QuadInputIndex_Vertices)]], - constant Quad *quads [[buffer(QuadInputIndex_Quads)]], - constant QuadUniforms *uniforms [[buffer(QuadInputIndex_Uniforms)]]) { +vertex QuadVertexOutput quad_vertex(uint unit_vertex_id [[vertex_id]], + uint quad_id [[instance_id]], + constant float2 *unit_vertices + [[buffer(QuadInputIndex_Vertices)]], + constant Quad *quads + [[buffer(QuadInputIndex_Quads)]], + constant Size_DevicePixels *viewport_size + [[buffer(QuadInputIndex_ViewportSize)]]) { float2 unit_vertex = unit_vertices[unit_vertex_id]; Quad quad = quads[quad_id]; - float2 position_2d = - unit_vertex * float2(quad.bounds.size.width, quad.bounds.size.height) + - float2(quad.bounds.origin.x, quad.bounds.origin.y); - position_2d.x = max(quad.clip_bounds.origin.x, position_2d.x); - position_2d.x = min(quad.clip_bounds.origin.x + quad.clip_bounds.size.width, - position_2d.x); - position_2d.y = max(quad.clip_bounds.origin.y, position_2d.y); - position_2d.y = min(quad.clip_bounds.origin.y + quad.clip_bounds.size.height, - position_2d.y); - - float2 viewport_size = float2((float)uniforms->viewport_size.width, - (float)uniforms->viewport_size.height); - float4 device_position = to_device_position(position_2d, viewport_size); + float4 device_position = to_device_position(unit_vertex, quad.bounds, + quad.clip_bounds, viewport_size); float4 background_color = hsla_to_rgba(quad.background); float4 border_color = hsla_to_rgba(quad.border_color); return QuadVertexOutput{device_position, background_color, border_color, quad_id}; } -float quad_sdf(float2 point, Bounds_Pixels bounds, - Corners_Pixels corner_radii) { - float2 half_size = float2(bounds.size.width, bounds.size.height) / 2.; - float2 center = float2(bounds.origin.x, bounds.origin.y) + half_size; - float2 center_to_point = point - center; - float corner_radius; - if (center_to_point.x < 0.) { - if (center_to_point.y < 0.) { - corner_radius = corner_radii.top_left; - } else { - corner_radius = corner_radii.bottom_left; - } - } else { - if (center_to_point.y < 0.) { - corner_radius = corner_radii.top_right; - } else { - corner_radius = corner_radii.bottom_right; - } - } - - float2 rounded_edge_to_point = - abs(center_to_point) - half_size + corner_radius; - float distance = - length(max(0., rounded_edge_to_point)) + - min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - - corner_radius; - - return distance; -} - fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]], constant Quad *quads [[buffer(QuadInputIndex_Quads)]]) { @@ -145,6 +110,57 @@ fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]], saturate(0.5 - distance) * saturate(0.5 - clip_distance)); } +struct MonochromeSpriteVertexOutput { + float4 position [[position]]; + float2 tile_position; + float4 color [[flat]]; + uint sprite_id [[flat]]; +}; + +vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex( + uint unit_vertex_id [[vertex_id]], uint sprite_id [[instance_id]], + constant float2 *unit_vertices + [[buffer(MonochromeSpriteInputIndex_Vertices)]], + constant MonochromeSprite *sprites + [[buffer(MonochromeSpriteInputIndex_Sprites)]], + constant Size_DevicePixels *viewport_size + [[buffer(MonochromeSpriteInputIndex_ViewportSize)]], + constant Size_DevicePixels *atlas_size + [[buffer(MonochromeSpriteInputIndex_AtlasSize)]]) { + + float2 unit_vertex = unit_vertices[unit_vertex_id]; + MonochromeSprite sprite = sprites[sprite_id]; + float4 device_position = to_device_position( + unit_vertex, sprite.bounds, sprite.clip_bounds, viewport_size); + + float2 tile_origin = + float2(sprite.tile.bounds.origin.x, sprite.tile.bounds.origin.y); + float2 tile_size = + float2(sprite.tile.bounds.size.width, sprite.tile.bounds.size.height); + float2 tile_position = + (tile_origin + unit_vertex * tile_size) / + float2((float)atlas_size->width, (float)atlas_size->height); + float4 color = hsla_to_rgba(sprite.color); + return MonochromeSpriteVertexOutput{device_position, tile_position, color, + sprite_id}; +} + +fragment float4 monochrome_sprite_fragment( + MonochromeSpriteVertexOutput input [[stage_in]], + constant MonochromeSprite *sprites + [[buffer(MonochromeSpriteInputIndex_Sprites)]], + texture2d atlas + [[texture(MonochromeSpriteInputIndex_AtlasTexture)]]) { + MonochromeSprite sprite = sprites[input.sprite_id]; + constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear); + float4 sample = atlas.sample(atlas_sampler, input.tile_position); + float clip_distance = + quad_sdf(input.position.xy, sprite.clip_bounds, sprite.clip_corner_radii); + float4 color = input.color; + color.a *= sample.a * saturate(0.5 - clip_distance); + return color; +} + float4 hsla_to_rgba(Hsla hsla) { float h = hsla.h * 6.0; // Now, it's an angle but scaled in [0, 6) range float s = hsla.s; @@ -193,10 +209,52 @@ float4 hsla_to_rgba(Hsla hsla) { return rgba; } -float4 to_device_position(float2 pixel_position, float2 viewport_size) { - return float4(pixel_position / viewport_size * float2(2., -2.) + - float2(-1., 1.), - 0., 1.); +float4 to_device_position(float2 unit_vertex, Bounds_Pixels bounds, + Bounds_Pixels clip_bounds, + constant Size_DevicePixels *input_viewport_size) { + float2 position = + unit_vertex * float2(bounds.size.width, bounds.size.height) + + float2(bounds.origin.x, bounds.origin.y); + position.x = max(clip_bounds.origin.x, position.x); + position.x = min(clip_bounds.origin.x + clip_bounds.size.width, position.x); + position.y = max(clip_bounds.origin.y, position.y); + position.y = min(clip_bounds.origin.y + clip_bounds.size.height, position.y); + + float2 viewport_size = float2((float)input_viewport_size->width, + (float)input_viewport_size->height); + float2 device_position = + position / viewport_size * float2(2., -2.) + float2(-1., 1.); + return float4(device_position, 0., 1.); +} + +float quad_sdf(float2 point, Bounds_Pixels bounds, + Corners_Pixels corner_radii) { + float2 half_size = float2(bounds.size.width, bounds.size.height) / 2.; + float2 center = float2(bounds.origin.x, bounds.origin.y) + half_size; + float2 center_to_point = point - center; + float corner_radius; + if (center_to_point.x < 0.) { + if (center_to_point.y < 0.) { + corner_radius = corner_radii.top_left; + } else { + corner_radius = corner_radii.bottom_left; + } + } else { + if (center_to_point.y < 0.) { + corner_radius = corner_radii.top_right; + } else { + corner_radius = corner_radii.bottom_right; + } + } + + float2 rounded_edge_to_point = + abs(center_to_point) - half_size + corner_radius; + float distance = + length(max(0., rounded_edge_to_point)) + + min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - + corner_radius; + + return distance; } // struct SpriteFragmentInput { diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index b6974ef17209429267115b26dfcc2d302cc1ba5c..0be5ebefdc3d214e7d89137b80b70d76a2465ba6 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -1,10 +1,10 @@ use super::{ns_string, MetalRenderer, NSRange}; use crate::{ - point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, - MetalAtlas, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, - MouseUpEvent, NSRectExt, Pixels, Platform, PlatformAtlas, PlatformDispatcher, - PlatformInputHandler, PlatformScreen, PlatformWindow, Point, RasterizedGlyphId, Scene, Size, - Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel, + point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers, + ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt, + Pixels, Platform, PlatformAtlas, PlatformDispatcher, PlatformInputHandler, PlatformScreen, + PlatformWindow, Point, RasterizedGlyphId, Scene, Size, Timer, WindowAppearance, WindowBounds, + WindowKind, WindowOptions, WindowPromptLevel, }; use block::ConcreteBlock; use cocoa::{ @@ -20,7 +20,6 @@ use core_graphics::display::CGRect; use ctor::ctor; use foreign_types::ForeignTypeRef; use futures::channel::oneshot; -use metal::{MTLPixelFormat, TextureDescriptor}; use objc::{ class, declare::ClassDecl, diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index 5ca3f14c0ae482f40df26cf9c174fe765dee2ee1..d8ca9f729d69c697ca8bfc3d07b869f108c0eea2 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -243,6 +243,8 @@ impl From for Primitive { pub struct MonochromeSprite { pub order: u32, pub bounds: Bounds, + pub clip_bounds: Bounds, + pub clip_corner_radii: Corners, pub color: Hsla, pub tile: AtlasTile, } diff --git a/crates/gpui3/src/text_system.rs b/crates/gpui3/src/text_system.rs index ba5cadba984fd66d424ede5254b53226a5730f29..b3ddb468c34bf4aee6e9120a74922a9cb9d22adc 100644 --- a/crates/gpui3/src/text_system.rs +++ b/crates/gpui3/src/text_system.rs @@ -10,8 +10,8 @@ use line_wrapper::*; pub use text_layout_cache::*; use crate::{ - px, Bounds, DevicePixels, Hsla, Pixels, PlatformTextSystem, Point, RasterizationOptions, - Result, SharedString, Size, UnderlineStyle, + px, Bounds, DevicePixels, Hsla, Pixels, PlatformTextSystem, Point, Result, SharedString, Size, + UnderlineStyle, }; use collections::HashMap; use core::fmt; diff --git a/crates/gpui3/src/text_system/line.rs b/crates/gpui3/src/text_system/line.rs index 98b0abcdbd774e51c1c0a3ea23360a55eca594e6..6f8f9d5b0ccd5af78ab12fa25f696ac700b07d70 100644 --- a/crates/gpui3/src/text_system/line.rs +++ b/crates/gpui3/src/text_system/line.rs @@ -1,7 +1,6 @@ use crate::{ - black, point, px, Bounds, FontId, Hsla, Layout, MonochromeSprite, Pixels, Point, - RasterizedGlyphId, RunStyle, ShapedBoundary, ShapedLine, ShapedRun, UnderlineStyle, - WindowContext, + black, point, px, Bounds, Corners, FontId, Hsla, Layout, MonochromeSprite, Pixels, Point, + RunStyle, ShapedBoundary, ShapedLine, ShapedRun, UnderlineStyle, WindowContext, }; use anyhow::Result; use smallvec::SmallVec; @@ -192,20 +191,13 @@ impl Line { MonochromeSprite { order: layout.order, bounds, + clip_bounds: bounds, + clip_corner_radii: Corners::default(), color, tile, }, ); } - - // cx.scene().insert(Symbol { - // order: layout.order, - // origin, - // font_id: run.font_id, - // font_size: self.layout.font_size, - // id: glyph.id, - // color, - // }); } } diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index de518fb93fe25876bedac379e9fa37a7e0b70011..929823b977a58f2550836c3f63b0d74ea783b7b1 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -200,7 +200,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { let mut origin = (target_position * scale_factor).map(|p| p.floor()); // Position glyph within bounding box origin += offset.map(|o| px(u32::from(o) as f32)); - let size = tile.bounds_in_atlas.size.map(|b| px(b.0 as f32)); + let size = tile.bounds.size.map(|b| px(b.0 as f32)); let bounds = Bounds { origin, size }; Ok((tile, bounds)) From a8c1958c7548f8a20b213d9a348067b2d0d4c1bd Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 3 Oct 2023 13:03:29 -0600 Subject: [PATCH 11/28] Checkpoint --- crates/gpui3/src/geometry.rs | 62 ++++++++-- crates/gpui3/src/platform.rs | 10 +- crates/gpui3/src/platform/mac/metal_atlas.rs | 22 ++-- .../gpui3/src/platform/mac/metal_renderer.rs | 110 +++++++++++++++--- crates/gpui3/src/platform/mac/shaders.metal | 59 +--------- crates/gpui3/src/platform/mac/text_system.rs | 52 +++++---- crates/gpui3/src/platform/mac/window.rs | 6 +- crates/gpui3/src/scene.rs | 23 ++-- crates/gpui3/src/style.rs | 17 ++- crates/gpui3/src/text_system.rs | 10 +- crates/gpui3/src/text_system/line.rs | 24 ++-- crates/gpui3/src/window.rs | 36 ++---- crates/storybook2/src/workspace.rs | 3 +- 13 files changed, 248 insertions(+), 186 deletions(-) diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index 951b1125cd964e6eda1d9330df3fba450c43733f..e5dadbd58da43c3c60480b21bdd283f26d82dd35 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -28,6 +28,15 @@ impl Point { } } +impl Point { + pub fn scale(&self, factor: f32) -> Point { + Point { + x: self.x.scale(factor), + y: self.y.scale(factor), + } + } +} + impl Mul for Point where T: Mul + Clone + Debug, @@ -122,6 +131,15 @@ impl Size { } } +impl Size { + pub fn scale(&self, factor: f32) -> Size { + Size { + width: self.width.scale(factor), + height: self.height.scale(factor), + } + } +} + impl Size { pub fn max(&self, other: &Self) -> Self { Size { @@ -207,7 +225,6 @@ pub struct Bounds { pub size: Size, } -// Bounds * Pixels = Bounds impl Mul for Bounds where T: Mul + Clone + Debug, @@ -277,6 +294,15 @@ impl> Bounds { } } +impl Bounds { + pub fn scale(&self, factor: f32) -> Bounds { + Bounds { + origin: self.origin.scale(factor), + size: self.size.scale(factor), + } + } +} + impl Copy for Bounds {} #[derive(Refineable, Clone, Default, Debug, Eq, PartialEq)] @@ -462,8 +488,8 @@ impl Pixels { Self(self.0.floor()) } - pub fn to_device_pixels(&self, scale: f32) -> DevicePixels { - DevicePixels((self.0 * scale).ceil() as u32) + pub fn scale(&self, factor: f32) -> ScaledPixels { + ScaledPixels(self.0 * factor) } } @@ -542,22 +568,22 @@ impl From for f64 { SubAssign, )] #[repr(transparent)] -pub struct DevicePixels(pub(crate) u32); +pub struct DevicePixels(pub(crate) i32); impl DevicePixels { pub fn to_bytes(&self, bytes_per_pixel: u8) -> u32 { - self.0 * bytes_per_pixel as u32 + self.0 as u32 * bytes_per_pixel as u32 } } -impl From for u32 { +impl From for i32 { fn from(device_pixels: DevicePixels) -> Self { device_pixels.0 } } -impl From for DevicePixels { - fn from(val: u32) -> Self { +impl From for DevicePixels { + fn from(val: i32) -> Self { DevicePixels(val) } } @@ -570,7 +596,25 @@ impl From for u64 { impl From for DevicePixels { fn from(val: u64) -> Self { - DevicePixels(val as u32) + DevicePixels(val as i32) + } +} + +#[derive(Clone, Copy, Default, Add, AddAssign, Sub, SubAssign, Div, PartialEq, PartialOrd)] +#[repr(transparent)] +pub struct ScaledPixels(pub(crate) f32); + +impl Eq for ScaledPixels {} + +impl Debug for ScaledPixels { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} px (scaled)", self.0) + } +} + +impl From for DevicePixels { + fn from(scaled: ScaledPixels) -> Self { + DevicePixels(scaled.0.ceil() as i32) } } diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 5e5127fcf15cd0bcd692b13803fbf930f59e08b7..940633caaf0e2ce25a86310f2bcee66864b2ca90 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -7,7 +7,7 @@ mod test; use crate::{ AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point, - RasterizedGlyphId, Result, Scene, ShapedLine, SharedString, Size, + RasterizeGlyphParams, Result, Scene, ShapedLine, SharedString, Size, }; use anyhow::anyhow; use async_task::Runnable; @@ -147,7 +147,7 @@ pub trait PlatformWindow { fn is_topmost_for_position(&self, position: Point) -> bool; fn draw(&self, scene: Scene); - fn glyph_atlas(&self) -> Arc>; + fn glyph_atlas(&self) -> Arc>; } pub trait PlatformDispatcher: Send + Sync { @@ -165,8 +165,8 @@ pub trait PlatformTextSystem: Send + Sync { fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option; fn rasterize_glyph( &self, - glyph_id: &RasterizedGlyphId, - ) -> Result<(Bounds, Vec)>; + glyph_id: &RasterizeGlyphParams, + ) -> Result<(Size, Vec)>; fn layout_line(&self, text: &str, font_size: Pixels, runs: &[(usize, FontId)]) -> ShapedLine; fn wrap_line( &self, @@ -197,7 +197,7 @@ pub struct AtlasTile { #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(C)] -pub(crate) struct AtlasTextureId(pub(crate) u32); +pub(crate) struct AtlasTextureId(pub(crate) u32); // We use u32 instead of usize for Metal Shader Language compatibility #[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 6924acf52ed175d62a222d773031b5c8a7a6be86..ae2d7b2d514d7f91f09375d16b27a80c7b8a2558 100644 --- a/crates/gpui3/src/platform/mac/metal_atlas.rs +++ b/crates/gpui3/src/platform/mac/metal_atlas.rs @@ -28,6 +28,10 @@ impl MetalAtlas { 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() + } } struct MetalAtlasState { @@ -123,10 +127,10 @@ impl MetalAtlasTexture { bounds: allocation.rectangle.into(), }; let region = metal::MTLRegion::new_2d( - u32::from(tile.bounds.origin.x) as u64, - u32::from(tile.bounds.origin.y) as u64, - u32::from(tile.bounds.size.width) as u64, - u32::from(tile.bounds.size.height) as u64, + tile.bounds.origin.x.into(), + tile.bounds.origin.y.into(), + tile.bounds.size.width.into(), + tile.bounds.size.height.into(), ); self.metal_texture.replace_region( region, @@ -149,15 +153,15 @@ impl MetalAtlasTexture { impl From> for etagere::Size { fn from(size: Size) -> Self { - etagere::Size::new(u32::from(size.width) as i32, u32::from(size.width) as i32) + etagere::Size::new(size.width.into(), size.width.into()) } } impl From for Point { fn from(value: etagere::Point) -> Self { Point { - x: DevicePixels::from(value.x as u32), - y: DevicePixels::from(value.y as u32), + x: DevicePixels::from(value.x), + y: DevicePixels::from(value.y), } } } @@ -165,8 +169,8 @@ impl From for Point { impl From for Size { fn from(size: etagere::Size) -> Self { Size { - width: DevicePixels::from(size.width as u32), - height: DevicePixels::from(size.height as u32), + width: DevicePixels::from(size.width), + height: DevicePixels::from(size.height), } } } diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index 19b5374e585c53ed3736e91011090d6c8e28dae7..dc93a8cfa0520d57616feb1b0b3ce483dea00f15 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -1,6 +1,6 @@ use crate::{ point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, Quad, - RasterizedGlyphId, Scene, Size, + RasterizeGlyphParams, Scene, Size, }; use cocoa::{ base::{NO, YES}, @@ -18,10 +18,11 @@ pub struct MetalRenderer { device: metal::Device, layer: metal::MetalLayer, command_queue: CommandQueue, - quad_pipeline_state: metal::RenderPipelineState, + quads_pipeline_state: metal::RenderPipelineState, + sprites_pipeline_state: metal::RenderPipelineState, unit_vertices: metal::Buffer, instances: metal::Buffer, - glyph_atlas: Arc>, + glyph_atlas: Arc>, } impl MetalRenderer { @@ -81,15 +82,24 @@ impl MetalRenderer { MTLResourceOptions::StorageModeManaged, ); - let quad_pipeline_state = build_pipeline_state( + let quads_pipeline_state = build_pipeline_state( &device, &library, - "quad", + "quads", "quad_vertex", "quad_fragment", PIXEL_FORMAT, ); + let sprites_pipeline_state = build_pipeline_state( + &device, + &library, + "sprites", + "monochrome_sprite_vertex", + "monochrome_sprite_fragment", + PIXEL_FORMAT, + ); + let command_queue = device.new_command_queue(); let glyph_atlas = Arc::new(MetalAtlas::new( Size { @@ -104,7 +114,8 @@ impl MetalRenderer { device, layer, command_queue, - quad_pipeline_state, + quads_pipeline_state, + sprites_pipeline_state, unit_vertices, instances, glyph_atlas, @@ -115,7 +126,7 @@ impl MetalRenderer { &*self.layer } - pub fn glyph_atlas(&self) -> &Arc> { + pub fn glyph_atlas(&self) -> &Arc> { &self.glyph_atlas } @@ -123,8 +134,8 @@ impl MetalRenderer { let layer = self.layer.clone(); let viewport_size = layer.drawable_size(); let viewport_size: Size = size( - (viewport_size.width.ceil() as u32).into(), - (viewport_size.height.ceil() as u32).into(), + (viewport_size.width.ceil() as i32).into(), + (viewport_size.height.ceil() as i32).into(), ); let drawable = if let Some(drawable) = layer.next_drawable() { drawable @@ -144,8 +155,8 @@ impl MetalRenderer { depth_texture_desc.set_pixel_format(metal::MTLPixelFormat::Depth32Float); depth_texture_desc.set_storage_mode(metal::MTLStorageMode::Private); depth_texture_desc.set_usage(metal::MTLTextureUsage::RenderTarget); - depth_texture_desc.set_width(u32::from(viewport_size.width) as u64); - depth_texture_desc.set_height(u32::from(viewport_size.height) as u64); + depth_texture_desc.set_width(i32::from(viewport_size.width) as u64); + depth_texture_desc.set_height(i32::from(viewport_size.height) as u64); let depth_texture = self.device.new_texture(&depth_texture_desc); let depth_attachment = render_pass_descriptor.depth_attachment().unwrap(); @@ -168,8 +179,8 @@ impl MetalRenderer { command_encoder.set_viewport(metal::MTLViewport { originX: 0.0, originY: 0.0, - width: u32::from(viewport_size.width) as f64, - height: u32::from(viewport_size.height) as f64, + width: i32::from(viewport_size.width) as f64, + height: i32::from(viewport_size.height) as f64, znear: 0.0, zfar: 1.0, }); @@ -226,7 +237,7 @@ impl MetalRenderer { } align_offset(offset); - command_encoder.set_render_pipeline_state(&self.quad_pipeline_state); + command_encoder.set_render_pipeline_state(&self.quads_pipeline_state); command_encoder.set_vertex_buffer( QuadInputIndex::Vertices as u64, Some(&self.unit_vertices), @@ -273,12 +284,77 @@ impl MetalRenderer { fn draw_monochrome_sprites( &mut self, texture_id: AtlasTextureId, - monochrome: &[MonochromeSprite], + sprites: &[MonochromeSprite], offset: &mut usize, viewport_size: Size, command_encoder: &metal::RenderCommandEncoderRef, ) { - // todo!() + // dbg!(sprites); + + if sprites.is_empty() { + return; + } + align_offset(offset); + + let texture = self.glyph_atlas.texture(texture_id); + let texture_size = size( + DevicePixels(texture.width() as i32), + DevicePixels(texture.height() as i32), + ); + command_encoder.set_render_pipeline_state(&self.sprites_pipeline_state); + command_encoder.set_vertex_buffer( + MonochromeSpriteInputIndex::Vertices as u64, + Some(&self.unit_vertices), + 0, + ); + command_encoder.set_vertex_buffer( + MonochromeSpriteInputIndex::Sprites as u64, + Some(&self.instances), + *offset as u64, + ); + command_encoder.set_vertex_bytes( + MonochromeSpriteInputIndex::ViewportSize as u64, + mem::size_of_val(&viewport_size) as u64, + &viewport_size as *const Size as *const _, + ); + command_encoder.set_vertex_bytes( + MonochromeSpriteInputIndex::AtlasTextureSize as u64, + mem::size_of_val(&texture_size) as u64, + &texture_size as *const Size as *const _, + ); + command_encoder.set_fragment_buffer( + MonochromeSpriteInputIndex::Sprites as u64, + Some(&self.instances), + *offset as u64, + ); + command_encoder.set_fragment_texture( + MonochromeSpriteInputIndex::AtlasTexture as u64, + Some(&texture), + ); + + let sprite_bytes_len = mem::size_of::() * sprites.len(); + let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) }; + unsafe { + ptr::copy_nonoverlapping( + sprites.as_ptr() as *const u8, + buffer_contents, + sprite_bytes_len, + ); + } + + let next_offset = *offset + sprite_bytes_len; + assert!( + next_offset <= INSTANCE_BUFFER_SIZE, + "instance buffer exhausted" + ); + + command_encoder.draw_primitives_instanced( + metal::MTLPrimitiveType::Triangle, + 0, + 6, + sprites.len() as u64, + ); + *offset = next_offset; } } @@ -334,6 +410,6 @@ enum MonochromeSpriteInputIndex { Vertices = 0, Sprites = 1, ViewportSize = 2, - AtlasSize = 3, + AtlasTextureSize = 3, AtlasTexture = 4, } diff --git a/crates/gpui3/src/platform/mac/shaders.metal b/crates/gpui3/src/platform/mac/shaders.metal index 3fd1f3748b4741490b27eee6db8c3490ff98800e..7c98db779bc9f8f8ed19fd76bc771f8a967ab92f 100644 --- a/crates/gpui3/src/platform/mac/shaders.metal +++ b/crates/gpui3/src/platform/mac/shaders.metal @@ -126,7 +126,7 @@ vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex( constant Size_DevicePixels *viewport_size [[buffer(MonochromeSpriteInputIndex_ViewportSize)]], constant Size_DevicePixels *atlas_size - [[buffer(MonochromeSpriteInputIndex_AtlasSize)]]) { + [[buffer(MonochromeSpriteInputIndex_AtlasTextureSize)]]) { float2 unit_vertex = unit_vertices[unit_vertex_id]; MonochromeSprite sprite = sprites[sprite_id]; @@ -149,11 +149,13 @@ fragment float4 monochrome_sprite_fragment( MonochromeSpriteVertexOutput input [[stage_in]], constant MonochromeSprite *sprites [[buffer(MonochromeSpriteInputIndex_Sprites)]], - texture2d atlas + texture2d atlas_texture [[texture(MonochromeSpriteInputIndex_AtlasTexture)]]) { MonochromeSprite sprite = sprites[input.sprite_id]; - constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear); - float4 sample = atlas.sample(atlas_sampler, input.tile_position); + constexpr sampler atlas_texture_sampler(mag_filter::linear, + min_filter::linear); + float4 sample = + atlas_texture.sample(atlas_texture_sampler, input.tile_position); float clip_distance = quad_sdf(input.position.xy, sprite.clip_bounds, sprite.clip_corner_radii); float4 color = input.color; @@ -256,52 +258,3 @@ float quad_sdf(float2 point, Bounds_Pixels bounds, return distance; } - -// struct SpriteFragmentInput { -// float4 position [[position]]; -// float2 atlas_position; -// float4 color [[flat]]; -// uchar compute_winding [[flat]]; -// }; - -// vertex SpriteFragmentInput sprite_vertex( -// uint unit_vertex_id [[vertex_id]], -// uint sprite_id [[instance_id]], -// constant float2 *unit_vertices -// [[buffer(GPUISpriteVertexInputIndexVertices)]], constant GPUISprite -// *sprites [[buffer(GPUISpriteVertexInputIndexSprites)]], constant float2 -// *viewport_size [[buffer(GPUISpriteVertexInputIndexViewportSize)]], -// constant float2 *atlas_size -// [[buffer(GPUISpriteVertexInputIndexAtlasSize)]] -// ) { -// float2 unit_vertex = unit_vertices[unit_vertex_id]; -// GPUISprite sprite = sprites[sprite_id]; -// float2 position = unit_vertex * sprite.target_size + sprite.origin; -// float4 device_position = to_device_position(position, *viewport_size); -// float2 atlas_position = (unit_vertex * sprite.source_size + -// sprite.atlas_origin) / *atlas_size; - -// return SpriteFragmentInput { -// device_position, -// atlas_position, -// coloru_to_colorf(sprite.color), -// sprite.compute_winding -// }; -// } - -// fragment float4 sprite_fragment( -// SpriteFragmentInput input [[stage_in]], -// texture2d atlas [[ texture(GPUISpriteFragmentInputIndexAtlas) ]] -// ) { -// constexpr sampler atlas_sampler(mag_filter::linear, min_filter::linear); -// float4 color = input.color; -// float4 sample = atlas.sample(atlas_sampler, input.atlas_position); -// float mask; -// if (input.compute_winding) { -// mask = 1. - abs(1. - fmod(sample.r, 2.)); -// } else { -// mask = sample.a; -// } -// color.a *= mask; -// return color; -// } diff --git a/crates/gpui3/src/platform/mac/text_system.rs b/crates/gpui3/src/platform/mac/text_system.rs index f28badbb629216f64a1109c7c28bc92bcd1a5e3a..ea7c6a03022d60915fb0f7a3d42722637240a0bc 100644 --- a/crates/gpui3/src/platform/mac/text_system.rs +++ b/crates/gpui3/src/platform/mac/text_system.rs @@ -1,7 +1,7 @@ use crate::{ point, px, size, Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontStyle, - FontWeight, GlyphId, Pixels, PlatformTextSystem, Point, RasterizedGlyphId, Result, ShapedGlyph, - ShapedLine, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS, + FontWeight, GlyphId, Pixels, PlatformTextSystem, Point, RasterizeGlyphParams, Result, + ShapedGlyph, ShapedLine, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS, }; use anyhow::anyhow; use cocoa::appkit::{CGFloat, CGPoint}; @@ -136,8 +136,8 @@ impl PlatformTextSystem for MacTextSystem { fn rasterize_glyph( &self, - glyph_id: &RasterizedGlyphId, - ) -> Result<(Bounds, Vec)> { + glyph_id: &RasterizeGlyphParams, + ) -> Result<(Size, Vec)> { self.0.read().rasterize_glyph(glyph_id) } @@ -232,8 +232,8 @@ impl MacTextSystemState { fn rasterize_glyph( &self, - glyph_id: &RasterizedGlyphId, - ) -> Result<(Bounds, Vec)> { + glyph_id: &RasterizeGlyphParams, + ) -> Result<(Size, Vec)> { let font = &self.fonts[glyph_id.font_id.0]; let scale = Transform2F::from_scale(glyph_id.scale_factor); let glyph_bounds = font.raster_bounds( @@ -252,18 +252,15 @@ impl MacTextSystemState { glyph_id.subpixel_variant.x.min(1) as i32, glyph_id.subpixel_variant.y.min(1) as i32, ); - let cx_bounds = RectI::new( - glyph_bounds.origin(), - glyph_bounds.size() + subpixel_padding, - ); + let bitmap_size = glyph_bounds.size() + subpixel_padding; - let mut bytes = vec![0; cx_bounds.width() as usize * cx_bounds.height() as usize]; + let mut bytes = vec![0; bitmap_size.x() as usize * bitmap_size.y() as usize]; let cx = CGContext::create_bitmap_context( Some(bytes.as_mut_ptr() as *mut _), - cx_bounds.width() as usize, - cx_bounds.height() as usize, + bitmap_size.x() as usize, + bitmap_size.y() as usize, 8, - cx_bounds.width() as usize, + bitmap_size.x() as usize, &CGColorSpace::create_device_gray(), kCGImageAlphaOnly, ); @@ -298,7 +295,7 @@ impl MacTextSystemState { cx, ); - Ok((cx_bounds.into(), bytes)) + Ok((bitmap_size.into(), bytes)) } } @@ -511,14 +508,23 @@ impl From for Bounds { impl From for Bounds { fn from(rect: RectI) -> Self { Bounds { - origin: point( - DevicePixels(rect.origin_x() as u32), - DevicePixels(rect.origin_y() as u32), - ), - size: size( - DevicePixels(rect.width() as u32), - DevicePixels(rect.height() as u32), - ), + origin: point(DevicePixels(rect.origin_x()), DevicePixels(rect.origin_y())), + size: size(DevicePixels(rect.width()), DevicePixels(rect.height())), + } + } +} + +impl From for Size { + fn from(value: Vector2I) -> Self { + size(value.x().into(), value.y().into()) + } +} + +impl From for Bounds { + fn from(rect: RectI) -> Self { + Bounds { + origin: point(rect.origin_x(), rect.origin_y()), + size: size(rect.width(), rect.height()), } } } diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index 0be5ebefdc3d214e7d89137b80b70d76a2465ba6..7d27976563a43ce7811e94276e50a510a8b80922 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -3,8 +3,8 @@ use crate::{ point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt, Pixels, Platform, PlatformAtlas, PlatformDispatcher, PlatformInputHandler, PlatformScreen, - PlatformWindow, Point, RasterizedGlyphId, Scene, Size, Timer, WindowAppearance, WindowBounds, - WindowKind, WindowOptions, WindowPromptLevel, + PlatformWindow, Point, RasterizeGlyphParams, Scene, Size, Timer, WindowAppearance, + WindowBounds, WindowKind, WindowOptions, WindowPromptLevel, }; use block::ConcreteBlock; use cocoa::{ @@ -886,7 +886,7 @@ impl PlatformWindow for MacWindow { } } - fn glyph_atlas(&self) -> Arc> { + fn glyph_atlas(&self) -> Arc> { self.0.lock().renderer.glyph_atlas().clone() } } diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index d8ca9f729d69c697ca8bfc3d07b869f108c0eea2..b4752ab3e9b03f882d8d2184f5b999cf4f01cf02 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -1,7 +1,7 @@ use std::{iter::Peekable, mem}; use super::{Bounds, Hsla, Pixels, Point}; -use crate::{AtlasTextureId, AtlasTile, Corners, Edges}; +use crate::{AtlasTextureId, AtlasTile, Corners, Edges, ScaledPixels}; use collections::BTreeMap; use smallvec::SmallVec; @@ -36,7 +36,6 @@ impl Scene { let primitive = primitive.into(); match primitive { Primitive::Quad(mut quad) => { - quad.scale(self.scale_factor); layer.quads.push(quad); } Primitive::Sprite(sprite) => { @@ -187,17 +186,17 @@ pub(crate) enum PrimitiveBatch<'a> { #[repr(C)] pub struct Quad { pub order: u32, - pub bounds: Bounds, - pub clip_bounds: Bounds, - pub clip_corner_radii: Corners, + pub bounds: Bounds, + pub clip_bounds: Bounds, + pub clip_corner_radii: Corners, pub background: Hsla, pub border_color: Hsla, - pub corner_radii: Corners, - pub border_widths: Edges, + pub corner_radii: Corners, + pub border_widths: Edges, } impl Quad { - pub fn vertices(&self) -> impl Iterator> { + pub fn vertices(&self) -> impl Iterator> { let x1 = self.bounds.origin.x; let y1 = self.bounds.origin.y; let x2 = x1 + self.bounds.size.width; @@ -210,14 +209,6 @@ impl Quad { ] .into_iter() } - - pub fn scale(&mut self, factor: f32) { - self.bounds *= factor; - self.clip_bounds *= factor; - self.clip_corner_radii *= factor; - self.corner_radii *= factor; - self.border_widths *= factor; - } } impl Ord for Quad { diff --git a/crates/gpui3/src/style.rs b/crates/gpui3/src/style.rs index aae76349c6e92811b59ff0caaae9ad28f7be43b9..431a87491f1f5d6f9a6a87114b48c119b881b2c3 100644 --- a/crates/gpui3/src/style.rs +++ b/crates/gpui3/src/style.rs @@ -182,6 +182,7 @@ impl Style { /// Paints the background of an element styled with this style. pub fn paint(&self, order: u32, bounds: Bounds, cx: &mut ViewContext) { let rem_size = cx.rem_size(); + let scale = cx.scale_factor(); let background_color = self.fill.as_ref().and_then(Fill::color); if background_color.is_some() || self.is_border_visible() { @@ -190,13 +191,19 @@ impl Style { layer_id, Quad { order, - bounds, - clip_bounds: bounds, // todo! - clip_corner_radii: self.corner_radii.map(|length| length.to_pixels(rem_size)), + bounds: bounds.scale(scale), + clip_bounds: bounds.scale(scale), // todo! + clip_corner_radii: self + .corner_radii + .map(|length| length.to_pixels(rem_size).scale(scale)), background: background_color.unwrap_or_default(), border_color: self.border_color.unwrap_or_default(), - corner_radii: self.corner_radii.map(|length| length.to_pixels(rem_size)), - border_widths: self.border_widths.map(|length| length.to_pixels(rem_size)), + corner_radii: self + .corner_radii + .map(|length| length.to_pixels(rem_size).scale(scale)), + border_widths: self + .border_widths + .map(|length| length.to_pixels(rem_size).scale(scale)), }, ); } diff --git a/crates/gpui3/src/text_system.rs b/crates/gpui3/src/text_system.rs index b3ddb468c34bf4aee6e9120a74922a9cb9d22adc..ec0a85ca1656593b61009081c5d42a4b6b8643af 100644 --- a/crates/gpui3/src/text_system.rs +++ b/crates/gpui3/src/text_system.rs @@ -217,8 +217,8 @@ impl TextSystem { pub fn rasterize_glyph( &self, - glyph_id: &RasterizedGlyphId, - ) -> Result<(Bounds, Vec)> { + glyph_id: &RasterizeGlyphParams, + ) -> Result<(Size, Vec)> { self.platform_text_system.rasterize_glyph(glyph_id) } } @@ -380,7 +380,7 @@ pub struct ShapedGlyph { } #[derive(Clone, Debug, PartialEq)] -pub struct RasterizedGlyphId { +pub struct RasterizeGlyphParams { pub(crate) font_id: FontId, pub(crate) glyph_id: GlyphId, pub(crate) font_size: Pixels, @@ -388,9 +388,9 @@ pub struct RasterizedGlyphId { pub(crate) scale_factor: f32, } -impl Eq for RasterizedGlyphId {} +impl Eq for RasterizeGlyphParams {} -impl Hash for RasterizedGlyphId { +impl Hash for RasterizeGlyphParams { fn hash(&self, state: &mut H) { self.font_id.0.hash(state); self.glyph_id.0.hash(state); diff --git a/crates/gpui3/src/text_system/line.rs b/crates/gpui3/src/text_system/line.rs index 6f8f9d5b0ccd5af78ab12fa25f696ac700b07d70..a3a578c7b1cedfdc5c69a9bfb58f016acf6852ba 100644 --- a/crates/gpui3/src/text_system/line.rs +++ b/crates/gpui3/src/text_system/line.rs @@ -157,35 +157,29 @@ impl Line { if let Some((_underline_origin, _underline_style)) = finished_underline { todo!() - // cx.scene().insert(Underline { - // origin: underline_origin, - // width: glyph_origin.x - underline_origin.x, - // thickness: underline_style.thickness.into(), - // color: underline_style.color.unwrap(), - // squiggly: underline_style.squiggly, - // }); } if glyph.is_emoji { todo!() - // cx.scene().push_image_glyph(scene::ImageGlyph { - // font_id: run.font_id, - // font_size: self.layout.font_size, - // id: glyph.id, - // origin: glyph_origin, - // }); } else { - if let Some((tile, bounds)) = cx + if let Some(tile) = cx .rasterize_glyph( run.font_id, glyph.id, self.layout.font_size, - cx.scale_factor(), glyph_origin, + cx.scale_factor(), ) .log_err() { let layer_id = cx.current_layer_id(); + + let bounds = Bounds { + origin: glyph_origin + todo!(), + size: todo!(), + }; + // cx.text_system().raster_bounds() + cx.scene().insert( layer_id, MonochromeSprite { diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 929823b977a58f2550836c3f63b0d74ea783b7b1..dc7dca03269bf90f48575a278962c26e83e67f20 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,9 +1,8 @@ use crate::{ - px, AnyView, AppContext, AtlasTile, AvailableSpace, Bounds, Context, DevicePixels, Effect, - Element, EntityId, FontId, GlyphId, Handle, LayoutId, MainThread, MainThreadOnly, - MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, RasterizedGlyphId, Reference, - Scene, Size, StackContext, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, - SUBPIXEL_VARIANTS, + px, AnyView, AppContext, AtlasTile, AvailableSpace, Bounds, Context, Effect, Element, EntityId, + FontId, GlyphId, Handle, LayoutId, MainThread, MainThreadOnly, Pixels, PlatformAtlas, + PlatformWindow, Point, RasterizeGlyphParams, Reference, Scene, Size, StackContext, + StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; use futures::Future; @@ -16,7 +15,7 @@ pub struct AnyWindow {} pub struct Window { handle: AnyWindowHandle, platform_window: MainThreadOnly>, - glyph_atlas: Arc>, + glyph_atlas: Arc>, rem_size: Pixels, content_size: Size, layout_engine: TaffyLayoutEngine, @@ -171,39 +170,26 @@ impl<'a, 'w> WindowContext<'a, 'w> { font_id: FontId, glyph_id: GlyphId, font_size: Pixels, - scale_factor: f32, target_position: Point, - ) -> Result<(AtlasTile, Bounds)> { + scale_factor: f32, + ) -> Result { let target_position = target_position * scale_factor; let subpixel_variant = Point { x: (target_position.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, y: (target_position.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, }; - let rasterized_glyph_id = RasterizedGlyphId { + let rasterized_glyph_id = RasterizeGlyphParams { font_id, glyph_id, font_size, subpixel_variant, scale_factor, }; - let mut offset = Default::default(); - let tile = self - .window + self.window .glyph_atlas .get_or_insert_with(&rasterized_glyph_id, &mut || { - let (bounds, pixels) = self.text_system().rasterize_glyph(&rasterized_glyph_id)?; - offset = bounds.origin; - Ok((bounds.size, pixels)) - })?; - - // Align bounding box surrounding glyph to pixel grid - let mut origin = (target_position * scale_factor).map(|p| p.floor()); - // Position glyph within bounding box - origin += offset.map(|o| px(u32::from(o) as f32)); - let size = tile.bounds.size.map(|b| px(b.0 as f32)); - let bounds = Bounds { origin, size }; - - Ok((tile, bounds)) + self.text_system().rasterize_glyph(&rasterized_glyph_id) + }) } pub(crate) fn draw(&mut self) -> Result<()> { diff --git a/crates/storybook2/src/workspace.rs b/crates/storybook2/src/workspace.rs index adc091b4b9047091723dc5819ce5e70feea9a91d..c48993a9afd5955dbafc4bb5b0e0f7512638fa25 100644 --- a/crates/storybook2/src/workspace.rs +++ b/crates/storybook2/src/workspace.rs @@ -4,7 +4,7 @@ use crate::{ themes::rose_pine_dawn, }; use gpui3::{ - div, img, svg, view, Context, Element, ParentElement, RootView, StyleHelpers, View, + black, div, img, svg, view, Context, Element, ParentElement, RootView, StyleHelpers, View, ViewContext, WindowContext, }; @@ -29,6 +29,7 @@ impl Workspace { let theme = rose_pine_dawn(); div() .font("Helvetica") + .text_color(black()) .text_base() .size_full() .fill(theme.middle.positive.default.background) From 3b27d41c72eedce9e1e97fcff80a0b3a5d441d26 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 3 Oct 2023 13:52:10 -0600 Subject: [PATCH 12/28] Checkpoint --- crates/gpui3/src/geometry.rs | 79 ++++++++++++++++ crates/gpui3/src/platform.rs | 12 ++- .../gpui3/src/platform/mac/metal_renderer.rs | 8 +- crates/gpui3/src/platform/mac/text_system.rs | 91 ++++++++++++------- crates/gpui3/src/platform/mac/window.rs | 12 +-- crates/gpui3/src/scene.rs | 10 +- crates/gpui3/src/text_system.rs | 12 ++- crates/gpui3/src/text_system/line.rs | 43 ++------- crates/gpui3/src/window.rs | 62 ++++++++++++- 9 files changed, 235 insertions(+), 94 deletions(-) diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index e5dadbd58da43c3c60480b21bdd283f26d82dd35..5814730763214877c9adcc7f336a8425f2928adb 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -618,6 +618,12 @@ impl From for DevicePixels { } } +impl From for ScaledPixels { + fn from(device: DevicePixels) -> Self { + ScaledPixels(device.0 as f32) + } +} + #[derive(Clone, Copy, Default, Add, Sub, Mul, Div)] pub struct Rems(f32); @@ -802,3 +808,76 @@ impl From<()> for Length { Self::Definite(DefiniteLength::default()) } } + +pub trait IsZero { + fn is_zero(&self) -> bool; +} + +impl IsZero for DevicePixels { + fn is_zero(&self) -> bool { + self.0 == 0 + } +} + +impl IsZero for ScaledPixels { + fn is_zero(&self) -> bool { + self.0 == 0. + } +} + +impl IsZero for Pixels { + fn is_zero(&self) -> bool { + self.0 == 0. + } +} + +impl IsZero for Rems { + fn is_zero(&self) -> bool { + self.0 == 0. + } +} + +impl IsZero for AbsoluteLength { + fn is_zero(&self) -> bool { + match self { + AbsoluteLength::Pixels(pixels) => pixels.is_zero(), + AbsoluteLength::Rems(rems) => rems.is_zero(), + } + } +} + +impl IsZero for DefiniteLength { + fn is_zero(&self) -> bool { + match self { + DefiniteLength::Absolute(length) => length.is_zero(), + DefiniteLength::Fraction(fraction) => *fraction == 0., + } + } +} + +impl IsZero for Length { + fn is_zero(&self) -> bool { + match self { + Length::Definite(length) => length.is_zero(), + Length::Auto => false, + } + } +} + +impl IsZero for Point { + fn is_zero(&self) -> bool { + self.x.is_zero() && self.y.is_zero() + } +} + +impl IsZero for Size { + fn is_zero(&self) -> bool { + self.width.is_zero() && self.height.is_zero() + } +} + +impl IsZero for Bounds { + fn is_zero(&self) -> bool { + self.origin.is_zero() && self.size.is_zero() + } +} diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 940633caaf0e2ce25a86310f2bcee66864b2ca90..9552bb227c4b03a4aeb2a86e9e57e48ba41502b9 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -6,8 +6,8 @@ mod mac; mod test; use crate::{ - AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point, - RasterizeGlyphParams, Result, Scene, ShapedLine, SharedString, Size, + AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, + GlyphRasterizationParams, Pixels, Point, Result, Scene, ShapedLine, SharedString, Size, }; use anyhow::anyhow; use async_task::Runnable; @@ -147,7 +147,7 @@ pub trait PlatformWindow { fn is_topmost_for_position(&self, position: Point) -> bool; fn draw(&self, scene: Scene); - fn glyph_atlas(&self) -> Arc>; + fn glyph_atlas(&self) -> Arc>; } pub trait PlatformDispatcher: Send + Sync { @@ -163,9 +163,13 @@ pub trait PlatformTextSystem: Send + Sync { fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result>; fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result>; fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option; + fn glyph_raster_bounds( + &self, + params: &GlyphRasterizationParams, + ) -> Result>; fn rasterize_glyph( &self, - glyph_id: &RasterizeGlyphParams, + params: &GlyphRasterizationParams, ) -> Result<(Size, Vec)>; fn layout_line(&self, text: &str, font_size: Pixels, runs: &[(usize, FontId)]) -> ShapedLine; fn wrap_line( diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index dc93a8cfa0520d57616feb1b0b3ce483dea00f15..11866dae6889f649a94febf1473d47fc3fc36552 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -1,6 +1,6 @@ use crate::{ - point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, Quad, - RasterizeGlyphParams, Scene, Size, + point, size, AtlasTextureId, DevicePixels, GlyphRasterizationParams, MetalAtlas, + MonochromeSprite, Quad, Scene, Size, }; use cocoa::{ base::{NO, YES}, @@ -22,7 +22,7 @@ pub struct MetalRenderer { sprites_pipeline_state: metal::RenderPipelineState, unit_vertices: metal::Buffer, instances: metal::Buffer, - glyph_atlas: Arc>, + glyph_atlas: Arc>, } impl MetalRenderer { @@ -126,7 +126,7 @@ impl MetalRenderer { &*self.layer } - pub fn glyph_atlas(&self) -> &Arc> { + pub fn glyph_atlas(&self) -> &Arc> { &self.glyph_atlas } diff --git a/crates/gpui3/src/platform/mac/text_system.rs b/crates/gpui3/src/platform/mac/text_system.rs index ea7c6a03022d60915fb0f7a3d42722637240a0bc..9aae748079f8736995e3c920a2783969678dd300 100644 --- a/crates/gpui3/src/platform/mac/text_system.rs +++ b/crates/gpui3/src/platform/mac/text_system.rs @@ -1,6 +1,6 @@ use crate::{ point, px, size, Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontStyle, - FontWeight, GlyphId, Pixels, PlatformTextSystem, Point, RasterizeGlyphParams, Result, + FontWeight, GlyphId, GlyphRasterizationParams, Pixels, PlatformTextSystem, Point, Result, ShapedGlyph, ShapedLine, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS, }; use anyhow::anyhow; @@ -134,9 +134,16 @@ impl PlatformTextSystem for MacTextSystem { self.0.read().glyph_for_char(font_id, ch) } + fn glyph_raster_bounds( + &self, + params: &GlyphRasterizationParams, + ) -> Result> { + self.0.read().raster_bounds(params) + } + fn rasterize_glyph( &self, - glyph_id: &RasterizeGlyphParams, + glyph_id: &GlyphRasterizationParams, ) -> Result<(Size, Vec)> { self.0.read().rasterize_glyph(glyph_id) } @@ -230,37 +237,54 @@ impl MacTextSystemState { }) } + fn raster_bounds(&self, params: &GlyphRasterizationParams) -> Result> { + let font = &self.fonts[params.font_id.0]; + let scale = Transform2F::from_scale(params.scale_factor); + Ok(font + .raster_bounds( + params.glyph_id.into(), + params.font_size.into(), + scale, + HintingOptions::None, + font_kit::canvas::RasterizationOptions::GrayscaleAa, + )? + .into()) + } + fn rasterize_glyph( &self, - glyph_id: &RasterizeGlyphParams, + params: &GlyphRasterizationParams, ) -> Result<(Size, Vec)> { - let font = &self.fonts[glyph_id.font_id.0]; - let scale = Transform2F::from_scale(glyph_id.scale_factor); - let glyph_bounds = font.raster_bounds( - glyph_id.glyph_id.into(), - glyph_id.font_size.into(), - scale, - HintingOptions::None, - font_kit::canvas::RasterizationOptions::GrayscaleAa, - )?; - - if glyph_bounds.width() == 0 || glyph_bounds.height() == 0 { + let glyph_bounds = self.raster_bounds(params)?; + + // let scale = Transform2F::from_scale(params.scale_factor); + // let glyph_bounds = font.raster_bounds( + // params.glyph_id.into(), + // params.font_size.into(), + // scale, + // HintingOptions::None, + // font_kit::canvas::RasterizationOptions::GrayscaleAa, + // )?; + + if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 { Err(anyhow!("glyph bounds are empty")) } else { - // Make room for subpixel variants. - let subpixel_padding = Vector2I::new( - glyph_id.subpixel_variant.x.min(1) as i32, - glyph_id.subpixel_variant.y.min(1) as i32, - ); - let bitmap_size = glyph_bounds.size() + subpixel_padding; + // Add an extra pixel when the subpixel variant isn't zero to make room for anti-aliasing. + let mut bitmap_size = glyph_bounds.size; + if params.subpixel_variant.x > 0 { + bitmap_size.width += DevicePixels(1); + } + if params.subpixel_variant.y > 0 { + bitmap_size.height += DevicePixels(1); + } - let mut bytes = vec![0; bitmap_size.x() as usize * bitmap_size.y() as usize]; + let mut bytes = vec![0; bitmap_size.width.0 as usize * bitmap_size.height.0 as usize]; let cx = CGContext::create_bitmap_context( Some(bytes.as_mut_ptr() as *mut _), - bitmap_size.x() as usize, - bitmap_size.y() as usize, + bitmap_size.width.0 as usize, + bitmap_size.height.0 as usize, 8, - bitmap_size.x() as usize, + bitmap_size.width.0 as usize, &CGColorSpace::create_device_gray(), kCGImageAlphaOnly, ); @@ -268,26 +292,27 @@ impl MacTextSystemState { // Move the origin to bottom left and account for scaling, this // makes drawing text consistent with the font-kit's raster_bounds. cx.translate( - -glyph_bounds.origin_x() as CGFloat, - (glyph_bounds.origin_y() + glyph_bounds.height()) as CGFloat, + -glyph_bounds.origin.x.0 as CGFloat, + (glyph_bounds.origin.y.0 + glyph_bounds.size.height.0) as CGFloat, ); cx.scale( - glyph_id.scale_factor as CGFloat, - glyph_id.scale_factor as CGFloat, + params.scale_factor as CGFloat, + params.scale_factor as CGFloat, ); - let subpixel_shift = glyph_id + let subpixel_shift = params .subpixel_variant - .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32 / glyph_id.scale_factor); + .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32 / params.scale_factor); cx.set_allows_font_subpixel_positioning(true); cx.set_should_subpixel_position_fonts(true); cx.set_allows_font_subpixel_quantization(false); cx.set_should_subpixel_quantize_fonts(false); - font.native_font() - .clone_with_font_size(f32::from(glyph_id.font_size) as CGFloat) + self.fonts[params.font_id.0] + .native_font() + .clone_with_font_size(f32::from(params.font_size) as CGFloat) .draw_glyphs( - &[u32::from(glyph_id.glyph_id) as CGGlyph], + &[u32::from(params.glyph_id) as CGGlyph], &[CGPoint::new( subpixel_shift.x as CGFloat, subpixel_shift.y as CGFloat, diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index 7d27976563a43ce7811e94276e50a510a8b80922..4c3b8e1a8c1b4595fb2234cdd724a482aeb76c42 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -1,10 +1,10 @@ use super::{ns_string, MetalRenderer, NSRange}; use crate::{ - point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers, - ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt, - Pixels, Platform, PlatformAtlas, PlatformDispatcher, PlatformInputHandler, PlatformScreen, - PlatformWindow, Point, RasterizeGlyphParams, Scene, Size, Timer, WindowAppearance, - WindowBounds, WindowKind, WindowOptions, WindowPromptLevel, + point, px, size, AnyWindowHandle, Bounds, Event, GlyphRasterizationParams, KeyDownEvent, + Keystroke, MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, + MouseMovedEvent, MouseUpEvent, NSRectExt, Pixels, Platform, PlatformAtlas, PlatformDispatcher, + PlatformInputHandler, PlatformScreen, PlatformWindow, Point, Scene, Size, Timer, + WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel, }; use block::ConcreteBlock; use cocoa::{ @@ -886,7 +886,7 @@ impl PlatformWindow for MacWindow { } } - fn glyph_atlas(&self) -> Arc> { + fn glyph_atlas(&self) -> Arc> { self.0.lock().renderer.glyph_atlas().clone() } } diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index b4752ab3e9b03f882d8d2184f5b999cf4f01cf02..5f55dd73fba8eadf81eedb63eb580f2b85289204 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -1,6 +1,6 @@ use std::{iter::Peekable, mem}; -use super::{Bounds, Hsla, Pixels, Point}; +use super::{Bounds, Hsla, Point}; use crate::{AtlasTextureId, AtlasTile, Corners, Edges, ScaledPixels}; use collections::BTreeMap; use smallvec::SmallVec; @@ -35,7 +35,7 @@ impl Scene { let primitive = primitive.into(); match primitive { - Primitive::Quad(mut quad) => { + Primitive::Quad(quad) => { layer.quads.push(quad); } Primitive::Sprite(sprite) => { @@ -233,9 +233,9 @@ impl From for Primitive { #[repr(C)] pub struct MonochromeSprite { pub order: u32, - pub bounds: Bounds, - pub clip_bounds: Bounds, - pub clip_corner_radii: Corners, + pub bounds: Bounds, + pub clip_bounds: Bounds, + pub clip_corner_radii: Corners, pub color: Hsla, pub tile: AtlasTile, } diff --git a/crates/gpui3/src/text_system.rs b/crates/gpui3/src/text_system.rs index ec0a85ca1656593b61009081c5d42a4b6b8643af..899554e4b02d360334ce756bb94e623ecf3ed4ed 100644 --- a/crates/gpui3/src/text_system.rs +++ b/crates/gpui3/src/text_system.rs @@ -215,9 +215,13 @@ impl TextSystem { }) } + pub fn raster_bounds(&self, params: &GlyphRasterizationParams) -> Result> { + self.platform_text_system.glyph_raster_bounds(params) + } + pub fn rasterize_glyph( &self, - glyph_id: &RasterizeGlyphParams, + glyph_id: &GlyphRasterizationParams, ) -> Result<(Size, Vec)> { self.platform_text_system.rasterize_glyph(glyph_id) } @@ -380,7 +384,7 @@ pub struct ShapedGlyph { } #[derive(Clone, Debug, PartialEq)] -pub struct RasterizeGlyphParams { +pub struct GlyphRasterizationParams { pub(crate) font_id: FontId, pub(crate) glyph_id: GlyphId, pub(crate) font_size: Pixels, @@ -388,9 +392,9 @@ pub struct RasterizeGlyphParams { pub(crate) scale_factor: f32, } -impl Eq for RasterizeGlyphParams {} +impl Eq for GlyphRasterizationParams {} -impl Hash for RasterizeGlyphParams { +impl Hash for GlyphRasterizationParams { fn hash(&self, state: &mut H) { self.font_id.0.hash(state); self.glyph_id.0.hash(state); diff --git a/crates/gpui3/src/text_system/line.rs b/crates/gpui3/src/text_system/line.rs index a3a578c7b1cedfdc5c69a9bfb58f016acf6852ba..3cccd51977df52f56a9bfe19db8a11006654e1b0 100644 --- a/crates/gpui3/src/text_system/line.rs +++ b/crates/gpui3/src/text_system/line.rs @@ -1,11 +1,10 @@ use crate::{ - black, point, px, Bounds, Corners, FontId, Hsla, Layout, MonochromeSprite, Pixels, Point, - RunStyle, ShapedBoundary, ShapedLine, ShapedRun, UnderlineStyle, WindowContext, + black, point, px, Bounds, FontId, Hsla, Layout, Pixels, Point, RunStyle, ShapedBoundary, + ShapedLine, ShapedRun, UnderlineStyle, WindowContext, }; use anyhow::Result; use smallvec::SmallVec; use std::sync::Arc; -use util::ResultExt; #[derive(Default, Debug, Clone)] pub struct Line { @@ -162,36 +161,14 @@ impl Line { if glyph.is_emoji { todo!() } else { - if let Some(tile) = cx - .rasterize_glyph( - run.font_id, - glyph.id, - self.layout.font_size, - glyph_origin, - cx.scale_factor(), - ) - .log_err() - { - let layer_id = cx.current_layer_id(); - - let bounds = Bounds { - origin: glyph_origin + todo!(), - size: todo!(), - }; - // cx.text_system().raster_bounds() - - cx.scene().insert( - layer_id, - MonochromeSprite { - order: layout.order, - bounds, - clip_bounds: bounds, - clip_corner_radii: Corners::default(), - color, - tile, - }, - ); - } + cx.paint_glyph( + glyph_origin, + layout.order, + run.font_id, + glyph.id, + self.layout.font_size, + color, + )?; } } diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index dc7dca03269bf90f48575a278962c26e83e67f20..58f06874823f48cd9a53a273e100db2dd68c044d 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,8 +1,9 @@ use crate::{ px, AnyView, AppContext, AtlasTile, AvailableSpace, Bounds, Context, Effect, Element, EntityId, - FontId, GlyphId, Handle, LayoutId, MainThread, MainThreadOnly, Pixels, PlatformAtlas, - PlatformWindow, Point, RasterizeGlyphParams, Reference, Scene, Size, StackContext, - StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, + FontId, GlyphId, GlyphRasterizationParams, Handle, Hsla, IsZero, LayoutId, MainThread, + MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, Reference, + Scene, Size, StackContext, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, + SUBPIXEL_VARIANTS, }; use anyhow::Result; use futures::Future; @@ -15,7 +16,7 @@ pub struct AnyWindow {} pub struct Window { handle: AnyWindowHandle, platform_window: MainThreadOnly>, - glyph_atlas: Arc>, + glyph_atlas: Arc>, rem_size: Pixels, content_size: Size, layout_engine: TaffyLayoutEngine, @@ -165,6 +166,57 @@ impl<'a, 'w> WindowContext<'a, 'w> { }) } + pub fn paint_glyph( + &mut self, + origin: Point, + order: u32, + font_id: FontId, + glyph_id: GlyphId, + font_size: Pixels, + color: Hsla, + ) -> Result<()> { + let scale_factor = self.scale_factor(); + let origin = origin.scale(scale_factor); + let subpixel_variant = Point { + x: (origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, + y: (origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, + }; + let params = GlyphRasterizationParams { + font_id, + glyph_id, + font_size, + subpixel_variant, + scale_factor, + }; + + let raster_bounds = self.text_system().raster_bounds(¶ms)?; + + if !raster_bounds.is_zero() { + let layer_id = self.current_layer_id(); + let bounds = Bounds { + origin: origin + raster_bounds.origin.map(Into::into), + size: raster_bounds.size.map(Into::into), + }; + let tile = self + .window + .glyph_atlas + .get_or_insert_with(¶ms, &mut || self.text_system().rasterize_glyph(¶ms))?; + + self.window.scene.insert( + layer_id, + MonochromeSprite { + order, + bounds, + clip_bounds: bounds, + clip_corner_radii: Default::default(), + color, + tile, + }, + ); + } + Ok(()) + } + pub fn rasterize_glyph( &self, font_id: FontId, @@ -178,7 +230,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { x: (target_position.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, y: (target_position.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, }; - let rasterized_glyph_id = RasterizeGlyphParams { + let rasterized_glyph_id = GlyphRasterizationParams { font_id, glyph_id, font_size, From d3916b84c93c1bf5c2be849812ec47216e76a942 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 3 Oct 2023 13:55:53 -0600 Subject: [PATCH 13/28] Checkpoint --- crates/gpui3/src/color.rs | 9 +++++++++ crates/gpui3/src/elements/text.rs | 2 -- crates/gpui3/src/window.rs | 11 ----------- crates/storybook2/src/workspace.rs | 6 +++--- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/crates/gpui3/src/color.rs b/crates/gpui3/src/color.rs index 58bbcb5db2af506ae97bc196e685d859f1e37b14..1ace030482bee912acc5aebf424d8bc10c60407c 100644 --- a/crates/gpui3/src/color.rs +++ b/crates/gpui3/src/color.rs @@ -146,6 +146,15 @@ pub fn black() -> Hsla { } } +pub fn white() -> Hsla { + Hsla { + h: 0., + s: 0., + l: 1., + a: 1., + } +} + impl Hsla { /// Returns true if the HSLA color is fully transparent, false otherwise. pub fn is_transparent(&self) -> bool { diff --git a/crates/gpui3/src/elements/text.rs b/crates/gpui3/src/elements/text.rs index db94b96b7747afdda0ff2cdcdcc12238af5e2e75..b5f8fc5ec520c4284742f29008901b7b5b54cc94 100644 --- a/crates/gpui3/src/elements/text.rs +++ b/crates/gpui3/src/elements/text.rs @@ -39,8 +39,6 @@ impl Element for Text { _view: &mut S, cx: &mut ViewContext, ) -> Result<(LayoutId, Self::FrameState)> { - dbg!("layout text"); - let text_system = cx.text_system().clone(); let text_style = cx.text_style(); let font_size = text_style.font_size * cx.rem_size(); diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 58f06874823f48cd9a53a273e100db2dd68c044d..605f1390bfbf635884101607ecf3bf0e615c1838 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -251,15 +251,11 @@ impl<'a, 'w> WindowContext<'a, 'w> { let (root_layout_id, mut frame_state) = root_view.layout(&mut (), cx)?; let available_space = cx.window.content_size.map(Into::into); - dbg!("computing layout"); cx.window .layout_engine .compute_layout(root_layout_id, available_space)?; - dbg!("asking for layout"); let layout = cx.window.layout_engine.layout(root_layout_id)?; - dbg!("painting root view"); - root_view.paint(layout, &mut (), &mut frame_state, cx)?; cx.window.root_view = Some(root_view); let scene = cx.window.scene.take(); @@ -277,13 +273,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { } } -impl MainThread> { - // todo!("implement other methods that use platform window") - fn platform_window(&self) -> &dyn PlatformWindow { - self.window.platform_window.borrow_on_main_thread().as_ref() - } -} - impl Context for WindowContext<'_, '_> { type EntityContext<'a, 'w, T: 'static + Send + Sync> = ViewContext<'a, 'w, T>; type Result = T; diff --git a/crates/storybook2/src/workspace.rs b/crates/storybook2/src/workspace.rs index c48993a9afd5955dbafc4bb5b0e0f7512638fa25..1ffb536713eb30737c170d1481c33dabd6d31865 100644 --- a/crates/storybook2/src/workspace.rs +++ b/crates/storybook2/src/workspace.rs @@ -4,7 +4,7 @@ use crate::{ themes::rose_pine_dawn, }; use gpui3::{ - black, div, img, svg, view, Context, Element, ParentElement, RootView, StyleHelpers, View, + div, img, svg, view, white, Context, Element, ParentElement, RootView, StyleHelpers, View, ViewContext, WindowContext, }; @@ -29,10 +29,10 @@ impl Workspace { let theme = rose_pine_dawn(); div() .font("Helvetica") - .text_color(black()) + .text_color(white()) .text_base() .size_full() - .fill(theme.middle.positive.default.background) + .fill(theme.middle.base.default.background) .child("Hello world") // TODO: Implement style. From 45429b5400e9f05562888fdbd22e532b0434a9aa Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 3 Oct 2023 14:25:29 -0600 Subject: [PATCH 14/28] WIP --- crates/gpui/src/platform/mac/renderer.rs | 4 +- crates/gpui2/src/color.rs | 9 ++++ crates/gpui3/src/geometry.rs | 10 +++-- crates/gpui3/src/window.rs | 10 +++-- crates/storybook/src/workspace.rs | 55 ++++++++++++++---------- crates/storybook2/src/workspace.rs | 12 +++--- 6 files changed, 62 insertions(+), 38 deletions(-) diff --git a/crates/gpui/src/platform/mac/renderer.rs b/crates/gpui/src/platform/mac/renderer.rs index 55ec3e9e9a2f3f72c03103adf045721105d3da83..ef0757ea3e79bceb94f2f0c22c3142920335866d 100644 --- a/crates/gpui/src/platform/mac/renderer.rs +++ b/crates/gpui/src/platform/mac/renderer.rs @@ -631,7 +631,9 @@ impl Renderer { glyph.origin, ) { // Snap sprite to pixel grid. - let origin = (glyph.origin * scale_factor).floor() + sprite.offset.to_f32(); + let origin = dbg!( + dbg!((glyph.origin * scale_factor).floor()) + dbg!(sprite.offset.to_f32()) + ); sprites_by_atlas .entry(sprite.atlas_id) .or_insert_with(Vec::new) diff --git a/crates/gpui2/src/color.rs b/crates/gpui2/src/color.rs index 11590f967cdba57edebf52a23fd1721cbf1fc899..b128ea691c188247a1d47057b653c93c246f6571 100644 --- a/crates/gpui2/src/color.rs +++ b/crates/gpui2/src/color.rs @@ -160,6 +160,15 @@ pub fn black() -> Hsla { } } +pub fn white() -> Hsla { + Hsla { + h: 0., + s: 0., + l: 1., + a: 1., + } +} + impl From for Hsla { fn from(color: Rgba) -> Self { let r = color.r; diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index 5814730763214877c9adcc7f336a8425f2928adb..ca8e2e5761dbb323ca27ca98d954c6d5b1eeb1d4 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -484,10 +484,6 @@ impl Pixels { Self(self.0.round()) } - pub fn floor(&self) -> Self { - Self(self.0.floor()) - } - pub fn scale(&self, factor: f32) -> ScaledPixels { ScaledPixels(self.0 * factor) } @@ -604,6 +600,12 @@ impl From for DevicePixels { #[repr(transparent)] pub struct ScaledPixels(pub(crate) f32); +impl ScaledPixels { + pub fn floor(&self) -> Self { + Self(self.0.floor()) + } +} + impl Eq for ScaledPixels {} impl Debug for ScaledPixels { diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 605f1390bfbf635884101607ecf3bf0e615c1838..d6a7e23604eb76d2e882c9c81e94cd7588a098f0 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -176,10 +176,10 @@ impl<'a, 'w> WindowContext<'a, 'w> { color: Hsla, ) -> Result<()> { let scale_factor = self.scale_factor(); - let origin = origin.scale(scale_factor); + let glyph_origin = origin.scale(scale_factor); let subpixel_variant = Point { - x: (origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, - y: (origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, + x: (glyph_origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, + y: (glyph_origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, }; let params = GlyphRasterizationParams { font_id, @@ -193,10 +193,12 @@ impl<'a, 'w> WindowContext<'a, 'w> { if !raster_bounds.is_zero() { let layer_id = self.current_layer_id(); + let offset = raster_bounds.origin.map(Into::into); let bounds = Bounds { - origin: origin + raster_bounds.origin.map(Into::into), + origin: dbg!(dbg!(glyph_origin.map(|px| px.floor())) + dbg!(offset)), size: raster_bounds.size.map(Into::into), }; + let tile = self .window .glyph_atlas diff --git a/crates/storybook/src/workspace.rs b/crates/storybook/src/workspace.rs index d9f9c22fcb41f73856c8ebb6be9b0d0024d48780..0ec7b1881d64641ea046f4bb00956e32664b8109 100644 --- a/crates/storybook/src/workspace.rs +++ b/crates/storybook/src/workspace.rs @@ -1,8 +1,9 @@ use crate::{collab_panel::collab_panel, theme::theme}; use gpui2::{ + black, elements::{div, div::ScrollState, img, svg}, style::{StyleHelpers, Styleable}, - Element, IntoElement, ParentElement, ViewContext, + white, Element, IntoElement, ParentElement, ViewContext, }; #[derive(Element, Default)] @@ -19,29 +20,37 @@ impl WorkspaceElement { fn render(&mut self, _: &mut V, cx: &mut ViewContext) -> impl IntoElement { let theme = theme(cx); - div() + return div() .size_full() - .flex() - .flex_col() - .font("Zed Sans Extended") - .gap_0() - .justify_start() - .items_start() - .text_color(theme.lowest.base.default.foreground) - .fill(theme.middle.base.default.background) - .child(titlebar()) - .child( - div() - .flex_1() - .w_full() - .flex() - .flex_row() - .overflow_hidden() - .child(collab_panel(self.left_scroll_state.clone())) - .child(div().h_full().flex_1()) - .child(collab_panel(self.right_scroll_state.clone())), - ) - .child(statusbar()) + .fill(white()) + .font("Helvetica") + .text_base() + .text_color(black()) + .child("Hey"); + + // div() + // .size_full() + // .flex() + // .flex_col() + // .font("Zed Sans Extended") + // .gap_0() + // .justify_start() + // .items_start() + // .text_color(theme.lowest.base.default.foreground) + // .fill(theme.middle.base.default.background) + // .child(titlebar()) + // .child( + // div() + // .flex_1() + // .w_full() + // .flex() + // .flex_row() + // .overflow_hidden() + // .child(collab_panel(self.left_scroll_state.clone())) + // .child(div().h_full().flex_1()) + // .child(collab_panel(self.right_scroll_state.clone())), + // ) + // .child(statusbar()) } } diff --git a/crates/storybook2/src/workspace.rs b/crates/storybook2/src/workspace.rs index 1ffb536713eb30737c170d1481c33dabd6d31865..f66f4832e8eb0be81eab44d48ec0c0e497bd4fec 100644 --- a/crates/storybook2/src/workspace.rs +++ b/crates/storybook2/src/workspace.rs @@ -4,8 +4,8 @@ use crate::{ themes::rose_pine_dawn, }; use gpui3::{ - div, img, svg, view, white, Context, Element, ParentElement, RootView, StyleHelpers, View, - ViewContext, WindowContext, + black, div, img, svg, view, white, Context, Element, ParentElement, RootView, StyleHelpers, + View, ViewContext, WindowContext, }; pub struct Workspace { @@ -28,12 +28,12 @@ impl Workspace { fn render(&mut self, cx: &mut ViewContext) -> impl Element { let theme = rose_pine_dawn(); div() + .size_full() .font("Helvetica") - .text_color(white()) .text_base() - .size_full() - .fill(theme.middle.base.default.background) - .child("Hello world") + .fill(white()) + .text_color(black()) + .child("Hey") // TODO: Implement style. //.size_full().fill(gpui3::hsla(0.83, 1., 0.5, 1.)) From 4208ac2958cac0de65d82d0da30cb9c8a5c741fc Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 3 Oct 2023 15:17:45 -0600 Subject: [PATCH 15/28] WIP --- crates/gpui/src/platform/mac/renderer.rs | 5 +-- crates/gpui/src/platform/mac/sprite_cache.rs | 2 +- crates/gpui3/src/geometry.rs | 21 ++++------- crates/gpui3/src/platform/mac/metal_atlas.rs | 12 ++++-- crates/gpui3/src/platform/mac/text_system.rs | 11 ------ crates/gpui3/src/window.rs | 39 ++++---------------- crates/storybook/src/workspace.rs | 2 +- 7 files changed, 26 insertions(+), 66 deletions(-) diff --git a/crates/gpui/src/platform/mac/renderer.rs b/crates/gpui/src/platform/mac/renderer.rs index ef0757ea3e79bceb94f2f0c22c3142920335866d..9b33e4c92a75eb079cd7f500894bb0535aca7df9 100644 --- a/crates/gpui/src/platform/mac/renderer.rs +++ b/crates/gpui/src/platform/mac/renderer.rs @@ -631,9 +631,8 @@ impl Renderer { glyph.origin, ) { // Snap sprite to pixel grid. - let origin = dbg!( - dbg!((glyph.origin * scale_factor).floor()) + dbg!(sprite.offset.to_f32()) - ); + let origin = (glyph.origin * scale_factor).floor() + sprite.offset.to_f32(); + // dbg!(origin); sprites_by_atlas .entry(sprite.atlas_id) .or_insert_with(Vec::new) diff --git a/crates/gpui/src/platform/mac/sprite_cache.rs b/crates/gpui/src/platform/mac/sprite_cache.rs index d283fe52aa8208e72e1d90ec3e8cbb99a11a4538..c579d66577f6a987abde2331f174c1b9d8250a7a 100644 --- a/crates/gpui/src/platform/mac/sprite_cache.rs +++ b/crates/gpui/src/platform/mac/sprite_cache.rs @@ -114,7 +114,7 @@ impl SpriteCache { let (alloc_id, atlas_bounds) = self .atlases - .upload(glyph_bounds.size(), &mask) + .upload(dbg!(glyph_bounds.size()), &mask) .expect("could not upload glyph"); Some(GlyphSprite { atlas_id: alloc_id.atlas_id, diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index ca8e2e5761dbb323ca27ca98d954c6d5b1eeb1d4..4ab7fc803031cae9f1f02c4411e7c95fa28c2b06 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -548,20 +548,7 @@ impl From for f64 { } #[derive( - Add, - AddAssign, - Clone, - Copy, - Debug, - Default, - Div, - Eq, - Hash, - Ord, - PartialEq, - PartialOrd, - Sub, - SubAssign, + Add, AddAssign, Clone, Copy, Default, Div, Eq, Hash, Ord, PartialEq, PartialOrd, Sub, SubAssign, )] #[repr(transparent)] pub struct DevicePixels(pub(crate) i32); @@ -572,6 +559,12 @@ impl DevicePixels { } } +impl std::fmt::Debug for DevicePixels { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} px (device)", self.0) + } +} + impl From for i32 { fn from(device_pixels: DevicePixels) -> Self { device_pixels.0 diff --git a/crates/gpui3/src/platform/mac/metal_atlas.rs b/crates/gpui3/src/platform/mac/metal_atlas.rs index ae2d7b2d514d7f91f09375d16b27a80c7b8a2558..34892fd44d1cfa7fd0ed812d3facf67f5d2abbcb 100644 --- a/crates/gpui3/src/platform/mac/metal_atlas.rs +++ b/crates/gpui3/src/platform/mac/metal_atlas.rs @@ -59,10 +59,10 @@ where .textures .iter_mut() .rev() - .find_map(|texture| texture.allocate(size, &bytes)) + .find_map(|texture| texture.upload(size, &bytes)) .or_else(|| { let texture = lock.push_texture(size); - texture.allocate(size, &bytes) + texture.upload(size, &bytes) }) .ok_or_else(|| anyhow!("could not allocate in new texture"))?; lock.tiles_by_key.insert(key.clone(), tile.clone()); @@ -118,7 +118,8 @@ struct MetalAtlasTexture { } impl MetalAtlasTexture { - fn allocate(&mut self, size: Size, bytes: &[u8]) -> Option { + fn upload(&mut self, size: Size, bytes: &[u8]) -> Option { + dbg!(size); let size = size.into(); let allocation = self.allocator.allocate(size)?; let tile = AtlasTile { @@ -126,6 +127,9 @@ impl MetalAtlasTexture { tile_id: allocation.id.into(), bounds: allocation.rectangle.into(), }; + + // eprintln!("upload {:?}", tile.bounds); + let region = metal::MTLRegion::new_2d( tile.bounds.origin.x.into(), tile.bounds.origin.y.into(), @@ -153,7 +157,7 @@ impl MetalAtlasTexture { impl From> for etagere::Size { fn from(size: Size) -> Self { - etagere::Size::new(size.width.into(), size.width.into()) + etagere::Size::new(size.width.into(), size.height.into()) } } diff --git a/crates/gpui3/src/platform/mac/text_system.rs b/crates/gpui3/src/platform/mac/text_system.rs index 9aae748079f8736995e3c920a2783969678dd300..bbe2080fd580c226145d399c2b44e9c918cd5d5a 100644 --- a/crates/gpui3/src/platform/mac/text_system.rs +++ b/crates/gpui3/src/platform/mac/text_system.rs @@ -256,16 +256,6 @@ impl MacTextSystemState { params: &GlyphRasterizationParams, ) -> Result<(Size, Vec)> { let glyph_bounds = self.raster_bounds(params)?; - - // let scale = Transform2F::from_scale(params.scale_factor); - // let glyph_bounds = font.raster_bounds( - // params.glyph_id.into(), - // params.font_size.into(), - // scale, - // HintingOptions::None, - // font_kit::canvas::RasterizationOptions::GrayscaleAa, - // )?; - if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 { Err(anyhow!("glyph bounds are empty")) } else { @@ -303,7 +293,6 @@ impl MacTextSystemState { let subpixel_shift = params .subpixel_variant .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32 / params.scale_factor); - cx.set_allows_font_subpixel_positioning(true); cx.set_should_subpixel_position_fonts(true); cx.set_allows_font_subpixel_quantization(false); diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index d6a7e23604eb76d2e882c9c81e94cd7588a098f0..56004d991277e6899d2ac4700a28801bb17f71ac 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,8 +1,8 @@ use crate::{ - px, AnyView, AppContext, AtlasTile, AvailableSpace, Bounds, Context, Effect, Element, EntityId, - FontId, GlyphId, GlyphRasterizationParams, Handle, Hsla, IsZero, LayoutId, MainThread, - MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, Reference, - Scene, Size, StackContext, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, + px, AnyView, AppContext, AvailableSpace, Bounds, Context, Effect, Element, EntityId, FontId, + GlyphId, GlyphRasterizationParams, Handle, Hsla, IsZero, LayoutId, MainThread, MainThreadOnly, + MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, Reference, Scene, Size, + StackContext, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; @@ -194,8 +194,10 @@ impl<'a, 'w> WindowContext<'a, 'w> { if !raster_bounds.is_zero() { let layer_id = self.current_layer_id(); let offset = raster_bounds.origin.map(Into::into); + let glyph_origin = glyph_origin.map(|px| px.floor()) + offset; + // dbg!(glyph_origin); let bounds = Bounds { - origin: dbg!(dbg!(glyph_origin.map(|px| px.floor())) + dbg!(offset)), + origin: glyph_origin, size: raster_bounds.size.map(Into::into), }; @@ -219,33 +221,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { Ok(()) } - pub fn rasterize_glyph( - &self, - font_id: FontId, - glyph_id: GlyphId, - font_size: Pixels, - target_position: Point, - scale_factor: f32, - ) -> Result { - let target_position = target_position * scale_factor; - let subpixel_variant = Point { - x: (target_position.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, - y: (target_position.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, - }; - let rasterized_glyph_id = GlyphRasterizationParams { - font_id, - glyph_id, - font_size, - subpixel_variant, - scale_factor, - }; - self.window - .glyph_atlas - .get_or_insert_with(&rasterized_glyph_id, &mut || { - self.text_system().rasterize_glyph(&rasterized_glyph_id) - }) - } - pub(crate) fn draw(&mut self) -> Result<()> { let unit_entity = self.unit_entity.clone(); self.update_entity(&unit_entity, |_, cx| { diff --git a/crates/storybook/src/workspace.rs b/crates/storybook/src/workspace.rs index 0ec7b1881d64641ea046f4bb00956e32664b8109..64181b2bd7fb47470ed6a387c8bab3a35287022e 100644 --- a/crates/storybook/src/workspace.rs +++ b/crates/storybook/src/workspace.rs @@ -1,4 +1,4 @@ -use crate::{collab_panel::collab_panel, theme::theme}; +use crate::theme::theme; use gpui2::{ black, elements::{div, div::ScrollState, img, svg}, From 550d9a9f71dfe1e4dba7d9d76c5ddd0d299ca7c9 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 3 Oct 2023 16:17:25 -0600 Subject: [PATCH 16/28] Checkpoint --- crates/gpui/src/platform/mac/atlas.rs | 5 +- crates/gpui/src/platform/mac/renderer.rs | 3 +- crates/gpui/src/platform/mac/sprite_cache.rs | 2 +- crates/gpui3/src/app/entity_map.rs | 11 +-- crates/gpui3/src/platform.rs | 16 ++--- crates/gpui3/src/platform/mac/metal_atlas.rs | 13 ++-- .../gpui3/src/platform/mac/metal_renderer.rs | 71 +++++++++++++------ crates/gpui3/src/platform/mac/text_system.rs | 24 +++---- crates/gpui3/src/platform/mac/window.rs | 8 +-- crates/gpui3/src/text_system.rs | 10 +-- crates/gpui3/src/window.rs | 13 ++-- 11 files changed, 96 insertions(+), 80 deletions(-) diff --git a/crates/gpui/src/platform/mac/atlas.rs b/crates/gpui/src/platform/mac/atlas.rs index a529513ef5ef38faab25cf983e4820066ce9d28b..d73cc3351309b7f7a6b545702de96cbe044a560d 100644 --- a/crates/gpui/src/platform/mac/atlas.rs +++ b/crates/gpui/src/platform/mac/atlas.rs @@ -109,6 +109,7 @@ impl AtlasAllocator { }; descriptor.set_width(size.x() as u64); descriptor.set_height(size.y() as u64); + self.device.new_texture(&descriptor) } else { self.device.new_texture(&self.texture_descriptor) @@ -146,10 +147,10 @@ impl Atlas { bounds.size().y() as u64, ); self.texture.replace_region( - region, + dbg!(region), 0, bytes.as_ptr() as *const _, - (bounds.size().x() * self.bytes_per_pixel() as i32) as u64, + dbg!((bounds.size().x() * self.bytes_per_pixel() as i32) as u64), ); } diff --git a/crates/gpui/src/platform/mac/renderer.rs b/crates/gpui/src/platform/mac/renderer.rs index 9b33e4c92a75eb079cd7f500894bb0535aca7df9..9ef46beaa5910e5cf7013bb4c5d3e7951469039c 100644 --- a/crates/gpui/src/platform/mac/renderer.rs +++ b/crates/gpui/src/platform/mac/renderer.rs @@ -632,7 +632,8 @@ impl Renderer { ) { // Snap sprite to pixel grid. let origin = (glyph.origin * scale_factor).floor() + sprite.offset.to_f32(); - // dbg!(origin); + + dbg!(origin); sprites_by_atlas .entry(sprite.atlas_id) .or_insert_with(Vec::new) diff --git a/crates/gpui/src/platform/mac/sprite_cache.rs b/crates/gpui/src/platform/mac/sprite_cache.rs index c579d66577f6a987abde2331f174c1b9d8250a7a..d283fe52aa8208e72e1d90ec3e8cbb99a11a4538 100644 --- a/crates/gpui/src/platform/mac/sprite_cache.rs +++ b/crates/gpui/src/platform/mac/sprite_cache.rs @@ -114,7 +114,7 @@ impl SpriteCache { let (alloc_id, atlas_bounds) = self .atlases - .upload(dbg!(glyph_bounds.size()), &mask) + .upload(glyph_bounds.size(), &mask) .expect("could not upload glyph"); Some(GlyphSprite { atlas_id: alloc_id.atlas_id, diff --git a/crates/gpui3/src/app/entity_map.rs b/crates/gpui3/src/app/entity_map.rs index 4976b67aae889a116a772d78e1b7809863a73837..29db57bb39cc518b8c74bdcac13dd4c551d215f1 100644 --- a/crates/gpui3/src/app/entity_map.rs +++ b/crates/gpui3/src/app/entity_map.rs @@ -125,11 +125,12 @@ impl Clone for Handle { impl Drop for Handle { fn drop(&mut self) { - if let Some(ref_counts) = self.ref_counts.upgrade() { - if let Some(count) = ref_counts.read().get(self.id) { - let prev_count = count.fetch_sub(1, SeqCst); - assert_ne!(prev_count, 0, "Detected over-release of a handle."); - } + if let Some(_ref_counts) = self.ref_counts.upgrade() { + // todo!() + // if let Some(count) = ref_counts.read().get(self.id) { + // let prev_count = count.fetch_sub(1, SeqCst); + // assert_ne!(prev_count, 0, "Detected over-release of a handle."); + // } } } } diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 9552bb227c4b03a4aeb2a86e9e57e48ba41502b9..8fd70ba44168e1530b07b6c50646365a7ca1a8aa 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -6,8 +6,8 @@ mod mac; mod test; use crate::{ - AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, - GlyphRasterizationParams, Pixels, Point, Result, Scene, ShapedLine, SharedString, Size, + AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, GlyphRasterParams, + Pixels, Point, Result, Scene, ShapedLine, SharedString, Size, }; use anyhow::anyhow; use async_task::Runnable; @@ -147,7 +147,7 @@ pub trait PlatformWindow { fn is_topmost_for_position(&self, position: Point) -> bool; fn draw(&self, scene: Scene); - fn glyph_atlas(&self) -> Arc>; + fn glyph_atlas(&self) -> Arc>; } pub trait PlatformDispatcher: Send + Sync { @@ -163,14 +163,8 @@ pub trait PlatformTextSystem: Send + Sync { fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result>; fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result>; fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option; - fn glyph_raster_bounds( - &self, - params: &GlyphRasterizationParams, - ) -> Result>; - fn rasterize_glyph( - &self, - params: &GlyphRasterizationParams, - ) -> Result<(Size, Vec)>; + fn glyph_raster_bounds(&self, params: &GlyphRasterParams) -> Result>; + fn rasterize_glyph(&self, params: &GlyphRasterParams) -> Result<(Size, Vec)>; fn layout_line(&self, text: &str, font_size: Pixels, runs: &[(usize, FontId)]) -> ShapedLine; fn wrap_line( &self, diff --git a/crates/gpui3/src/platform/mac/metal_atlas.rs b/crates/gpui3/src/platform/mac/metal_atlas.rs index 34892fd44d1cfa7fd0ed812d3facf67f5d2abbcb..7f649800eada0e8daf668172b4a0f3541f3acc4d 100644 --- a/crates/gpui3/src/platform/mac/metal_atlas.rs +++ b/crates/gpui3/src/platform/mac/metal_atlas.rs @@ -93,7 +93,7 @@ impl MetalAtlasState { }; descriptor.set_width(min_size.width.into()); descriptor.set_height(min_size.height.into()); - + descriptor.set_pixel_format(metal::MTLPixelFormat::Depth32Float); size = min_size; metal_texture = self.device.new_texture(&descriptor); } else { @@ -119,17 +119,16 @@ struct MetalAtlasTexture { impl MetalAtlasTexture { fn upload(&mut self, size: Size, bytes: &[u8]) -> Option { - dbg!(size); - let size = size.into(); - let allocation = self.allocator.allocate(size)?; + let allocation = self.allocator.allocate(size.into())?; let tile = AtlasTile { texture_id: self.id, tile_id: allocation.id.into(), - bounds: allocation.rectangle.into(), + bounds: Bounds { + origin: allocation.rectangle.min.into(), + size, + }, }; - // eprintln!("upload {:?}", tile.bounds); - let region = metal::MTLRegion::new_2d( tile.bounds.origin.x.into(), tile.bounds.origin.y.into(), diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index 11866dae6889f649a94febf1473d47fc3fc36552..b06d186f3497691018ee45ccc9601a0484d13877 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -1,6 +1,40 @@ +// How can I fix this? +// -[MTLDebugRenderCommandEncoder setRenderPipelineState:]:1580: failed assertion `Set Render Pipeline State Validation +// For depth attachment, the render pipeline's pixelFormat (MTLPixelFormatInvalid) does not match the framebuffer's pixelFormat (MTLPixelFormatDepth32Float). +// ' +// -[MTLDebugRenderCommandEncoder setRenderPipelineState:]:1580: failed assertion `Set Render Pipeline State Validation +// For depth attachment, the render pipeline's pixelFormat (MTLPixelFormatInvalid) does not match the framebuffer's pixelFormat (MTLPixelFormatDepth32Float). +// // It seems like the error you're facing has to do with the difference between the +// pixel format of the render pipeline and the framebuffer. If the pixel format of +// those two doesn't match, Metal throws an error. To resolve this issue, you need +// to set the pixel format of your depth attachment and your render pipeline state +// to the same value. + +// In this code: +// --- +/* +descriptor.set_depth_attachment_pixel_format(MTLPixelFormat::Depth32Float); +*/ +// --- +// you've commented out the line where you set the depth attachment pixel format +// to MTLPixelFormat::Depth32Float. If you uncomment this line, it should resolve +// the error as your depth attachment's pixel format will then match your framebuffer's. + +// If you still encounter the same problem, you might be configuring another render +// pipeline state elsewhere in your code with a different depth pixel format. Make +// sure all configurations have matching pixel formats. + +// Additionally, be aware of the limitations of certain pixel formats. For example, +// not all pixel formats support depth stencil attachments, and some are only +// compatible with certain types of GPU hardware. Implementation of pixel formats +// can vary between different versions of iOS, so ensure that your choice of pixel +// format is compatible with your minimum target version. +// +// I want it to be UANorm + use crate::{ - point, size, AtlasTextureId, DevicePixels, GlyphRasterizationParams, MetalAtlas, - MonochromeSprite, Quad, Scene, Size, + point, size, AtlasTextureId, DevicePixels, GlyphRasterParams, MetalAtlas, MonochromeSprite, + Quad, Scene, Size, }; use cocoa::{ base::{NO, YES}, @@ -22,7 +56,7 @@ pub struct MetalRenderer { sprites_pipeline_state: metal::RenderPipelineState, unit_vertices: metal::Buffer, instances: metal::Buffer, - glyph_atlas: Arc>, + glyph_atlas: Arc>, } impl MetalRenderer { @@ -104,7 +138,7 @@ impl MetalRenderer { let glyph_atlas = Arc::new(MetalAtlas::new( Size { width: DevicePixels(1024), - height: DevicePixels(1024), + height: DevicePixels(768), }, MTLPixelFormat::A8Unorm, device.clone(), @@ -126,7 +160,7 @@ impl MetalRenderer { &*self.layer } - pub fn glyph_atlas(&self) -> &Arc> { + pub fn glyph_atlas(&self) -> &Arc> { &self.glyph_atlas } @@ -151,18 +185,17 @@ impl MetalRenderer { let render_pass_descriptor = metal::RenderPassDescriptor::new(); - let depth_texture_desc = metal::TextureDescriptor::new(); - depth_texture_desc.set_pixel_format(metal::MTLPixelFormat::Depth32Float); - depth_texture_desc.set_storage_mode(metal::MTLStorageMode::Private); - depth_texture_desc.set_usage(metal::MTLTextureUsage::RenderTarget); - depth_texture_desc.set_width(i32::from(viewport_size.width) as u64); - depth_texture_desc.set_height(i32::from(viewport_size.height) as u64); - let depth_texture = self.device.new_texture(&depth_texture_desc); - let depth_attachment = render_pass_descriptor.depth_attachment().unwrap(); - - depth_attachment.set_texture(Some(&depth_texture)); - depth_attachment.set_clear_depth(1.); - depth_attachment.set_store_action(metal::MTLStoreAction::Store); + // let depth_texture_desc = metal::TextureDescriptor::new(); + // depth_texture_desc.set_pixel_format(metal::MTLPixelFormat::Depth32Float); + // depth_texture_desc.set_storage_mode(metal::MTLStorageMode::Private); + // depth_texture_desc.set_usage(metal::MTLTextureUsage::RenderTarget); + // depth_texture_desc.set_width(i32::from(viewport_size.width) as u64); + // depth_texture_desc.set_height(i32::from(viewport_size.height) as u64); + // let depth_texture = self.device.new_texture(&depth_texture_desc); + // let depth_attachment = render_pass_descriptor.depth_attachment().unwrap(); + // depth_attachment.set_texture(Some(&depth_texture)); + // depth_attachment.set_clear_depth(1.); + // depth_attachment.set_store_action(metal::MTLStoreAction::Store); let color_attachment = render_pass_descriptor .color_attachments() @@ -289,8 +322,6 @@ impl MetalRenderer { viewport_size: Size, command_encoder: &metal::RenderCommandEncoderRef, ) { - // dbg!(sprites); - if sprites.is_empty() { return; } @@ -386,7 +417,7 @@ fn build_pipeline_state( color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One); color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha); color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One); - // descriptor.set_depth_attachment_pixel_format(MTLPixelFormat::Depth32Float); + descriptor.set_depth_attachment_pixel_format(MTLPixelFormat::Invalid); device .new_render_pipeline_state(&descriptor) diff --git a/crates/gpui3/src/platform/mac/text_system.rs b/crates/gpui3/src/platform/mac/text_system.rs index bbe2080fd580c226145d399c2b44e9c918cd5d5a..29c97f4754bfafde61cfa9d9aa444cfee8cb7213 100644 --- a/crates/gpui3/src/platform/mac/text_system.rs +++ b/crates/gpui3/src/platform/mac/text_system.rs @@ -1,7 +1,7 @@ use crate::{ point, px, size, Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontStyle, - FontWeight, GlyphId, GlyphRasterizationParams, Pixels, PlatformTextSystem, Point, Result, - ShapedGlyph, ShapedLine, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS, + FontWeight, GlyphId, GlyphRasterParams, Pixels, PlatformTextSystem, Point, Result, ShapedGlyph, + ShapedLine, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS, }; use anyhow::anyhow; use cocoa::appkit::{CGFloat, CGPoint}; @@ -134,16 +134,13 @@ impl PlatformTextSystem for MacTextSystem { self.0.read().glyph_for_char(font_id, ch) } - fn glyph_raster_bounds( - &self, - params: &GlyphRasterizationParams, - ) -> Result> { + fn glyph_raster_bounds(&self, params: &GlyphRasterParams) -> Result> { self.0.read().raster_bounds(params) } fn rasterize_glyph( &self, - glyph_id: &GlyphRasterizationParams, + glyph_id: &GlyphRasterParams, ) -> Result<(Size, Vec)> { self.0.read().rasterize_glyph(glyph_id) } @@ -237,7 +234,7 @@ impl MacTextSystemState { }) } - fn raster_bounds(&self, params: &GlyphRasterizationParams) -> Result> { + fn raster_bounds(&self, params: &GlyphRasterParams) -> Result> { let font = &self.fonts[params.font_id.0]; let scale = Transform2F::from_scale(params.scale_factor); Ok(font @@ -251,10 +248,7 @@ impl MacTextSystemState { .into()) } - fn rasterize_glyph( - &self, - params: &GlyphRasterizationParams, - ) -> Result<(Size, Vec)> { + fn rasterize_glyph(&self, params: &GlyphRasterParams) -> Result<(Size, Vec)> { let glyph_bounds = self.raster_bounds(params)?; if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 { Err(anyhow!("glyph bounds are empty")) @@ -292,7 +286,7 @@ impl MacTextSystemState { let subpixel_shift = params .subpixel_variant - .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32 / params.scale_factor); + .map(|v| v as f32 / SUBPIXEL_VARIANTS as f32); cx.set_allows_font_subpixel_positioning(true); cx.set_should_subpixel_position_fonts(true); cx.set_allows_font_subpixel_quantization(false); @@ -303,8 +297,8 @@ impl MacTextSystemState { .draw_glyphs( &[u32::from(params.glyph_id) as CGGlyph], &[CGPoint::new( - subpixel_shift.x as CGFloat, - subpixel_shift.y as CGFloat, + (subpixel_shift.x / params.scale_factor) as CGFloat, + (subpixel_shift.y / params.scale_factor) as CGFloat, )], cx, ); diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index 4c3b8e1a8c1b4595fb2234cdd724a482aeb76c42..6e87543fc94a7a423052d7b06f9e1ac349de7b80 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -1,8 +1,8 @@ use super::{ns_string, MetalRenderer, NSRange}; use crate::{ - point, px, size, AnyWindowHandle, Bounds, Event, GlyphRasterizationParams, KeyDownEvent, - Keystroke, MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, - MouseMovedEvent, MouseUpEvent, NSRectExt, Pixels, Platform, PlatformAtlas, PlatformDispatcher, + point, px, size, AnyWindowHandle, Bounds, Event, GlyphRasterParams, KeyDownEvent, Keystroke, + MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, + MouseUpEvent, NSRectExt, Pixels, Platform, PlatformAtlas, PlatformDispatcher, PlatformInputHandler, PlatformScreen, PlatformWindow, Point, Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel, }; @@ -886,7 +886,7 @@ impl PlatformWindow for MacWindow { } } - fn glyph_atlas(&self) -> Arc> { + fn glyph_atlas(&self) -> Arc> { self.0.lock().renderer.glyph_atlas().clone() } } diff --git a/crates/gpui3/src/text_system.rs b/crates/gpui3/src/text_system.rs index 899554e4b02d360334ce756bb94e623ecf3ed4ed..4529214ea710e7fce3917c7a7e38d162b7959c65 100644 --- a/crates/gpui3/src/text_system.rs +++ b/crates/gpui3/src/text_system.rs @@ -215,13 +215,13 @@ impl TextSystem { }) } - pub fn raster_bounds(&self, params: &GlyphRasterizationParams) -> Result> { + pub fn raster_bounds(&self, params: &GlyphRasterParams) -> Result> { self.platform_text_system.glyph_raster_bounds(params) } pub fn rasterize_glyph( &self, - glyph_id: &GlyphRasterizationParams, + glyph_id: &GlyphRasterParams, ) -> Result<(Size, Vec)> { self.platform_text_system.rasterize_glyph(glyph_id) } @@ -384,7 +384,7 @@ pub struct ShapedGlyph { } #[derive(Clone, Debug, PartialEq)] -pub struct GlyphRasterizationParams { +pub struct GlyphRasterParams { pub(crate) font_id: FontId, pub(crate) glyph_id: GlyphId, pub(crate) font_size: Pixels, @@ -392,9 +392,9 @@ pub struct GlyphRasterizationParams { pub(crate) scale_factor: f32, } -impl Eq for GlyphRasterizationParams {} +impl Eq for GlyphRasterParams {} -impl Hash for GlyphRasterizationParams { +impl Hash for GlyphRasterParams { fn hash(&self, state: &mut H) { self.font_id.0.hash(state); self.glyph_id.0.hash(state); diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 56004d991277e6899d2ac4700a28801bb17f71ac..d524a43fb17d999338c9865c3dbde84b94283c7d 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,6 +1,6 @@ use crate::{ px, AnyView, AppContext, AvailableSpace, Bounds, Context, Effect, Element, EntityId, FontId, - GlyphId, GlyphRasterizationParams, Handle, Hsla, IsZero, LayoutId, MainThread, MainThreadOnly, + GlyphId, GlyphRasterParams, Handle, Hsla, IsZero, LayoutId, MainThread, MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, Reference, Scene, Size, StackContext, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, @@ -16,7 +16,7 @@ pub struct AnyWindow {} pub struct Window { handle: AnyWindowHandle, platform_window: MainThreadOnly>, - glyph_atlas: Arc>, + glyph_atlas: Arc>, rem_size: Pixels, content_size: Size, layout_engine: TaffyLayoutEngine, @@ -181,7 +181,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { x: (glyph_origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, y: (glyph_origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, }; - let params = GlyphRasterizationParams { + let params = GlyphRasterParams { font_id, glyph_id, font_size, @@ -190,17 +190,12 @@ 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_layer_id(); - let offset = raster_bounds.origin.map(Into::into); - let glyph_origin = glyph_origin.map(|px| px.floor()) + offset; - // dbg!(glyph_origin); let bounds = Bounds { - origin: glyph_origin, + origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into), size: raster_bounds.size.map(Into::into), }; - let tile = self .window .glyph_atlas From c57e19c8faa040fb9e165df9247a39026f154e99 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 3 Oct 2023 17:29:36 -0600 Subject: [PATCH 17/28] Checkpoint: Glyphs rendering how I'd like --- crates/gpui/src/platform/mac/atlas.rs | 4 ++-- crates/gpui/src/platform/mac/renderer.rs | 1 - crates/gpui3/src/platform/mac/metal_renderer.rs | 2 -- crates/gpui3/src/window.rs | 8 ++++---- crates/storybook/src/workspace.rs | 2 +- crates/storybook2/src/workspace.rs | 5 +---- 6 files changed, 8 insertions(+), 14 deletions(-) diff --git a/crates/gpui/src/platform/mac/atlas.rs b/crates/gpui/src/platform/mac/atlas.rs index d73cc3351309b7f7a6b545702de96cbe044a560d..57a137479d7e8eaca79effad754862ddb996e366 100644 --- a/crates/gpui/src/platform/mac/atlas.rs +++ b/crates/gpui/src/platform/mac/atlas.rs @@ -147,10 +147,10 @@ impl Atlas { bounds.size().y() as u64, ); self.texture.replace_region( - dbg!(region), + region, 0, bytes.as_ptr() as *const _, - dbg!((bounds.size().x() * self.bytes_per_pixel() as i32) as u64), + (bounds.size().x() * self.bytes_per_pixel() as i32) as u64, ); } diff --git a/crates/gpui/src/platform/mac/renderer.rs b/crates/gpui/src/platform/mac/renderer.rs index 9ef46beaa5910e5cf7013bb4c5d3e7951469039c..85f0af1ffddb902f00bcdb15012e474547e68fa7 100644 --- a/crates/gpui/src/platform/mac/renderer.rs +++ b/crates/gpui/src/platform/mac/renderer.rs @@ -633,7 +633,6 @@ impl Renderer { // Snap sprite to pixel grid. let origin = (glyph.origin * scale_factor).floor() + sprite.offset.to_f32(); - dbg!(origin); sprites_by_atlas .entry(sprite.atlas_id) .or_insert_with(Vec::new) diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index b06d186f3497691018ee45ccc9601a0484d13877..a7a63354ce11af8349e90c21f9d6ef0788839b9e 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -49,7 +49,6 @@ const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shader const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value. pub struct MetalRenderer { - device: metal::Device, layer: metal::MetalLayer, command_queue: CommandQueue, quads_pipeline_state: metal::RenderPipelineState, @@ -145,7 +144,6 @@ impl MetalRenderer { )); Self { - device, layer, command_queue, quads_pipeline_state, diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index d524a43fb17d999338c9865c3dbde84b94283c7d..9e9b4c36b620c3f7453bd67d3bf1d648294f89cb 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -192,14 +192,14 @@ 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_layer_id(); - let bounds = Bounds { - origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into), - size: raster_bounds.size.map(Into::into), - }; let tile = self .window .glyph_atlas .get_or_insert_with(¶ms, &mut || self.text_system().rasterize_glyph(¶ms))?; + let bounds = Bounds { + origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into), + size: tile.bounds.size.map(Into::into), + }; self.window.scene.insert( layer_id, diff --git a/crates/storybook/src/workspace.rs b/crates/storybook/src/workspace.rs index 64181b2bd7fb47470ed6a387c8bab3a35287022e..c284ba1bca47e2a863bd05afa933deba6fc49896 100644 --- a/crates/storybook/src/workspace.rs +++ b/crates/storybook/src/workspace.rs @@ -26,7 +26,7 @@ impl WorkspaceElement { .font("Helvetica") .text_base() .text_color(black()) - .child("Hey"); + .child("The quick brown fox ran over the lazy dog."); // div() // .size_full() diff --git a/crates/storybook2/src/workspace.rs b/crates/storybook2/src/workspace.rs index f66f4832e8eb0be81eab44d48ec0c0e497bd4fec..cd13b19a8a26b10c42157a7b58d4bf559e26cad4 100644 --- a/crates/storybook2/src/workspace.rs +++ b/crates/storybook2/src/workspace.rs @@ -33,14 +33,11 @@ impl Workspace { .text_base() .fill(white()) .text_color(black()) - .child("Hey") + .child("The quick brown fox ran over the lazy dog.") // TODO: Implement style. //.size_full().fill(gpui3::hsla(0.83, 1., 0.5, 1.)) - // TODO: Debug font not font. - //.child("Is this thing on?") - // themed(rose_pine_dawn(), cx, |cx| { // div() // .size_full() From d995192dde9edc37772e547875ea11a531ab4016 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 3 Oct 2023 17:39:03 -0600 Subject: [PATCH 18/28] Checkpoint: Get basic workspace rendering --- .../gpui3/src/platform/mac/metal_renderer.rs | 47 -------------- crates/storybook2/src/storybook2.rs | 9 +-- crates/storybook2/src/workspace.rs | 64 ++++++++----------- 3 files changed, 29 insertions(+), 91 deletions(-) diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index a7a63354ce11af8349e90c21f9d6ef0788839b9e..a61c73156fb8cc7b72ffeb8fdcd3d3069c27bccb 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -1,37 +1,3 @@ -// How can I fix this? -// -[MTLDebugRenderCommandEncoder setRenderPipelineState:]:1580: failed assertion `Set Render Pipeline State Validation -// For depth attachment, the render pipeline's pixelFormat (MTLPixelFormatInvalid) does not match the framebuffer's pixelFormat (MTLPixelFormatDepth32Float). -// ' -// -[MTLDebugRenderCommandEncoder setRenderPipelineState:]:1580: failed assertion `Set Render Pipeline State Validation -// For depth attachment, the render pipeline's pixelFormat (MTLPixelFormatInvalid) does not match the framebuffer's pixelFormat (MTLPixelFormatDepth32Float). -// // It seems like the error you're facing has to do with the difference between the -// pixel format of the render pipeline and the framebuffer. If the pixel format of -// those two doesn't match, Metal throws an error. To resolve this issue, you need -// to set the pixel format of your depth attachment and your render pipeline state -// to the same value. - -// In this code: -// --- -/* -descriptor.set_depth_attachment_pixel_format(MTLPixelFormat::Depth32Float); -*/ -// --- -// you've commented out the line where you set the depth attachment pixel format -// to MTLPixelFormat::Depth32Float. If you uncomment this line, it should resolve -// the error as your depth attachment's pixel format will then match your framebuffer's. - -// If you still encounter the same problem, you might be configuring another render -// pipeline state elsewhere in your code with a different depth pixel format. Make -// sure all configurations have matching pixel formats. - -// Additionally, be aware of the limitations of certain pixel formats. For example, -// not all pixel formats support depth stencil attachments, and some are only -// compatible with certain types of GPU hardware. Implementation of pixel formats -// can vary between different versions of iOS, so ensure that your choice of pixel -// format is compatible with your minimum target version. -// -// I want it to be UANorm - use crate::{ point, size, AtlasTextureId, DevicePixels, GlyphRasterParams, MetalAtlas, MonochromeSprite, Quad, Scene, Size, @@ -182,19 +148,6 @@ impl MetalRenderer { let command_buffer = command_queue.new_command_buffer(); let render_pass_descriptor = metal::RenderPassDescriptor::new(); - - // let depth_texture_desc = metal::TextureDescriptor::new(); - // depth_texture_desc.set_pixel_format(metal::MTLPixelFormat::Depth32Float); - // depth_texture_desc.set_storage_mode(metal::MTLStorageMode::Private); - // depth_texture_desc.set_usage(metal::MTLTextureUsage::RenderTarget); - // depth_texture_desc.set_width(i32::from(viewport_size.width) as u64); - // depth_texture_desc.set_height(i32::from(viewport_size.height) as u64); - // let depth_texture = self.device.new_texture(&depth_texture_desc); - // let depth_attachment = render_pass_descriptor.depth_attachment().unwrap(); - // depth_attachment.set_texture(Some(&depth_texture)); - // depth_attachment.set_clear_depth(1.); - // depth_attachment.set_store_action(metal::MTLStoreAction::Store); - let color_attachment = render_pass_descriptor .color_attachments() .object_at(0) diff --git a/crates/storybook2/src/storybook2.rs b/crates/storybook2/src/storybook2.rs index 4e93b75ef4d1c1b04f4ae1707e4dad24380e3d0a..00bff69841e1d55cedc5a8d1e7677b5dbcb49037 100644 --- a/crates/storybook2/src/storybook2.rs +++ b/crates/storybook2/src/storybook2.rs @@ -1,6 +1,6 @@ #![allow(dead_code, unused_variables)] -use gpui3::{Bounds, WindowBounds, WindowOptions}; +use gpui3::{px, size, Bounds, WindowBounds, WindowOptions}; use log::LevelFilter; use simplelog::SimpleLogger; @@ -23,11 +23,8 @@ fn main() { let window = cx.open_window( WindowOptions { bounds: WindowBounds::Fixed(Bounds { - size: gpui3::Size { - width: 800_f32.into(), - height: 600_f32.into(), - }, - ..Default::default() + origin: Default::default(), + size: size(px(800.), px(600.)), }), ..Default::default() }, diff --git a/crates/storybook2/src/workspace.rs b/crates/storybook2/src/workspace.rs index cd13b19a8a26b10c42157a7b58d4bf559e26cad4..c3e815242fd724ec3ed1195aad482f3cc6fe44f4 100644 --- a/crates/storybook2/src/workspace.rs +++ b/crates/storybook2/src/workspace.rs @@ -1,6 +1,6 @@ use crate::{ collab_panel::{collab_panel, CollabPanel}, - theme::theme, + theme::{theme, themed}, themes::rose_pine_dawn, }; use gpui3::{ @@ -27,43 +27,31 @@ impl Workspace { fn render(&mut self, cx: &mut ViewContext) -> impl Element { let theme = rose_pine_dawn(); - div() - .size_full() - .font("Helvetica") - .text_base() - .fill(white()) - .text_color(black()) - .child("The quick brown fox ran over the lazy dog.") - - // TODO: Implement style. - //.size_full().fill(gpui3::hsla(0.83, 1., 0.5, 1.)) - - // themed(rose_pine_dawn(), cx, |cx| { - // div() - // .size_full() - // .flex() - // .flex_col() - // .font("Zed Sans Extended") - // .gap_0() - // .justify_start() - // .items_start() - // .text_color(theme.lowest.base.default.foreground) - // // .fill(theme.middle.base.default.background) - // .fill(gpui3::hsla(0.83, 1., 0.5, 1.)) - // .child(titlebar(cx)) - // .child( - // div() - // .flex_1() - // .w_full() - // .flex() - // .flex_row() - // .overflow_hidden() - // .child(self.left_panel.clone()) - // .child(div().h_full().flex_1()) - // .child(self.right_panel.clone()), - // ) - // .child(statusbar::statusbar(cx)) - // }) + themed(rose_pine_dawn(), cx, |cx| { + div() + .size_full() + .flex() + .flex_col() + .font("Zed Sans Extended") + .gap_0() + .justify_start() + .items_start() + .text_color(theme.lowest.base.default.foreground) + .fill(theme.middle.base.default.background) + .child(titlebar(cx)) + .child( + div() + .flex_1() + .w_full() + .flex() + .flex_row() + .overflow_hidden() + .child(self.left_panel.clone()) + .child(div().h_full().flex_1()) + .child(self.right_panel.clone()), + ) + .child(statusbar::statusbar(cx)) + }) } } From 7f9e3bc787045bd5fbdab8e8adf2580c00e764e4 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 3 Oct 2023 17:58:11 -0600 Subject: [PATCH 19/28] Checkpoint --- crates/gpui3/src/scene.rs | 6 ++-- crates/gpui3/src/window.rs | 51 ++++++++++++++++++++++++++---- crates/storybook2/src/workspace.rs | 4 +-- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index 5f55dd73fba8eadf81eedb63eb580f2b85289204..fb66da7f5ccd6b8b4bc89b0a8b483eb994dfb7a6 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -7,12 +7,12 @@ use smallvec::SmallVec; // Exported to metal pub type PointF = Point; -pub type StackingOrder = SmallVec<[u32; 16]>; +pub type LayerId = SmallVec<[u32; 16]>; #[derive(Debug)] pub struct Scene { pub(crate) scale_factor: f32, - pub(crate) layers: BTreeMap, + pub(crate) layers: BTreeMap, } impl Scene { @@ -30,7 +30,7 @@ impl Scene { } } - pub fn insert(&mut self, stacking_order: StackingOrder, primitive: impl Into) { + pub fn insert(&mut self, stacking_order: LayerId, primitive: impl Into) { let layer = self.layers.entry(stacking_order).or_default(); let primitive = primitive.into(); diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 9e9b4c36b620c3f7453bd67d3bf1d648294f89cb..50533f8f2840d516db3b02acbe8032ed570cba45 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,8 +1,8 @@ use crate::{ - px, AnyView, AppContext, AvailableSpace, Bounds, Context, Effect, Element, EntityId, FontId, - GlyphId, GlyphRasterParams, Handle, Hsla, IsZero, LayoutId, MainThread, MainThreadOnly, - MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, Reference, Scene, Size, - StackContext, StackingOrder, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, + px, AnyView, AppContext, AvailableSpace, Bounds, Context, Corners, Effect, Element, EntityId, + FontId, GlyphId, GlyphRasterParams, Handle, Hsla, IsZero, LayerId, LayoutId, MainThread, + MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, Reference, + Scene, Size, StackContext, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; @@ -22,7 +22,8 @@ pub struct Window { layout_engine: TaffyLayoutEngine, pub(crate) root_view: Option>, mouse_position: Point, - current_layer_id: StackingOrder, + current_layer_id: LayerId, + content_mask_stack: Vec, pub(crate) scene: Scene, pub(crate) dirty: bool, } @@ -64,12 +65,19 @@ impl Window { root_view: None, mouse_position, current_layer_id: SmallVec::new(), + content_mask_stack: Vec::new(), scene: Scene::new(scale_factor), dirty: true, } } } +#[derive(Clone, Debug)] +pub struct ContentMask { + bounds: Bounds, + corner_radii: Corners, +} + pub struct WindowContext<'a, 'w> { app: Reference<'a, AppContext>, window: Reference<'w, Window>, @@ -145,10 +153,41 @@ impl<'a, 'w> WindowContext<'a, 'w> { result } - pub fn current_layer_id(&self) -> StackingOrder { + pub fn clip( + &mut self, + bounds: Bounds, + corner_radii: Corners, + f: impl FnOnce(&mut Self) -> R, + ) -> R { + let clip_mask = ContentMask { + bounds, + corner_radii, + }; + + self.window.content_mask_stack.push(clip_mask); + let result = f(self); + self.window.content_mask_stack.pop(); + result + } + + pub fn current_layer_id(&self) -> LayerId { self.window.current_layer_id.clone() } + pub fn current_clipping_mask(&self) -> ContentMask { + self.window + .content_mask_stack + .last() + .cloned() + .unwrap_or_else(|| ContentMask { + bounds: Bounds { + origin: Point::default(), + size: self.window.content_size, + }, + corner_radii: Default::default(), + }) + } + pub fn run_on_main( &self, f: impl FnOnce(&mut MainThread) -> R + Send + 'static, diff --git a/crates/storybook2/src/workspace.rs b/crates/storybook2/src/workspace.rs index c3e815242fd724ec3ed1195aad482f3cc6fe44f4..15f26e52a527507b29013983f430f7dbb1f8b7b7 100644 --- a/crates/storybook2/src/workspace.rs +++ b/crates/storybook2/src/workspace.rs @@ -4,8 +4,8 @@ use crate::{ themes::rose_pine_dawn, }; use gpui3::{ - black, div, img, svg, view, white, Context, Element, ParentElement, RootView, StyleHelpers, - View, ViewContext, WindowContext, + div, img, svg, view, Context, Element, ParentElement, RootView, StyleHelpers, View, + ViewContext, WindowContext, }; pub struct Workspace { From da211bef96db64858a6081c002b62fb8c879b8fc Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 3 Oct 2023 20:04:17 -0600 Subject: [PATCH 20/28] Checkpoint --- crates/gpui3/build.rs | 3 + crates/gpui3/src/elements/div.rs | 33 +++--- crates/gpui3/src/geometry.rs | 36 +++++++ crates/gpui3/src/gpui3.rs | 12 +-- crates/gpui3/src/platform/mac/shaders.metal | 18 ++-- crates/gpui3/src/scene.rs | 5 +- crates/gpui3/src/style.rs | 59 +++++++++- crates/gpui3/src/util.rs | 6 -- crates/gpui3/src/window.rs | 114 +++++++++++++++----- crates/storybook2/src/theme.rs | 4 +- 10 files changed, 212 insertions(+), 78 deletions(-) diff --git a/crates/gpui3/build.rs b/crates/gpui3/build.rs index 64d14ee28536c9e62e7ac9569563f401ad175f90..15e655ced682aaca8c92e7b27367ef47d67c6e74 100644 --- a/crates/gpui3/build.rs +++ b/crates/gpui3/build.rs @@ -45,6 +45,7 @@ fn generate_shader_bindings() -> PathBuf { "Pixels".into(), "PointF".into(), "Hsla".into(), + "ScaledContentMask".into(), "Uniforms".into(), "AtlasTile".into(), "Quad".into(), @@ -58,12 +59,14 @@ fn generate_shader_bindings() -> PathBuf { .with_src(crate_dir.join("src/scene.rs")) .with_src(crate_dir.join("src/geometry.rs")) .with_src(crate_dir.join("src/color.rs")) + .with_src(crate_dir.join("src/window.rs")) .with_src(crate_dir.join("src/platform.rs")) .with_src(crate_dir.join("src/platform/mac/metal_renderer.rs")) .with_config(config) .generate() .expect("Unable to generate bindings") .write_to_file(&output_path); + output_path } diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 6bae65c5a26ba5b7d33f26bdc0987027b6a7c167..535ccb995306a14252565f99ebb9eb130cb11230 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -1,6 +1,6 @@ use crate::{ AnyElement, Bounds, Element, Layout, LayoutId, Overflow, ParentElement, Pixels, Point, - Refineable, RefinementCascade, Result, StackContext, Style, StyleHelpers, Styled, ViewContext, + Refineable, RefinementCascade, Result, Style, StyleHelpers, Styled, ViewContext, }; use parking_lot::Mutex; use smallvec::SmallVec; @@ -33,16 +33,9 @@ impl Element for Div { cx: &mut ViewContext, ) -> Result<(LayoutId, Self::FrameState)> { let style = self.computed_style(); - let child_layout_ids = if let Some(text_style) = style.text_style(cx) { - cx.with_text_style(text_style.clone(), |cx| self.layout_children(view, cx))? - } else { - self.layout_children(view, cx)? - }; - - Ok(( - cx.request_layout(style.into(), child_layout_ids.clone())?, - child_layout_ids, - )) + let child_layout_ids = style.apply_text_style(cx, |cx| self.layout_children(view, cx))?; + let layout_id = cx.request_layout(style.into(), child_layout_ids.clone())?; + Ok((layout_id, child_layout_ids)) } fn paint( @@ -56,20 +49,18 @@ impl Element for Div { let style = self.computed_style(); style.paint(order, bounds, cx); - let overflow = &style.overflow; + // // todo!("support only one dimension being hidden") + let overflow = &style.overflow; // if style.overflow.y != Overflow::Visible || style.overflow.x != Overflow::Visible { - // cx.scene().push_layer(Some(bounds)); - // pop_layer = true; + // cx.clip(layout.bounds, style.corner_radii, || ) // } - if let Some(text_style) = style.text_style(cx) { - cx.with_text_style(text_style.clone(), |cx| { - self.paint_children(overflow, state, cx) - })?; - } else { - self.paint_children(overflow, state, cx)?; - } + style.apply_text_style(cx, |cx| { + style.apply_overflow(layout.bounds, cx, |cx| { + self.paint_children(overflow, state, cx) + }) + })?; self.handle_scroll(order, bounds, style.overflow.clone(), child_layouts, cx); // todo!("enable inspector") diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index 4ab7fc803031cae9f1f02c4411e7c95fa28c2b06..183554204493ff8f4acbd4043f6d64793e4b4c84 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -225,6 +225,20 @@ pub struct Bounds { pub size: Size, } +impl> Bounds { + pub fn from_corners(upper_left: Point, lower_right: Point) -> Self { + let origin = Point { + x: upper_left.x.clone(), + y: upper_left.y.clone(), + }; + let size = Size { + width: lower_right.x - upper_left.x, + height: lower_right.y - upper_left.y, + }; + Bounds { origin, size } + } +} + impl Mul for Bounds where T: Mul + Clone + Debug, @@ -418,6 +432,28 @@ pub struct Corners { pub bottom_left: T, } +impl Corners { + pub fn to_pixels(&self, rem_size: Pixels) -> Corners { + Corners { + top_left: self.top_left.to_pixels(rem_size), + top_right: self.top_right.to_pixels(rem_size), + bottom_right: self.bottom_right.to_pixels(rem_size), + bottom_left: self.bottom_left.to_pixels(rem_size), + } + } +} + +impl Corners { + pub fn scale(&self, factor: f32) -> Corners { + Corners { + top_left: self.top_left.scale(factor), + top_right: self.top_right.scale(factor), + bottom_right: self.bottom_right.scale(factor), + bottom_left: self.bottom_left.scale(factor), + } + } +} + impl Corners { pub fn map U>(&self, f: F) -> Corners { Corners { diff --git a/crates/gpui3/src/gpui3.rs b/crates/gpui3/src/gpui3.rs index d143c979df061083ddd5b1de7553940aaa815a41..8f6a2680519fb2d17279a8b99056db6ab397b223 100644 --- a/crates/gpui3/src/gpui3.rs +++ b/crates/gpui3/src/gpui3.rs @@ -84,16 +84,16 @@ impl DerefMut for MainThread { } } -pub trait StackContext { - fn app(&mut self) -> &mut AppContext; +pub trait BorrowAppContext { + fn app_mut(&mut self) -> &mut AppContext; fn with_text_style(&mut self, style: TextStyleRefinement, f: F) -> R where F: FnOnce(&mut Self) -> R, { - self.app().push_text_style(style); + self.app_mut().push_text_style(style); let result = f(self); - self.app().pop_text_style(); + self.app_mut().pop_text_style(); result } @@ -101,9 +101,9 @@ pub trait StackContext { where F: FnOnce(&mut Self) -> R, { - self.app().push_state(state); + self.app_mut().push_state(state); let result = f(self); - self.app().pop_state::(); + self.app_mut().pop_state::(); result } } diff --git a/crates/gpui3/src/platform/mac/shaders.metal b/crates/gpui3/src/platform/mac/shaders.metal index 7c98db779bc9f8f8ed19fd76bc771f8a967ab92f..3de028deee4863fbc7e1af21b195e65f86ef49f6 100644 --- a/crates/gpui3/src/platform/mac/shaders.metal +++ b/crates/gpui3/src/platform/mac/shaders.metal @@ -4,10 +4,10 @@ using namespace metal; float4 hsla_to_rgba(Hsla hsla); -float4 to_device_position(float2 unit_vertex, Bounds_Pixels bounds, - Bounds_Pixels clip_bounds, +float4 to_device_position(float2 unit_vertex, Bounds_ScaledPixels bounds, + Bounds_ScaledPixels clip_bounds, constant Size_DevicePixels *viewport_size); -float quad_sdf(float2 point, Bounds_Pixels bounds, Corners_Pixels corner_radii); +float quad_sdf(float2 point, Bounds_ScaledPixels bounds, Corners_ScaledPixels corner_radii); struct QuadVertexOutput { float4 position [[position]]; @@ -131,7 +131,7 @@ vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex( float2 unit_vertex = unit_vertices[unit_vertex_id]; MonochromeSprite sprite = sprites[sprite_id]; float4 device_position = to_device_position( - unit_vertex, sprite.bounds, sprite.clip_bounds, viewport_size); + unit_vertex, sprite.bounds, sprite.content_mask.bounds, viewport_size); float2 tile_origin = float2(sprite.tile.bounds.origin.x, sprite.tile.bounds.origin.y); @@ -157,7 +157,7 @@ fragment float4 monochrome_sprite_fragment( float4 sample = atlas_texture.sample(atlas_texture_sampler, input.tile_position); float clip_distance = - quad_sdf(input.position.xy, sprite.clip_bounds, sprite.clip_corner_radii); + quad_sdf(input.position.xy, sprite.content_mask.bounds, sprite.content_mask.corner_radii); float4 color = input.color; color.a *= sample.a * saturate(0.5 - clip_distance); return color; @@ -211,8 +211,8 @@ float4 hsla_to_rgba(Hsla hsla) { return rgba; } -float4 to_device_position(float2 unit_vertex, Bounds_Pixels bounds, - Bounds_Pixels clip_bounds, +float4 to_device_position(float2 unit_vertex, Bounds_ScaledPixels bounds, + Bounds_ScaledPixels clip_bounds, constant Size_DevicePixels *input_viewport_size) { float2 position = unit_vertex * float2(bounds.size.width, bounds.size.height) + @@ -229,8 +229,8 @@ float4 to_device_position(float2 unit_vertex, Bounds_Pixels bounds, return float4(device_position, 0., 1.); } -float quad_sdf(float2 point, Bounds_Pixels bounds, - Corners_Pixels corner_radii) { +float quad_sdf(float2 point, Bounds_ScaledPixels bounds, + Corners_ScaledPixels corner_radii) { float2 half_size = float2(bounds.size.width, bounds.size.height) / 2.; float2 center = float2(bounds.origin.x, bounds.origin.y) + half_size; float2 center_to_point = point - center; diff --git a/crates/gpui3/src/scene.rs b/crates/gpui3/src/scene.rs index fb66da7f5ccd6b8b4bc89b0a8b483eb994dfb7a6..6ffc6e0bb3aa698296ce2b4e27b0b2cbe34597ed 100644 --- a/crates/gpui3/src/scene.rs +++ b/crates/gpui3/src/scene.rs @@ -1,7 +1,7 @@ use std::{iter::Peekable, mem}; use super::{Bounds, Hsla, Point}; -use crate::{AtlasTextureId, AtlasTile, Corners, Edges, ScaledPixels}; +use crate::{AtlasTextureId, AtlasTile, Corners, Edges, ScaledContentMask, ScaledPixels}; use collections::BTreeMap; use smallvec::SmallVec; @@ -234,8 +234,7 @@ impl From for Primitive { pub struct MonochromeSprite { pub order: u32, pub bounds: Bounds, - pub clip_bounds: Bounds, - pub clip_corner_radii: Corners, + pub content_mask: ScaledContentMask, pub color: Hsla, pub tile: AtlasTile, } diff --git a/crates/gpui3/src/style.rs b/crates/gpui3/src/style.rs index 431a87491f1f5d6f9a6a87114b48c119b881b2c3..c4ce8250c0bbde2c7298698116bc912f8dc2bc9e 100644 --- a/crates/gpui3/src/style.rs +++ b/crates/gpui3/src/style.rs @@ -1,8 +1,8 @@ use crate::{ - phi, rems, AbsoluteLength, Bounds, Corners, CornersRefinement, DefiniteLength, Edges, - EdgesRefinement, Font, FontFeatures, FontStyle, FontWeight, Hsla, Length, Pixels, Point, - PointRefinement, Quad, Rems, Result, RunStyle, SharedString, Size, SizeRefinement, ViewContext, - WindowContext, + 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, + SharedString, Size, SizeRefinement, ViewContext, WindowContext, }; use refineable::Refineable; pub use taffy::style::{ @@ -179,6 +179,57 @@ impl Style { } } + pub fn apply_text_style(&self, cx: &mut C, f: F) -> R + where + C: BorrowAppContext, + F: FnOnce(&mut C) -> R, + { + if self.text.is_some() { + cx.with_text_style(self.text.clone(), f) + } else { + f(cx) + } + } + + /// Apply overflow to content mask + pub fn apply_overflow(&self, bounds: Bounds, cx: &mut C, f: F) -> R + where + C: BorrowWindow, + F: FnOnce(&mut C) -> R, + { + let current_mask = cx.content_mask(); + + let min = current_mask.bounds.origin; + let max = current_mask.bounds.lower_right(); + + let mask_corner_radii = Corners::default(); + let mask_bounds = match ( + self.overflow.x == Overflow::Visible, + self.overflow.y == Overflow::Visible, + ) { + // x and y both visible + (true, true) => return f(cx), + // x visible, y hidden + (true, false) => Bounds::from_corners( + point(min.x, bounds.origin.y), + point(max.x, bounds.lower_right().y), + ), + // x hidden, y visible + (false, true) => Bounds::from_corners( + point(bounds.origin.x, min.y), + point(bounds.lower_right().x, max.y), + ), + // both hidden + (false, false) => bounds, + }; + let mask = ContentMask { + bounds: mask_bounds, + corner_radii: mask_corner_radii, + }; + + cx.with_content_mask(mask, f) + } + /// Paints the background of an element styled with this style. pub fn paint(&self, order: u32, bounds: Bounds, cx: &mut ViewContext) { let rem_size = cx.rem_size(); diff --git a/crates/gpui3/src/util.rs b/crates/gpui3/src/util.rs index 11cdfdc3ab7db1862c1e5e0827e94ebbbcc4e3d8..c76408392402a7a151ec93f54ea0a6aaeca0b720 100644 --- a/crates/gpui3/src/util.rs +++ b/crates/gpui3/src/util.rs @@ -2,12 +2,6 @@ use smol::future::FutureExt; use std::{future::Future, time::Duration}; pub use util::*; -pub fn post_inc(value: &mut usize) -> usize { - let prev = *value; - *value += 1; - prev -} - pub async fn timeout(timeout: Duration, f: F) -> Result where F: Future, diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 50533f8f2840d516db3b02acbe8032ed570cba45..34048b54519a8c80509d31698ae9b20897860748 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,8 +1,8 @@ use crate::{ - px, AnyView, AppContext, AvailableSpace, Bounds, Context, Corners, Effect, Element, EntityId, - FontId, GlyphId, GlyphRasterParams, Handle, Hsla, IsZero, LayerId, LayoutId, MainThread, - MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, Reference, - Scene, Size, StackContext, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, + px, AnyView, AppContext, AvailableSpace, BorrowAppContext, Bounds, Context, Corners, Effect, + Element, EntityId, FontId, GlyphId, GlyphRasterParams, Handle, Hsla, IsZero, LayerId, LayoutId, + MainThread, MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, + Reference, ScaledPixels, Scene, Size, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; @@ -74,8 +74,24 @@ impl Window { #[derive(Clone, Debug)] pub struct ContentMask { - bounds: Bounds, - corner_radii: Corners, + pub bounds: Bounds, + pub corner_radii: Corners, +} + +impl ContentMask { + pub fn scale(&self, factor: f32) -> ScaledContentMask { + ScaledContentMask { + bounds: self.bounds.scale(factor), + corner_radii: self.corner_radii.scale(factor), + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +#[repr(C)] +pub struct ScaledContentMask { + bounds: Bounds, + corner_radii: Corners, } pub struct WindowContext<'a, 'w> { @@ -174,20 +190,6 @@ impl<'a, 'w> WindowContext<'a, 'w> { self.window.current_layer_id.clone() } - pub fn current_clipping_mask(&self) -> ContentMask { - self.window - .content_mask_stack - .last() - .cloned() - .unwrap_or_else(|| ContentMask { - bounds: Bounds { - origin: Point::default(), - size: self.window.content_size, - }, - corner_radii: Default::default(), - }) - } - pub fn run_on_main( &self, f: impl FnOnce(&mut MainThread) -> R + Send + 'static, @@ -239,14 +241,14 @@ impl<'a, 'w> WindowContext<'a, 'w> { origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into), size: tile.bounds.size.map(Into::into), }; + let content_mask = self.content_mask().scale(scale_factor); self.window.scene.insert( layer_id, MonochromeSprite { order, bounds, - clip_bounds: bounds, - clip_corner_radii: Default::default(), + content_mask, color, tile, }, @@ -330,8 +332,60 @@ impl<'a, 'w> std::ops::DerefMut for WindowContext<'a, 'w> { } } -impl StackContext for ViewContext<'_, '_, S> { - fn app(&mut self) -> &mut AppContext { +impl BorrowAppContext for WindowContext<'_, '_> { + fn app_mut(&mut self) -> &mut AppContext { + &mut *self.app + } +} + +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 { + self.window_mut().content_mask_stack.push(mask); + let result = f(self); + self.window_mut().content_mask_stack.pop(); + result + } + + fn content_mask(&self) -> ContentMask { + self.window() + .content_mask_stack + .last() + .cloned() + .unwrap_or_else(|| ContentMask { + bounds: Bounds { + origin: Point::default(), + size: self.window().content_size, + }, + corner_radii: Default::default(), + }) + } + + fn rem_size(&self) -> Pixels { + self.window().rem_size + } +} + +impl BorrowWindow for WindowContext<'_, '_> { + fn window(&self) -> &Window { + &*self.window + } + + fn window_mut(&mut self) -> &mut Window { + &mut *self.window + } +} + +pub struct ViewContext<'a, 'w, S> { + window_cx: WindowContext<'a, 'w>, + entity_type: PhantomData, + entity_id: EntityId, +} + +impl BorrowAppContext for ViewContext<'_, '_, S> { + fn app_mut(&mut self) -> &mut AppContext { &mut *self.window_cx.app } @@ -356,10 +410,14 @@ impl StackContext for ViewContext<'_, '_, S> { } } -pub struct ViewContext<'a, 'w, S> { - window_cx: WindowContext<'a, 'w>, - entity_type: PhantomData, - entity_id: EntityId, +impl BorrowWindow for ViewContext<'_, '_, S> { + fn window(&self) -> &Window { + &self.window_cx.window + } + + fn window_mut(&mut self) -> &mut Window { + &mut *self.window_cx.window + } } impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { diff --git a/crates/storybook2/src/theme.rs b/crates/storybook2/src/theme.rs index 88bc21775cbe7ee69655f61b7784a0adf3b73e68..b28a0f289cca4d4ece5f6b6a340890018bd5ca61 100644 --- a/crates/storybook2/src/theme.rs +++ b/crates/storybook2/src/theme.rs @@ -1,4 +1,6 @@ -use gpui3::{Element, Hsla, Layout, LayoutId, Result, StackContext, ViewContext, WindowContext}; +use gpui3::{ + BorrowAppContext, Element, Hsla, Layout, LayoutId, Result, ViewContext, WindowContext, +}; use serde::{de::Visitor, Deserialize, Deserializer}; use std::{collections::HashMap, fmt}; From 1e0ff653373ae2b89a75b7288c946771022b5a55 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 3 Oct 2023 20:19:59 -0600 Subject: [PATCH 21/28] Checkpoint --- crates/gpui3/src/platform.rs | 26 ++++++++++++++----- crates/gpui3/src/platform/mac/metal_atlas.rs | 22 +++++++--------- .../gpui3/src/platform/mac/metal_renderer.rs | 7 +++-- crates/gpui3/src/platform/mac/text_system.rs | 10 +++---- crates/gpui3/src/platform/mac/window.rs | 12 ++++----- crates/gpui3/src/text_system.rs | 10 +++---- crates/gpui3/src/window.rs | 16 +++++++----- 7 files changed, 57 insertions(+), 46 deletions(-) diff --git a/crates/gpui3/src/platform.rs b/crates/gpui3/src/platform.rs index 8fd70ba44168e1530b07b6c50646365a7ca1a8aa..718056e152537f433e7025640d6a39b42809f062 100644 --- a/crates/gpui3/src/platform.rs +++ b/crates/gpui3/src/platform.rs @@ -6,8 +6,8 @@ mod mac; mod test; use crate::{ - AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, GlyphRasterParams, - Pixels, Point, Result, Scene, ShapedLine, SharedString, Size, + AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point, + RenderGlyphParams, Result, Scene, ShapedLine, SharedString, Size, }; use anyhow::anyhow; use async_task::Runnable; @@ -147,7 +147,7 @@ pub trait PlatformWindow { fn is_topmost_for_position(&self, position: Point) -> bool; fn draw(&self, scene: Scene); - fn glyph_atlas(&self) -> Arc>; + fn glyph_atlas(&self) -> Arc; } pub trait PlatformDispatcher: Send + Sync { @@ -163,8 +163,8 @@ pub trait PlatformTextSystem: Send + Sync { fn typographic_bounds(&self, font_id: FontId, glyph_id: GlyphId) -> Result>; fn advance(&self, font_id: FontId, glyph_id: GlyphId) -> Result>; fn glyph_for_char(&self, font_id: FontId, ch: char) -> Option; - fn glyph_raster_bounds(&self, params: &GlyphRasterParams) -> Result>; - fn rasterize_glyph(&self, params: &GlyphRasterParams) -> Result<(Size, Vec)>; + fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result>; + fn rasterize_glyph(&self, params: &RenderGlyphParams) -> Result<(Size, Vec)>; fn layout_line(&self, text: &str, font_size: Pixels, runs: &[(usize, FontId)]) -> ShapedLine; fn wrap_line( &self, @@ -175,10 +175,22 @@ pub trait PlatformTextSystem: Send + Sync { ) -> Vec; } -pub trait PlatformAtlas: Send + Sync { +#[derive(PartialEq, Eq, Hash, Clone)] +pub enum AtlasKey { + Glyph(RenderGlyphParams), + // Svg(RenderSvgParams), +} + +impl From for AtlasKey { + fn from(params: RenderGlyphParams) -> Self { + Self::Glyph(params) + } +} + +pub trait PlatformAtlas: Send + Sync { fn get_or_insert_with( &self, - key: &Key, + key: &AtlasKey, build: &mut dyn FnMut() -> Result<(Size, Vec)>, ) -> Result; diff --git a/crates/gpui3/src/platform/mac/metal_atlas.rs b/crates/gpui3/src/platform/mac/metal_atlas.rs index 7f649800eada0e8daf668172b4a0f3541f3acc4d..3753be5bfd7b6a60865620ee23feb3656222c04a 100644 --- a/crates/gpui3/src/platform/mac/metal_atlas.rs +++ b/crates/gpui3/src/platform/mac/metal_atlas.rs @@ -1,4 +1,6 @@ -use crate::{AtlasTextureId, AtlasTile, Bounds, DevicePixels, PlatformAtlas, Point, Size}; +use crate::{ + AtlasKey, AtlasTextureId, AtlasTile, Bounds, DevicePixels, PlatformAtlas, Point, Size, +}; use anyhow::{anyhow, Result}; use collections::HashMap; use derive_more::{Deref, DerefMut}; @@ -7,11 +9,10 @@ use foreign_types::ForeignType; use metal::{Device, TextureDescriptor}; use objc::{msg_send, sel, sel_impl}; use parking_lot::Mutex; -use std::hash::Hash; -pub struct MetalAtlas(Mutex>); +pub struct MetalAtlas(Mutex); -impl MetalAtlas { +impl MetalAtlas { pub fn new( size: Size, pixel_format: metal::MTLPixelFormat, @@ -34,20 +35,17 @@ impl MetalAtlas { } } -struct MetalAtlasState { +struct MetalAtlasState { device: AssertSend, texture_descriptor: AssertSend, textures: Vec, - tiles_by_key: HashMap, + tiles_by_key: HashMap, } -impl PlatformAtlas for MetalAtlas -where - Key: Clone + Eq + Hash + Send, -{ +impl PlatformAtlas for MetalAtlas { fn get_or_insert_with( &self, - key: &Key, + key: &AtlasKey, build: &mut dyn FnMut() -> Result<(Size, Vec)>, ) -> Result { let mut lock = self.0.lock(); @@ -75,7 +73,7 @@ where } } -impl MetalAtlasState { +impl MetalAtlasState { fn push_texture(&mut self, min_size: Size) -> &mut MetalAtlasTexture { let default_atlas_size = Size { width: self.texture_descriptor.width().into(), diff --git a/crates/gpui3/src/platform/mac/metal_renderer.rs b/crates/gpui3/src/platform/mac/metal_renderer.rs index a61c73156fb8cc7b72ffeb8fdcd3d3069c27bccb..d88e27e8dc49096a6c0388fd6a30ebfb451f2e70 100644 --- a/crates/gpui3/src/platform/mac/metal_renderer.rs +++ b/crates/gpui3/src/platform/mac/metal_renderer.rs @@ -1,6 +1,5 @@ use crate::{ - point, size, AtlasTextureId, DevicePixels, GlyphRasterParams, MetalAtlas, MonochromeSprite, - Quad, Scene, Size, + point, size, AtlasTextureId, DevicePixels, MetalAtlas, MonochromeSprite, Quad, Scene, Size, }; use cocoa::{ base::{NO, YES}, @@ -21,7 +20,7 @@ pub struct MetalRenderer { sprites_pipeline_state: metal::RenderPipelineState, unit_vertices: metal::Buffer, instances: metal::Buffer, - glyph_atlas: Arc>, + glyph_atlas: Arc, } impl MetalRenderer { @@ -124,7 +123,7 @@ impl MetalRenderer { &*self.layer } - pub fn glyph_atlas(&self) -> &Arc> { + pub fn glyph_atlas(&self) -> &Arc { &self.glyph_atlas } diff --git a/crates/gpui3/src/platform/mac/text_system.rs b/crates/gpui3/src/platform/mac/text_system.rs index 29c97f4754bfafde61cfa9d9aa444cfee8cb7213..7dd13d52fba69c61e9dfa72998268b82d7181001 100644 --- a/crates/gpui3/src/platform/mac/text_system.rs +++ b/crates/gpui3/src/platform/mac/text_system.rs @@ -1,6 +1,6 @@ use crate::{ point, px, size, Bounds, DevicePixels, Font, FontFeatures, FontId, FontMetrics, FontStyle, - FontWeight, GlyphId, GlyphRasterParams, Pixels, PlatformTextSystem, Point, Result, ShapedGlyph, + FontWeight, GlyphId, Pixels, PlatformTextSystem, Point, RenderGlyphParams, Result, ShapedGlyph, ShapedLine, ShapedRun, SharedString, Size, SUBPIXEL_VARIANTS, }; use anyhow::anyhow; @@ -134,13 +134,13 @@ impl PlatformTextSystem for MacTextSystem { self.0.read().glyph_for_char(font_id, ch) } - fn glyph_raster_bounds(&self, params: &GlyphRasterParams) -> Result> { + fn glyph_raster_bounds(&self, params: &RenderGlyphParams) -> Result> { self.0.read().raster_bounds(params) } fn rasterize_glyph( &self, - glyph_id: &GlyphRasterParams, + glyph_id: &RenderGlyphParams, ) -> Result<(Size, Vec)> { self.0.read().rasterize_glyph(glyph_id) } @@ -234,7 +234,7 @@ impl MacTextSystemState { }) } - fn raster_bounds(&self, params: &GlyphRasterParams) -> Result> { + fn raster_bounds(&self, params: &RenderGlyphParams) -> Result> { let font = &self.fonts[params.font_id.0]; let scale = Transform2F::from_scale(params.scale_factor); Ok(font @@ -248,7 +248,7 @@ impl MacTextSystemState { .into()) } - fn rasterize_glyph(&self, params: &GlyphRasterParams) -> Result<(Size, Vec)> { + fn rasterize_glyph(&self, params: &RenderGlyphParams) -> Result<(Size, Vec)> { let glyph_bounds = self.raster_bounds(params)?; if glyph_bounds.size.width.0 == 0 || glyph_bounds.size.height.0 == 0 { Err(anyhow!("glyph bounds are empty")) diff --git a/crates/gpui3/src/platform/mac/window.rs b/crates/gpui3/src/platform/mac/window.rs index 6e87543fc94a7a423052d7b06f9e1ac349de7b80..a669b1cf485736e2a06972ab2306c64593dc03f5 100644 --- a/crates/gpui3/src/platform/mac/window.rs +++ b/crates/gpui3/src/platform/mac/window.rs @@ -1,10 +1,10 @@ use super::{ns_string, MetalRenderer, NSRange}; use crate::{ - point, px, size, AnyWindowHandle, Bounds, Event, GlyphRasterParams, KeyDownEvent, Keystroke, - MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, - MouseUpEvent, NSRectExt, Pixels, Platform, PlatformAtlas, PlatformDispatcher, - PlatformInputHandler, PlatformScreen, PlatformWindow, Point, Scene, Size, Timer, - WindowAppearance, WindowBounds, WindowKind, WindowOptions, WindowPromptLevel, + point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers, + ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt, + Pixels, Platform, PlatformAtlas, PlatformDispatcher, PlatformInputHandler, PlatformScreen, + PlatformWindow, Point, Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind, + WindowOptions, WindowPromptLevel, }; use block::ConcreteBlock; use cocoa::{ @@ -886,7 +886,7 @@ impl PlatformWindow for MacWindow { } } - fn glyph_atlas(&self) -> Arc> { + fn glyph_atlas(&self) -> Arc { self.0.lock().renderer.glyph_atlas().clone() } } diff --git a/crates/gpui3/src/text_system.rs b/crates/gpui3/src/text_system.rs index 4529214ea710e7fce3917c7a7e38d162b7959c65..6deaad925c41b1de5c95feb02d3b7feeefc735bd 100644 --- a/crates/gpui3/src/text_system.rs +++ b/crates/gpui3/src/text_system.rs @@ -215,13 +215,13 @@ impl TextSystem { }) } - pub fn raster_bounds(&self, params: &GlyphRasterParams) -> Result> { + pub fn raster_bounds(&self, params: &RenderGlyphParams) -> Result> { self.platform_text_system.glyph_raster_bounds(params) } pub fn rasterize_glyph( &self, - glyph_id: &GlyphRasterParams, + glyph_id: &RenderGlyphParams, ) -> Result<(Size, Vec)> { self.platform_text_system.rasterize_glyph(glyph_id) } @@ -384,7 +384,7 @@ pub struct ShapedGlyph { } #[derive(Clone, Debug, PartialEq)] -pub struct GlyphRasterParams { +pub struct RenderGlyphParams { pub(crate) font_id: FontId, pub(crate) glyph_id: GlyphId, pub(crate) font_size: Pixels, @@ -392,9 +392,9 @@ pub struct GlyphRasterParams { pub(crate) scale_factor: f32, } -impl Eq for GlyphRasterParams {} +impl Eq for RenderGlyphParams {} -impl Hash for GlyphRasterParams { +impl Hash for RenderGlyphParams { fn hash(&self, state: &mut H) { self.font_id.0.hash(state); self.glyph_id.0.hash(state); diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 34048b54519a8c80509d31698ae9b20897860748..2e2f6eea5441efe21b3f02128d25ebfed4b1aa87 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,9 +1,9 @@ use crate::{ px, AnyView, AppContext, AvailableSpace, BorrowAppContext, Bounds, Context, Corners, Effect, - Element, EntityId, FontId, GlyphId, GlyphRasterParams, Handle, Hsla, IsZero, LayerId, LayoutId, - MainThread, MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, - Reference, ScaledPixels, Scene, Size, Style, TaffyLayoutEngine, WeakHandle, WindowOptions, - SUBPIXEL_VARIANTS, + Element, EntityId, FontId, GlyphId, Handle, Hsla, IsZero, LayerId, LayoutId, MainThread, + MainThreadOnly, MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, Reference, + RenderGlyphParams, ScaledPixels, Scene, Size, Style, TaffyLayoutEngine, WeakHandle, + WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; use futures::Future; @@ -16,7 +16,7 @@ pub struct AnyWindow {} pub struct Window { handle: AnyWindowHandle, platform_window: MainThreadOnly>, - glyph_atlas: Arc>, + glyph_atlas: Arc, rem_size: Pixels, content_size: Size, layout_engine: TaffyLayoutEngine, @@ -222,7 +222,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { x: (glyph_origin.x.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, y: (glyph_origin.y.0.fract() * SUBPIXEL_VARIANTS as f32).floor() as u8, }; - let params = GlyphRasterParams { + let params = RenderGlyphParams { font_id, glyph_id, font_size, @@ -236,7 +236,9 @@ impl<'a, 'w> WindowContext<'a, 'w> { let tile = self .window .glyph_atlas - .get_or_insert_with(¶ms, &mut || self.text_system().rasterize_glyph(¶ms))?; + .get_or_insert_with(¶ms.clone().into(), &mut || { + self.text_system().rasterize_glyph(¶ms) + })?; let bounds = Bounds { origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into), size: tile.bounds.size.map(Into::into), From 25a2554bdd976dc7834d6645031adeaf918a29cc Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Tue, 3 Oct 2023 21:23:32 -0600 Subject: [PATCH 22/28] Checkpoint --- crates/gpui3/src/assets.rs | 60 +++++++++++++++++++ crates/gpui3/src/geometry.rs | 36 +++++++---- crates/gpui3/src/gpui3.rs | 10 ++++ crates/gpui3/src/svg_library.rs | 102 ++++++++++++++++++++++++++++++++ 4 files changed, 196 insertions(+), 12 deletions(-) create mode 100644 crates/gpui3/src/assets.rs create mode 100644 crates/gpui3/src/svg_library.rs diff --git a/crates/gpui3/src/assets.rs b/crates/gpui3/src/assets.rs new file mode 100644 index 0000000000000000000000000000000000000000..5d737fcd2f84685c15278fddb9df9df64df7f54b --- /dev/null +++ b/crates/gpui3/src/assets.rs @@ -0,0 +1,60 @@ +use crate::{size, DevicePixels, Result, SharedString, Size}; +use anyhow::anyhow; +use image::{Bgra, ImageBuffer}; +use std::{ + borrow::Cow, + fmt, + sync::atomic::{AtomicUsize, Ordering::SeqCst}, +}; + +pub trait AssetSource: 'static + Send + Sync { + fn load(&self, path: &SharedString) -> Result>; + fn list(&self, path: &SharedString) -> Result>; +} + +impl AssetSource for () { + fn load(&self, path: &SharedString) -> Result> { + Err(anyhow!( + "get called on empty asset provider with \"{}\"", + path + )) + } + + fn list(&self, _path: &SharedString) -> Result> { + Ok(vec![]) + } +} + +pub struct ImageData { + pub id: usize, + data: ImageBuffer, Vec>, +} + +impl ImageData { + pub fn from_raw(size: Size, bytes: Vec) -> Self { + static NEXT_ID: AtomicUsize = AtomicUsize::new(0); + + Self { + id: NEXT_ID.fetch_add(1, SeqCst), + data: ImageBuffer::from_raw(size.width.into(), size.height.into(), bytes).unwrap(), + } + } + + pub fn as_bytes(&self) -> &[u8] { + &self.data + } + + pub fn size(&self) -> Size { + let (width, height) = self.data.dimensions(); + size(width.into(), height.into()) + } +} + +impl fmt::Debug for ImageData { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ImageData") + .field("id", &self.id) + .field("size", &self.data.dimensions()) + .finish() + } +} diff --git a/crates/gpui3/src/geometry.rs b/crates/gpui3/src/geometry.rs index 183554204493ff8f4acbd4043f6d64793e4b4c84..98bf802402c6fd0732919ee95b534a453e2e2e36 100644 --- a/crates/gpui3/src/geometry.rs +++ b/crates/gpui3/src/geometry.rs @@ -182,10 +182,10 @@ impl, S: Clone> MulAssign for Size { impl Eq for Size {} impl From>> for Size> { - fn from(val: Size>) -> Self { + fn from(size: Size>) -> Self { Size { - width: val.width.map(|p| p.0 as f32), - height: val.height.map(|p| p.0 as f32), + width: size.width.map(|p| p.0 as f32), + height: size.height.map(|p| p.0 as f32), } } } @@ -548,14 +548,14 @@ impl std::hash::Hash for Pixels { } impl From for Pixels { - fn from(val: f64) -> Self { - Pixels(val as f32) + fn from(pixels: f64) -> Self { + Pixels(pixels as f32) } } impl From for Pixels { - fn from(val: f32) -> Self { - Pixels(val) + fn from(pixels: f32) -> Self { + Pixels(pixels) } } @@ -608,8 +608,20 @@ impl From for i32 { } impl From for DevicePixels { - fn from(val: i32) -> Self { - DevicePixels(val) + fn from(device_pixels: i32) -> Self { + DevicePixels(device_pixels) + } +} + +impl From for DevicePixels { + fn from(device_pixels: u32) -> Self { + DevicePixels(device_pixels as i32) + } +} + +impl From for u32 { + fn from(device_pixels: DevicePixels) -> Self { + device_pixels.0 as u32 } } @@ -620,8 +632,8 @@ impl From for u64 { } impl From for DevicePixels { - fn from(val: u64) -> Self { - DevicePixels(val as i32) + fn from(device_pixels: u64) -> Self { + DevicePixels(device_pixels as i32) } } @@ -903,7 +915,7 @@ impl IsZero for Point { impl IsZero for Size { fn is_zero(&self) -> bool { - self.width.is_zero() && self.height.is_zero() + self.width.is_zero() || self.height.is_zero() } } diff --git a/crates/gpui3/src/gpui3.rs b/crates/gpui3/src/gpui3.rs index 8f6a2680519fb2d17279a8b99056db6ab397b223..b37c621a2b66e2d0b71acd8db1d9217071eedad6 100644 --- a/crates/gpui3/src/gpui3.rs +++ b/crates/gpui3/src/gpui3.rs @@ -1,4 +1,5 @@ mod app; +mod assets; mod color; mod element; mod elements; @@ -9,6 +10,7 @@ mod scene; mod style; mod style_helpers; mod styled; +mod svg_library; mod taffy; mod text_system; mod util; @@ -17,12 +19,14 @@ mod window; pub use anyhow::Result; pub use app::*; +pub use assets::*; pub use color::*; pub use element::*; pub use elements::*; pub use executor::*; pub use geometry::*; pub use gpui3_macros::*; +pub use svg_library::*; pub use platform::*; pub use refineable::*; @@ -145,6 +149,12 @@ impl std::fmt::Debug for SharedString { } } +impl std::fmt::Display for SharedString { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0.as_ref()) + } +} + impl>> From for SharedString { fn from(value: T) -> Self { Self(value.into()) diff --git a/crates/gpui3/src/svg_library.rs b/crates/gpui3/src/svg_library.rs new file mode 100644 index 0000000000000000000000000000000000000000..19023f2980bbb0d69ab3939b487c5ee826bf27d2 --- /dev/null +++ b/crates/gpui3/src/svg_library.rs @@ -0,0 +1,102 @@ +use crate::{AssetSource, DevicePixels, ImageData, IsZero, Result, SharedString, Size}; +use anyhow::anyhow; +use collections::HashMap; +use parking_lot::{RwLock, RwLockUpgradableReadGuard}; +use std::hash::Hash; +use std::sync::Arc; +use usvg::Tree as SvgTree; + +#[derive(Clone, PartialEq, Hash, Eq)] +pub struct SvgRenderParams { + path: SharedString, + size: Size, +} + +pub struct SvgRenderer { + asset_source: Arc, + trees_by_path: RwLock>, + rendered: RwLock>>, +} + +impl SvgRenderer { + pub fn render(&self, params: SvgRenderParams) -> Result> { + if params.size.is_zero() { + return Err(anyhow!("can't render at a zero size")); + } + + let rendered = self.rendered.upgradable_read(); + if let Some(image_data) = rendered.get(¶ms) { + Ok(image_data.clone()) + } else { + // There's no rendered SVG for the path at the requested size. + // Have we already loaded a tree for the path? + let trees_by_path = self.trees_by_path.upgradable_read(); + let tree = if let Some(tree) = trees_by_path.get(¶ms.path) { + tree.clone() + } else { + // Load the tree + let bytes = self.asset_source.load(¶ms.path)?; + let tree = usvg::Tree::from_data(&bytes, &usvg::Options::default())?; + let mut trees_by_path = RwLockUpgradableReadGuard::upgrade(trees_by_path); + trees_by_path.insert(params.path.clone(), tree.clone()); + tree + }; + + // Render the SVG to a pixmap with the specified width and height. + // Convert the pixmap's pixels into an image data and cache it in `rendered`. + let mut pixmap = + tiny_skia::Pixmap::new(params.size.width.into(), params.size.height.into()) + .unwrap(); + resvg::render( + &tree, + usvg::FitTo::Width(params.size.width.into()), + pixmap.as_mut(), + ); + let alpha_mask = pixmap + .pixels() + .iter() + .map(|p| p.alpha()) + .collect::>(); + let mut rendered = RwLockUpgradableReadGuard::upgrade(rendered); + let image_data = Arc::new(ImageData::from_raw(params.size, alpha_mask)); + rendered.insert(params, image_data.clone()); + + Ok(image_data) + } + } +} + +// impl SvgRenderer { +// pub fn render_svg( +// &mut self, +// size: Vector2I, +// path: Cow<'static, str>, +// svg: usvg::Tree, +// ) -> Option { +// let mut pixmap = tiny_skia::Pixmap::new(size.x() as u32, size.y() as u32)?; +// resvg::render(&svg, usvg::FitTo::Width(size.x() as u32), pixmap.as_mut()); + +// let atlases = &mut self.atlases; +// match self.icons.entry(IconDescriptor { +// path, +// width: size.x(), +// height: size.y(), +// }) { +// Entry::Occupied(entry) => Some(entry.get().clone()), +// Entry::Vacant(entry) => { +// let mask = pixmap +// .pixels() +// .iter() +// .map(|a| a.alpha()) +// .collect::>(); +// let (alloc_id, atlas_bounds) = atlases.upload(size, &mask)?; +// let icon_sprite = IconSprite { +// atlas_id: alloc_id.atlas_id, +// atlas_origin: atlas_bounds.origin(), +// size, +// }; +// Some(entry.insert(icon_sprite).clone()) +// } +// } +// } +// } From a1ee2db6d1887ec962bb8c46b42e16c49fae5741 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 4 Oct 2023 08:48:05 +0200 Subject: [PATCH 23/28] Use Courier for now, to avoid panicking --- crates/gpui3/src/elements/text.rs | 6 +++--- crates/storybook2/src/collab_panel.rs | 2 +- crates/storybook2/src/workspace.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/gpui3/src/elements/text.rs b/crates/gpui3/src/elements/text.rs index b5f8fc5ec520c4284742f29008901b7b5b54cc94..77390e79a41841699354bb7d7685ade610a01c30 100644 --- a/crates/gpui3/src/elements/text.rs +++ b/crates/gpui3/src/elements/text.rs @@ -46,11 +46,11 @@ impl Element for Text { .line_height .to_pixels(font_size.into(), cx.rem_size()); let text = self.text.clone(); - let paint_state = Arc::new(Mutex::new(None)); + let frame_state = Arc::new(Mutex::new(None)); let rem_size = cx.rem_size(); let layout_id = cx.request_measured_layout(Default::default(), rem_size, { - let frame_state = paint_state.clone(); + let frame_state = frame_state.clone(); move |_, _| { let Some(line_layout) = text_system .layout_line( @@ -77,7 +77,7 @@ impl Element for Text { } }); - Ok((layout_id?, paint_state)) + Ok((layout_id?, frame_state)) } fn paint<'a>( diff --git a/crates/storybook2/src/collab_panel.rs b/crates/storybook2/src/collab_panel.rs index 415598d0cbd21ea808c8dc496980859c92feeedc..0f4b2f49716f126af5e13051474d380f8b3eed13 100644 --- a/crates/storybook2/src/collab_panel.rs +++ b/crates/storybook2/src/collab_panel.rs @@ -30,7 +30,7 @@ impl CollabPanel { .h_full() .flex() .flex_col() - .font("Zed Sans Extended") + .font("Courier") .text_color(theme.middle.base.default.foreground) .border_color(theme.middle.base.default.border) .border() diff --git a/crates/storybook2/src/workspace.rs b/crates/storybook2/src/workspace.rs index 15f26e52a527507b29013983f430f7dbb1f8b7b7..9e0083379d473a09034c4999715ddf326e08110a 100644 --- a/crates/storybook2/src/workspace.rs +++ b/crates/storybook2/src/workspace.rs @@ -32,7 +32,7 @@ impl Workspace { .size_full() .flex() .flex_col() - .font("Zed Sans Extended") + .font("Courier") .gap_0() .justify_start() .items_start() From 4cf2ba20c22ee5a819674278c0828c68022543c5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 4 Oct 2023 10:51:47 +0200 Subject: [PATCH 24/28] Checkpoint: render SVGs --- crates/gpui3/src/app.rs | 18 ++-- crates/gpui3/src/assets.rs | 4 + crates/gpui3/src/elements/svg.rs | 30 ++---- crates/gpui3/src/gpui3.rs | 4 +- crates/gpui3/src/platform.rs | 12 ++- .../gpui3/src/platform/mac/metal_renderer.rs | 12 +-- crates/gpui3/src/platform/mac/window.rs | 4 +- crates/gpui3/src/svg_library.rs | 102 ------------------ crates/gpui3/src/svg_renderer.rs | 47 ++++++++ crates/gpui3/src/window.rs | 59 ++++++++-- crates/storybook2/src/assets.rs | 30 ++++++ crates/storybook2/src/storybook2.rs | 30 ++---- crates/util/src/arc_cow.rs | 10 ++ 13 files changed, 187 insertions(+), 175 deletions(-) delete mode 100644 crates/gpui3/src/svg_library.rs create mode 100644 crates/gpui3/src/svg_renderer.rs create mode 100644 crates/storybook2/src/assets.rs diff --git a/crates/gpui3/src/app.rs b/crates/gpui3/src/app.rs index 124cc55c3c0cf71772669e32412f80bc94d525cc..da0153d383458b415c2f2cae255386a85fed5146 100644 --- a/crates/gpui3/src/app.rs +++ b/crates/gpui3/src/app.rs @@ -8,9 +8,9 @@ pub use model_context::*; use refineable::Refineable; use crate::{ - current_platform, run_on_main, spawn_on_main, Context, LayoutId, MainThread, MainThreadOnly, - Platform, PlatformDispatcher, RootView, TextStyle, TextStyleRefinement, TextSystem, Window, - WindowContext, WindowHandle, WindowId, + current_platform, run_on_main, spawn_on_main, AssetSource, Context, LayoutId, MainThread, + MainThreadOnly, Platform, PlatformDispatcher, RootView, SvgRenderer, TextStyle, + TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId, }; use anyhow::{anyhow, Result}; use collections::{HashMap, VecDeque}; @@ -29,16 +29,18 @@ use util::ResultExt; pub struct App(Arc>>); impl App { - pub fn production() -> Self { - Self::new(current_platform()) + pub fn production(asset_source: Arc) -> Self { + Self::new(current_platform(), asset_source) } #[cfg(any(test, feature = "test"))] pub fn test() -> Self { - Self::new(Arc::new(super::TestPlatform::new())) + let platform = Arc::new(super::TestPlatform::new()); + let asset_source = Arc::new(()); + Self::new(platform, asset_source) } - fn new(platform: Arc) -> Self { + fn new(platform: Arc, asset_source: Arc) -> Self { let dispatcher = platform.dispatcher(); let text_system = Arc::new(TextSystem::new(platform.text_system())); let entities = EntityMap::new(); @@ -49,6 +51,7 @@ impl App { platform: MainThreadOnly::new(platform, dispatcher.clone()), dispatcher, text_system, + svg_renderer: SvgRenderer::new(asset_source), pending_updates: 0, text_style_stack: Vec::new(), state_stacks_by_type: HashMap::default(), @@ -83,6 +86,7 @@ pub struct AppContext { dispatcher: Arc, text_system: Arc, pending_updates: usize, + pub(crate) svg_renderer: SvgRenderer, pub(crate) text_style_stack: Vec, pub(crate) state_stacks_by_type: HashMap>>, pub(crate) unit_entity: Handle<()>, diff --git a/crates/gpui3/src/assets.rs b/crates/gpui3/src/assets.rs index 5d737fcd2f84685c15278fddb9df9df64df7f54b..15af277f6620830adf7a73cbb317eb9572cda3d8 100644 --- a/crates/gpui3/src/assets.rs +++ b/crates/gpui3/src/assets.rs @@ -44,6 +44,10 @@ impl ImageData { &self.data } + pub fn into_bytes(self) -> Vec { + self.data.into_raw() + } + pub fn size(&self) -> Size { let (width, height) = self.data.dimensions(); size(width.into(), height.into()) diff --git a/crates/gpui3/src/elements/svg.rs b/crates/gpui3/src/elements/svg.rs index 70abf63cb4e62fd95af760c8a8538b96e70cf772..dbb4ffb15539f05337a64da45525ecf05f01e37c 100644 --- a/crates/gpui3/src/elements/svg.rs +++ b/crates/gpui3/src/elements/svg.rs @@ -1,9 +1,9 @@ -use crate::{Element, Layout, LayoutId, Result, Style, StyleHelpers, Styled}; +use crate::{Element, Layout, LayoutId, Result, SharedString, Style, StyleHelpers, Styled}; use refineable::RefinementCascade; -use std::{borrow::Cow, marker::PhantomData}; +use std::marker::PhantomData; pub struct Svg { - path: Option>, + path: Option, style: RefinementCascade