From 2bb53ad6e7ea2689f2f56662e5840a8d363b3108 Mon Sep 17 00:00:00 2001
From: Héctor Ramón Jiménez <hector@hecrj.dev>
Date: Fri, 29 Mar 2024 04:02:24 +0100
Subject: Use a `StagingBelt` in `iced_wgpu` for regular buffer uploads

---
 Cargo.toml                    |  2 +-
 wgpu/src/backend.rs           | 26 +++++++++++++++++++++-----
 wgpu/src/buffer.rs            | 14 ++++++++++++--
 wgpu/src/image.rs             | 36 ++++++++++++++++++++++++------------
 wgpu/src/quad.rs              | 28 ++++++++++++++++++++--------
 wgpu/src/quad/gradient.rs     |  5 +++--
 wgpu/src/quad/solid.rs        |  5 +++--
 wgpu/src/text.rs              |  2 ++
 wgpu/src/triangle.rs          | 35 ++++++++++++++++++++++++++---------
 wgpu/src/window/compositor.rs |  1 +
 10 files changed, 113 insertions(+), 41 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index e2639944..74ff3b84 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -129,7 +129,7 @@ cosmic-text = "0.10"
 dark-light = "1.0"
 futures = "0.3"
 glam = "0.25"
-glyphon = "0.5"
+glyphon = { git = "https://github.com/hecrj/glyphon.git", branch = "use-staging-belt" }
 guillotiere = "0.6"
 half = "2.2"
 image = "0.24"
diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs
index 5019191c..129e9bca 100644
--- a/wgpu/src/backend.rs
+++ b/wgpu/src/backend.rs
@@ -30,6 +30,7 @@ pub struct Backend {
     pipeline_storage: pipeline::Storage,
     #[cfg(any(feature = "image", feature = "svg"))]
     image_pipeline: image::Pipeline,
+    staging_belt: wgpu::util::StagingBelt,
 }
 
 impl Backend {
@@ -61,6 +62,11 @@ impl Backend {
 
             #[cfg(any(feature = "image", feature = "svg"))]
             image_pipeline,
+
+            // TODO: Resize belt smartly (?)
+            // It would be great if the `StagingBelt` API exposed methods
+            // for introspection to detect when a resize may be worth it.
+            staging_belt: wgpu::util::StagingBelt::new(1024 * 100),
         }
     }
 
@@ -105,6 +111,8 @@ impl Backend {
             &layers,
         );
 
+        self.staging_belt.finish();
+
         self.render(
             device,
             encoder,
@@ -123,12 +131,17 @@ impl Backend {
         self.image_pipeline.end_frame();
     }
 
+    ///
+    pub fn recall(&mut self) {
+        self.staging_belt.recall();
+    }
+
     fn prepare(
         &mut self,
         device: &wgpu::Device,
         queue: &wgpu::Queue,
         format: wgpu::TextureFormat,
-        _encoder: &mut wgpu::CommandEncoder,
+        encoder: &mut wgpu::CommandEncoder,
         scale_factor: f32,
         target_size: Size<u32>,
         transformation: Transformation,
@@ -144,7 +157,8 @@ impl Backend {
             if !layer.quads.is_empty() {
                 self.quad_pipeline.prepare(
                     device,
-                    queue,
+                    encoder,
+                    &mut self.staging_belt,
                     &layer.quads,
                     transformation,
                     scale_factor,
@@ -157,7 +171,8 @@ impl Backend {
 
                 self.triangle_pipeline.prepare(
                     device,
-                    queue,
+                    encoder,
+                    &mut self.staging_belt,
                     &layer.meshes,
                     scaled,
                 );
@@ -171,8 +186,8 @@ impl Backend {
 
                     self.image_pipeline.prepare(
                         device,
-                        queue,
-                        _encoder,
+                        encoder,
+                        &mut self.staging_belt,
                         &layer.images,
                         scaled,
                         scale_factor,
@@ -184,6 +199,7 @@ impl Backend {
                 self.text_pipeline.prepare(
                     device,
                     queue,
+                    encoder,
                     &layer.text,
                     layer.bounds,
                     scale_factor,
diff --git a/wgpu/src/buffer.rs b/wgpu/src/buffer.rs
index ef00c58f..f8828d46 100644
--- a/wgpu/src/buffer.rs
+++ b/wgpu/src/buffer.rs
@@ -61,12 +61,22 @@ impl<T: bytemuck::Pod> Buffer<T> {
     /// Returns the size of the written bytes.
     pub fn write(
         &mut self,
-        queue: &wgpu::Queue,
+        device: &wgpu::Device,
+        encoder: &mut wgpu::CommandEncoder,
+        belt: &mut wgpu::util::StagingBelt,
         offset: usize,
         contents: &[T],
     ) -> usize {
         let bytes: &[u8] = bytemuck::cast_slice(contents);
-        queue.write_buffer(&self.raw, offset as u64, bytes);
+
+        belt.write_buffer(
+            encoder,
+            &self.raw,
+            offset as u64,
+            (bytes.len() as u64).try_into().expect("Non-empty write"),
+            device,
+        )
+        .copy_from_slice(bytes);
 
         self.offsets.push(offset as u64);
 
diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs
index 067b77ab..d0bf1182 100644
--- a/wgpu/src/image.rs
+++ b/wgpu/src/image.rs
@@ -83,21 +83,31 @@ impl Layer {
     fn prepare(
         &mut self,
         device: &wgpu::Device,
-        queue: &wgpu::Queue,
+        encoder: &mut wgpu::CommandEncoder,
+        belt: &mut wgpu::util::StagingBelt,
         nearest_instances: &[Instance],
         linear_instances: &[Instance],
         transformation: Transformation,
     ) {
-        queue.write_buffer(
+        let uniforms = Uniforms {
+            transform: transformation.into(),
+        };
+
+        let bytes = bytemuck::bytes_of(&uniforms);
+
+        belt.write_buffer(
+            encoder,
             &self.uniforms,
             0,
-            bytemuck::bytes_of(&Uniforms {
-                transform: transformation.into(),
-            }),
-        );
+            (bytes.len() as u64).try_into().expect("Sized uniforms"),
+            device,
+        )
+        .copy_from_slice(bytes);
+
+        self.nearest
+            .upload(device, encoder, belt, nearest_instances);
 
-        self.nearest.upload(device, queue, nearest_instances);
-        self.linear.upload(device, queue, linear_instances);
+        self.linear.upload(device, encoder, belt, linear_instances);
     }
 
     fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
@@ -158,7 +168,8 @@ impl Data {
     fn upload(
         &mut self,
         device: &wgpu::Device,
-        queue: &wgpu::Queue,
+        encoder: &mut wgpu::CommandEncoder,
+        belt: &mut wgpu::util::StagingBelt,
         instances: &[Instance],
     ) {
         self.instance_count = instances.len();
@@ -168,7 +179,7 @@ impl Data {
         }
 
         let _ = self.instances.resize(device, instances.len());
-        let _ = self.instances.write(queue, 0, instances);
+        let _ = self.instances.write(device, encoder, belt, 0, instances);
     }
 
     fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
@@ -383,8 +394,8 @@ impl Pipeline {
     pub fn prepare(
         &mut self,
         device: &wgpu::Device,
-        queue: &wgpu::Queue,
         encoder: &mut wgpu::CommandEncoder,
+        belt: &mut wgpu::util::StagingBelt,
         images: &[layer::Image],
         transformation: Transformation,
         _scale: f32,
@@ -501,7 +512,8 @@ impl Pipeline {
 
         layer.prepare(
             device,
-            queue,
+            encoder,
+            belt,
             nearest_instances,
             linear_instances,
             transformation,
diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs
index b932f54f..0717a031 100644
--- a/wgpu/src/quad.rs
+++ b/wgpu/src/quad.rs
@@ -57,7 +57,8 @@ impl Pipeline {
     pub fn prepare(
         &mut self,
         device: &wgpu::Device,
-        queue: &wgpu::Queue,
+        encoder: &mut wgpu::CommandEncoder,
+        belt: &mut wgpu::util::StagingBelt,
         quads: &Batch,
         transformation: Transformation,
         scale: f32,
@@ -67,7 +68,7 @@ impl Pipeline {
         }
 
         let layer = &mut self.layers[self.prepare_layer];
-        layer.prepare(device, queue, quads, transformation, scale);
+        layer.prepare(device, encoder, belt, quads, transformation, scale);
 
         self.prepare_layer += 1;
     }
@@ -162,7 +163,8 @@ impl Layer {
     pub fn prepare(
         &mut self,
         device: &wgpu::Device,
-        queue: &wgpu::Queue,
+        encoder: &mut wgpu::CommandEncoder,
+        belt: &mut wgpu::util::StagingBelt,
         quads: &Batch,
         transformation: Transformation,
         scale: f32,
@@ -171,15 +173,25 @@ impl Layer {
         let _ = info_span!("Wgpu::Quad", "PREPARE").entered();
 
         let uniforms = Uniforms::new(transformation, scale);
+        let bytes = bytemuck::bytes_of(&uniforms);
 
-        queue.write_buffer(
+        belt.write_buffer(
+            encoder,
             &self.constants_buffer,
             0,
-            bytemuck::bytes_of(&uniforms),
-        );
+            (bytes.len() as u64).try_into().expect("Sized uniforms"),
+            device,
+        )
+        .copy_from_slice(bytes);
 
-        self.solid.prepare(device, queue, &quads.solids);
-        self.gradient.prepare(device, queue, &quads.gradients);
+        if !quads.solids.is_empty() {
+            self.solid.prepare(device, encoder, belt, &quads.solids);
+        }
+
+        if !quads.gradients.is_empty() {
+            self.gradient
+                .prepare(device, encoder, belt, &quads.gradients);
+        }
     }
 }
 
diff --git a/wgpu/src/quad/gradient.rs b/wgpu/src/quad/gradient.rs
index 560fcad2..5b32c52a 100644
--- a/wgpu/src/quad/gradient.rs
+++ b/wgpu/src/quad/gradient.rs
@@ -46,11 +46,12 @@ impl Layer {
     pub fn prepare(
         &mut self,
         device: &wgpu::Device,
-        queue: &wgpu::Queue,
+        encoder: &mut wgpu::CommandEncoder,
+        belt: &mut wgpu::util::StagingBelt,
         instances: &[Gradient],
     ) {
         let _ = self.instances.resize(device, instances.len());
-        let _ = self.instances.write(queue, 0, instances);
+        let _ = self.instances.write(device, encoder, belt, 0, instances);
 
         self.instance_count = instances.len();
     }
diff --git a/wgpu/src/quad/solid.rs b/wgpu/src/quad/solid.rs
index 771eee34..1cead367 100644
--- a/wgpu/src/quad/solid.rs
+++ b/wgpu/src/quad/solid.rs
@@ -40,11 +40,12 @@ impl Layer {
     pub fn prepare(
         &mut self,
         device: &wgpu::Device,
-        queue: &wgpu::Queue,
+        encoder: &mut wgpu::CommandEncoder,
+        belt: &mut wgpu::util::StagingBelt,
         instances: &[Solid],
     ) {
         let _ = self.instances.resize(device, instances.len());
-        let _ = self.instances.write(queue, 0, instances);
+        let _ = self.instances.write(device, encoder, belt, 0, instances);
 
         self.instance_count = instances.len();
     }
diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs
index 6fa1922d..97ff77f5 100644
--- a/wgpu/src/text.rs
+++ b/wgpu/src/text.rs
@@ -53,6 +53,7 @@ impl Pipeline {
         &mut self,
         device: &wgpu::Device,
         queue: &wgpu::Queue,
+        encoder: &mut wgpu::CommandEncoder,
         sections: &[Text<'_>],
         layer_bounds: Rectangle,
         scale_factor: f32,
@@ -262,6 +263,7 @@ impl Pipeline {
         let result = renderer.prepare(
             device,
             queue,
+            encoder,
             font_system,
             &mut self.atlas,
             glyphon::Resolution {
diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs
index 2bb6f307..b6be54d4 100644
--- a/wgpu/src/triangle.rs
+++ b/wgpu/src/triangle.rs
@@ -48,7 +48,8 @@ impl Layer {
     fn prepare(
         &mut self,
         device: &wgpu::Device,
-        queue: &wgpu::Queue,
+        encoder: &mut wgpu::CommandEncoder,
+        belt: &mut wgpu::util::StagingBelt,
         solid: &solid::Pipeline,
         gradient: &gradient::Pipeline,
         meshes: &[Mesh<'_>],
@@ -103,33 +104,47 @@ impl Layer {
             let uniforms =
                 Uniforms::new(transformation * mesh.transformation());
 
-            index_offset +=
-                self.index_buffer.write(queue, index_offset, indices);
+            index_offset += self.index_buffer.write(
+                device,
+                encoder,
+                belt,
+                index_offset,
+                indices,
+            );
+
             self.index_strides.push(indices.len() as u32);
 
             match mesh {
                 Mesh::Solid { buffers, .. } => {
                     solid_vertex_offset += self.solid.vertices.write(
-                        queue,
+                        device,
+                        encoder,
+                        belt,
                         solid_vertex_offset,
                         &buffers.vertices,
                     );
 
                     solid_uniform_offset += self.solid.uniforms.write(
-                        queue,
+                        device,
+                        encoder,
+                        belt,
                         solid_uniform_offset,
                         &[uniforms],
                     );
                 }
                 Mesh::Gradient { buffers, .. } => {
                     gradient_vertex_offset += self.gradient.vertices.write(
-                        queue,
+                        device,
+                        encoder,
+                        belt,
                         gradient_vertex_offset,
                         &buffers.vertices,
                     );
 
                     gradient_uniform_offset += self.gradient.uniforms.write(
-                        queue,
+                        device,
+                        encoder,
+                        belt,
                         gradient_uniform_offset,
                         &[uniforms],
                     );
@@ -237,7 +252,8 @@ impl Pipeline {
     pub fn prepare(
         &mut self,
         device: &wgpu::Device,
-        queue: &wgpu::Queue,
+        encoder: &mut wgpu::CommandEncoder,
+        belt: &mut wgpu::util::StagingBelt,
         meshes: &[Mesh<'_>],
         transformation: Transformation,
     ) {
@@ -252,7 +268,8 @@ impl Pipeline {
         let layer = &mut self.layers[self.prepare_layer];
         layer.prepare(
             device,
-            queue,
+            encoder,
+            belt,
             &self.solid,
             &self.gradient,
             meshes,
diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs
index 9a3e3b34..482d705b 100644
--- a/wgpu/src/window/compositor.rs
+++ b/wgpu/src/window/compositor.rs
@@ -243,6 +243,7 @@ pub fn present<T: AsRef<str>>(
 
             // Submit work
             let _submission = compositor.queue.submit(Some(encoder.finish()));
+            backend.recall();
             frame.present();
 
             Ok(())
-- 
cgit