Add render time meter

Optimization is impossible without measurement!
This commit is contained in:
Joe Wilm 2016-05-21 11:08:50 -07:00
parent c70acbac0b
commit 855ae75697
No known key found for this signature in database
GPG Key ID: 39B57C6972F518DA
5 changed files with 173 additions and 23 deletions

View File

@ -9,13 +9,15 @@ pub fn num_cells_axis(cell_width: u32, cell_sep: i32, screen_width: u32) -> u32
#[derive(Clone)]
pub struct Cell {
pub character: Option<String>,
pub character: String,
}
impl Cell {
pub fn new(c: Option<String>) -> Cell {
pub fn new<S>(c: S) -> Cell
where S: Into<String>
{
Cell {
character: c,
character: c.into(),
}
}
}
@ -76,7 +78,7 @@ pub struct Row(Vec<Cell>);
impl Row {
pub fn new(columns: usize) -> Row {
Row(vec![Cell::new(None); columns])
Row(vec![Cell::new(""); columns])
}
pub fn cols(&self) -> usize {

View File

@ -11,6 +11,7 @@ mod list_fonts;
mod text;
mod renderer;
mod grid;
mod meter;
use renderer::{Glyph, QuadRenderer};
use text::FontDesc;
@ -25,6 +26,28 @@ static INIT_LIST: &'static str = "abcdefghijklmnopqrstuvwxyz\
01234567890\
~`!@#$%^&*()[]{}-_=+\\|\"/?.,<>;:";
type GlyphCache = HashMap<String, renderer::Glyph>;
/// Render a string in a predefined location. Used for printing render time for profiling and
/// optimization.
fn render_string(s: &str,
renderer: &QuadRenderer,
glyph_cache: &GlyphCache,
cell_width: u32,
color: &renderer::Rgb)
{
let (mut x, mut y) = (200f32, 20f32);
for c in s.chars() {
let s: String = c.escape_default().collect();
if let Some(glyph) = glyph_cache.get(&s[..]) {
renderer.render(glyph, x, y, color);
}
x += cell_width as f32 + 2f32;
}
}
fn main() {
let window = glutin::Window::new().unwrap();
let (width, height) = window.get_inner_size_pixels().unwrap();
@ -83,7 +106,7 @@ fn main() {
continue;
}
grid[row][col] = grid::Cell::new(Some(c.escape_default().collect()));
grid[row][col] = grid::Cell::new(c.escape_default().collect::<String>());
col += 1;
}
@ -102,35 +125,49 @@ fn main() {
let renderer = QuadRenderer::new(width, height);
for event in window.wait_events() {
let mut meter = meter::Meter::new();
'main_loop: loop {
for event in window.poll_events() {
match event {
glutin::Event::Closed => break 'main_loop,
_ => println!("event: {:?}", event)
}
}
unsafe {
gl::ClearColor(0.0, 0.0, 0.00, 1.0);
gl::Clear(gl::COLOR_BUFFER_BIT);
}
for i in 0..grid.rows() {
let row = &grid[i];
for j in 0..row.cols() {
let cell = &row[j];
if let Some(ref c) = cell.character {
if let Some(glyph) = glyph_cache.get(&c[..]) {
let y = (cell_height as f32 + sep_y as f32) * (i as f32);
let x = (cell_width as f32 + sep_x as f32) * (j as f32);
{
let color = renderer::Rgb { r: 0.917, g: 0.917, b: 0.917 };
let _sampler = meter.sampler();
let y_inverted = (height as f32) - y - (cell_height as f32);
for i in 0..grid.rows() {
let row = &grid[i];
for j in 0..row.cols() {
let cell = &row[j];
if !cell.character.is_empty() {
if let Some(glyph) = glyph_cache.get(&cell.character[..]) {
let y = (cell_height as f32 + sep_y as f32) * (i as f32);
let x = (cell_width as f32 + sep_x as f32) * (j as f32);
renderer.render(glyph, x, y_inverted);
let y_inverted = (height as f32) - y - (cell_height as f32);
renderer.render(glyph, x, y_inverted, &color);
}
}
}
}
}
let timing = format!("{:.3} usec", meter.average());
let color = renderer::Rgb { r: 0.835, g: 0.306, b: 0.325 };
render_string(&timing[..], &renderer, &glyph_cache, cell_width, &color);
window.swap_buffers().unwrap();
match event {
glutin::Event::Closed => break,
_ => ()
}
// ::std::thread::sleep(::std::time::Duration::from_millis(17));
}
}

104
src/meter.rs Normal file
View File

@ -0,0 +1,104 @@
//! Rendering time meter
//!
//! Used to track rendering times and provide moving averages.
//!
//! # Examples
//!
//! ```rust
//! // create a meter
//! let mut meter = Meter::new();
//!
//! // Sample something.
//! {
//! let _sampler = meter.sampler();
//! }
//!
//! // Get the moving average. The meter tracks a fixed number of samles, and the average won't mean
//! // much until it's filled up at least once.
//! printf!("Average time: {}", meter.average());
use std::time::{Instant, Duration};
const NUM_SAMPLES: usize = 60;
/// The meter
pub struct Meter {
/// Track last 60 timestamps
times: [f64; NUM_SAMPLES],
/// Average sample time in microseconds
avg: f64,
/// Index of next time to update.
index: usize,
}
/// Sampler
///
/// Samplers record how long they are "alive" for and update the meter on drop.
pub struct Sampler<'a> {
/// Reference to meter that created the sampler
meter: &'a mut Meter,
// When the sampler was created
created_at: Instant,
}
impl<'a> Sampler<'a> {
fn new(meter: &'a mut Meter) -> Sampler<'a> {
Sampler {
meter: meter,
created_at: Instant::now(),
}
}
#[inline]
fn alive_duration(&self) -> Duration {
self.created_at.elapsed()
}
}
impl<'a> Drop for Sampler<'a> {
fn drop(&mut self) {
// Work around borrowck
let duration = self.alive_duration();
self.meter.add_sample(duration);
}
}
impl Meter {
/// Create a meter
pub fn new() -> Meter {
Meter {
times: [0.0; NUM_SAMPLES],
avg: 0.0,
index: 0,
}
}
/// Get a sampler
pub fn sampler(&mut self) -> Sampler {
Sampler::new(self)
}
/// Get the current average sample duration in microseconds
pub fn average(&self) -> f64 {
self.avg
}
/// Add a sample
///
/// Used by Sampler::drop.
fn add_sample(&mut self, sample: Duration) {
let mut usec = 0f64;
usec += (sample.subsec_nanos() as f64) / 1e3;
usec += (sample.as_secs() as f64) * 1e6;
let prev = self.times[self.index];
self.times[self.index] = usec;
self.avg -= prev / NUM_SAMPLES as f64;
self.avg += usec / NUM_SAMPLES as f64;
self.index = (self.index + 1) % NUM_SAMPLES;
}
}

View File

@ -32,6 +32,13 @@ pub struct PackedVertex {
v: f32,
}
#[derive(Debug)]
pub struct Rgb {
pub r: f32,
pub g: f32,
pub b: f32,
}
impl QuadRenderer {
// TODO should probably hand this a transform instead of width/height
pub fn new(width: u32, height: u32) -> QuadRenderer {
@ -92,11 +99,11 @@ impl QuadRenderer {
}
}
pub fn render(&self, glyph: &Glyph, x: f32, y: f32) {
pub fn render(&self, glyph: &Glyph, x: f32, y: f32, color: &Rgb) {
self.program.activate();
unsafe {
// set color
gl::Uniform3f(self.program.u_color, 0.917, 0.917, 0.917);
gl::Uniform3f(self.program.u_color, color.r, color.g, color.b);
}
let rect = get_rect(glyph, x, y);

View File

@ -93,7 +93,7 @@ impl Rasterizer {
unsafe {
let ft_lib = self.library.raw();
freetype::ffi::FT_Library_SetLcdFilter(ft_lib, freetype::ffi::FT_LCD_FILTER_LIGHT);
freetype::ffi::FT_Library_SetLcdFilter(ft_lib, freetype::ffi::FT_LCD_FILTER_DEFAULT);
}
let bitmap = glyph.bitmap();