From c69090f74b99e5a33832ec912890d72ae485e391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 29 Mar 2024 03:55:20 +0100 Subject: [PATCH] Use a `StagingBelt` for regular buffer uploads `Queue::write_buffer` allocates very regularly. A `StagingBelt` gives us much more control. --- examples/hello-world.rs | 6 ++++-- src/text_render.rs | 42 ++++++++++++++++++++++++++++++++++------- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/examples/hello-world.rs b/examples/hello-world.rs index 1ec5872..42c03e5 100644 --- a/examples/hello-world.rs +++ b/examples/hello-world.rs @@ -101,6 +101,9 @@ async fn run() { window.request_redraw(); } WindowEvent::RedrawRequested => { + let mut encoder = device + .create_command_encoder(&CommandEncoderDescriptor { label: None }); + viewport.update( &queue, Resolution { @@ -113,6 +116,7 @@ async fn run() { .prepare( &device, &queue, + &mut encoder, &mut font_system, &mut atlas, &viewport, @@ -135,8 +139,6 @@ async fn run() { 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, diff --git a/src/text_render.rs b/src/text_render.rs index 23ca20f..63fc37e 100644 --- a/src/text_render.rs +++ b/src/text_render.rs @@ -2,15 +2,17 @@ use crate::{ ColorMode, FontSystem, GlyphDetails, GlyphToRender, GpuCacheStatus, PrepareError, RenderError, SwashCache, SwashContent, TextArea, TextAtlas, Viewport, }; -use std::{iter, slice, sync::Arc}; +use std::{iter, num::NonZeroU64, slice, sync::Arc}; +use wgpu::util::StagingBelt; use wgpu::{ - Buffer, BufferDescriptor, BufferUsages, DepthStencilState, Device, Extent3d, ImageCopyTexture, - ImageDataLayout, IndexFormat, MultisampleState, Origin3d, Queue, RenderPass, RenderPipeline, - TextureAspect, COPY_BUFFER_ALIGNMENT, + Buffer, BufferDescriptor, BufferUsages, CommandEncoder, DepthStencilState, Device, Extent3d, + ImageCopyTexture, ImageDataLayout, IndexFormat, MultisampleState, Origin3d, Queue, RenderPass, + RenderPipeline, TextureAspect, COPY_BUFFER_ALIGNMENT, }; /// A text renderer that uses cached glyphs to render text into an existing render pass. pub struct TextRenderer { + staging_belt: StagingBelt, vertex_buffer: Buffer, vertex_buffer_size: u64, index_buffer: Buffer, @@ -48,6 +50,7 @@ impl TextRenderer { let pipeline = atlas.get_or_create_pipeline(device, multisample, depth_stencil); Self { + staging_belt: StagingBelt::new(vertex_buffer_size), vertex_buffer, vertex_buffer_size, index_buffer, @@ -64,6 +67,7 @@ impl TextRenderer { &mut self, device: &Device, queue: &Queue, + encoder: &mut CommandEncoder, font_system: &mut FontSystem, atlas: &mut TextAtlas, viewport: &Viewport, @@ -71,10 +75,11 @@ impl TextRenderer { cache: &mut SwashCache, mut metadata_to_depth: impl FnMut(usize) -> f32, ) -> Result<(), PrepareError> { + self.staging_belt.recall(); self.glyph_vertices.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 { @@ -307,7 +312,15 @@ impl TextRenderer { }; if self.vertex_buffer_size >= vertices_raw.len() as u64 { - queue.write_buffer(&self.vertex_buffer, 0, vertices_raw); + self.staging_belt + .write_buffer( + encoder, + &self.vertex_buffer, + 0, + NonZeroU64::new(vertices_raw.len() as u64).expect("Non-empty vertices"), + device, + ) + .copy_from_slice(vertices_raw); } else { self.vertex_buffer.destroy(); @@ -320,6 +333,9 @@ impl TextRenderer { self.vertex_buffer = buffer; self.vertex_buffer_size = buffer_size; + + self.staging_belt.finish(); + self.staging_belt = StagingBelt::new(buffer_size); } let indices = self.glyph_indices.as_slice(); @@ -331,7 +347,15 @@ impl TextRenderer { }; if self.index_buffer_size >= indices_raw.len() as u64 { - queue.write_buffer(&self.index_buffer, 0, indices_raw); + self.staging_belt + .write_buffer( + encoder, + &self.index_buffer, + 0, + NonZeroU64::new(indices_raw.len() as u64).expect("Non-empty indices"), + device, + ) + .copy_from_slice(indices_raw); } else { self.index_buffer.destroy(); @@ -346,6 +370,8 @@ impl TextRenderer { self.index_buffer_size = buffer_size; } + self.staging_belt.finish(); + Ok(()) } @@ -353,6 +379,7 @@ impl TextRenderer { &mut self, device: &Device, queue: &Queue, + encoder: &mut CommandEncoder, font_system: &mut FontSystem, atlas: &mut TextAtlas, viewport: &Viewport, @@ -362,6 +389,7 @@ impl TextRenderer { self.prepare_with_depth( device, queue, + encoder, font_system, atlas, viewport,