diff --git a/examples/hello-world.rs b/examples/hello-world.rs index 29d1768..1ec5872 100644 --- a/examples/hello-world.rs +++ b/examples/hello-world.rs @@ -1,6 +1,6 @@ use glyphon::{ Attrs, Buffer, Cache, Color, Family, FontSystem, Metrics, Resolution, Shaping, SwashCache, - TextArea, TextAtlas, TextBounds, TextRenderer, + TextArea, TextAtlas, TextBounds, TextRenderer, Viewport, }; use wgpu::{ CommandEncoderDescriptor, CompositeAlphaMode, DeviceDescriptor, Features, Instance, @@ -73,6 +73,7 @@ async fn run() { let mut font_system = FontSystem::new(); let mut swash_cache = SwashCache::new(); let cache = Cache::new(&device); + let mut viewport = Viewport::new(&device, &cache); let mut atlas = TextAtlas::new(&device, &queue, &cache, swapchain_format); let mut text_renderer = TextRenderer::new(&mut atlas, &device, MultisampleState::default(), None); @@ -100,16 +101,21 @@ async fn run() { window.request_redraw(); } WindowEvent::RedrawRequested => { + viewport.update( + &queue, + Resolution { + width: config.width, + height: config.height, + }, + ); + text_renderer .prepare( &device, &queue, &mut font_system, &mut atlas, - Resolution { - width: config.width, - height: config.height, - }, + &viewport, [TextArea { buffer: &buffer, left: 10.0, @@ -147,7 +153,7 @@ async fn run() { occlusion_query_set: None, }); - text_renderer.render(&atlas, &mut pass).unwrap(); + text_renderer.render(&atlas, &viewport, &mut pass).unwrap(); } queue.submit(Some(encoder.finish())); diff --git a/src/lib.rs b/src/lib.rs index 07c2993..3b31120 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,11 +8,13 @@ mod cache; mod error; mod text_atlas; mod text_render; +mod viewport; pub use cache::Cache; pub use error::{PrepareError, RenderError}; pub use text_atlas::{ColorMode, TextAtlas}; pub use text_render::TextRenderer; +pub use viewport::Viewport; use text_render::ContentType; diff --git a/src/text_atlas.rs b/src/text_atlas.rs index e32c1b0..1cabbc4 100644 --- a/src/text_atlas.rs +++ b/src/text_atlas.rs @@ -6,7 +6,7 @@ use lru::LruCache; use rustc_hash::FxHasher; use std::{collections::HashSet, hash::BuildHasherDefault, sync::Arc}; use wgpu::{ - BindGroup, Buffer, DepthStencilState, Device, Extent3d, ImageCopyTexture, ImageDataLayout, + BindGroup, DepthStencilState, Device, Extent3d, ImageCopyTexture, ImageDataLayout, MultisampleState, Origin3d, Queue, RenderPipeline, Texture, TextureAspect, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView, TextureViewDescriptor, }; @@ -350,10 +350,6 @@ impl TextAtlas { .get_or_create_pipeline(device, self.format, multisample, depth_stencil) } - pub(crate) fn create_uniforms_bind_group(&self, device: &Device, buffer: &Buffer) -> BindGroup { - self.cache.create_uniforms_bind_group(device, buffer) - } - fn rebind(&mut self, device: &wgpu::Device) { self.bind_group = self.cache.create_atlas_bind_group( device, diff --git a/src/text_render.rs b/src/text_render.rs index 0102e84..23ca20f 100644 --- a/src/text_render.rs +++ b/src/text_render.rs @@ -1,8 +1,8 @@ use crate::{ - ColorMode, FontSystem, GlyphDetails, GlyphToRender, GpuCacheStatus, Params, PrepareError, - RenderError, Resolution, SwashCache, SwashContent, TextArea, TextAtlas, + ColorMode, FontSystem, GlyphDetails, GlyphToRender, GpuCacheStatus, PrepareError, RenderError, + SwashCache, SwashContent, TextArea, TextAtlas, Viewport, }; -use std::{iter, mem::size_of, slice, sync::Arc}; +use std::{iter, slice, sync::Arc}; use wgpu::{ Buffer, BufferDescriptor, BufferUsages, DepthStencilState, Device, Extent3d, ImageCopyTexture, ImageDataLayout, IndexFormat, MultisampleState, Origin3d, Queue, RenderPass, RenderPipeline, @@ -11,15 +11,12 @@ use wgpu::{ /// A text renderer that uses cached glyphs to render text into an existing render pass. pub struct TextRenderer { - params: Params, - params_buffer: Buffer, vertex_buffer: Buffer, vertex_buffer_size: u64, index_buffer: Buffer, index_buffer_size: u64, vertices_to_render: u32, pipeline: Arc, - bind_group: wgpu::BindGroup, glyph_vertices: Vec, glyph_indices: Vec, } @@ -48,34 +45,15 @@ impl TextRenderer { mapped_at_creation: false, }); - let params = Params { - screen_resolution: Resolution { - width: 0, - height: 0, - }, - _pad: [0, 0], - }; - - let params_buffer = device.create_buffer(&BufferDescriptor { - label: Some("glyphon params"), - size: size_of::() as u64, - usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - let pipeline = atlas.get_or_create_pipeline(device, multisample, depth_stencil); - let bind_group = atlas.create_uniforms_bind_group(device, ¶ms_buffer); Self { - params, - params_buffer, vertex_buffer, vertex_buffer_size, index_buffer, index_buffer_size, vertices_to_render: 0, pipeline, - bind_group, glyph_vertices: Vec::new(), glyph_indices: Vec::new(), } @@ -88,26 +66,17 @@ impl TextRenderer { queue: &Queue, font_system: &mut FontSystem, atlas: &mut TextAtlas, - screen_resolution: Resolution, + viewport: &Viewport, text_areas: impl IntoIterator>, cache: &mut SwashCache, mut metadata_to_depth: impl FnMut(usize) -> f32, ) -> Result<(), PrepareError> { - if self.params.screen_resolution != screen_resolution { - self.params.screen_resolution = screen_resolution; - - queue.write_buffer(&self.params_buffer, 0, unsafe { - slice::from_raw_parts( - &self.params as *const Params as *const u8, - size_of::(), - ) - }); - } - self.glyph_vertices.clear(); self.glyph_indices.clear(); let mut glyphs_added = 0; + let resolution = viewport.resolution(); + for text_area in text_areas { for run in text_area.buffer.layout_runs() { for glyph in run.glyphs.iter() { @@ -238,8 +207,8 @@ impl TextRenderer { let bounds_min_x = text_area.bounds.left.max(0); let bounds_min_y = text_area.bounds.top.max(0); - let bounds_max_x = text_area.bounds.right.min(screen_resolution.width as i32); - let bounds_max_y = text_area.bounds.bottom.min(screen_resolution.height as i32); + let bounds_max_x = text_area.bounds.right.min(resolution.width as i32); + let bounds_max_y = text_area.bounds.bottom.min(resolution.height as i32); // Starts beyond right edge or ends beyond left edge let max_x = x + width; @@ -386,7 +355,7 @@ impl TextRenderer { queue: &Queue, font_system: &mut FontSystem, atlas: &mut TextAtlas, - screen_resolution: Resolution, + viewport: &Viewport, text_areas: impl IntoIterator>, cache: &mut SwashCache, ) -> Result<(), PrepareError> { @@ -395,7 +364,7 @@ impl TextRenderer { queue, font_system, atlas, - screen_resolution, + viewport, text_areas, cache, zero_depth, @@ -406,6 +375,7 @@ impl TextRenderer { pub fn render<'pass>( &'pass self, atlas: &'pass TextAtlas, + viewport: &'pass Viewport, pass: &mut RenderPass<'pass>, ) -> Result<(), RenderError> { if self.vertices_to_render == 0 { @@ -414,7 +384,7 @@ impl TextRenderer { pass.set_pipeline(&self.pipeline); pass.set_bind_group(0, &atlas.bind_group, &[]); - pass.set_bind_group(1, &self.bind_group, &[]); + pass.set_bind_group(1, &viewport.bind_group, &[]); pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); pass.set_index_buffer(self.index_buffer.slice(..), IndexFormat::Uint32); pass.draw_indexed(0..self.vertices_to_render, 0, 0..1); diff --git a/src/viewport.rs b/src/viewport.rs new file mode 100644 index 0000000..14d3412 --- /dev/null +++ b/src/viewport.rs @@ -0,0 +1,57 @@ +use crate::{Cache, Params, Resolution}; + +use wgpu::{BindGroup, Buffer, BufferDescriptor, BufferUsages, Device, Queue}; + +use std::mem; +use std::slice; + +#[derive(Debug)] +pub struct Viewport { + params: Params, + params_buffer: Buffer, + pub(crate) bind_group: BindGroup, +} + +impl Viewport { + pub fn new(device: &Device, cache: &Cache) -> Self { + let params = Params { + screen_resolution: Resolution { + width: 0, + height: 0, + }, + _pad: [0, 0], + }; + + let params_buffer = device.create_buffer(&BufferDescriptor { + label: Some("glyphon params"), + size: mem::size_of::() as u64, + usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + let bind_group = cache.create_uniforms_bind_group(device, ¶ms_buffer); + + Self { + params, + params_buffer, + bind_group, + } + } + + pub fn update(&mut self, queue: &Queue, resolution: Resolution) { + if self.params.screen_resolution != resolution { + self.params.screen_resolution = resolution; + + queue.write_buffer(&self.params_buffer, 0, unsafe { + slice::from_raw_parts( + &self.params as *const Params as *const u8, + mem::size_of::(), + ) + }); + } + } + + pub fn resolution(&self) -> Resolution { + self.params.screen_resolution + } +}