Introduce explicit Viewport sharing (#96)

This commit is contained in:
Héctor Ramón 2024-05-08 16:17:58 +02:00 committed by GitHub
parent 5aed9e1477
commit b411ea71e7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 84 additions and 53 deletions

View file

@ -1,6 +1,6 @@
use glyphon::{ use glyphon::{
Attrs, Buffer, Cache, Color, Family, FontSystem, Metrics, Resolution, Shaping, SwashCache, Attrs, Buffer, Cache, Color, Family, FontSystem, Metrics, Resolution, Shaping, SwashCache,
TextArea, TextAtlas, TextBounds, TextRenderer, TextArea, TextAtlas, TextBounds, TextRenderer, Viewport,
}; };
use wgpu::{ use wgpu::{
CommandEncoderDescriptor, CompositeAlphaMode, DeviceDescriptor, Features, Instance, CommandEncoderDescriptor, CompositeAlphaMode, DeviceDescriptor, Features, Instance,
@ -73,6 +73,7 @@ async fn run() {
let mut font_system = FontSystem::new(); let mut font_system = FontSystem::new();
let mut swash_cache = SwashCache::new(); let mut swash_cache = SwashCache::new();
let cache = Cache::new(&device); let cache = Cache::new(&device);
let mut viewport = Viewport::new(&device, &cache);
let mut atlas = TextAtlas::new(&device, &queue, &cache, swapchain_format); let mut atlas = TextAtlas::new(&device, &queue, &cache, swapchain_format);
let mut text_renderer = let mut text_renderer =
TextRenderer::new(&mut atlas, &device, MultisampleState::default(), None); TextRenderer::new(&mut atlas, &device, MultisampleState::default(), None);
@ -100,16 +101,21 @@ async fn run() {
window.request_redraw(); window.request_redraw();
} }
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
viewport.update(
&queue,
Resolution {
width: config.width,
height: config.height,
},
);
text_renderer text_renderer
.prepare( .prepare(
&device, &device,
&queue, &queue,
&mut font_system, &mut font_system,
&mut atlas, &mut atlas,
Resolution { &viewport,
width: config.width,
height: config.height,
},
[TextArea { [TextArea {
buffer: &buffer, buffer: &buffer,
left: 10.0, left: 10.0,
@ -147,7 +153,7 @@ async fn run() {
occlusion_query_set: None, occlusion_query_set: None,
}); });
text_renderer.render(&atlas, &mut pass).unwrap(); text_renderer.render(&atlas, &viewport, &mut pass).unwrap();
} }
queue.submit(Some(encoder.finish())); queue.submit(Some(encoder.finish()));

View file

@ -8,11 +8,13 @@ mod cache;
mod error; mod error;
mod text_atlas; mod text_atlas;
mod text_render; mod text_render;
mod viewport;
pub use cache::Cache; pub use cache::Cache;
pub use error::{PrepareError, RenderError}; pub use error::{PrepareError, RenderError};
pub use text_atlas::{ColorMode, TextAtlas}; pub use text_atlas::{ColorMode, TextAtlas};
pub use text_render::TextRenderer; pub use text_render::TextRenderer;
pub use viewport::Viewport;
use text_render::ContentType; use text_render::ContentType;

View file

@ -6,7 +6,7 @@ use lru::LruCache;
use rustc_hash::FxHasher; use rustc_hash::FxHasher;
use std::{collections::HashSet, hash::BuildHasherDefault, sync::Arc}; use std::{collections::HashSet, hash::BuildHasherDefault, sync::Arc};
use wgpu::{ use wgpu::{
BindGroup, Buffer, DepthStencilState, Device, Extent3d, ImageCopyTexture, ImageDataLayout, BindGroup, DepthStencilState, Device, Extent3d, ImageCopyTexture, ImageDataLayout,
MultisampleState, Origin3d, Queue, RenderPipeline, Texture, TextureAspect, TextureDescriptor, MultisampleState, Origin3d, Queue, RenderPipeline, Texture, TextureAspect, TextureDescriptor,
TextureDimension, TextureFormat, TextureUsages, TextureView, TextureViewDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView, TextureViewDescriptor,
}; };
@ -350,10 +350,6 @@ impl TextAtlas {
.get_or_create_pipeline(device, self.format, multisample, depth_stencil) .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) { fn rebind(&mut self, device: &wgpu::Device) {
self.bind_group = self.cache.create_atlas_bind_group( self.bind_group = self.cache.create_atlas_bind_group(
device, device,

View file

@ -1,8 +1,8 @@
use crate::{ use crate::{
ColorMode, FontSystem, GlyphDetails, GlyphToRender, GpuCacheStatus, Params, PrepareError, ColorMode, FontSystem, GlyphDetails, GlyphToRender, GpuCacheStatus, PrepareError, RenderError,
RenderError, Resolution, SwashCache, SwashContent, TextArea, TextAtlas, SwashCache, SwashContent, TextArea, TextAtlas, Viewport,
}; };
use std::{iter, mem::size_of, slice, sync::Arc}; use std::{iter, slice, sync::Arc};
use wgpu::{ use wgpu::{
Buffer, BufferDescriptor, BufferUsages, DepthStencilState, Device, Extent3d, ImageCopyTexture, Buffer, BufferDescriptor, BufferUsages, DepthStencilState, Device, Extent3d, ImageCopyTexture,
ImageDataLayout, IndexFormat, MultisampleState, Origin3d, Queue, RenderPass, RenderPipeline, 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. /// A text renderer that uses cached glyphs to render text into an existing render pass.
pub struct TextRenderer { pub struct TextRenderer {
params: Params,
params_buffer: Buffer,
vertex_buffer: Buffer, vertex_buffer: Buffer,
vertex_buffer_size: u64, vertex_buffer_size: u64,
index_buffer: Buffer, index_buffer: Buffer,
index_buffer_size: u64, index_buffer_size: u64,
vertices_to_render: u32, vertices_to_render: u32,
pipeline: Arc<RenderPipeline>, pipeline: Arc<RenderPipeline>,
bind_group: wgpu::BindGroup,
glyph_vertices: Vec<GlyphToRender>, glyph_vertices: Vec<GlyphToRender>,
glyph_indices: Vec<u32>, glyph_indices: Vec<u32>,
} }
@ -48,34 +45,15 @@ impl TextRenderer {
mapped_at_creation: false, 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::<Params>() as u64,
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let pipeline = atlas.get_or_create_pipeline(device, multisample, depth_stencil); let pipeline = atlas.get_or_create_pipeline(device, multisample, depth_stencil);
let bind_group = atlas.create_uniforms_bind_group(device, &params_buffer);
Self { Self {
params,
params_buffer,
vertex_buffer, vertex_buffer,
vertex_buffer_size, vertex_buffer_size,
index_buffer, index_buffer,
index_buffer_size, index_buffer_size,
vertices_to_render: 0, vertices_to_render: 0,
pipeline, pipeline,
bind_group,
glyph_vertices: Vec::new(), glyph_vertices: Vec::new(),
glyph_indices: Vec::new(), glyph_indices: Vec::new(),
} }
@ -88,26 +66,17 @@ impl TextRenderer {
queue: &Queue, queue: &Queue,
font_system: &mut FontSystem, font_system: &mut FontSystem,
atlas: &mut TextAtlas, atlas: &mut TextAtlas,
screen_resolution: Resolution, viewport: &Viewport,
text_areas: impl IntoIterator<Item = TextArea<'a>>, text_areas: impl IntoIterator<Item = TextArea<'a>>,
cache: &mut SwashCache, cache: &mut SwashCache,
mut metadata_to_depth: impl FnMut(usize) -> f32, mut metadata_to_depth: impl FnMut(usize) -> f32,
) -> Result<(), PrepareError> { ) -> 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::<Params>(),
)
});
}
self.glyph_vertices.clear(); self.glyph_vertices.clear();
self.glyph_indices.clear(); self.glyph_indices.clear();
let mut glyphs_added = 0; let mut glyphs_added = 0;
let resolution = viewport.resolution();
for text_area in text_areas { for text_area in text_areas {
for run in text_area.buffer.layout_runs() { for run in text_area.buffer.layout_runs() {
for glyph in run.glyphs.iter() { 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_x = text_area.bounds.left.max(0);
let bounds_min_y = text_area.bounds.top.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_x = text_area.bounds.right.min(resolution.width as i32);
let bounds_max_y = text_area.bounds.bottom.min(screen_resolution.height as i32); let bounds_max_y = text_area.bounds.bottom.min(resolution.height as i32);
// Starts beyond right edge or ends beyond left edge // Starts beyond right edge or ends beyond left edge
let max_x = x + width; let max_x = x + width;
@ -386,7 +355,7 @@ impl TextRenderer {
queue: &Queue, queue: &Queue,
font_system: &mut FontSystem, font_system: &mut FontSystem,
atlas: &mut TextAtlas, atlas: &mut TextAtlas,
screen_resolution: Resolution, viewport: &Viewport,
text_areas: impl IntoIterator<Item = TextArea<'a>>, text_areas: impl IntoIterator<Item = TextArea<'a>>,
cache: &mut SwashCache, cache: &mut SwashCache,
) -> Result<(), PrepareError> { ) -> Result<(), PrepareError> {
@ -395,7 +364,7 @@ impl TextRenderer {
queue, queue,
font_system, font_system,
atlas, atlas,
screen_resolution, viewport,
text_areas, text_areas,
cache, cache,
zero_depth, zero_depth,
@ -406,6 +375,7 @@ impl TextRenderer {
pub fn render<'pass>( pub fn render<'pass>(
&'pass self, &'pass self,
atlas: &'pass TextAtlas, atlas: &'pass TextAtlas,
viewport: &'pass Viewport,
pass: &mut RenderPass<'pass>, pass: &mut RenderPass<'pass>,
) -> Result<(), RenderError> { ) -> Result<(), RenderError> {
if self.vertices_to_render == 0 { if self.vertices_to_render == 0 {
@ -414,7 +384,7 @@ impl TextRenderer {
pass.set_pipeline(&self.pipeline); pass.set_pipeline(&self.pipeline);
pass.set_bind_group(0, &atlas.bind_group, &[]); 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_vertex_buffer(0, self.vertex_buffer.slice(..));
pass.set_index_buffer(self.index_buffer.slice(..), IndexFormat::Uint32); pass.set_index_buffer(self.index_buffer.slice(..), IndexFormat::Uint32);
pass.draw_indexed(0..self.vertices_to_render, 0, 0..1); pass.draw_indexed(0..self.vertices_to_render, 0, 0..1);

57
src/viewport.rs Normal file
View file

@ -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::<Params>() as u64,
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let bind_group = cache.create_uniforms_bind_group(device, &params_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::<Params>(),
)
});
}
}
pub fn resolution(&self) -> Resolution {
self.params.screen_resolution
}
}