diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a412ef..1cc87ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,15 +14,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Transformation::rotate`. Creates a transformation representing a rotation. [#28] - `Batch::clear`. Clears the batch contents, useful to reuse batches in different frames. +- Implementation of `Extend` for `Batch`. [#37] +- Implementation of `ParallelExtend` for `Batch`. A `Batch` can now be populated + using multiple threads, useful to improve performance when dealing with many + thousands of quads. [#37] - Multiple gravity centers based on mouse clicks in the particles example. [#30] ### Changed -- The performance of the particles example has been improved on Windows. +- The performance of the particles example has been improved considerably on all + platforms. [#37] [#25]: https://github.com/hecrj/coffee/pull/25 [#26]: https://github.com/hecrj/coffee/pull/26 [#28]: https://github.com/hecrj/coffee/pull/28 [#30]: https://github.com/hecrj/coffee/pull/30 +[#37]: https://github.com/hecrj/coffee/pull/37 ## [0.2.0] - 2019-04-28 diff --git a/Cargo.toml b/Cargo.toml index 046476a..92732ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ debug = [] [dependencies] image = "0.21" nalgebra = "0.18" +rayon = "1.0" # gfx (OpenGL) gfx = { version = "0.18", optional = true } @@ -47,4 +48,3 @@ wgpu_glyph = { version = "0.3", optional = true } [dev-dependencies] # Example dependencies rand = "0.6" -rayon = "1.0" diff --git a/examples/particles.rs b/examples/particles.rs index ec0790f..3efb8ae 100644 --- a/examples/particles.rs +++ b/examples/particles.rs @@ -28,8 +28,8 @@ struct Particles { } impl Particles { - // Try increasing this value! I (@hecrj) can render 350k particles at 100fps - // on my system. I have not tried going above that, yet... + // Try increasing this value! I (@hecrj) can render 1 MILLION particles at + // 90 fps on my system (4790k, GTX 980, Windows 7) using Vulkan. const AMOUNT: u32 = 50_000; // Play with these values to alter the way gravity works. @@ -148,20 +148,19 @@ impl Game for Particles { fn draw(&self, view: &mut View, frame: &mut Frame, timer: &Timer) { frame.clear(Color::BLACK); - // Draw particles all at once! - view.batch.clear(); - + // When interpolating, we need to know how close the next tick is let delta_factor = if view.interpolate { timer.next_tick_proximity() } else { 0.0 }; - for particle in &self.particles { + // Generate sprites in parallel! <3 rayon + let sprites = self.particles.par_iter().map(|particle| { let velocity = particle.velocity + particle.acceleration * delta_factor; - view.batch.add(Sprite { + Sprite { source: Rectangle { x: View::particle_color(velocity), y: 0, @@ -169,9 +168,16 @@ impl Game for Particles { height: 1, }, position: particle.position + velocity * delta_factor, - }); - } + } + }); + + // Clear batch contents from previous frame + view.batch.clear(); + // Use the parallel iterator to populate the batch efficiently + view.batch.par_extend(sprites); + + // Draw particles all at once! view.batch .draw(Point::new(0.0, 0.0), &mut frame.as_target()); diff --git a/src/graphics/batch.rs b/src/graphics/batch.rs index 66ae550..02f1f94 100644 --- a/src/graphics/batch.rs +++ b/src/graphics/batch.rs @@ -1,3 +1,5 @@ +use rayon::prelude::*; + use crate::graphics::gpu; use crate::graphics::{Image, IntoQuad, Point, Target, Transformation, Vector}; @@ -66,3 +68,44 @@ impl std::fmt::Debug for Batch { write!(f, "Batch {{ image: {:?} }}", self.image,) } } + +impl Extend for Batch { + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + let iter = iter.into_iter(); + let x_unit = self.x_unit; + let y_unit = self.y_unit; + + self.instances.extend( + iter.map(|quad| { + gpu::Instance::from(quad.into_quad(x_unit, y_unit)) + }), + ); + } +} + +/// Extend the [`Batch`] using a parallel iterator from [`rayon`]. +/// +/// If you are dealing with many thousands of quads, `par_extend` can help you +/// speed up your drawing by using multiple threads to populate a [`Batch`]. +/// +/// [`Batch`]: struct.Batch.html +/// [`rayon`]: https://docs.rs/rayon/1.0/rayon/ +impl ParallelExtend for Batch { + fn par_extend(&mut self, par_iter: I) + where + I: IntoParallelIterator, + { + let par_iter = par_iter.into_par_iter(); + let x_unit = self.x_unit; + let y_unit = self.y_unit; + + self.instances.par_extend( + par_iter.map(|quad| { + gpu::Instance::from(quad.into_quad(x_unit, y_unit)) + }), + ); + } +}