From 5e93481037e9ed04ec564ef534f0477e1eee58cc Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Thu, 30 May 2024 11:57:40 +0200 Subject: [PATCH 01/17] logs: Capture log output, interpret ansi escape sequences and show it --- examples/logs/Cargo.toml | 21 + examples/logs/examples/logs.rs | 358 ++++++++++++++++++ examples/logs/src/lib.rs | 14 + examples/shared/src/lib.rs | 4 +- examples/shared/src/terminal/color.rs | 277 ++++++++++++++ examples/shared/src/terminal/color_schemes.rs | 103 +++++ examples/shared/src/terminal/config.rs | 204 ++++++++++ examples/shared/src/terminal/mod.rs | 11 + examples/shared/src/terminal/named_color.rs | 129 +++++++ 9 files changed, 1120 insertions(+), 1 deletion(-) create mode 100644 examples/logs/Cargo.toml create mode 100644 examples/logs/examples/logs.rs create mode 100644 examples/logs/src/lib.rs create mode 100644 examples/shared/src/terminal/color.rs create mode 100644 examples/shared/src/terminal/color_schemes.rs create mode 100644 examples/shared/src/terminal/config.rs create mode 100644 examples/shared/src/terminal/mod.rs create mode 100644 examples/shared/src/terminal/named_color.rs diff --git a/examples/logs/Cargo.toml b/examples/logs/Cargo.toml new file mode 100644 index 0000000..5b1cc96 --- /dev/null +++ b/examples/logs/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "logs" +version = "0.1.0" +edition = "2021" + +[dev-dependencies] +termwiz = "0.22.0" + +shared = { path = "../shared" } + +massive-geometry = { workspace = true } +massive-shell = { workspace = true } +massive-scene = { workspace = true } +massive-shapes = { workspace = true } + +anyhow = { workspace = true } +cosmic-text = { workspace = true } +log = { workspace = true } +winit = { workspace = true } +env_logger = { workspace = true } +tokio = { workspace = true } diff --git a/examples/logs/examples/logs.rs b/examples/logs/examples/logs.rs new file mode 100644 index 0000000..ec44ef0 --- /dev/null +++ b/examples/logs/examples/logs.rs @@ -0,0 +1,358 @@ +use std::{ + io, iter, + ops::Range, + sync::{Arc, Mutex}, +}; + +use anyhow::Result; +use cosmic_text::{fontdb, FontSystem}; +use env_logger::{Builder, Target, WriteStyle}; +use log::{error, info}; +use termwiz::{ + cell::Intensity, + color::ColorSpec, + escape::{self, csi::Sgr, Action, ControlCode, CSI}, +}; +use tokio::sync::mpsc::{self, UnboundedReceiver}; +use winit::dpi::LogicalSize; + +use massive_geometry::{Camera, Color}; +use massive_scene::PositionedShape; +use massive_shapes::TextWeight; +use massive_shell::{shell, ApplicationContext}; +use shared::{ + application::{Application, UpdateResponse}, + code_viewer::{self, TextAttribute}, + terminal::{color_schemes, Rgb}, +}; + +const CANVAS_ID: &str = "massive-logs"; + +fn main() -> Result<()> { + let (sender, receiver) = mpsc::unbounded_channel(); + Builder::from_default_env() + // Colors please! + .write_style(WriteStyle::Always) + .target(Target::Pipe(Box::new(Sender(sender)))) + .init(); + + shared::main(|| async_main(receiver)) +} + +struct Sender(mpsc::UnboundedSender>); + +impl io::Write for Sender { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0 + .send(buf.to_vec()) + .map_err(|_| io::Error::from(io::ErrorKind::BrokenPipe))?; + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +async fn async_main(receiver: UnboundedReceiver>) -> Result<()> { + shell::run(|ctx| logs(receiver, ctx)).await +} + +async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationContext) -> Result<()> { + error!("TEST"); + + let bytes = receiver.recv().await.unwrap(); + let mut parser = escape::parser::Parser::new(); + let parsed = parser.parse_as_vec(&bytes); + + let mut processor = Processor::new(color_schemes::light::PAPER); + for action in parsed { + processor.process(action) + } + + let (text, attributes) = processor.into_text_and_attribute_ranges(); + + // boilerplate + + let mut font_system = { + let mut db = fontdb::Database::new(); + db.load_font_data(shared::fonts::JETBRAINS_MONO.to_vec()); + // Use an invariant locale. + FontSystem::new_with_locale_and_db("en-US".into(), db) + }; + + let font_size = 32.; + let line_height = 40.; + + let (runs, height) = + code_viewer::shape_text(&mut font_system, &text, &attributes, font_size, line_height); + + // Window + + let window_size = LogicalSize::new(1280., 800.); + + let window = ctx.new_window(window_size, Some(CANVAS_ID))?; + + // Camera + + let camera = { + let fovy: f64 = 45.0; + let camera_distance = 1.0 / (fovy / 2.0).to_radians().tan(); + Camera::new((0.0, 0.0, camera_distance), (0.0, 0.0, 0.0)) + }; + + // Shell + + let font_system = Arc::new(Mutex::new(font_system)); + + let (mut renderer, mut director) = window + .new_renderer(font_system, camera, window.inner_size()) + .await?; + + // Application + + let page_size = (1280, height as u64); + let mut application = Application::new(page_size); + let mut current_matrix = application.matrix(); + let matrix = director.cast(current_matrix); + + // Hold the positioned shapes in this context, otherwise they will disappear. + let _positioned_shapes: Vec<_> = runs + .into_iter() + .map(|run| director.cast(PositionedShape::new(matrix.clone(), run))) + .collect(); + + director.action()?; + + loop { + let window_event = ctx.wait_for_event(&mut renderer).await?; + + info!("Window Event: {window_event:?}"); + + match application.update(window_event) { + UpdateResponse::Exit => return Ok(()), + UpdateResponse::Continue => {} + } + + // DI: This check has to be done in the renderer and the renderer has to decide when it + // needs to redraw. + let new_matrix = application.matrix(); + if new_matrix != current_matrix { + matrix.update(new_matrix); + current_matrix = new_matrix; + director.action()?; + } + } +} + +#[derive(Debug)] +struct Processor { + default: Attributes, + current: Attributes, + color_scheme: color_schemes::Scheme, + text: String, + text_attributes: Vec, +} + +#[derive(Debug, Copy, Clone, PartialEq)] +struct Attributes { + pub foreground_color: Color, + pub bold: bool, +} + +impl Processor { + pub fn new(color_scheme: color_schemes::Scheme) -> Self { + let default_attributes = Attributes { + foreground_color: rgb_to_color(color_scheme.primary.foreground), + bold: false, + }; + + Self { + default: default_attributes, + current: default_attributes, + color_scheme, + text: String::new(), + // TODO: Not quite efficient storing the attributes for each u8 inside a string. + text_attributes: Vec::new(), + } + } + + pub fn into_text_and_attribute_ranges(self) -> (String, Vec) { + // TODO: this is something like a slicetools candidate. AFAI(and ChatGPT)K all solutions to + // this problem are either inefficient (generate intermediate Vecs) or hard to read. + + let mut ranges: Vec = Vec::new(); + + if self.text_attributes.is_empty() { + return (self.text, Vec::new()); + } + + let mut current_start = 0; + + for i in 1..self.text_attributes.len() { + let prev = &self.text_attributes[i - 1]; + if *prev != self.text_attributes[i] { + ranges.push(ta(current_start..i, prev)); + current_start = i; + } + } + + ranges.push(ta( + current_start..self.text_attributes.len(), + &self.text_attributes[current_start], + )); + + return (self.text, ranges); + + fn ta(range: Range, attr: &Attributes) -> TextAttribute { + TextAttribute { + range, + color: attr.foreground_color, + weight: if attr.bold { + TextWeight::BOLD + } else { + TextWeight::NORMAL + }, + } + } + } + + pub fn process(&mut self, action: escape::Action) { + match action { + Action::Print(ch) => { + self.text.push(ch); + self.text_attributes.push(self.current) + } + Action::PrintString(string) => { + self.text.push_str(&string); + self.text_attributes + .extend(iter::repeat(self.current).take(string.len())) + } + Action::Control(control) => match control { + ControlCode::Null => {} + ControlCode::StartOfHeading => {} + ControlCode::StartOfText => {} + ControlCode::EndOfText => {} + ControlCode::EndOfTransmission => {} + ControlCode::Enquiry => {} + ControlCode::Acknowledge => {} + ControlCode::Bell => {} + ControlCode::Backspace => {} + ControlCode::HorizontalTab => {} + ControlCode::LineFeed => { + self.text.push('\n'); + self.text_attributes.push(self.current); + } + ControlCode::VerticalTab => {} + ControlCode::FormFeed => {} + ControlCode::CarriageReturn => { + self.text.push('\r'); + self.text_attributes.push(self.current); + } + ControlCode::ShiftOut => {} + ControlCode::ShiftIn => {} + ControlCode::DataLinkEscape => {} + ControlCode::DeviceControlOne => {} + ControlCode::DeviceControlTwo => {} + ControlCode::DeviceControlThree => {} + ControlCode::DeviceControlFour => {} + ControlCode::NegativeAcknowledge => {} + ControlCode::SynchronousIdle => {} + ControlCode::EndOfTransmissionBlock => {} + ControlCode::Cancel => {} + ControlCode::EndOfMedium => {} + ControlCode::Substitute => {} + ControlCode::Escape => {} + ControlCode::FileSeparator => {} + ControlCode::GroupSeparator => {} + ControlCode::RecordSeparator => {} + ControlCode::UnitSeparator => {} + ControlCode::BPH => {} + ControlCode::NBH => {} + ControlCode::IND => {} + ControlCode::NEL => {} + ControlCode::SSA => {} + ControlCode::ESA => {} + ControlCode::HTS => {} + ControlCode::HTJ => {} + ControlCode::VTS => {} + ControlCode::PLD => {} + ControlCode::PLU => {} + ControlCode::RI => {} + ControlCode::SS2 => {} + ControlCode::SS3 => {} + ControlCode::DCS => {} + ControlCode::PU1 => {} + ControlCode::PU2 => {} + ControlCode::STS => {} + ControlCode::CCH => {} + ControlCode::MW => {} + ControlCode::SPA => {} + ControlCode::EPA => {} + ControlCode::SOS => {} + ControlCode::SCI => {} + ControlCode::CSI => {} + ControlCode::ST => {} + ControlCode::OSC => {} + ControlCode::PM => {} + ControlCode::APC => {} + }, + Action::DeviceControl(_) => {} + Action::OperatingSystemCommand(_) => {} + Action::CSI(csi) => match csi { + CSI::Sgr(sgr) => match sgr { + Sgr::Reset => self.current = self.default, + Sgr::Intensity(intensity) => match intensity { + Intensity::Normal => self.current.bold = false, + Intensity::Bold => self.current.bold = true, + Intensity::Half => {} + }, + Sgr::Underline(_) => {} + Sgr::UnderlineColor(_) => {} + Sgr::Blink(_) => {} + Sgr::Italic(_) => {} + Sgr::Inverse(_) => {} + Sgr::Invisible(_) => {} + Sgr::StrikeThrough(_) => {} + Sgr::Font(_) => {} + Sgr::Foreground(foreground) => match foreground { + ColorSpec::Default => { + self.current.foreground_color = self.default.foreground_color + } + ColorSpec::PaletteIndex(index) => { + // TODO: this panics if the index is out of range. + let rgb = if index > 7 { + self.color_scheme.bright[(index - 8) as _] + } else { + self.color_scheme.normal[index as _] + }; + + self.current.foreground_color = rgb_to_color(rgb); + } + ColorSpec::TrueColor(_) => {} + }, + Sgr::Background(_) => {} + Sgr::Overline(_) => {} + Sgr::VerticalAlign(_) => {} + }, + CSI::Cursor(_) => {} + CSI::Edit(_) => {} + CSI::Mode(_) => {} + CSI::Device(_) => {} + CSI::Mouse(_) => {} + CSI::Window(_) => {} + CSI::Keyboard(_) => {} + CSI::SelectCharacterPath(_, _) => {} + CSI::Unspecified(_) => {} + }, + Action::Esc(_) => {} + Action::Sixel(_) => {} + Action::XtGetTcap(_) => {} + Action::KittyImage(_) => {} + } + } +} + +fn rgb_to_color(value: Rgb) -> Color { + (value.r, value.g, value.b).into() +} diff --git a/examples/logs/src/lib.rs b/examples/logs/src/lib.rs new file mode 100644 index 0000000..7d12d9a --- /dev/null +++ b/examples/logs/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/examples/shared/src/lib.rs b/examples/shared/src/lib.rs index 82f8ebc..643db17 100644 --- a/examples/shared/src/lib.rs +++ b/examples/shared/src/lib.rs @@ -2,6 +2,7 @@ pub mod application; pub mod code_viewer; pub mod fonts; pub mod positioning; +pub mod terminal; use std::future::Future; @@ -13,7 +14,8 @@ where { #[cfg(not(target_arch = "wasm32"))] { - env_logger::init(); + // Don't force initialization of the env logger (calling main may already initialized it) + let _ = env_logger::try_init(); let rt = tokio::runtime::Runtime::new().expect("Failed to create tokio runtime"); // Use the runtime to block on the async function diff --git a/examples/shared/src/terminal/color.rs b/examples/shared/src/terminal/color.rs new file mode 100644 index 0000000..f273698 --- /dev/null +++ b/examples/shared/src/terminal/color.rs @@ -0,0 +1,277 @@ +use std::fmt; +use std::ops::{Index, IndexMut, Mul}; +use std::str::FromStr; + +use super::NamedColor; + +pub const COUNT: usize = 269; + +pub const RED: Rgb = Rgb { + r: 0xff, + g: 0x0, + b: 0x0, +}; +pub const YELLOW: Rgb = Rgb { + r: 0xff, + g: 0xff, + b: 0x0, +}; + +#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)] +pub struct Rgb { + pub r: u8, + pub g: u8, + pub b: u8, +} + +// a multiply function for Rgb, as the default dim is just *2/3 +impl Mul for Rgb { + type Output = Rgb; + + fn mul(self, rhs: f32) -> Rgb { + let result = Rgb { + r: (f32::from(self.r) * rhs).max(0.0).min(255.0) as u8, + g: (f32::from(self.g) * rhs).max(0.0).min(255.0) as u8, + b: (f32::from(self.b) * rhs).max(0.0).min(255.0) as u8, + }; + + result + } +} + +impl FromStr for Rgb { + type Err = (); + + fn from_str(s: &str) -> ::std::result::Result { + let mut chars = s.chars(); + let mut rgb = Rgb::default(); + + macro_rules! component { + ($($c:ident),*) => { + $( + match chars.next().and_then(|c| c.to_digit(16)) { + Some(val) => rgb.$c = (val as u8) << 4, + None => return Err(()) + } + + match chars.next().and_then(|c| c.to_digit(16)) { + Some(val) => rgb.$c |= val as u8, + None => return Err(()) + } + )* + } + } + + match chars.next() { + Some('0') => { + if chars.next() != Some('x') { + return Err(()); + } + } + Some('#') => (), + _ => return Err(()), + } + + component!(r, g, b); + + Ok(rgb) + } +} + +// /// List of indexed colors +// /// +// /// The first 16 entries are the standard ansi named colors. Items 16..232 are +// /// the color cube. Items 233..256 are the grayscale ramp. Item 256 is +// /// the configured foreground color, item 257 is the configured background +// /// color, item 258 is the cursor color. Following that are 8 positions for dim colors. +// /// Item 267 is the bright foreground color, 268 the dim foreground. +// #[derive(Copy, Clone)] +// pub struct List([Rgb; COUNT]); + +// impl<'a> From<&'a Colors> for List { +// fn from(colors: &Colors) -> List { +// // Type inference fails without this annotation +// let mut list = List([Rgb::default(); COUNT]); + +// list.fill_named(colors); +// list.fill_cube(colors); +// list.fill_gray_ramp(colors); + +// list +// } +// } + +// impl List { +// pub fn fill_named(&mut self, colors: &Colors) { +// // Normals +// self[NamedColor::Black] = colors.normal().black; +// self[NamedColor::Red] = colors.normal().red; +// self[NamedColor::Green] = colors.normal().green; +// self[NamedColor::Yellow] = colors.normal().yellow; +// self[NamedColor::Blue] = colors.normal().blue; +// self[NamedColor::Magenta] = colors.normal().magenta; +// self[NamedColor::Cyan] = colors.normal().cyan; +// self[NamedColor::White] = colors.normal().white; + +// // Br +// self[NamedColor::BrightBlack] = colors.bright().black; +// self[NamedColor::BrightRed] = colors.bright().red; +// self[NamedColor::BrightGreen] = colors.bright().green; +// self[NamedColor::BrightYellow] = colors.bright().yellow; +// self[NamedColor::BrightBlue] = colors.bright().blue; +// self[NamedColor::BrightMagenta] = colors.bright().magenta; +// self[NamedColor::BrightCyan] = colors.bright().cyan; +// self[NamedColor::BrightWhite] = colors.bright().white; +// self[NamedColor::BrightForeground] = colors +// .primary +// .bright_foreground +// .unwrap_or(colors.primary.foreground); + +// // Foreground and background +// self[NamedColor::Foreground] = colors.primary.foreground; +// self[NamedColor::Background] = colors.primary.background; + +// // Background for custom cursor colors +// self[NamedColor::Cursor] = colors.cursor.cursor.unwrap_or_else(Rgb::default); + +// // Dims +// self[ansi::NamedColor::DimForeground] = colors +// .primary +// .dim_foreground +// .unwrap_or(colors.primary.foreground * 0.66); +// match colors.dim { +// Some(ref dim) => { +// self[ansi::NamedColor::DimBlack] = dim.black; +// self[ansi::NamedColor::DimRed] = dim.red; +// self[ansi::NamedColor::DimGreen] = dim.green; +// self[ansi::NamedColor::DimYellow] = dim.yellow; +// self[ansi::NamedColor::DimBlue] = dim.blue; +// self[ansi::NamedColor::DimMagenta] = dim.magenta; +// self[ansi::NamedColor::DimCyan] = dim.cyan; +// self[ansi::NamedColor::DimWhite] = dim.white; +// } +// None => { +// self[ansi::NamedColor::DimBlack] = colors.normal().black * 0.66; +// self[ansi::NamedColor::DimRed] = colors.normal().red * 0.66; +// self[ansi::NamedColor::DimGreen] = colors.normal().green * 0.66; +// self[ansi::NamedColor::DimYellow] = colors.normal().yellow * 0.66; +// self[ansi::NamedColor::DimBlue] = colors.normal().blue * 0.66; +// self[ansi::NamedColor::DimMagenta] = colors.normal().magenta * 0.66; +// self[ansi::NamedColor::DimCyan] = colors.normal().cyan * 0.66; +// self[ansi::NamedColor::DimWhite] = colors.normal().white * 0.66; +// } +// } +// } + +// pub fn fill_cube(&mut self, colors: &Colors) { +// let mut index: usize = 16; +// // Build colors +// for r in 0..6 { +// for g in 0..6 { +// for b in 0..6 { +// // Override colors 16..232 with the config (if present) +// if let Some(indexed_color) = colors +// .indexed_colors +// .iter() +// .find(|ic| ic.index == index as u8) +// { +// self[index] = indexed_color.color; +// } else { +// self[index] = Rgb { +// r: if r == 0 { 0 } else { r * 40 + 55 }, +// b: if b == 0 { 0 } else { b * 40 + 55 }, +// g: if g == 0 { 0 } else { g * 40 + 55 }, +// }; +// } +// index += 1; +// } +// } +// } + +// debug_assert!(index == 232); +// } + +// pub fn fill_gray_ramp(&mut self, colors: &Colors) { +// let mut index: usize = 232; + +// for i in 0..24 { +// // Index of the color is number of named colors + number of cube colors + i +// let color_index = 16 + 216 + i; + +// // Override colors 232..256 with the config (if present) +// if let Some(indexed_color) = colors +// .indexed_colors +// .iter() +// .find(|ic| ic.index == color_index) +// { +// self[index] = indexed_color.color; +// index += 1; +// continue; +// } + +// let value = i * 10 + 8; +// self[index] = Rgb { +// r: value, +// g: value, +// b: value, +// }; +// index += 1; +// } + +// debug_assert!(index == 256); +// } +// } + +// impl fmt::Debug for List { +// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +// f.write_str("List[..]") +// } +// } + +// impl Index for List { +// type Output = Rgb; + +// #[inline] +// fn index(&self, idx: ansi::NamedColor) -> &Self::Output { +// &self.0[idx as usize] +// } +// } + +// impl IndexMut for List { +// #[inline] +// fn index_mut(&mut self, idx: ansi::NamedColor) -> &mut Self::Output { +// &mut self.0[idx as usize] +// } +// } + +// impl Index for List { +// type Output = Rgb; + +// #[inline] +// fn index(&self, idx: usize) -> &Self::Output { +// &self.0[idx] +// } +// } + +// impl IndexMut for List { +// #[inline] +// fn index_mut(&mut self, idx: usize) -> &mut Self::Output { +// &mut self.0[idx] +// } +// } + +// impl Index for List { +// type Output = Rgb; + +// #[inline] +// fn index(&self, idx: u8) -> &Self::Output { +// &self.0[idx as usize] +// } +// } + +// impl IndexMut for List { +// #[inline] +// fn index_mut(&mut self, idx: u8) -> &mut Self::Output { +// &mut self.0[idx as usize] +// } +// } diff --git a/examples/shared/src/terminal/color_schemes.rs b/examples/shared/src/terminal/color_schemes.rs new file mode 100644 index 0000000..2883d8b --- /dev/null +++ b/examples/shared/src/terminal/color_schemes.rs @@ -0,0 +1,103 @@ +use super::{AnsiColors, Rgb}; + +#[derive(Debug)] +pub struct Scheme { + pub primary: Primary, + pub normal: AnsiColors, + pub bright: AnsiColors, +} + +#[derive(Debug)] +pub struct Primary { + pub background: Rgb, + pub foreground: Rgb, +} + +pub mod light { + use super::{ac, rgb, Primary, Scheme}; + + pub const PENCIL: Scheme = Scheme { + primary: Primary { + background: rgb(0x424242), + foreground: rgb(0xf1f1f1), + }, + normal: ac([ + 0x212121, 0xc30771, 0x10a778, 0xa89c14, 0x008ec4, 0x523c79, 0x20a5ba, 0xe0e0e0, + ]), + bright: ac([ + 0x212121, 0xfb007a, 0x5fd7af, 0xf3e430, 0x20bbfc, 0x6855de, 0x4fb8cc, 0xf1f1f1, + ]), + }; + + pub const SEABIRD: Scheme = Scheme { + primary: Primary { + background: rgb(0xffffff), + foreground: rgb(0x61707a), + }, + normal: ac([ + 0x0b141a, 0xff4053, 0x11ab00, 0xbf8c00, 0x0099ff, 0x9854ff, 0x00a5ab, 0xffffff, + ]), + bright: ac([ + 0x0b141a, 0xff4053, 0x11ab00, 0xbf8c00, 0x0099ff, 0x9854ff, 0x00a5ab, 0xffffff, + ]), + }; + + pub const SOLARIZED: Scheme = Scheme { + primary: Primary { + background: rgb(0xfdf6e3), + foreground: rgb(0x657b83), + }, + normal: ac([ + 0x073642, 0xdc322f, 0x859900, 0xb58900, 0x268bd2, 0xd33682, 0x2aa198, 0xeee8d5, + ]), + bright: ac([ + 0x002b36, 0xcb4b16, 0x586e75, 0x657b83, 0x839496, 0x6c71c4, 0x93a1a1, 0xfdf6e3, + ]), + }; + + // https://github.com/NLKNguyen/papercolor-theme/blob/master/colors/PaperColor.vim + pub const PAPER: Scheme = Scheme { + primary: Primary { + background: rgb(0xffffff), + foreground: rgb(0x000000), + }, + normal: ac([ + 0xeeeeee, 0xaf0000, 0x008700, 0x5f8700, 0x0087af, 0x878787, 0x005f87, 0x444444, + ]), + bright: ac([ + 0xbcbcbc, 0xd70000, 0xd70087, 0x8700af, 0xd75f00, 0xd75f00, 0x005faf, 0x005f87, + ]), + }; +} + +const fn ac(colors: [u32; 8]) -> AnsiColors { + AnsiColors { + black: rgb(colors[0]), + red: rgb(colors[1]), + green: rgb(colors[2]), + yellow: rgb(colors[3]), + blue: rgb(colors[4]), + magenta: rgb(colors[5]), + cyan: rgb(colors[6]), + white: rgb(colors[7]), + } +} + +const fn rgb(x: u32) -> Rgb { + Rgb { + r: (x >> 16) as u8, + g: (x >> 8) as u8, + b: x as u8, + } +} + +// impl From for List { +// fn from(s: Scheme) -> Self { +// let mut colors = Colors::default(); +// colors.primary.foreground = s.primary.foreground; +// colors.primary.background = s.primary.background; +// colors.normal = NormalColors(s.normal); +// colors.bright = BrightColors(s.bright); +// Self::from(&colors) +// } +// } diff --git a/examples/shared/src/terminal/config.rs b/examples/shared/src/terminal/config.rs new file mode 100644 index 0000000..6889b72 --- /dev/null +++ b/examples/shared/src/terminal/config.rs @@ -0,0 +1,204 @@ +use std::ops::Index; + +use super::Rgb; + +#[derive(Debug, Default, PartialEq, Eq)] +pub struct Colors { + pub primary: PrimaryColors, + pub cursor: CursorColors, + pub selection: SelectionColors, + pub(crate) normal: NormalColors, + pub(crate) bright: BrightColors, + pub dim: Option, + pub indexed_colors: Vec, +} + +impl Colors { + pub fn normal(&self) -> &AnsiColors { + &self.normal.0 + } + + pub fn bright(&self) -> &AnsiColors { + &self.bright.0 + } +} + +#[derive(Default, Debug, PartialEq, Eq)] +pub struct IndexedColor { + pub index: u8, + pub color: Rgb, +} + +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] +pub struct CursorColors { + pub text: Option, + pub cursor: Option, +} + +#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] +pub struct SelectionColors { + pub text: Option, + pub background: Option, +} + +#[derive(Debug, PartialEq, Eq)] +pub struct PrimaryColors { + pub background: Rgb, + pub foreground: Rgb, + pub bright_foreground: Option, + pub dim_foreground: Option, +} + +impl Default for PrimaryColors { + fn default() -> Self { + PrimaryColors { + background: default_background(), + foreground: default_foreground(), + bright_foreground: Default::default(), + dim_foreground: Default::default(), + } + } +} + +fn default_background() -> Rgb { + Rgb { r: 0, g: 0, b: 0 } +} + +fn default_foreground() -> Rgb { + Rgb { + r: 0xea, + g: 0xea, + b: 0xea, + } +} + +/// The 8-colors sections of config +#[derive(Debug, PartialEq, Eq)] +pub struct AnsiColors { + pub black: Rgb, + pub red: Rgb, + pub green: Rgb, + pub yellow: Rgb, + pub blue: Rgb, + pub magenta: Rgb, + pub cyan: Rgb, + pub white: Rgb, +} + +impl Index for AnsiColors { + type Output = Rgb; + + fn index(&self, index: usize) -> &Self::Output { + match index { + 0 => &self.black, + 1 => &self.red, + 2 => &self.green, + 3 => &self.yellow, + 4 => &self.blue, + 5 => &self.magenta, + 6 => &self.cyan, + 7 => &self.white, + _ => panic!("Invalid color index: {index}"), + } + } +} + +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct NormalColors(pub(crate) AnsiColors); + +impl Default for NormalColors { + fn default() -> Self { + NormalColors(AnsiColors { + black: Rgb { + r: 0x00, + g: 0x00, + b: 0x00, + }, + red: Rgb { + r: 0xd5, + g: 0x4e, + b: 0x53, + }, + green: Rgb { + r: 0xb9, + g: 0xca, + b: 0x4a, + }, + yellow: Rgb { + r: 0xe6, + g: 0xc5, + b: 0x47, + }, + blue: Rgb { + r: 0x7a, + g: 0xa6, + b: 0xda, + }, + magenta: Rgb { + r: 0xc3, + g: 0x97, + b: 0xd8, + }, + cyan: Rgb { + r: 0x70, + g: 0xc0, + b: 0xba, + }, + white: Rgb { + r: 0xea, + g: 0xea, + b: 0xea, + }, + }) + } +} + +#[derive(Debug, PartialEq, Eq)] +pub(crate) struct BrightColors(pub(crate) AnsiColors); + +impl Default for BrightColors { + fn default() -> Self { + BrightColors(AnsiColors { + black: Rgb { + r: 0x66, + g: 0x66, + b: 0x66, + }, + red: Rgb { + r: 0xff, + g: 0x33, + b: 0x34, + }, + green: Rgb { + r: 0x9e, + g: 0xc4, + b: 0x00, + }, + yellow: Rgb { + r: 0xe7, + g: 0xc5, + b: 0x47, + }, + blue: Rgb { + r: 0x7a, + g: 0xa6, + b: 0xda, + }, + magenta: Rgb { + r: 0xb7, + g: 0x7e, + b: 0xe0, + }, + cyan: Rgb { + r: 0x54, + g: 0xce, + b: 0xd6, + }, + white: Rgb { + r: 0xff, + g: 0xff, + b: 0xff, + }, + }) + } +} diff --git a/examples/shared/src/terminal/mod.rs b/examples/shared/src/terminal/mod.rs new file mode 100644 index 0000000..1e8686d --- /dev/null +++ b/examples/shared/src/terminal/mod.rs @@ -0,0 +1,11 @@ +//! This module contains types and color schemes useful to process colored terminal output. +//! source: + +mod color; +pub mod color_schemes; +mod config; +mod named_color; + +pub use color::Rgb; +pub use config::AnsiColors; +pub use named_color::NamedColor; diff --git a/examples/shared/src/terminal/named_color.rs b/examples/shared/src/terminal/named_color.rs new file mode 100644 index 0000000..a703e74 --- /dev/null +++ b/examples/shared/src/terminal/named_color.rs @@ -0,0 +1,129 @@ +// Copyright 2016 Joe Wilm, The Alacritty Project Contributors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// Standard colors +/// +/// The order here matters since the enum should be castable to a `usize` for +/// indexing a color list. +#[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] +pub enum NamedColor { + /// Black + Black = 0, + /// Red + Red, + /// Green + Green, + /// Yellow + Yellow, + /// Blue + Blue, + /// Magenta + Magenta, + /// Cyan + Cyan, + /// White + White, + /// Bright black + BrightBlack, + /// Bright red + BrightRed, + /// Bright green + BrightGreen, + /// Bright yellow + BrightYellow, + /// Bright blue + BrightBlue, + /// Bright magenta + BrightMagenta, + /// Bright cyan + BrightCyan, + /// Bright white + BrightWhite, + /// The foreground color + Foreground = 256, + /// The background color + Background, + /// Color for the cursor itself + Cursor, + /// Dim black + DimBlack, + /// Dim red + DimRed, + /// Dim green + DimGreen, + /// Dim yellow + DimYellow, + /// Dim blue + DimBlue, + /// Dim magenta + DimMagenta, + /// Dim cyan + DimCyan, + /// Dim white + DimWhite, + /// The bright foreground color + BrightForeground, + /// Dim foreground + DimForeground, +} + +impl NamedColor { + pub fn to_bright(self) -> Self { + match self { + NamedColor::Foreground => NamedColor::BrightForeground, + NamedColor::Black => NamedColor::BrightBlack, + NamedColor::Red => NamedColor::BrightRed, + NamedColor::Green => NamedColor::BrightGreen, + NamedColor::Yellow => NamedColor::BrightYellow, + NamedColor::Blue => NamedColor::BrightBlue, + NamedColor::Magenta => NamedColor::BrightMagenta, + NamedColor::Cyan => NamedColor::BrightCyan, + NamedColor::White => NamedColor::BrightWhite, + NamedColor::DimForeground => NamedColor::Foreground, + NamedColor::DimBlack => NamedColor::Black, + NamedColor::DimRed => NamedColor::Red, + NamedColor::DimGreen => NamedColor::Green, + NamedColor::DimYellow => NamedColor::Yellow, + NamedColor::DimBlue => NamedColor::Blue, + NamedColor::DimMagenta => NamedColor::Magenta, + NamedColor::DimCyan => NamedColor::Cyan, + NamedColor::DimWhite => NamedColor::White, + val => val, + } + } + + pub fn to_dim(self) -> Self { + match self { + NamedColor::Black => NamedColor::DimBlack, + NamedColor::Red => NamedColor::DimRed, + NamedColor::Green => NamedColor::DimGreen, + NamedColor::Yellow => NamedColor::DimYellow, + NamedColor::Blue => NamedColor::DimBlue, + NamedColor::Magenta => NamedColor::DimMagenta, + NamedColor::Cyan => NamedColor::DimCyan, + NamedColor::White => NamedColor::DimWhite, + NamedColor::Foreground => NamedColor::DimForeground, + NamedColor::BrightBlack => NamedColor::Black, + NamedColor::BrightRed => NamedColor::Red, + NamedColor::BrightGreen => NamedColor::Green, + NamedColor::BrightYellow => NamedColor::Yellow, + NamedColor::BrightBlue => NamedColor::Blue, + NamedColor::BrightMagenta => NamedColor::Magenta, + NamedColor::BrightCyan => NamedColor::Cyan, + NamedColor::BrightWhite => NamedColor::White, + NamedColor::BrightForeground => NamedColor::Foreground, + val => val, + } + } +} From ef91110f9fb44fc20b20d04009d7b4ac73fdfe7f Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Mon, 24 Jun 2024 07:40:49 +0200 Subject: [PATCH 02/17] clippy --- examples/shared/src/terminal/color.rs | 40 ++++++++++++-------------- examples/shared/src/terminal/config.rs | 18 ++++++------ 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/examples/shared/src/terminal/color.rs b/examples/shared/src/terminal/color.rs index f273698..147f5f2 100644 --- a/examples/shared/src/terminal/color.rs +++ b/examples/shared/src/terminal/color.rs @@ -1,21 +1,19 @@ -use std::fmt; -use std::ops::{Index, IndexMut, Mul}; -use std::str::FromStr; +use std::{ops::Mul, str::FromStr}; -use super::NamedColor; +// use super::NamedColor; -pub const COUNT: usize = 269; +// pub const COUNT: usize = 269; -pub const RED: Rgb = Rgb { - r: 0xff, - g: 0x0, - b: 0x0, -}; -pub const YELLOW: Rgb = Rgb { - r: 0xff, - g: 0xff, - b: 0x0, -}; +// pub const RED: Rgb = Rgb { +// r: 0xff, +// g: 0x0, +// b: 0x0, +// }; +// pub const YELLOW: Rgb = Rgb { +// r: 0xff, +// g: 0xff, +// b: 0x0, +// }; #[derive(Debug, Eq, PartialEq, Copy, Clone, Default)] pub struct Rgb { @@ -29,13 +27,11 @@ impl Mul for Rgb { type Output = Rgb; fn mul(self, rhs: f32) -> Rgb { - let result = Rgb { - r: (f32::from(self.r) * rhs).max(0.0).min(255.0) as u8, - g: (f32::from(self.g) * rhs).max(0.0).min(255.0) as u8, - b: (f32::from(self.b) * rhs).max(0.0).min(255.0) as u8, - }; - - result + Rgb { + r: (f32::from(self.r) * rhs).clamp(0.0, 255.0) as u8, + g: (f32::from(self.g) * rhs).clamp(0.0, 255.0) as u8, + b: (f32::from(self.b) * rhs).clamp(0.0, 255.0) as u8, + } } } diff --git a/examples/shared/src/terminal/config.rs b/examples/shared/src/terminal/config.rs index 6889b72..a722e95 100644 --- a/examples/shared/src/terminal/config.rs +++ b/examples/shared/src/terminal/config.rs @@ -13,15 +13,15 @@ pub struct Colors { pub indexed_colors: Vec, } -impl Colors { - pub fn normal(&self) -> &AnsiColors { - &self.normal.0 - } - - pub fn bright(&self) -> &AnsiColors { - &self.bright.0 - } -} +// impl Colors { +// pub fn normal(&self) -> &AnsiColors { +// &self.normal.0 +// } + +// pub fn bright(&self) -> &AnsiColors { +// &self.bright.0 +// } +// } #[derive(Default, Debug, PartialEq, Eq)] pub struct IndexedColor { From e4d34407c3f9c9bf61bf1cd84875e534baf5b2e1 Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Mon, 24 Jun 2024 07:48:26 +0200 Subject: [PATCH 03/17] Move terminal support from shared into the logs example library --- examples/logs/examples/logs.rs | 2 +- examples/logs/src/lib.rs | 15 +-------------- examples/{shared => logs}/src/terminal/color.rs | 0 .../src/terminal/color_schemes.rs | 0 examples/{shared => logs}/src/terminal/config.rs | 0 examples/{shared => logs}/src/terminal/mod.rs | 0 .../{shared => logs}/src/terminal/named_color.rs | 0 examples/shared/src/lib.rs | 1 - 8 files changed, 2 insertions(+), 16 deletions(-) rename examples/{shared => logs}/src/terminal/color.rs (100%) rename examples/{shared => logs}/src/terminal/color_schemes.rs (100%) rename examples/{shared => logs}/src/terminal/config.rs (100%) rename examples/{shared => logs}/src/terminal/mod.rs (100%) rename examples/{shared => logs}/src/terminal/named_color.rs (100%) diff --git a/examples/logs/examples/logs.rs b/examples/logs/examples/logs.rs index ec44ef0..dbfc303 100644 --- a/examples/logs/examples/logs.rs +++ b/examples/logs/examples/logs.rs @@ -16,6 +16,7 @@ use termwiz::{ use tokio::sync::mpsc::{self, UnboundedReceiver}; use winit::dpi::LogicalSize; +use logs::terminal::{color_schemes, Rgb}; use massive_geometry::{Camera, Color}; use massive_scene::PositionedShape; use massive_shapes::TextWeight; @@ -23,7 +24,6 @@ use massive_shell::{shell, ApplicationContext}; use shared::{ application::{Application, UpdateResponse}, code_viewer::{self, TextAttribute}, - terminal::{color_schemes, Rgb}, }; const CANVAS_ID: &str = "massive-logs"; diff --git a/examples/logs/src/lib.rs b/examples/logs/src/lib.rs index 7d12d9a..a566381 100644 --- a/examples/logs/src/lib.rs +++ b/examples/logs/src/lib.rs @@ -1,14 +1 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +pub mod terminal; diff --git a/examples/shared/src/terminal/color.rs b/examples/logs/src/terminal/color.rs similarity index 100% rename from examples/shared/src/terminal/color.rs rename to examples/logs/src/terminal/color.rs diff --git a/examples/shared/src/terminal/color_schemes.rs b/examples/logs/src/terminal/color_schemes.rs similarity index 100% rename from examples/shared/src/terminal/color_schemes.rs rename to examples/logs/src/terminal/color_schemes.rs diff --git a/examples/shared/src/terminal/config.rs b/examples/logs/src/terminal/config.rs similarity index 100% rename from examples/shared/src/terminal/config.rs rename to examples/logs/src/terminal/config.rs diff --git a/examples/shared/src/terminal/mod.rs b/examples/logs/src/terminal/mod.rs similarity index 100% rename from examples/shared/src/terminal/mod.rs rename to examples/logs/src/terminal/mod.rs diff --git a/examples/shared/src/terminal/named_color.rs b/examples/logs/src/terminal/named_color.rs similarity index 100% rename from examples/shared/src/terminal/named_color.rs rename to examples/logs/src/terminal/named_color.rs diff --git a/examples/shared/src/lib.rs b/examples/shared/src/lib.rs index 643db17..0710165 100644 --- a/examples/shared/src/lib.rs +++ b/examples/shared/src/lib.rs @@ -2,7 +2,6 @@ pub mod application; pub mod code_viewer; pub mod fonts; pub mod positioning; -pub mod terminal; use std::future::Future; From cc5148454237596725200047c18b0f7b3d85eb1b Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Mon, 24 Jun 2024 09:29:46 +0200 Subject: [PATCH 04/17] logs: Make what's being logged independent of the environment --- examples/logs/examples/logs.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/logs/examples/logs.rs b/examples/logs/examples/logs.rs index dbfc303..01656c8 100644 --- a/examples/logs/examples/logs.rs +++ b/examples/logs/examples/logs.rs @@ -13,7 +13,10 @@ use termwiz::{ color::ColorSpec, escape::{self, csi::Sgr, Action, ControlCode, CSI}, }; -use tokio::sync::mpsc::{self, UnboundedReceiver}; +use tokio::{ + select, + sync::mpsc::{self, UnboundedReceiver}, +}; use winit::dpi::LogicalSize; use logs::terminal::{color_schemes, Rgb}; @@ -30,8 +33,9 @@ const CANVAS_ID: &str = "massive-logs"; fn main() -> Result<()> { let (sender, receiver) = mpsc::unbounded_channel(); - Builder::from_default_env() - // Colors please! + + Builder::default() + .filter(Some("massive_shell"), log::LevelFilter::Info) .write_style(WriteStyle::Always) .target(Target::Pipe(Box::new(Sender(sender)))) .init(); From 2191fa8db8f3efa50eff5df6c1ab3ada513da47f Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Mon, 24 Jun 2024 09:30:03 +0200 Subject: [PATCH 05/17] logs: Simplify --- examples/logs/examples/logs.rs | 100 ++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 44 deletions(-) diff --git a/examples/logs/examples/logs.rs b/examples/logs/examples/logs.rs index 01656c8..f4848a2 100644 --- a/examples/logs/examples/logs.rs +++ b/examples/logs/examples/logs.rs @@ -65,32 +65,13 @@ async fn async_main(receiver: UnboundedReceiver>) -> Result<()> { async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationContext) -> Result<()> { error!("TEST"); - let bytes = receiver.recv().await.unwrap(); - let mut parser = escape::parser::Parser::new(); - let parsed = parser.parse_as_vec(&bytes); - - let mut processor = Processor::new(color_schemes::light::PAPER); - for action in parsed { - processor.process(action) - } - - let (text, attributes) = processor.into_text_and_attribute_ranges(); - - // boilerplate - - let mut font_system = { + let font_system = { let mut db = fontdb::Database::new(); db.load_font_data(shared::fonts::JETBRAINS_MONO.to_vec()); // Use an invariant locale. FontSystem::new_with_locale_and_db("en-US".into(), db) }; - let font_size = 32.; - let line_height = 40.; - - let (runs, height) = - code_viewer::shape_text(&mut font_system, &text, &attributes, font_size, line_height); - // Window let window_size = LogicalSize::new(1280., 800.); @@ -105,50 +86,81 @@ async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationCont Camera::new((0.0, 0.0, camera_distance), (0.0, 0.0, 0.0)) }; - // Shell - let font_system = Arc::new(Mutex::new(font_system)); let (mut renderer, mut director) = window - .new_renderer(font_system, camera, window.inner_size()) + .new_renderer(font_system.clone(), camera, window.inner_size()) .await?; // Application - let page_size = (1280, height as u64); + let page_size = (1280u32, 800); let mut application = Application::new(page_size); let mut current_matrix = application.matrix(); - let matrix = director.cast(current_matrix); + let matrix_handle = director.cast(current_matrix); // Hold the positioned shapes in this context, otherwise they will disappear. - let _positioned_shapes: Vec<_> = runs - .into_iter() - .map(|run| director.cast(PositionedShape::new(matrix.clone(), run))) - .collect(); - - director.action()?; + let mut positioned_shapes = Vec::new(); loop { - let window_event = ctx.wait_for_event(&mut renderer).await?; + select! { + Some(bytes) = receiver + .recv() => { + let (new_runs, height) = { + let mut font_system = font_system.lock().unwrap(); + shape_log_line(&bytes, &mut font_system) + }; + + positioned_shapes.extend( + new_runs.into_iter().map(|run| director.cast(PositionedShape::new(matrix_handle.clone(), run))) + ); + director.action()?; - info!("Window Event: {window_event:?}"); + }, - match application.update(window_event) { - UpdateResponse::Exit => return Ok(()), - UpdateResponse::Continue => {} - } + Ok(window_event) = ctx.wait_for_event(&mut renderer) => { + match application.update(window_event) { + UpdateResponse::Exit => return Ok(()), + UpdateResponse::Continue => {} + } - // DI: This check has to be done in the renderer and the renderer has to decide when it - // needs to redraw. - let new_matrix = application.matrix(); - if new_matrix != current_matrix { - matrix.update(new_matrix); - current_matrix = new_matrix; - director.action()?; + // DI: This check has to be done in the renderer and the renderer has to decide when it + // needs to redraw. + let new_matrix = application.matrix(); + if new_matrix != current_matrix { + matrix_handle.update(new_matrix); + current_matrix = new_matrix; + director.action()?; + } + } } } } +fn shape_log_line( + bytes: &[u8], + font_system: &mut FontSystem, +) -> (Vec, f64) { + // OO: Share Parser between runs. + let mut parser = escape::parser::Parser::new(); + let parsed = parser.parse_as_vec(bytes); + + // OO: Share Processor between runs. + let mut processor = Processor::new(color_schemes::light::PAPER); + for action in parsed { + processor.process(action) + } + + let (text, attributes) = processor.into_text_and_attribute_ranges(); + + let font_size = 32.; + let line_height = 40.; + + let (runs, height) = + code_viewer::shape_text(font_system, &text, &attributes, font_size, line_height); + (runs, height) +} + #[derive(Debug)] struct Processor { default: Attributes, From a4ad4805d4fc4d09b9fd506e7e2688d484343cfe Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Mon, 24 Jun 2024 09:42:04 +0200 Subject: [PATCH 06/17] Clippy --- examples/logs/examples/logs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/logs/examples/logs.rs b/examples/logs/examples/logs.rs index f4848a2..70bb6a1 100644 --- a/examples/logs/examples/logs.rs +++ b/examples/logs/examples/logs.rs @@ -7,7 +7,7 @@ use std::{ use anyhow::Result; use cosmic_text::{fontdb, FontSystem}; use env_logger::{Builder, Target, WriteStyle}; -use log::{error, info}; +use log::error; use termwiz::{ cell::Intensity, color::ColorSpec, @@ -106,7 +106,7 @@ async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationCont select! { Some(bytes) = receiver .recv() => { - let (new_runs, height) = { + let (new_runs, _height) = { let mut font_system = font_system.lock().unwrap(); shape_log_line(&bytes, &mut font_system) }; From 44ffdc31dee5017c0c01304afe09de80fa2ae6da Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Mon, 24 Jun 2024 09:53:07 +0200 Subject: [PATCH 07/17] code_viewer -> attributed_text --- examples/code/examples/code-viewer.rs | 6 +++--- examples/code/examples/code.rs | 8 ++++---- examples/logs/examples/logs.rs | 4 ++-- .../shared/src/{code_viewer.rs => attributed_text.rs} | 2 +- examples/shared/src/lib.rs | 2 +- examples/syntax/examples/syntax.rs | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) rename examples/shared/src/{code_viewer.rs => attributed_text.rs} (99%) diff --git a/examples/code/examples/code-viewer.rs b/examples/code/examples/code-viewer.rs index 6568517..d8adcbb 100644 --- a/examples/code/examples/code-viewer.rs +++ b/examples/code/examples/code-viewer.rs @@ -10,7 +10,7 @@ use massive_scene::PositionedShape; use massive_shell::{shell, ApplicationContext}; use shared::{ application::{Application, UpdateResponse}, - code_viewer::{self, AttributedCode}, + attributed_text::{self, AttributedText}, }; const CANVAS_ID: &str = "massive-code"; @@ -60,7 +60,7 @@ async fn code_viewer(mut ctx: ApplicationContext) -> Result<()> { // let code: AttributedCode = // serde_json::from_str(&fs::read_to_string("/tmp/code.json").unwrap()).unwrap(); - let code: AttributedCode = postcard::from_bytes(include_bytes!("code.postcard")).unwrap(); + let code: AttributedText = postcard::from_bytes(include_bytes!("code.postcard")).unwrap(); // Shape and layout text. @@ -69,7 +69,7 @@ async fn code_viewer(mut ctx: ApplicationContext) -> Result<()> { // let font_size = 16.; // let line_height = 20.; - let (glyph_runs, height) = code_viewer::shape_text( + let (glyph_runs, height) = attributed_text::shape_text( &mut font_system, &code.text, &code.attributes, diff --git a/examples/code/examples/code.rs b/examples/code/examples/code.rs index c06f372..4808567 100644 --- a/examples/code/examples/code.rs +++ b/examples/code/examples/code.rs @@ -19,7 +19,7 @@ use load_cargo::{LoadCargoConfig, ProcMacroServerChoice}; use project_model::CargoConfig; use shared::{ application::{Application, UpdateResponse}, - code_viewer, + attributed_text, }; use syntax::{AstNode, SyntaxKind, WalkEvent}; use tracing::info; @@ -27,7 +27,7 @@ use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilte use vfs::VfsPath; use winit::dpi::LogicalSize; -use crate::code_viewer::TextAttribute; +use crate::attributed_text::TextAttribute; use massive_geometry::{Camera, Color, SizeI}; use massive_scene::PositionedShape; use massive_shapes::TextWeight; @@ -225,7 +225,7 @@ async fn application(mut ctx: ApplicationContext) -> Result<()> { // Store for the web viewer. - let attributed_code = code_viewer::AttributedCode { + let attributed_code = attributed_text::AttributedText { text: file_text.to_string(), attributes: attributes.clone(), }; @@ -249,7 +249,7 @@ async fn application(mut ctx: ApplicationContext) -> Result<()> { // let font_size = 16.; // let line_height = 20.; - let (glyph_runs, height) = code_viewer::shape_text( + let (glyph_runs, height) = attributed_text::shape_text( &mut font_system, &file_text, &attributes, diff --git a/examples/logs/examples/logs.rs b/examples/logs/examples/logs.rs index 70bb6a1..3a9a983 100644 --- a/examples/logs/examples/logs.rs +++ b/examples/logs/examples/logs.rs @@ -26,7 +26,7 @@ use massive_shapes::TextWeight; use massive_shell::{shell, ApplicationContext}; use shared::{ application::{Application, UpdateResponse}, - code_viewer::{self, TextAttribute}, + attributed_text::{self, TextAttribute}, }; const CANVAS_ID: &str = "massive-logs"; @@ -157,7 +157,7 @@ fn shape_log_line( let line_height = 40.; let (runs, height) = - code_viewer::shape_text(font_system, &text, &attributes, font_size, line_height); + attributed_text::shape_text(font_system, &text, &attributes, font_size, line_height); (runs, height) } diff --git a/examples/shared/src/code_viewer.rs b/examples/shared/src/attributed_text.rs similarity index 99% rename from examples/shared/src/code_viewer.rs rename to examples/shared/src/attributed_text.rs index 1edc73e..12246f2 100644 --- a/examples/shared/src/code_viewer.rs +++ b/examples/shared/src/attributed_text.rs @@ -10,7 +10,7 @@ use crate::positioning; /// A serializable representation of highlighted code. #[derive(Debug, Serialize, Deserialize)] -pub struct AttributedCode { +pub struct AttributedText { pub text: String, pub attributes: Vec, } diff --git a/examples/shared/src/lib.rs b/examples/shared/src/lib.rs index 0710165..3c7e1f3 100644 --- a/examples/shared/src/lib.rs +++ b/examples/shared/src/lib.rs @@ -1,5 +1,5 @@ pub mod application; -pub mod code_viewer; +pub mod attributed_text; pub mod fonts; pub mod positioning; diff --git a/examples/syntax/examples/syntax.rs b/examples/syntax/examples/syntax.rs index b3f3c09..3b55dd5 100644 --- a/examples/syntax/examples/syntax.rs +++ b/examples/syntax/examples/syntax.rs @@ -16,7 +16,7 @@ use massive_shapes::TextWeight; use massive_shell::{shell, ApplicationContext}; use shared::{ application::{Application, UpdateResponse}, - code_viewer::{self, TextAttribute}, + attributed_text::{self, TextAttribute}, }; const CANVAS_ID: &str = "massive-syntax"; @@ -83,7 +83,7 @@ async fn syntax(mut ctx: ApplicationContext) -> Result<()> { Camera::new((0.0, 0.0, camera_distance), (0.0, 0.0, 0.0)) }; - let (glyph_runs, height) = code_viewer::shape_text( + let (glyph_runs, height) = attributed_text::shape_text( &mut font_system, &final_text, &text_attributes, From e7db140a3d9e81ecf27f16c3b95976475e641321 Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Mon, 24 Jun 2024 10:20:04 +0200 Subject: [PATCH 08/17] Support to specify arbitrary translations for attributed text shaping --- examples/code/examples/code-viewer.rs | 1 + examples/code/examples/code.rs | 1 + examples/logs/examples/logs.rs | 21 +++++++++++++++------ examples/shared/src/attributed_text.rs | 7 +++++-- examples/syntax/examples/syntax.rs | 1 + 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/examples/code/examples/code-viewer.rs b/examples/code/examples/code-viewer.rs index d8adcbb..bc78c22 100644 --- a/examples/code/examples/code-viewer.rs +++ b/examples/code/examples/code-viewer.rs @@ -75,6 +75,7 @@ async fn code_viewer(mut ctx: ApplicationContext) -> Result<()> { &code.attributes, font_size, line_height, + None, ); // Camera diff --git a/examples/code/examples/code.rs b/examples/code/examples/code.rs index 4808567..01679dc 100644 --- a/examples/code/examples/code.rs +++ b/examples/code/examples/code.rs @@ -255,6 +255,7 @@ async fn application(mut ctx: ApplicationContext) -> Result<()> { &attributes, font_size, line_height, + None, ); // Window diff --git a/examples/logs/examples/logs.rs b/examples/logs/examples/logs.rs index 3a9a983..6df976a 100644 --- a/examples/logs/examples/logs.rs +++ b/examples/logs/examples/logs.rs @@ -20,7 +20,7 @@ use tokio::{ use winit::dpi::LogicalSize; use logs::terminal::{color_schemes, Rgb}; -use massive_geometry::{Camera, Color}; +use massive_geometry::{Camera, Color, Vector3}; use massive_scene::PositionedShape; use massive_shapes::TextWeight; use massive_shell::{shell, ApplicationContext}; @@ -99,6 +99,8 @@ async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationCont let mut current_matrix = application.matrix(); let matrix_handle = director.cast(current_matrix); + let mut y = 0.; + // Hold the positioned shapes in this context, otherwise they will disappear. let mut positioned_shapes = Vec::new(); @@ -106,16 +108,16 @@ async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationCont select! { Some(bytes) = receiver .recv() => { - let (new_runs, _height) = { + let (new_runs, height) = { let mut font_system = font_system.lock().unwrap(); - shape_log_line(&bytes, &mut font_system) + shape_log_line(&bytes, y, &mut font_system) }; positioned_shapes.extend( new_runs.into_iter().map(|run| director.cast(PositionedShape::new(matrix_handle.clone(), run))) ); director.action()?; - + y += height; }, Ok(window_event) = ctx.wait_for_event(&mut renderer) => { @@ -139,6 +141,7 @@ async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationCont fn shape_log_line( bytes: &[u8], + y: f64, font_system: &mut FontSystem, ) -> (Vec, f64) { // OO: Share Parser between runs. @@ -156,8 +159,14 @@ fn shape_log_line( let font_size = 32.; let line_height = 40.; - let (runs, height) = - attributed_text::shape_text(font_system, &text, &attributes, font_size, line_height); + let (runs, height) = attributed_text::shape_text( + font_system, + &text, + &attributes, + font_size, + line_height, + Vector3::new(0., y, 0.), + ); (runs, height) } diff --git a/examples/shared/src/attributed_text.rs b/examples/shared/src/attributed_text.rs index 12246f2..cfc2c2d 100644 --- a/examples/shared/src/attributed_text.rs +++ b/examples/shared/src/attributed_text.rs @@ -28,6 +28,7 @@ pub fn shape_text( attributes: &[TextAttribute], font_size: f32, line_height: f32, + translation: impl Into>, ) -> (Vec, f64) { syntax::assert_covers_all_text( &attributes @@ -59,15 +60,17 @@ pub fn shape_text( let attributes: Vec<_> = attributes.iter().map(|ta| (ta.color, ta.weight)).collect(); + let translation = translation.into().unwrap_or(Vector3::new(0., 0., 0.)); + for run in buffer.layout_runs() { // Lines are positioned on line_height. - let translation = Vector3::new(0., run.line_top as f64, 0.); + let translation = translation + Vector3::new(0., run.line_top as f64, 0.); for run in positioning::to_attributed_glyph_runs(translation, &run, line_height, &attributes) { runs.push(run); } - height = height.max(translation.y + line_height as f64); + height = height.max(run.line_top as f64 + line_height as f64); } (runs, height) diff --git a/examples/syntax/examples/syntax.rs b/examples/syntax/examples/syntax.rs index 3b55dd5..a80a2b4 100644 --- a/examples/syntax/examples/syntax.rs +++ b/examples/syntax/examples/syntax.rs @@ -89,6 +89,7 @@ async fn syntax(mut ctx: ApplicationContext) -> Result<()> { &text_attributes, font_size, line_height, + None, ); let font_system = Arc::new(Mutex::new(font_system)); From d3a9f511ab6dda74070b55577e9a72e5978fef72 Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Fri, 28 Jun 2024 09:27:21 +0200 Subject: [PATCH 09/17] logs: Use positions instead of matrices --- examples/logs/examples/logs.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/logs/examples/logs.rs b/examples/logs/examples/logs.rs index 6df976a..0d1be37 100644 --- a/examples/logs/examples/logs.rs +++ b/examples/logs/examples/logs.rs @@ -98,6 +98,7 @@ async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationCont let mut application = Application::new(page_size); let mut current_matrix = application.matrix(); let matrix_handle = director.cast(current_matrix); + let position_handle = director.cast(matrix_handle.clone().into()); let mut y = 0.; @@ -110,11 +111,12 @@ async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationCont .recv() => { let (new_runs, height) = { let mut font_system = font_system.lock().unwrap(); + shape_log_line(&bytes, y, &mut font_system) }; positioned_shapes.extend( - new_runs.into_iter().map(|run| director.cast(PositionedShape::new(matrix_handle.clone(), run))) + new_runs.into_iter().map(|run| director.cast(PositionedShape::new(position_handle.clone(), run))) ); director.action()?; y += height; From d6bb2928aa1b13861214f54e1fc86d0164a58d65 Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Fri, 28 Jun 2024 09:29:00 +0200 Subject: [PATCH 10/17] Extend index buffer to use u32 intead of u16, we are approaching u16 index limits very fast --- renderer/src/quads/renderer.rs | 3 +- .../src/text_layer/color_atlas/renderer.rs | 9 +--- renderer/src/text_layer/sdf_atlas/renderer.rs | 10 +--- renderer/src/tools/quad_index_buffer.rs | 47 ++++++++++++++----- 4 files changed, 40 insertions(+), 29 deletions(-) diff --git a/renderer/src/quads/renderer.rs b/renderer/src/quads/renderer.rs index 7e7ceb9..8e14a0f 100644 --- a/renderer/src/quads/renderer.rs +++ b/renderer/src/quads/renderer.rs @@ -100,7 +100,8 @@ impl QuadsRenderer { // DI: May do this inside this renderer and pass a Matrix to prepare?. pass.set_bind_group(0, context.view_projection_bind_group, &[]); // DI: May share index buffers between renderers? - pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16); + // OO: This uploads to complete index buffer, is this needed? + self.index_buffer.set(pass, None); for QuadsLayer { model_matrix, diff --git a/renderer/src/text_layer/color_atlas/renderer.rs b/renderer/src/text_layer/color_atlas/renderer.rs index 63bdba3..a4f5b97 100644 --- a/renderer/src/text_layer/color_atlas/renderer.rs +++ b/renderer/src/text_layer/color_atlas/renderer.rs @@ -1,5 +1,3 @@ -use std::mem; - use wgpu::{ util::{BufferInitDescriptor, DeviceExt}, TextureFormat, @@ -149,12 +147,7 @@ impl ColorAtlasRenderer { .max() .unwrap_or_default(); - pass.set_index_buffer( - self.index_buffer.slice( - ..(max_quads * QuadIndexBuffer::INDICES_PER_QUAD * mem::size_of::()) as u64, - ), - wgpu::IndexFormat::Uint16, - ); + self.index_buffer.set(pass, Some(max_quads)); for QuadBatch { model_matrix, diff --git a/renderer/src/text_layer/sdf_atlas/renderer.rs b/renderer/src/text_layer/sdf_atlas/renderer.rs index 4db2329..c9d95cb 100644 --- a/renderer/src/text_layer/sdf_atlas/renderer.rs +++ b/renderer/src/text_layer/sdf_atlas/renderer.rs @@ -1,5 +1,3 @@ -use std::mem; - use wgpu::{ util::{BufferInitDescriptor, DeviceExt}, TextureFormat, @@ -136,6 +134,7 @@ impl SdfAtlasRenderer { return; } + // OO: This resolves to a &mut &mut, is this really needed? let pass = &mut context.pass; pass.set_pipeline(&self.pipeline); // DI: May do this inside this renderer and pass a Matrix to prepare?. @@ -151,12 +150,7 @@ impl SdfAtlasRenderer { .max() .unwrap_or_default(); - pass.set_index_buffer( - self.index_buffer.slice( - ..(max_quads * QuadIndexBuffer::INDICES_PER_QUAD * mem::size_of::()) as u64, - ), - wgpu::IndexFormat::Uint16, - ); + self.index_buffer.set(pass, Some(max_quads)); for QuadBatch { model_matrix, diff --git a/renderer/src/tools/quad_index_buffer.rs b/renderer/src/tools/quad_index_buffer.rs index d473175..bac6cec 100644 --- a/renderer/src/tools/quad_index_buffer.rs +++ b/renderer/src/tools/quad_index_buffer.rs @@ -1,15 +1,20 @@ -use std::mem::size_of_val; +use std::mem::{self, size_of_val}; use log::debug; -use wgpu::util::DeviceExt; +use wgpu::{util::DeviceExt, BufferSlice, IndexFormat, RenderPass}; -#[derive(Debug, derive_more::Deref)] +#[derive(Debug)] pub struct QuadIndexBuffer(wgpu::Buffer); +type Index = u32; + impl QuadIndexBuffer { + // OO: Use only 16 bit if not more is needed. + pub const INDEX_FORMAT: IndexFormat = IndexFormat::Uint32; + pub fn new(device: &wgpu::Device) -> Self { // OO: Provide a good initial size. - const NO_INDICES: [u16; 0] = []; + const NO_INDICES: [Index; 0] = []; Self(Self::create_buffer(device, &NO_INDICES)) } @@ -17,6 +22,25 @@ impl QuadIndexBuffer { (self.0.size() as usize) / size_of_val(Self::QUAD_INDICES) } + pub fn set<'a, 'rpass>(&'a self, pass: &mut RenderPass<'rpass>, max_quads: Option) + where + 'a: 'rpass, + { + let slice = { + match max_quads { + Some(max_quads) => self.slice(max_quads), + None => self.0.slice(..), + } + }; + + pass.set_index_buffer(slice, Self::INDEX_FORMAT) + } + + fn slice(&self, max_quads: usize) -> BufferSlice { + self.0 + .slice(..(max_quads * Self::INDICES_PER_QUAD * Self::INDEX_SIZE) as u64) + } + pub fn ensure_can_index_num_quads( &mut self, device: &wgpu::Device, @@ -42,24 +66,23 @@ impl QuadIndexBuffer { self.0 = Self::create_buffer(device, &indices); } - fn generate_array(&self, quads: usize) -> Vec { + fn generate_array(&self, quads: usize) -> Vec { let mut v = Vec::with_capacity(Self::QUAD_INDICES.len() * quads); (0..quads).for_each(|quad_index| { - v.extend( - Self::QUAD_INDICES - .iter() - .map(|i| *i + (quad_index << 2) as u16), - ) + let offset = quad_index * Self::VERTICES_PER_QUAD; + v.extend(Self::QUAD_INDICES.iter().map(|i| *i + offset as Index)) }); v } - pub const QUAD_INDICES: &'static [u16] = &[0, 1, 2, 0, 2, 3]; + pub const QUAD_INDICES: &'static [Index] = &[0, 1, 2, 0, 2, 3]; pub const INDICES_PER_QUAD: usize = Self::QUAD_INDICES.len(); + pub const VERTICES_PER_QUAD: usize = 4; + const INDEX_SIZE: usize = mem::size_of::(); - fn create_buffer(device: &wgpu::Device, indices: &[u16]) -> wgpu::Buffer { + fn create_buffer(device: &wgpu::Device, indices: &[Index]) -> wgpu::Buffer { device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Quad Index Buffer"), contents: bytemuck::cast_slice(indices), From cd9485deb724b3e7a6ad89646a8197700a73750c Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Fri, 28 Jun 2024 09:44:40 +0200 Subject: [PATCH 11/17] Use only the required range of indices of the QuadIndexBuffer --- renderer/src/quads/renderer.rs | 14 ++++++++++++-- renderer/src/text_layer/color_atlas/renderer.rs | 2 +- renderer/src/text_layer/sdf_atlas/renderer.rs | 2 +- renderer/src/tools/quad_index_buffer.rs | 11 ++--------- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/renderer/src/quads/renderer.rs b/renderer/src/quads/renderer.rs index 8e14a0f..bd7e712 100644 --- a/renderer/src/quads/renderer.rs +++ b/renderer/src/quads/renderer.rs @@ -95,13 +95,23 @@ impl QuadsRenderer { } pub fn render<'rpass>(&'rpass self, context: &mut RenderContext<'_, 'rpass>) { + let max_quads = self + .layers + .iter() + .map(|QuadsLayer { quad_count, .. }| *quad_count) + .max() + .unwrap_or_default(); + + if max_quads == 0 { + return; + } + let pass = &mut context.pass; pass.set_pipeline(&self.pipeline); // DI: May do this inside this renderer and pass a Matrix to prepare?. pass.set_bind_group(0, context.view_projection_bind_group, &[]); // DI: May share index buffers between renderers? - // OO: This uploads to complete index buffer, is this needed? - self.index_buffer.set(pass, None); + self.index_buffer.set(pass, max_quads); for QuadsLayer { model_matrix, diff --git a/renderer/src/text_layer/color_atlas/renderer.rs b/renderer/src/text_layer/color_atlas/renderer.rs index a4f5b97..068aaa0 100644 --- a/renderer/src/text_layer/color_atlas/renderer.rs +++ b/renderer/src/text_layer/color_atlas/renderer.rs @@ -147,7 +147,7 @@ impl ColorAtlasRenderer { .max() .unwrap_or_default(); - self.index_buffer.set(pass, Some(max_quads)); + self.index_buffer.set(pass, max_quads); for QuadBatch { model_matrix, diff --git a/renderer/src/text_layer/sdf_atlas/renderer.rs b/renderer/src/text_layer/sdf_atlas/renderer.rs index c9d95cb..cd2c2b5 100644 --- a/renderer/src/text_layer/sdf_atlas/renderer.rs +++ b/renderer/src/text_layer/sdf_atlas/renderer.rs @@ -150,7 +150,7 @@ impl SdfAtlasRenderer { .max() .unwrap_or_default(); - self.index_buffer.set(pass, Some(max_quads)); + self.index_buffer.set(pass, max_quads); for QuadBatch { model_matrix, diff --git a/renderer/src/tools/quad_index_buffer.rs b/renderer/src/tools/quad_index_buffer.rs index bac6cec..3af2cc2 100644 --- a/renderer/src/tools/quad_index_buffer.rs +++ b/renderer/src/tools/quad_index_buffer.rs @@ -22,18 +22,11 @@ impl QuadIndexBuffer { (self.0.size() as usize) / size_of_val(Self::QUAD_INDICES) } - pub fn set<'a, 'rpass>(&'a self, pass: &mut RenderPass<'rpass>, max_quads: Option) + pub fn set<'a, 'rpass>(&'a self, pass: &mut RenderPass<'rpass>, max_quads: usize) where 'a: 'rpass, { - let slice = { - match max_quads { - Some(max_quads) => self.slice(max_quads), - None => self.0.slice(..), - } - }; - - pass.set_index_buffer(slice, Self::INDEX_FORMAT) + pass.set_index_buffer(self.slice(max_quads), Self::INDEX_FORMAT) } fn slice(&self, max_quads: usize) -> BufferSlice { From 6656a5dbeb4a187ced51c43cb0e606770ce3c10d Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Fri, 28 Jun 2024 12:25:28 +0200 Subject: [PATCH 12/17] PositionedShape now carries multiple shapes --- README.md | 1 + renderer/src/quads/renderer.rs | 6 +++--- renderer/src/renderer.rs | 8 ++++---- renderer/src/scene/mod.rs | 14 +++++++------- renderer/src/text_layer/renderer.rs | 9 +++++---- scene/src/objects.rs | 29 +++++++++++++++++++++-------- 6 files changed, 41 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 8824713..a9da36d 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ cargo run --release --example markdown ## Acronyms used in the code. +- DR: Decision Record. - OO: Optimization Opportunity - NI: Naming Issue - DI: Design Issue (e.g. something does not seem to belong here) diff --git a/renderer/src/quads/renderer.rs b/renderer/src/quads/renderer.rs index bd7e712..be9d98b 100644 --- a/renderer/src/quads/renderer.rs +++ b/renderer/src/quads/renderer.rs @@ -65,10 +65,10 @@ impl QuadsRenderer { } } - pub fn prepare( + pub fn prepare<'a>( &mut self, context: &mut PreparationContext, - shapes: &[(Matrix4, &[&Shape])], + shapes: &[(Matrix4, impl Iterator + Clone)], ) -> Result<()> { self.layers.clear(); @@ -78,7 +78,7 @@ impl QuadsRenderer { if let Some(quads_layer) = self.prepare_quads( context, matrix, - shapes.iter().filter_map(|s| match s { + shapes.clone().filter_map(|s| match s { Shape::GlyphRun(_) => None, Shape::Quads(quads) => Some(quads), }), diff --git a/renderer/src/renderer.rs b/renderer/src/renderer.rs index 14c5232..4fd243a 100644 --- a/renderer/src/renderer.rs +++ b/renderer/src/renderer.rs @@ -122,16 +122,16 @@ impl<'window> Renderer<'window> { font_system, }; - // OO: Lot's of allocations here. + // OO: Avoid allocations. let grouped_shapes: Vec<_> = self.scene.grouped_shapes().collect(); let pixel_matrix = self.pixel_matrix(); - // OO: Lot's of allocations here. // Group by matrix and apply the pixel matrix. + // OO: Lot's of allocations here. Modify Matrix in-place? let grouped_by_matrix: Vec<_> = grouped_shapes - .iter() - .map(|(m, v)| (pixel_matrix * *m, v.as_slice())) + .into_iter() + .map(|(m, v)| (pixel_matrix * m, v)) .collect(); // OO: parallelize? diff --git a/renderer/src/scene/mod.rs b/renderer/src/scene/mod.rs index c3a90e2..c53280f 100644 --- a/renderer/src/scene/mod.rs +++ b/renderer/src/scene/mod.rs @@ -42,14 +42,14 @@ impl Scene { } /// Returns a set of grouped shape by matrix. - /// - /// TODO: This should not be &mut self, because it updates computed values only. - pub fn grouped_shapes(&self) -> impl Iterator)> { - let mut map: HashMap> = HashMap::new(); + pub fn grouped_shapes( + &self, + ) -> impl Iterator + Clone)> { + let mut map: HashMap> = HashMap::new(); for positioned in self.shapes.iter_some() { let position_id = positioned.position; - map.entry(position_id).or_default().push(&positioned.shape); + map.entry(position_id).or_default().push(&positioned.shapes); } // Update all matrices that are in use. @@ -66,9 +66,9 @@ impl Scene { map.into_iter().map(move |(position_id, shapes)| { // Ensure the matrix is up2date. - // We can't return a reference to matrix, because this would also borrow `caches``. + // We can't return a reference to matrix, because this would also borrow `caches`. let matrix = *caches.positions_matrix[position_id]; - (matrix, shapes) + (matrix, shapes.into_iter().flatten()) }) } diff --git a/renderer/src/text_layer/renderer.rs b/renderer/src/text_layer/renderer.rs index f1b02d1..57596ee 100644 --- a/renderer/src/text_layer/renderer.rs +++ b/renderer/src/text_layer/renderer.rs @@ -68,21 +68,22 @@ impl TextLayerRenderer { } } - pub fn prepare( + pub fn prepare<'a>( &mut self, context: &mut PreparationContext, - shapes: &[(Matrix4, &[&Shape])], + shapes: &[(Matrix4, impl Iterator + Clone)], ) -> Result<()> { self.sdf_batches.clear(); self.color_batches.clear(); - for (matrix, shapes) in shapes { + for (matrix, ref shapes) in shapes { // NB: could deref the pointer here using unsafe. let (sdf_batch, color_batch) = self.prepare_runs( context, matrix, // DI: Move this filter up (callers should just pass here what's needed). - shapes.iter().filter_map(|s| match s { + // OO: clone() will clone the backing Vec. + shapes.clone().filter_map(|s| match s { Shape::GlyphRun(run) => Some(run), Shape::Quads(_) => None, }), diff --git a/scene/src/objects.rs b/scene/src/objects.rs index dd425c6..e8be6c2 100644 --- a/scene/src/objects.rs +++ b/scene/src/objects.rs @@ -13,13 +13,20 @@ pub enum Shape { #[derive(Debug)] pub struct PositionedShape { pub position: Handle, - pub shape: Shape, + /// DR: Clients should be able to use [`PositionedShape`] directly as a an abstract thing. Like + /// for example a line which contains multiple Shapes (runs, quads, etc.). Therefore + /// `Vec` and not just `Shape`. + /// + /// GI: Another idea is to add `Shape::Combined(Vec)`, but this makes extraction per + /// renderer a bit more complex. This would also point to sharing Shapes as handles ... which + /// could go in direction of layout? + pub shapes: Vec, } #[derive(Debug)] pub struct PositionedRenderShape { pub position: Id, - pub shape: Shape, + pub shapes: Vec, } impl Object for PositionedShape { @@ -29,10 +36,10 @@ impl Object for PositionedShape { type Change = PositionedRenderShape; fn split(self) -> (Self::Keep, Self::Change) { - let PositionedShape { position, shape } = self; + let PositionedShape { position, shapes } = self; let shape = PositionedRenderShape { position: position.id(), - shape, + shapes, }; (position, shape) } @@ -43,14 +50,20 @@ impl Object for PositionedShape { } impl PositionedShape { - pub fn new(position: Handle, shape: impl Into) -> Self { + pub fn new(position: Handle, shapes: impl Into>) -> Self { Self { position, - shape: shape.into(), + shapes: shapes.into(), } } } +impl From for Vec { + fn from(value: Shape) -> Self { + vec![value] + } +} + #[derive(Debug, Clone)] pub struct Position { pub parent: Option>, @@ -149,10 +162,10 @@ pub mod legacy { let positioned = match shape { Shape::GlyphRun(GlyphRunShape { run, .. }) => { - PositionedShape::new(position.clone(), run) + PositionedShape::new(position.clone(), super::Shape::from(run)) } Shape::Quads(QuadsShape { quads, .. }) => { - PositionedShape::new(position.clone(), quads) + PositionedShape::new(position.clone(), super::Shape::from(quads)) } }; From 6786251ea1bda16d02c3757560628963a91399f8 Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Mon, 1 Jul 2024 09:46:53 +0200 Subject: [PATCH 13/17] Fix compilation error: port to the new PositionedShape that is able to combine multiple shapes --- examples/code/examples/code-viewer.rs | 8 ++++---- examples/code/examples/code.rs | 11 +++++++---- examples/logs/examples/logs.rs | 5 +++-- examples/markdown/examples/emojis.rs | 11 +++++++---- examples/markdown/examples/markdown.rs | 11 +++++++---- examples/syntax/examples/syntax.rs | 11 +++++++---- 6 files changed, 35 insertions(+), 22 deletions(-) diff --git a/examples/code/examples/code-viewer.rs b/examples/code/examples/code-viewer.rs index bc78c22..1b2a602 100644 --- a/examples/code/examples/code-viewer.rs +++ b/examples/code/examples/code-viewer.rs @@ -105,10 +105,10 @@ async fn code_viewer(mut ctx: ApplicationContext) -> Result<()> { let position = director.cast(matrix.clone().into()); // Hold the positioned shapes in this context, otherwise they will disappear. - let _positioned_shapes: Vec<_> = glyph_runs - .into_iter() - .map(|run| director.cast(PositionedShape::new(position.clone(), run))) - .collect(); + let _positioned_shape = director.cast(PositionedShape::new( + position.clone(), + glyph_runs.into_iter().map(|m| m.into()).collect::>(), + )); director.action()?; diff --git a/examples/code/examples/code.rs b/examples/code/examples/code.rs index 01679dc..cc6b622 100644 --- a/examples/code/examples/code.rs +++ b/examples/code/examples/code.rs @@ -285,10 +285,13 @@ async fn application(mut ctx: ApplicationContext) -> Result<()> { let matrix = director.cast(current_matrix); let position = director.cast(matrix.clone().into()); - let _positioned_shapes: Vec<_> = glyph_runs - .into_iter() - .map(|run| director.cast(PositionedShape::new(position.clone(), run))) - .collect(); + let _positioned_shape = director.cast(PositionedShape::new( + position.clone(), + glyph_runs + .into_iter() + .map(|run| run.into()) + .collect::>(), + )); director.action()?; diff --git a/examples/logs/examples/logs.rs b/examples/logs/examples/logs.rs index 0d1be37..443a884 100644 --- a/examples/logs/examples/logs.rs +++ b/examples/logs/examples/logs.rs @@ -115,8 +115,9 @@ async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationCont shape_log_line(&bytes, y, &mut font_system) }; - positioned_shapes.extend( - new_runs.into_iter().map(|run| director.cast(PositionedShape::new(position_handle.clone(), run))) + positioned_shapes.push( + director.cast(PositionedShape::new(position_handle.clone(), new_runs.into_iter().map( + |run| run.into()).collect::>())) ); director.action()?; y += height; diff --git a/examples/markdown/examples/emojis.rs b/examples/markdown/examples/emojis.rs index 3151648..fcdb30f 100644 --- a/examples/markdown/examples/emojis.rs +++ b/examples/markdown/examples/emojis.rs @@ -174,10 +174,13 @@ async fn emojis(mut ctx: ApplicationContext) -> Result<()> { let position = director.cast(matrix.clone().into()); // Hold the positioned shapes in this context, otherwise they will disappear. - let _positioned_shapes: Vec<_> = glyph_runs - .into_iter() - .map(|run| director.cast(PositionedShape::new(position.clone(), run))) - .collect(); + let _positioned_shape = director.cast(PositionedShape::new( + position.clone(), + glyph_runs + .into_iter() + .map(|run| run.into()) + .collect::>(), + )); director.action()?; diff --git a/examples/markdown/examples/markdown.rs b/examples/markdown/examples/markdown.rs index 583eb01..8d47df8 100644 --- a/examples/markdown/examples/markdown.rs +++ b/examples/markdown/examples/markdown.rs @@ -92,10 +92,13 @@ async fn application(mut ctx: ApplicationContext) -> Result<()> { let position = director.cast(matrix.clone().into()); // Hold the positioned shapes in this context, otherwise they will disappear. - let _positioned_shapes: Vec<_> = glyph_runs - .into_iter() - .map(|run| director.cast(PositionedShape::new(position.clone(), run))) - .collect(); + let _positioned_shape = director.cast(PositionedShape::new( + position.clone(), + glyph_runs + .into_iter() + .map(|run| run.into()) + .collect::>(), + )); director.action()?; diff --git a/examples/syntax/examples/syntax.rs b/examples/syntax/examples/syntax.rs index a80a2b4..8e16cb2 100644 --- a/examples/syntax/examples/syntax.rs +++ b/examples/syntax/examples/syntax.rs @@ -110,10 +110,13 @@ async fn syntax(mut ctx: ApplicationContext) -> Result<()> { let position = director.cast(matrix.clone().into()); // Hold the positioned shapes in this context, otherwise they will disappear. - let _positioned_shapes: Vec<_> = glyph_runs - .into_iter() - .map(|run| director.cast(PositionedShape::new(position.clone(), run))) - .collect(); + let _positioned_shape = director.cast(PositionedShape::new( + position.clone(), + glyph_runs + .into_iter() + .map(|run| run.into()) + .collect::>(), + )); director.action()?; From ea785b23a057b7884acbb947c659b120db3b7a2b Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Mon, 1 Jul 2024 10:24:02 +0200 Subject: [PATCH 14/17] Application: Simplify by passing page_size to the matrix computation --- examples/code/examples/code-viewer.rs | 7 ++++--- examples/code/examples/code.rs | 7 ++++--- examples/logs/examples/logs.rs | 27 +++++++++++++++++--------- examples/markdown/examples/emojis.rs | 7 ++++--- examples/markdown/examples/markdown.rs | 6 +++--- examples/shared/src/application.rs | 25 ++++++------------------ examples/syntax/examples/syntax.rs | 7 ++++--- 7 files changed, 43 insertions(+), 43 deletions(-) diff --git a/examples/code/examples/code-viewer.rs b/examples/code/examples/code-viewer.rs index 1b2a602..16807d5 100644 --- a/examples/code/examples/code-viewer.rs +++ b/examples/code/examples/code-viewer.rs @@ -99,8 +99,9 @@ async fn code_viewer(mut ctx: ApplicationContext) -> Result<()> { ) .await?; - let mut application = Application::new(SizeI::new(1280, height as u64)); - let mut current_matrix = application.matrix(); + let page_size = SizeI::new(1280, height as u64); + let mut application = Application::default(); + let mut current_matrix = application.matrix(page_size); let matrix = director.cast(current_matrix); let position = director.cast(matrix.clone().into()); @@ -124,7 +125,7 @@ async fn code_viewer(mut ctx: ApplicationContext) -> Result<()> { // DI: This check has to be done in the renderer and the renderer has to decide when it // needs to redraw. - let new_matrix = application.matrix(); + let new_matrix = application.matrix(page_size); if new_matrix != current_matrix { matrix.update(new_matrix); current_matrix = new_matrix; diff --git a/examples/code/examples/code.rs b/examples/code/examples/code.rs index cc6b622..bcb3803 100644 --- a/examples/code/examples/code.rs +++ b/examples/code/examples/code.rs @@ -273,7 +273,8 @@ async fn application(mut ctx: ApplicationContext) -> Result<()> { // Application - let mut application = Application::new(SizeI::new(1280, height as u64)); + let page_size = SizeI::new(1280, height as u64); + let mut application = Application::default(); let font_system = Arc::new(Mutex::new(font_system)); @@ -281,7 +282,7 @@ async fn application(mut ctx: ApplicationContext) -> Result<()> { .new_renderer(font_system, camera, initial_size) .await?; - let mut current_matrix = application.matrix(); + let mut current_matrix = application.matrix(page_size); let matrix = director.cast(current_matrix); let position = director.cast(matrix.clone().into()); @@ -307,7 +308,7 @@ async fn application(mut ctx: ApplicationContext) -> Result<()> { // DI: This check has to be done in the renderer and the renderer has to decide when it // needs to redraw. - let new_matrix = application.matrix(); + let new_matrix = application.matrix(page_size); if new_matrix != current_matrix { matrix.update(new_matrix); current_matrix = new_matrix; diff --git a/examples/logs/examples/logs.rs b/examples/logs/examples/logs.rs index 443a884..52ea83c 100644 --- a/examples/logs/examples/logs.rs +++ b/examples/logs/examples/logs.rs @@ -1,4 +1,5 @@ use std::{ + collections::VecDeque, io, iter, ops::Range, sync::{Arc, Mutex}, @@ -95,15 +96,17 @@ async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationCont // Application let page_size = (1280u32, 800); - let mut application = Application::new(page_size); - let mut current_matrix = application.matrix(); + let mut application = Application::default(); + let mut current_matrix = application.matrix(page_size); let matrix_handle = director.cast(current_matrix); let position_handle = director.cast(matrix_handle.clone().into()); let mut y = 0.; - // Hold the positioned shapes in this context, otherwise they will disappear. - let mut positioned_shapes = Vec::new(); + let max_lines = 100; + + // Hold the positioned lines, otherwise they will disappear. + let mut positioned_lines = VecDeque::new(); loop { select! { @@ -115,11 +118,17 @@ async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationCont shape_log_line(&bytes, y, &mut font_system) }; - positioned_shapes.push( - director.cast(PositionedShape::new(position_handle.clone(), new_runs.into_iter().map( - |run| run.into()).collect::>())) - ); + let line = director.cast(PositionedShape::new(position_handle.clone(), new_runs.into_iter().map( + |run| run.into()).collect::>())); + + positioned_lines.push_back(line); + + while positioned_lines.len() > max_lines { + positioned_lines.pop_front(); + } + director.action()?; + y += height; }, @@ -131,7 +140,7 @@ async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationCont // DI: This check has to be done in the renderer and the renderer has to decide when it // needs to redraw. - let new_matrix = application.matrix(); + let new_matrix = application.matrix(page_size); if new_matrix != current_matrix { matrix_handle.update(new_matrix); current_matrix = new_matrix; diff --git a/examples/markdown/examples/emojis.rs b/examples/markdown/examples/emojis.rs index fcdb30f..107806b 100644 --- a/examples/markdown/examples/emojis.rs +++ b/examples/markdown/examples/emojis.rs @@ -168,8 +168,9 @@ async fn emojis(mut ctx: ApplicationContext) -> Result<()> { // Application - let mut application = Application::new(SizeI::new(page_width as _, page_height)); - let mut current_matrix = application.matrix(); + let page_size = SizeI::new(page_width as _, page_height); + let mut application = Application::default(); + let mut current_matrix = application.matrix(page_size); let matrix = director.cast(current_matrix); let position = director.cast(matrix.clone().into()); @@ -196,7 +197,7 @@ async fn emojis(mut ctx: ApplicationContext) -> Result<()> { // DI: This check has to be done in the renderer and the renderer has to decide when it // needs to redraw. - let new_matrix = application.matrix(); + let new_matrix = application.matrix(page_size); if new_matrix != current_matrix { matrix.update(new_matrix); current_matrix = new_matrix; diff --git a/examples/markdown/examples/markdown.rs b/examples/markdown/examples/markdown.rs index 8d47df8..24114af 100644 --- a/examples/markdown/examples/markdown.rs +++ b/examples/markdown/examples/markdown.rs @@ -86,8 +86,8 @@ async fn application(mut ctx: ApplicationContext) -> Result<()> { markdown, )?; - let mut application = Application::new(page_size); - let mut current_matrix = application.matrix(); + let mut application = Application::default(); + let mut current_matrix = application.matrix(page_size); let matrix = director.cast(current_matrix); let position = director.cast(matrix.clone().into()); @@ -114,7 +114,7 @@ async fn application(mut ctx: ApplicationContext) -> Result<()> { // DI: This check has to be done in the renderer and the renderer has to decide when it // needs to redraw. - let new_matrix = application.matrix(); + let new_matrix = application.matrix(page_size); if new_matrix != current_matrix { matrix.update(new_matrix); current_matrix = new_matrix; diff --git a/examples/shared/src/application.rs b/examples/shared/src/application.rs index 1a83ab3..a1c4ddd 100644 --- a/examples/shared/src/application.rs +++ b/examples/shared/src/application.rs @@ -14,9 +14,8 @@ enum ActiveGesture { Rotation(RotationGesture), } +#[derive(Default)] pub struct Application { - page_size: SizeI, - gesture: Option, /// Tracked positions of all devices. @@ -30,20 +29,6 @@ pub struct Application { rotation: PointI, } -impl Application { - pub fn new(page_size: impl Into) -> Self { - Self { - page_size: page_size.into(), - gesture: None, - positions: HashMap::new(), - modifiers: Modifiers::default(), - translation: PointI::default(), - translation_z: 0, - rotation: PointI::default(), - } - } -} - struct MovementGesture { origin: PointI, translation_origin: PointI, @@ -204,11 +189,13 @@ impl Application { UpdateResponse::Continue } - pub fn matrix(&self) -> Matrix4 { + pub fn matrix(&self, page_size: impl Into) -> Matrix4 { // let mut shapes = Vec::new(); - let page_x_center: f64 = -((self.page_size.width / 2) as f64); - let page_y_center: f64 = -((self.page_size.height / 2) as f64); + let page_size = page_size.into(); + + let page_x_center: f64 = -((page_size.width / 2) as f64); + let page_y_center: f64 = -((page_size.height / 2) as f64); let center_transformation = Matrix4::from_translation((page_x_center, page_y_center, 0.0).into()); let current_translation = Matrix4::from_translation( diff --git a/examples/syntax/examples/syntax.rs b/examples/syntax/examples/syntax.rs index 8e16cb2..5059e0d 100644 --- a/examples/syntax/examples/syntax.rs +++ b/examples/syntax/examples/syntax.rs @@ -104,8 +104,9 @@ async fn syntax(mut ctx: ApplicationContext) -> Result<()> { // Application - let mut application = Application::new((1280, height as u64)); - let mut current_matrix = application.matrix(); + let page_size = (1280, height as u64); + let mut application = Application::default(); + let mut current_matrix = application.matrix(page_size); let matrix = director.cast(current_matrix); let position = director.cast(matrix.clone().into()); @@ -130,7 +131,7 @@ async fn syntax(mut ctx: ApplicationContext) -> Result<()> { // DI: This check has to be done in the renderer and the renderer has to decide when it // needs to redraw. - let new_matrix = application.matrix(); + let new_matrix = application.matrix(page_size); if new_matrix != current_matrix { matrix.update(new_matrix); current_matrix = new_matrix; From 825d42e46cc918b34fe541de216993caa5d9126b Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Mon, 1 Jul 2024 10:46:18 +0200 Subject: [PATCH 15/17] logs example: Keep the log lines centered --- examples/logs/examples/logs.rs | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/examples/logs/examples/logs.rs b/examples/logs/examples/logs.rs index 52ea83c..c5edecc 100644 --- a/examples/logs/examples/logs.rs +++ b/examples/logs/examples/logs.rs @@ -21,8 +21,8 @@ use tokio::{ use winit::dpi::LogicalSize; use logs::terminal::{color_schemes, Rgb}; -use massive_geometry::{Camera, Color, Vector3}; -use massive_scene::PositionedShape; +use massive_geometry::{Camera, Color, Identity, Vector3}; +use massive_scene::{Matrix, Position, PositionedShape}; use massive_shapes::TextWeight; use massive_shell::{shell, ApplicationContext}; use shared::{ @@ -95,11 +95,19 @@ async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationCont // Application - let page_size = (1280u32, 800); + let mut page_size = (1280u32, 1); let mut application = Application::default(); let mut current_matrix = application.matrix(page_size); - let matrix_handle = director.cast(current_matrix); - let position_handle = director.cast(matrix_handle.clone().into()); + let page_matrix = director.cast(current_matrix); + let page_position = director.cast(Position::from(page_matrix.clone())); + // We move up the lines by their top position. + let move_up_matrix = director.cast(Matrix::identity()); + + // Final position for all lines (runs are y-translated, but only increasing). + let position = director.cast(Position { + parent: Some(page_position), + matrix: move_up_matrix.clone(), + }); let mut y = 0.; @@ -118,14 +126,21 @@ async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationCont shape_log_line(&bytes, y, &mut font_system) }; - let line = director.cast(PositionedShape::new(position_handle.clone(), new_runs.into_iter().map( + let line = director.cast(PositionedShape::new(position.clone(), new_runs.into_iter().map( |run| run.into()).collect::>())); - positioned_lines.push_back(line); + positioned_lines.push_back((y, height, line)); while positioned_lines.len() > max_lines { positioned_lines.pop_front(); - } + }; + + // Update page size. + + let top_line = positioned_lines.front().unwrap(); + move_up_matrix.update(Matrix::from_translation((0., -top_line.0, 0.).into())); + let last_line = positioned_lines.back().unwrap(); + page_size.1 = (last_line.0 + last_line.1 - top_line.0) as u32; director.action()?; @@ -142,7 +157,7 @@ async fn logs(mut receiver: UnboundedReceiver>, mut ctx: ApplicationCont // needs to redraw. let new_matrix = application.matrix(page_size); if new_matrix != current_matrix { - matrix_handle.update(new_matrix); + page_matrix.update(new_matrix); current_matrix = new_matrix; director.action()?; } From 55b0418fa78c7ccdddef61b6202262c6a2e3f5af Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Mon, 1 Jul 2024 11:24:50 +0200 Subject: [PATCH 16/17] Cosmetics / typos --- .vscode/settings.json | 3 +++ renderer/src/scene/mod.rs | 5 ++--- renderer/src/scene/versioning.rs | 2 +- renderer/src/text_layer/color_atlas/renderer.rs | 7 ++++--- renderer/src/text_layer/sdf_atlas/renderer.rs | 7 ++++--- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 2e0cb08..f51ec10 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,17 +20,20 @@ "hitbox", "inlyne", "Interactor", + "JETBRAINS", "mult", "multisampled", "multiview", "rasterizer", "reqwest", + "rpass", "rsqrt", "shortcodes", "SSEDT", "striked", "Syntect", "tasklist", + "termwiz", "texel", "texels", "textbox", diff --git a/renderer/src/scene/mod.rs b/renderer/src/scene/mod.rs index c53280f..6689334 100644 --- a/renderer/src/scene/mod.rs +++ b/renderer/src/scene/mod.rs @@ -65,7 +65,6 @@ impl Scene { let caches = self.caches.borrow(); map.into_iter().map(move |(position_id, shapes)| { - // Ensure the matrix is up2date. // We can't return a reference to matrix, because this would also borrow `caches`. let matrix = *caches.positions_matrix[position_id]; (matrix, shapes.into_iter().flatten()) @@ -78,7 +77,7 @@ impl Scene { /// version and can be used for rendering. /// /// We don't return a reference to the result here, because the borrow checker would make this - /// recursive function invocation uncessarily more complex. + /// recursive function invocation unnecessarily more complex. /// /// TODO: Unrecurse this. There might be degenerate cases of large dependency chains. fn resolve_positioned_matrix(&self, position_id: Id, caches: &mut SceneCaches) { @@ -99,7 +98,7 @@ impl Scene { let position = self.positions.unwrapped(position_id); let (parent_id, matrix) = (position.parent, position.matrix); - // Find out the max version of all the immeidate and (indirect / computed) dependencies. + // Find out the max version of all the immediate and (indirect / computed) dependencies. // Get the _three_ versions of the elements this one is computed on. // a) The self position's version. diff --git a/renderer/src/scene/versioning.rs b/renderer/src/scene/versioning.rs index 83ed3bd..6a5b87f 100644 --- a/renderer/src/scene/versioning.rs +++ b/renderer/src/scene/versioning.rs @@ -37,7 +37,7 @@ pub struct Computed { /// This is last the time the `max_deps_version` and computed value was validated to be /// consistent with its dependencies. /// - /// If `validated_at` is less than the curreent tick, `max_deps_version` and `value` may be + /// If `validated_at` is less than the current tick, `max_deps_version` and `value` may be /// outdated. pub validated_at: Version, /// The maximum version of all its dependencies. May be outdated if `checked_at` does not equals diff --git a/renderer/src/text_layer/color_atlas/renderer.rs b/renderer/src/text_layer/color_atlas/renderer.rs index 068aaa0..ad6ec6a 100644 --- a/renderer/src/text_layer/color_atlas/renderer.rs +++ b/renderer/src/text_layer/color_atlas/renderer.rs @@ -80,8 +80,9 @@ impl ColorAtlasRenderer { for instance in instances { let r = instance.atlas_rect; - // ADR: u/v normalization is dont in the shader, for once, its probably free, and scondly - // we don't have to care about the atlas texture growing as long the rects stay the same. + // ADR: u/v normalization is dont in the shader, for once, its probably free, and + // secondly we don't have to care about the atlas texture growing as long the rects stay + // the same. let (ltx, lty) = (r.min.x as f32, r.min.y as f32); let (rbx, rby) = (r.max.x as f32, r.max.y as f32); @@ -138,7 +139,7 @@ impl ColorAtlasRenderer { pass.set_bind_group(0, context.view_projection_bind_group, &[]); // DI: May share index buffers between renderers? // - // OO: Don't pass the full index buffer here, only what's actully needed (it is growing + // OO: Don't pass the full index buffer here, only what's actually needed (it is growing // only) let max_quads = batches diff --git a/renderer/src/text_layer/sdf_atlas/renderer.rs b/renderer/src/text_layer/sdf_atlas/renderer.rs index cd2c2b5..e0ab222 100644 --- a/renderer/src/text_layer/sdf_atlas/renderer.rs +++ b/renderer/src/text_layer/sdf_atlas/renderer.rs @@ -81,8 +81,9 @@ impl SdfAtlasRenderer { for instance in instances { let r = instance.atlas_rect; - // ADR: u/v normalization is dont in the shader, for once, its probably free, and scondly - // we don't have to care about the atlas texture growing as long the rects stay the same. + // ADR: u/v normalization is dont in the shader, for once, its probably free, and + // secondly we don't have to care about the atlas texture growing as long the rects stay + // the same. let (ltx, lty) = (r.min.x as f32, r.min.y as f32); let (rbx, rby) = (r.max.x as f32, r.max.y as f32); @@ -141,7 +142,7 @@ impl SdfAtlasRenderer { pass.set_bind_group(0, context.view_projection_bind_group, &[]); // DI: May share index buffers between renderers? // - // OO: Don't pass the full index buffer here, only what's actully needed (it is growing + // OO: Don't pass the full index buffer here, only what's actually needed (it is growing // only) let max_quads = batches From c5f7116d1a7dec6bd5182313133a2580d04ffb76 Mon Sep 17 00:00:00 2001 From: Armin Sander Date: Mon, 1 Jul 2024 11:28:27 +0200 Subject: [PATCH 17/17] Comments --- renderer/src/scene/versioning.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/renderer/src/scene/versioning.rs b/renderer/src/scene/versioning.rs index 6a5b87f..8dd7ff7 100644 --- a/renderer/src/scene/versioning.rs +++ b/renderer/src/scene/versioning.rs @@ -37,15 +37,15 @@ pub struct Computed { /// This is last the time the `max_deps_version` and computed value was validated to be /// consistent with its dependencies. /// - /// If `validated_at` is less than the current tick, `max_deps_version` and `value` may be + /// If `validated_at` is less than the latest tick, `max_deps_version` and `value` may be /// outdated. pub validated_at: Version, - /// The maximum version of all its dependencies. May be outdated if `checked_at` does not equals - /// the current version. + /// The maximum version of all its dependencies. May be outdated if `validated_at` does not + /// equal the latest version. pub max_deps_version: Version, - /// The value at `max_deps_version`. Because of laziness, this value may be computed at a later - /// time tick, but it always represents the result of a computation matching the dependencies at - /// `max_deps_version`. + /// The value computed value at `max_deps_version`. This value is computed on demand and may not + /// be up to date. but it always represents the result of a computation matching the + /// dependencies at `max_deps_version`. pub value: V, }