Skip to content

Commit

Permalink
add text_perf example (#348)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
brettchalupa authored Aug 5, 2024
1 parent 1d23de2 commit 8726989
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 1 deletion.
91 changes: 91 additions & 0 deletions examples/text_perf.rs
Original file line number Diff line number Diff line change
@@ -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<f32> = 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<i32>,
y_between: Uniform<i32>,
rng: ThreadRng,
}

impl GameState {
fn new(ctx: &mut Context) -> tetra::Result<GameState> {
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)
}
5 changes: 4 additions & 1 deletion src/graphics/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<RefCell<FontCache>>,
Expand Down

0 comments on commit 8726989

Please sign in to comment.