From 872698936b8b89d444f3da521e9bc01dafd6cc8f Mon Sep 17 00:00:00 2001 From: Brett Chalupa Date: Mon, 5 Aug 2024 18:00:01 -0400 Subject: [PATCH] add text_perf example (#348) the docs have been refined just a little bit, but this adds an example on how to render a lot of text and be able to benchmark the performance --- examples/text_perf.rs | 91 +++++++++++++++++++++++++++++++++++++++++++ src/graphics/text.rs | 5 ++- 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 examples/text_perf.rs diff --git a/examples/text_perf.rs b/examples/text_perf.rs new file mode 100644 index 00000000..fd443f85 --- /dev/null +++ b/examples/text_perf.rs @@ -0,0 +1,91 @@ +// This example demonstrates how you might go about reusing a `Font` instance for more performant +// text rendering. `Font` instances are expensive to create but cheap to `.clone()`. +// +// In your game, you could have a handful of different `Font` instances for different size text or +// different typefaces. You'd then draw your text accordingly based on which font is appropriate, +// `.clone`-ing those instances. +// +// Press SPACE to create more `Text` instances. + +use rand::distributions::{Distribution, Uniform}; +use rand::rngs::ThreadRng; +use tetra::graphics::text::{Font, Text}; +use tetra::graphics::{self, Color}; +use tetra::math::Vec2; +use tetra::{Context, ContextBuilder, State}; + +const TEXT_OFFSET: Vec2 = Vec2::new(16.0, 16.0); +const WIDTH: i32 = 1280; +const HEIGHT: i32 = 720; + +struct GameState { + texts: Vec<(Text, f32, f32)>, + font: Font, + x_between: Uniform, + y_between: Uniform, + rng: ThreadRng, +} + +impl GameState { + fn new(ctx: &mut Context) -> tetra::Result { + let mut state = GameState { + texts: vec![], + font: Font::vector(ctx, "./examples/resources/DejaVuSansMono.ttf", 16.0)?, + rng: rand::thread_rng(), + x_between: Uniform::from(0..WIDTH), + y_between: Uniform::from(0..HEIGHT), + }; + + state.add_texts(); + + Ok(state) + } + + fn add_texts(&mut self) { + for i in 1..=200 { + self.texts.push(( + // NOTE: rather than making a new font, we clone the already existing one. This is + // much more performant. + Text::new(format!("text {}", i), self.font.clone()), + self.x_between.sample(&mut self.rng) as f32, + self.y_between.sample(&mut self.rng) as f32, + )); + } + } +} + +impl State for GameState { + fn update(&mut self, ctx: &mut Context) -> tetra::Result { + tetra::window::set_title( + ctx, + &format!( + "Text Perf ({} texts, {:.0} FPS)", + self.texts.len(), + tetra::time::get_fps(ctx) + ), + ); + + if tetra::input::is_key_pressed(ctx, tetra::input::Key::Space) { + self.add_texts(); + } + + Ok(()) + } + + fn draw(&mut self, ctx: &mut Context) -> tetra::Result { + graphics::clear(ctx, Color::rgb(0.392, 0.584, 0.929)); + + for (text, x, y) in &mut self.texts { + text.draw(ctx, TEXT_OFFSET + Vec2::new(*x, *y)); + } + + Ok(()) + } +} + +fn main() -> tetra::Result { + ContextBuilder::new("Text Perf", WIDTH, HEIGHT) + .quit_on_escape(true) + .build()? + .run(GameState::new) +} diff --git a/src/graphics/text.rs b/src/graphics/text.rs index 9a24a7ac..83a15888 100644 --- a/src/graphics/text.rs +++ b/src/graphics/text.rs @@ -43,7 +43,7 @@ pub enum FontTextureStyle { /// /// Loading a font is quite an expensive operation, as it involves parsing the font itself and /// creating a cache on the GPU for the rendered characters. Try to reuse fonts, rather than -/// recreating them every frame. +/// creating multiple instances or recreating them every frame. /// /// You can clone a font cheaply, as it is [reference-counted](https://doc.rust-lang.org/std/rc/struct.Rc.html) /// internally. However, this does mean that modifying a font (e.g. setting the @@ -53,6 +53,9 @@ pub enum FontTextureStyle { /// /// The [`text`](https://github.com/17cupsofcoffee/tetra/blob/main/examples/text.rs) /// example demonstrates how to load a font and then draw some text. +/// +/// The [`text_perf`](https://github.com/17cupsofcoffee/tetra/blob/main/examples/text_perf.rs) +/// example demonstrates how to reuse a font for better performance. #[derive(Clone)] pub struct Font { data: Rc>,