From ca5f82e5e9b99cae7fd01baad821da2b0040c743 Mon Sep 17 00:00:00 2001 From: grovesNL Date: Mon, 15 Jan 2024 11:46:17 -0330 Subject: [PATCH 01/10] Grow by a factor of `2` This reduces the amount of growing necessary for large glyphs --- src/text_atlas.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/text_atlas.rs b/src/text_atlas.rs index 6bec57b..320697d 100644 --- a/src/text_atlas.rs +++ b/src/text_atlas.rs @@ -131,8 +131,10 @@ impl InnerAtlas { return false; } - // TODO: Better resizing logic (?) - let new_size = (self.size + Self::INITIAL_SIZE).min(self.max_texture_dimension_2d); + // Grow each dimension by a factor of 2. The growth factor was chosen to match the growth + // factor of `Vec`.` + const GROWTH_FACTOR: u32 = 2; + let new_size = (self.size * GROWTH_FACTOR).min(self.max_texture_dimension_2d); self.packer.grow(size2(new_size as i32, new_size as i32)); From ce394f9d5346639927ee6c6a43b53e4376831896 Mon Sep 17 00:00:00 2001 From: grovesNL Date: Mon, 15 Jan 2024 12:49:19 -0330 Subject: [PATCH 02/10] Internally convert text colors to linear --- src/shader.wgsl | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/shader.wgsl b/src/shader.wgsl index 53f78ce..337f788 100644 --- a/src/shader.wgsl +++ b/src/shader.wgsl @@ -32,6 +32,15 @@ var mask_atlas_texture: texture_2d; @group(0) @binding(3) var atlas_sampler: sampler; +fn srgb_to_linear(srgb: u32) -> f32 { + let c = f32(srgb) / 255.0; + if c <= 0.04045 { + return c / 12.92; + } else { + return pow((c + 0.055) / 1.055, 2.4); + } +} + @vertex fn vs_main(in_vert: VertexInput) -> VertexOutput { var pos = in_vert.pos; @@ -70,11 +79,11 @@ fn vs_main(in_vert: VertexInput) -> VertexOutput { vert_output.position.y *= -1.0; vert_output.color = vec4( - f32((color & 0x00ff0000u) >> 16u), - f32((color & 0x0000ff00u) >> 8u), - f32(color & 0x000000ffu), - f32((color & 0xff000000u) >> 24u), - ) / 255.0; + srgb_to_linear((color & 0x00ff0000u) >> 16u), + srgb_to_linear((color & 0x0000ff00u) >> 8u), + srgb_to_linear(color & 0x000000ffu), + f32((color & 0xff000000u) >> 24u) / 255.0, + ); var dim: vec2 = vec2(0u); switch in_vert.content_type { From c2469de81715e262f2cb1e09634fd219fc39a3a7 Mon Sep 17 00:00:00 2001 From: grovesNL Date: Mon, 15 Jan 2024 16:03:02 -0330 Subject: [PATCH 03/10] Update winit --- Cargo.toml | 2 +- examples/hello-world.rs | 150 +++++++++++++++++++--------------------- 2 files changed, 74 insertions(+), 78 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e0d50d8..85a4f8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,5 +14,5 @@ cosmic-text = "0.10" lru = "0.11" [dev-dependencies] -winit = "0.28.7" +winit = { version = "0.29.10", features = ["rwh_05"] } pollster = "0.3.0" diff --git a/examples/hello-world.rs b/examples/hello-world.rs index 1d4d2b7..213ba61 100644 --- a/examples/hello-world.rs +++ b/examples/hello-world.rs @@ -11,7 +11,7 @@ use wgpu::{ use winit::{ dpi::LogicalSize, event::{Event, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, + event_loop::EventLoop, window::WindowBuilder, }; @@ -22,7 +22,7 @@ fn main() { async fn run() { // Set up window let (width, height) = (800, 600); - let event_loop = EventLoop::new(); + let event_loop = EventLoop::new().unwrap(); let window = WindowBuilder::new() .with_inner_size(LogicalSize::new(width as f64, height as f64)) .with_title("glyphon hello world") @@ -76,84 +76,80 @@ async fn run() { buffer.set_text(&mut font_system, "Hello world! 👋\nThis is rendered with 🦅 glyphon 🦁\nThe text below should be partially clipped.\na b c d e f g h i j k l m n o p q r s t u v w x y z", Attrs::new().family(Family::SansSerif), Shaping::Advanced); buffer.shape_until_scroll(&mut font_system); - event_loop.run(move |event, _, control_flow| { - let _ = (&instance, &adapter); + event_loop + .run(move |event, target| { + if let Event::WindowEvent { + window_id: _, + event, + } = event + { + match event { + WindowEvent::Resized(size) => { + config.width = size.width; + config.height = size.height; + surface.configure(&device, &config); + window.request_redraw(); + } + WindowEvent::RedrawRequested => { + text_renderer + .prepare( + &device, + &queue, + &mut font_system, + &mut atlas, + Resolution { + width: config.width, + height: config.height, + }, + [TextArea { + buffer: &buffer, + left: 10.0, + top: 10.0, + scale: 1.0, + bounds: TextBounds { + left: 0, + top: 0, + right: 600, + bottom: 160, + }, + default_color: Color::rgb(255, 255, 255), + }], + &mut cache, + ) + .unwrap(); - *control_flow = ControlFlow::Poll; - match event { - Event::WindowEvent { - event: WindowEvent::Resized(size), - .. - } => { - config.width = size.width; - config.height = size.height; - surface.configure(&device, &config); - window.request_redraw(); - } - Event::RedrawRequested(_) => { - text_renderer - .prepare( - &device, - &queue, - &mut font_system, - &mut atlas, - Resolution { - width: config.width, - height: config.height, - }, - [TextArea { - buffer: &buffer, - left: 10.0, - top: 10.0, - scale: 1.0, - bounds: TextBounds { - left: 0, - top: 0, - right: 600, - bottom: 160, - }, - default_color: Color::rgb(255, 255, 255), - }], - &mut cache, - ) - .unwrap(); + let frame = surface.get_current_texture().unwrap(); + let view = frame.texture.create_view(&TextureViewDescriptor::default()); + let mut encoder = device + .create_command_encoder(&CommandEncoderDescriptor { label: None }); + { + let mut pass = encoder.begin_render_pass(&RenderPassDescriptor { + label: None, + color_attachments: &[Some(RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: Operations { + load: LoadOp::Clear(wgpu::Color::BLACK), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); - let frame = surface.get_current_texture().unwrap(); - let view = frame.texture.create_view(&TextureViewDescriptor::default()); - let mut encoder = - device.create_command_encoder(&CommandEncoderDescriptor { label: None }); - { - let mut pass = encoder.begin_render_pass(&RenderPassDescriptor { - label: None, - color_attachments: &[Some(RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: Operations { - load: LoadOp::Clear(wgpu::Color::BLACK), - store: wgpu::StoreOp::Store, - }, - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }); + text_renderer.render(&atlas, &mut pass).unwrap(); + } - text_renderer.render(&atlas, &mut pass).unwrap(); + queue.submit(Some(encoder.finish())); + frame.present(); + + atlas.trim(); + } + WindowEvent::CloseRequested => target.exit(), + _ => {} } - - queue.submit(Some(encoder.finish())); - frame.present(); - - atlas.trim(); } - Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => *control_flow = ControlFlow::Exit, - Event::MainEventsCleared => { - window.request_redraw(); - } - _ => {} - } - }); + }) + .unwrap(); } From 1bb46e41ff93da89a965567b11383c353942af7c Mon Sep 17 00:00:00 2001 From: grovesNL Date: Mon, 15 Jan 2024 16:03:19 -0330 Subject: [PATCH 04/10] Update lru --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 85a4f8d..851defc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT OR Apache-2.0 OR Zlib" wgpu = "0.18" etagere = "0.2.10" cosmic-text = "0.10" -lru = "0.11" +lru = "0.12.1" [dev-dependencies] winit = { version = "0.29.10", features = ["rwh_05"] } From 7f9afac93eeb348ea7a947fe60854b43a1763811 Mon Sep 17 00:00:00 2001 From: grovesNL Date: Mon, 15 Jan 2024 16:24:05 -0330 Subject: [PATCH 05/10] Release 0.4.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 851defc..1340590 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "glyphon" description = "Fast, simple 2D text rendering for wgpu" -version = "0.3.0" +version = "0.4.0" edition = "2021" homepage = "https://github.com/grovesNL/glyphon.git" repository = "https://github.com/grovesNL/glyphon" From bfb07147cde7353e2a3a0354862df0bf674ffd5f Mon Sep 17 00:00:00 2001 From: grovesNL Date: Mon, 15 Jan 2024 21:13:50 -0330 Subject: [PATCH 06/10] Don't convert web text colors to linear --- src/lib.rs | 2 +- src/shader.wgsl | 37 ++++++++++++++++++++++++++----------- src/text_atlas.rs | 2 ++ src/text_render.rs | 21 +++++++++++++++++---- 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 64373ca..b9194c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,7 +51,7 @@ pub(crate) struct GlyphToRender { dim: [u16; 2], uv: [u16; 2], color: u32, - content_type: u32, + content_type_with_srgb: [u16; 2], depth: f32, } diff --git a/src/shader.wgsl b/src/shader.wgsl index 337f788..d7f7d86 100644 --- a/src/shader.wgsl +++ b/src/shader.wgsl @@ -4,7 +4,7 @@ struct VertexInput { @location(1) dim: u32, @location(2) uv: u32, @location(3) color: u32, - @location(4) content_type: u32, + @location(4) content_type_with_srgb: u32, @location(5) depth: f32, } @@ -32,8 +32,7 @@ var mask_atlas_texture: texture_2d; @group(0) @binding(3) var atlas_sampler: sampler; -fn srgb_to_linear(srgb: u32) -> f32 { - let c = f32(srgb) / 255.0; +fn srgb_to_linear(c: f32) -> f32 { if c <= 0.04045 { return c / 12.92; } else { @@ -78,15 +77,31 @@ fn vs_main(in_vert: VertexInput) -> VertexOutput { vert_output.position.y *= -1.0; - vert_output.color = vec4( - srgb_to_linear((color & 0x00ff0000u) >> 16u), - srgb_to_linear((color & 0x0000ff00u) >> 8u), - srgb_to_linear(color & 0x000000ffu), - f32((color & 0xff000000u) >> 24u) / 255.0, - ); + let content_type = in_vert.content_type_with_srgb & 0xffffu; + let srgb = (in_vert.content_type_with_srgb & 0xffff0000u) >> 16u; + + switch srgb { + case 0u: { + vert_output.color = vec4( + f32((color & 0x00ff0000u) >> 16u) / 255.0, + f32((color & 0x0000ff00u) >> 8u) / 255.0, + f32(color & 0x000000ffu) / 255.0, + f32((color & 0xff000000u) >> 24u) / 255.0, + ); + } + case 1u: { + vert_output.color = vec4( + srgb_to_linear(f32((color & 0x00ff0000u) >> 16u) / 255.0), + srgb_to_linear(f32((color & 0x0000ff00u) >> 8u) / 255.0), + srgb_to_linear(f32(color & 0x000000ffu) / 255.0), + f32((color & 0xff000000u) >> 24u) / 255.0, + ); + } + default: {} + } var dim: vec2 = vec2(0u); - switch in_vert.content_type { + switch content_type { case 0u: { dim = textureDimensions(color_atlas_texture); break; @@ -98,7 +113,7 @@ fn vs_main(in_vert: VertexInput) -> VertexOutput { default: {} } - vert_output.content_type = in_vert.content_type; + vert_output.content_type = content_type; vert_output.uv = vec2(uv) / vec2(dim); diff --git a/src/text_atlas.rs b/src/text_atlas.rs index 320697d..4110554 100644 --- a/src/text_atlas.rs +++ b/src/text_atlas.rs @@ -271,6 +271,7 @@ pub struct TextAtlas { pub(crate) shader: ShaderModule, pub(crate) vertex_buffers: [wgpu::VertexBufferLayout<'static>; 1], pub(crate) format: TextureFormat, + pub(crate) color_mode: ColorMode, } impl TextAtlas { @@ -450,6 +451,7 @@ impl TextAtlas { shader, vertex_buffers, format, + color_mode, } } diff --git a/src/text_render.rs b/src/text_render.rs index a1395d3..9818bd1 100644 --- a/src/text_render.rs +++ b/src/text_render.rs @@ -1,6 +1,6 @@ use crate::{ - FontSystem, GlyphDetails, GlyphToRender, GpuCacheStatus, Params, PrepareError, RenderError, - Resolution, SwashCache, SwashContent, TextArea, TextAtlas, + ColorMode, FontSystem, GlyphDetails, GlyphToRender, GpuCacheStatus, Params, PrepareError, + RenderError, Resolution, SwashCache, SwashContent, TextArea, TextAtlas, }; use std::{iter, mem::size_of, slice, sync::Arc}; use wgpu::{ @@ -276,7 +276,13 @@ impl TextRenderer { dim: [width as u16, height as u16], uv: [atlas_x, atlas_y], color: color.0, - content_type: content_type as u32, + content_type_with_srgb: [ + content_type as u16, + match atlas.color_mode { + ColorMode::Accurate => TextColorConversion::ConvertToLinear, + ColorMode::Web => TextColorConversion::None, + } as u16, + ], depth, }) .take(4), @@ -405,13 +411,20 @@ impl TextRenderer { } } -#[repr(u32)] +#[repr(u16)] #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum ContentType { Color = 0, Mask = 1, } +#[repr(u16)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +enum TextColorConversion { + None = 0, + ConvertToLinear = 1, +} + fn next_copy_buffer_size(size: u64) -> u64 { let align_mask = COPY_BUFFER_ALIGNMENT - 1; ((size.next_power_of_two() + align_mask) & !align_mask).max(COPY_BUFFER_ALIGNMENT) From 3425efd52244bde6f401bb6e47f60e81da921ff4 Mon Sep 17 00:00:00 2001 From: grovesNL Date: Mon, 15 Jan 2024 21:33:29 -0330 Subject: [PATCH 07/10] Release 0.4.1 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1340590..c6d52f6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "glyphon" description = "Fast, simple 2D text rendering for wgpu" -version = "0.4.0" +version = "0.4.1" edition = "2021" homepage = "https://github.com/grovesNL/glyphon.git" repository = "https://github.com/grovesNL/glyphon" From 65825aa0d4b5309c8cad59f6bb7e981e37f6e353 Mon Sep 17 00:00:00 2001 From: TheEggShark Date: Wed, 17 Jan 2024 15:00:02 -0600 Subject: [PATCH 08/10] update to wgpu 0.19.0 --- Cargo.toml | 2 +- examples/hello-world.rs | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c6d52f6..e232a2a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ repository = "https://github.com/grovesNL/glyphon" license = "MIT OR Apache-2.0 OR Zlib" [dependencies] -wgpu = "0.18" +wgpu = "0.19" etagere = "0.2.10" cosmic-text = "0.10" lru = "0.12.1" diff --git a/examples/hello-world.rs b/examples/hello-world.rs index 213ba61..daafbb3 100644 --- a/examples/hello-world.rs +++ b/examples/hello-world.rs @@ -15,6 +15,8 @@ use winit::{ window::WindowBuilder, }; +use std::sync::Arc; + fn main() { pollster::block_on(run()); } @@ -23,11 +25,11 @@ async fn run() { // Set up window let (width, height) = (800, 600); let event_loop = EventLoop::new().unwrap(); - let window = WindowBuilder::new() + let window = Arc::new(WindowBuilder::new() .with_inner_size(LogicalSize::new(width as f64, height as f64)) .with_title("glyphon hello world") .build(&event_loop) - .unwrap(); + .unwrap()); let size = window.inner_size(); let scale_factor = window.scale_factor(); @@ -41,14 +43,15 @@ async fn run() { .request_device( &DeviceDescriptor { label: None, - features: Features::empty(), - limits: Limits::downlevel_defaults(), + required_features: Features::empty(), + required_limits: Limits::downlevel_defaults(), }, None, ) .await .unwrap(); - let surface = unsafe { instance.create_surface(&window) }.expect("Create surface"); + + let surface = instance.create_surface(window.clone()).expect("Create surface"); let swapchain_format = TextureFormat::Bgra8UnormSrgb; let mut config = SurfaceConfiguration { usage: TextureUsages::RENDER_ATTACHMENT, @@ -58,6 +61,7 @@ async fn run() { present_mode: PresentMode::Fifo, alpha_mode: CompositeAlphaMode::Opaque, view_formats: vec![], + desired_maximum_frame_latency: 2, }; surface.configure(&device, &config); From 4590abae089d813298ea98f5bfeb2b15d3027a27 Mon Sep 17 00:00:00 2001 From: grovesNL Date: Wed, 17 Jan 2024 20:30:28 -0330 Subject: [PATCH 09/10] Release 0.5.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e232a2a..222c2be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "glyphon" description = "Fast, simple 2D text rendering for wgpu" -version = "0.4.1" +version = "0.5.0" edition = "2021" homepage = "https://github.com/grovesNL/glyphon.git" repository = "https://github.com/grovesNL/glyphon" From 01ab64704b123d1ae61a5022b2b8ea2a7a4573e8 Mon Sep 17 00:00:00 2001 From: Isaac Mills Date: Mon, 18 Mar 2024 18:30:28 -0400 Subject: [PATCH 10/10] Add opacity --- Cargo.toml | 2 +- src/lib.rs | 4 +++- src/text_render.rs | 7 +++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 222c2be..8b158ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,8 @@ license = "MIT OR Apache-2.0 OR Zlib" [dependencies] wgpu = "0.19" etagere = "0.2.10" -cosmic-text = "0.10" lru = "0.12.1" +cosmic-text = "0.11.2" [dev-dependencies] winit = { version = "0.29.10", features = ["rwh_05"] } diff --git a/src/lib.rs b/src/lib.rs index b9194c5..5b4dc51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -111,6 +111,8 @@ pub struct TextArea<'a> { /// The visible bounds of the text area. This is used to clip the text and doesn't have to /// match the `left` and `top` values. pub bounds: TextBounds, - // The default color of the text area. + /// The default color of the text area. pub default_color: Color, + /// The opacity to set the text area to (in gamma space) + pub opacity: f32, } diff --git a/src/text_render.rs b/src/text_render.rs index 9818bd1..8a1a595 100644 --- a/src/text_render.rs +++ b/src/text_render.rs @@ -268,6 +268,13 @@ impl TextRenderer { None => text_area.default_color, }; + let color = cosmic_text::Color::rgba( + (color.r() as f32 * text_area.opacity + 0.5) as u8, + (color.g() as f32 * text_area.opacity + 0.5) as u8, + (color.b() as f32 * text_area.opacity + 0.5) as u8, + (color.a() as f32 * text_area.opacity + 0.5) as u8, + ); + let depth = metadata_to_depth(glyph.metadata); glyph_vertices.extend(