Skip to content

Commit

Permalink
Use a StagingBelt for regular buffer uploads
Browse files Browse the repository at this point in the history
`Queue::write_buffer` allocates very regularly, specially on Metal.

See: iced-rs/iced#2357

A `StagingBelt` gives us more control and predictability.
  • Loading branch information
hecrj committed Mar 30, 2024
1 parent 3e281d1 commit 5297680
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 8 deletions.
6 changes: 4 additions & 2 deletions examples/hello-world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,14 @@ async fn run() {
window.request_redraw();
}
WindowEvent::RedrawRequested => {
let mut encoder = device
.create_command_encoder(&CommandEncoderDescriptor { label: None });

text_renderer
.prepare(
&device,
&queue,
&mut encoder,
&mut font_system,
&mut atlas,
Resolution {
Expand All @@ -124,8 +128,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,
Expand Down
40 changes: 34 additions & 6 deletions src/text_render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@ use crate::{
ColorMode, FontSystem, GlyphDetails, GlyphToRender, GpuCacheStatus, Params, PrepareError,
RenderError, Resolution, SwashCache, SwashContent, TextArea, TextAtlas,
};
use std::{iter, mem::size_of, slice, sync::Arc};
use std::{iter, mem::size_of, num::NonZeroU64, slice, sync::Arc};
use wgpu::{
BindGroupDescriptor, BindGroupEntry, Buffer, BufferDescriptor, BufferUsages, DepthStencilState,
Device, Extent3d, ImageCopyTexture, ImageDataLayout, IndexFormat, MultisampleState, Origin3d,
Queue, RenderPass, RenderPipeline, TextureAspect, COPY_BUFFER_ALIGNMENT,
util::StagingBelt, BindGroupDescriptor, BindGroupEntry, 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 {
params: Params,
params_buffer: Buffer,
staging_belt: StagingBelt,
vertex_buffer: Buffer,
vertex_buffer_size: u64,
index_buffer: Buffer,
Expand Down Expand Up @@ -77,6 +79,7 @@ impl TextRenderer {
Self {
params,
params_buffer,
staging_belt: StagingBelt::new(vertex_buffer_size),
vertex_buffer,
vertex_buffer_size,
index_buffer,
Expand All @@ -94,6 +97,7 @@ impl TextRenderer {
&mut self,
device: &Device,
queue: &Queue,
encoder: &mut CommandEncoder,
font_system: &mut FontSystem,
atlas: &mut TextAtlas,
screen_resolution: Resolution,
Expand All @@ -111,6 +115,7 @@ impl TextRenderer {
});
}

self.staging_belt.recall();
self.glyph_vertices.clear();
self.glyph_indices.clear();
let mut glyphs_added = 0;
Expand Down Expand Up @@ -345,7 +350,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();

Expand All @@ -358,6 +371,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();
Expand All @@ -369,7 +385,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();

Expand All @@ -384,13 +408,16 @@ impl TextRenderer {
self.index_buffer_size = buffer_size;
}

self.staging_belt.finish();

Ok(())
}

pub fn prepare<'a>(
&mut self,
device: &Device,
queue: &Queue,
encoder: &mut CommandEncoder,
font_system: &mut FontSystem,
atlas: &mut TextAtlas,
screen_resolution: Resolution,
Expand All @@ -400,6 +427,7 @@ impl TextRenderer {
self.prepare_with_depth(
device,
queue,
encoder,
font_system,
atlas,
screen_resolution,
Expand Down

0 comments on commit 5297680

Please sign in to comment.