diff options
author | 2023-01-31 06:29:21 +0100 | |
---|---|---|
committer | 2023-02-24 13:19:48 +0100 | |
commit | baf51a8fcffc78e4ca20f7dcbba18ca3655f2840 (patch) | |
tree | fd7ba1920ac0ff57b6ddefd692d8cad5d57274cd /wgpu | |
parent | b9a9576207ddfc7afd89da30b7cfc7ca0d7e335c (diff) | |
download | iced-baf51a8fcffc78e4ca20f7dcbba18ca3655f2840.tar.gz iced-baf51a8fcffc78e4ca20f7dcbba18ca3655f2840.tar.bz2 iced-baf51a8fcffc78e4ca20f7dcbba18ca3655f2840.zip |
Draft `glyphon` implementation of text pipeline for `iced_wgpu`
Diffstat (limited to 'wgpu')
-rw-r--r-- | wgpu/Cargo.toml | 5 | ||||
-rw-r--r-- | wgpu/src/backend.rs | 19 | ||||
-rw-r--r-- | wgpu/src/text.rs | 160 | ||||
-rw-r--r-- | wgpu/src/window/compositor.rs | 3 |
4 files changed, 171 insertions, 16 deletions
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 5badeae6..d29c1129 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -33,6 +33,7 @@ log = "0.4" guillotiere = "0.6" futures = "0.3" bitflags = "1.2" +once_cell = "1.0" [dependencies.bytemuck] version = "1.9" @@ -46,6 +47,10 @@ path = "../native" version = "0.7" path = "../graphics" +[dependencies.glyphon] +version = "0.2" +path = "../../glyphon" + [dependencies.tracing] version = "0.1.6" optional = true diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index e9e23e80..77785760 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -18,7 +18,7 @@ use crate::image; /// /// [`wgpu`]: https://github.com/gfx-rs/wgpu-rs /// [`iced`]: https://github.com/iced-rs/iced -#[derive(Debug)] +#[allow(missing_debug_implementations)] pub struct Backend { quad_pipeline: quad::Pipeline, text_pipeline: text::Pipeline, @@ -34,11 +34,13 @@ impl Backend { /// Creates a new [`Backend`]. pub fn new( device: &wgpu::Device, + queue: &wgpu::Queue, settings: Settings, format: wgpu::TextureFormat, ) -> Self { let text_pipeline = text::Pipeline::new( device, + queue, format, settings.default_font, settings.text_multithreading, @@ -70,6 +72,7 @@ impl Backend { pub fn present<T: AsRef<str>>( &mut self, device: &wgpu::Device, + queue: &wgpu::Queue, staging_belt: &mut wgpu::util::StagingBelt, encoder: &mut wgpu::CommandEncoder, frame: &wgpu::TextureView, @@ -91,6 +94,7 @@ impl Backend { for layer in layers { self.flush( device, + queue, scale_factor, transformation, &layer, @@ -108,6 +112,7 @@ impl Backend { fn flush( &mut self, device: &wgpu::Device, + queue: &wgpu::Queue, scale_factor: f32, transformation: Transformation, layer: &Layer<'_>, @@ -171,11 +176,15 @@ impl Backend { } if !layer.text.is_empty() { - for _text in layer.text.iter() { - // TODO: Queue text sections - } + self.text_pipeline.prepare( + device, + queue, + &layer.text, + scale_factor, + target_size, + ); - // TODO: Draw queued + self.text_pipeline.render(encoder, target); } } } diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 125e6be0..ccb627cd 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -1,26 +1,166 @@ pub use iced_native::text::Hit; -#[derive(Debug)] -pub struct Pipeline; +use iced_graphics::layer::Text; +use iced_native::{Font, Size}; + +#[allow(missing_debug_implementations)] +pub struct Pipeline { + renderer: glyphon::TextRenderer, + atlas: glyphon::TextAtlas, + cache: glyphon::SwashCache<'static>, +} + +// TODO: Share with `iced_graphics` +static FONT_SYSTEM: once_cell::sync::Lazy<glyphon::FontSystem> = + once_cell::sync::Lazy::new(glyphon::FontSystem::new); impl Pipeline { pub fn new( - _device: &wgpu::Device, - _format: wgpu::TextureFormat, + device: &wgpu::Device, + queue: &wgpu::Queue, + format: wgpu::TextureFormat, _default_font: Option<&[u8]>, _multithreading: bool, ) -> Self { - Pipeline + Pipeline { + renderer: glyphon::TextRenderer::new(device, queue), + atlas: glyphon::TextAtlas::new(device, queue, format), + cache: glyphon::SwashCache::new(&FONT_SYSTEM), + } + } + + pub fn prepare( + &mut self, + device: &wgpu::Device, + queue: &wgpu::Queue, + sections: &[Text<'_>], + scale_factor: f32, + target_size: Size<u32>, + ) { + let buffers: Vec<_> = sections + .iter() + .map(|section| { + let metrics = glyphon::Metrics::new( + (section.size * scale_factor) as i32, + (section.size * 1.2 * scale_factor) as i32, + ); + + let mut buffer = glyphon::Buffer::new(&FONT_SYSTEM, metrics); + + buffer.set_size( + (section.bounds.width * scale_factor).ceil() as i32, + (section.bounds.height * scale_factor).ceil() as i32, + ); + + buffer.set_text( + section.content, + glyphon::Attrs::new() + .color({ + let [r, g, b, a] = section.color.into_rgba8(); + glyphon::Color::rgba(r, g, b, a) + }) + .family(match section.font { + Font::Default => glyphon::Family::SansSerif, + Font::External { name, .. } => { + glyphon::Family::Name(name) + } + }), + ); + + buffer.shape_until_scroll(); + + buffer + }) + .collect(); + + let text_areas: Vec<_> = sections + .iter() + .zip(buffers.iter()) + .map(|(section, buffer)| glyphon::TextArea { + buffer, + left: (section.bounds.x * scale_factor) as i32, + top: (section.bounds.y * scale_factor) as i32, + bounds: glyphon::TextBounds { + left: (section.bounds.x * scale_factor) as i32, + top: (section.bounds.y * scale_factor) as i32, + right: ((section.bounds.x + section.bounds.width) + * scale_factor) as i32, + bottom: ((section.bounds.y + section.bounds.height) + * scale_factor) as i32, + }, + }) + .collect(); + + self.renderer + .prepare( + device, + queue, + &mut self.atlas, + glyphon::Resolution { + width: target_size.width, + height: target_size.height, + }, + &text_areas, + glyphon::Color::rgb(0, 0, 0), + &mut self.cache, + ) + .expect("Prepare text sections"); + } + + pub fn render( + &mut self, + encoder: &mut wgpu::CommandEncoder, + target: &wgpu::TextureView, + ) { + let mut render_pass = + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: target, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: true, + }, + })], + depth_stencil_attachment: None, + }); + + self.renderer + .render(&self.atlas, &mut render_pass) + .expect("Render text"); } pub fn measure( &self, - _content: &str, - _size: f32, - _font: iced_native::Font, - _bounds: iced_native::Size, + content: &str, + size: f32, + font: Font, + bounds: Size, ) -> (f32, f32) { - (0.0, 0.0) + let attrs = match font { + Font::Default => glyphon::Attrs::new(), + Font::External { name, .. } => glyphon::Attrs { + family: glyphon::Family::Name(name), + ..glyphon::Attrs::new() + }, + }; + + let mut paragraph = + glyphon::BufferLine::new(content, glyphon::AttrsList::new(attrs)); + + // TODO: Cache layout + let layout = paragraph.layout( + &FONT_SYSTEM, + size as i32, + bounds.width as i32, + glyphon::Wrap::Word, + ); + + ( + layout.iter().fold(0.0, |max, line| line.w.max(max)), + size * 1.2 * layout.len() as f32, + ) } pub fn hit_test( diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs index 6d0c36f6..50231f7c 100644 --- a/wgpu/src/window/compositor.rs +++ b/wgpu/src/window/compositor.rs @@ -114,7 +114,7 @@ impl<Theme> Compositor<Theme> { /// Creates a new rendering [`Backend`] for this [`Compositor`]. pub fn create_backend(&self) -> Backend { - Backend::new(&self.device, self.settings, self.format) + Backend::new(&self.device, &self.queue, self.settings, self.format) } } @@ -227,6 +227,7 @@ impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> { renderer.with_primitives(|backend, primitives| { backend.present( &self.device, + &self.queue, &mut self.staging_belt, &mut encoder, view, |