Refactor renderer functions out of main.rs

This moves the rendering logic to draw the grid, to draw strings, and to
draw the cursor into the renderere module. In addition to being an
organizational improvement, this also allowed for some optimizations
managing OpenGL state. Render times for a moderate screen of text
dropped from ~10ms to ~4ms.
This commit is contained in:
Joe Wilm 2016-06-02 20:27:07 -07:00
parent a8024f64c7
commit 2f98871b02
No known key found for this signature in database
GPG Key ID: 39B57C6972F518DA
3 changed files with 127 additions and 65 deletions

View File

@ -55,23 +55,12 @@ static INIT_LIST: &'static str = "abcdefghijklmnopqrstuvwxyz\
type GlyphCache = HashMap<char, renderer::Glyph>; type GlyphCache = HashMap<char, renderer::Glyph>;
/// Render a string in a predefined location. Used for printing render time for profiling and struct TermProps {
/// optimization. cell_width: f32,
fn render_string(s: &str, sep_x: f32,
renderer: &QuadRenderer, cell_height: f32,
glyph_cache: &GlyphCache, sep_y: f32,
cell_width: u32, height: f32,
color: &Rgb)
{
let (mut x, mut y) = (200f32, 20f32);
for c in s.chars() {
if let Some(glyph) = glyph_cache.get(&c) {
renderer.render(glyph, x, y, color);
}
x += cell_width as f32 + 2f32;
}
} }
fn main() { fn main() {
@ -135,7 +124,7 @@ fn main() {
} }
}); });
let renderer = QuadRenderer::new(width, height); let mut renderer = QuadRenderer::new(width, height);
let mut terminal = Term::new(tty, grid); let mut terminal = Term::new(tty, grid);
let mut meter = Meter::new(); let mut meter = Meter::new();
@ -191,43 +180,27 @@ fn main() {
{ {
let _sampler = meter.sampler(); let _sampler = meter.sampler();
let props = TermProps {
cell_width: cell_width as f32,
sep_x: sep_x as f32,
cell_height: cell_height as f32,
sep_y: sep_y as f32,
height: height as f32,
};
// Draw the grid // Draw the grid
let grid = terminal.grid(); renderer.render_grid(terminal.grid(), &glyph_cache, &props);
for i in 0..grid.rows() {
let row = &grid[i];
for j in 0..row.cols() {
let cell = &row[j];
if cell.c != ' ' {
if let Some(glyph) = glyph_cache.get(&cell.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 y_inverted = (height as f32) - y - (cell_height as f32);
renderer.render(glyph, x, y_inverted, &cell.fg);
}
}
}
}
// Also draw the cursor // Also draw the cursor
if let Some(glyph) = glyph_cache.get(&term::CURSOR_SHAPE) { renderer.render_cursor(terminal.cursor(), &glyph_cache, &props);
let y = (cell_height as f32 + sep_y as f32) * (terminal.cursor_y() as f32);
let x = (cell_width as f32 + sep_x as f32) * (terminal.cursor_x() as f32);
let y_inverted = (height as f32) - y - (cell_height as f32);
renderer.render(glyph, x, y_inverted, &term::DEFAULT_FG);
}
} }
// Draw render timer
let timing = format!("{:.3} usec", meter.average()); let timing = format!("{:.3} usec", meter.average());
let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 }; let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
render_string(&timing[..], &renderer, &glyph_cache, cell_width, &color); renderer.render_string(&timing[..], &glyph_cache, cell_width, &color);
window.swap_buffers().unwrap(); window.swap_buffers().unwrap();
// ::std::thread::sleep(::std::time::Duration::from_millis(17));
} }
} }

View File

@ -2,16 +2,16 @@ use std::ffi::CString;
use std::mem::size_of; use std::mem::size_of;
use std::ptr; use std::ptr;
use gl; use cgmath::{self, Matrix};
use cgmath;
use euclid::{Rect, Size2D, Point2D}; use euclid::{Rect, Size2D, Point2D};
use gl::types::*; use gl::types::*;
use gl;
use cgmath::Matrix;
use text::RasterizedGlyph; use text::RasterizedGlyph;
use grid::Grid;
use term;
use super::{Rgb, TermProps, GlyphCache};
static TEXT_SHADER_V: &'static str = include_str!("../../res/text.v.glsl"); static TEXT_SHADER_V: &'static str = include_str!("../../res/text.v.glsl");
static TEXT_SHADER_F: &'static str = include_str!("../../res/text.f.glsl"); static TEXT_SHADER_F: &'static str = include_str!("../../res/text.f.glsl");
@ -21,6 +21,7 @@ pub struct QuadRenderer {
vao: GLuint, vao: GLuint,
vbo: GLuint, vbo: GLuint,
ebo: GLuint, ebo: GLuint,
active_color: Rgb,
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -32,7 +33,6 @@ pub struct PackedVertex {
v: f32, v: f32,
} }
use super::Rgb;
impl QuadRenderer { impl QuadRenderer {
// TODO should probably hand this a transform instead of width/height // TODO should probably hand this a transform instead of width/height
@ -91,14 +91,101 @@ impl QuadRenderer {
vao: vao, vao: vao,
vbo: vbo, vbo: vbo,
ebo: ebo, ebo: ebo,
active_color: Rgb { r: 0, g: 0, b: 0 },
} }
} }
pub fn render(&self, glyph: &Glyph, x: f32, y: f32, color: &Rgb) { /// Render a string in a predefined location. Used for printing render time for profiling and
self.program.activate(); /// optimization.
pub fn render_string(&mut self,
s: &str,
glyph_cache: &GlyphCache,
cell_width: u32,
color: &Rgb)
{
self.prepare_render();
let (mut x, mut y) = (200f32, 20f32);
for c in s.chars() {
if let Some(glyph) = glyph_cache.get(&c) {
self.render(glyph, x, y, color);
}
x += cell_width as f32 + 2f32;
}
self.finish_render();
}
pub fn render_cursor(&mut self,
cursor: term::Cursor,
glyph_cache: &GlyphCache,
props: &TermProps)
{
self.prepare_render();
if let Some(glyph) = glyph_cache.get(&term::CURSOR_SHAPE) {
let y = (props.cell_height + props.sep_y) * (cursor.y as f32);
let x = (props.cell_width + props.sep_x) * (cursor.x as f32);
let y_inverted = props.height - y - props.cell_height;
self.render(glyph, x, y_inverted, &term::DEFAULT_FG);
}
self.finish_render();
}
pub fn render_grid(&mut self, grid: &Grid, glyph_cache: &GlyphCache, props: &TermProps) {
self.prepare_render();
for i in 0..grid.rows() {
let row = &grid[i];
for j in 0..row.cols() {
let cell = &row[j];
if cell.c != ' ' {
if let Some(glyph) = glyph_cache.get(&cell.c) {
let y = (props.cell_height + props.sep_y) * (i as f32);
let x = (props.cell_width + props.sep_x) * (j as f32);
let y_inverted = (props.height) - y - (props.cell_height);
self.render(glyph, x, y_inverted, &cell.fg);
}
}
}
}
self.finish_render();
}
fn prepare_render(&self) {
unsafe { unsafe {
// set color self.program.activate();
gl::Uniform3i(self.program.u_color, color.r as i32, color.g as i32, color.b as i32);
gl::BindVertexArray(self.vao);
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
}
}
fn finish_render(&self) {
unsafe {
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
gl::BindVertexArray(0);
self.program.deactivate();
}
}
fn render(&mut self, glyph: &Glyph, x: f32, y: f32, color: &Rgb) {
if &self.active_color != color {
unsafe {
gl::Uniform3i(self.program.u_color,
color.r as i32,
color.g as i32,
color.b as i32);
}
self.active_color = color.to_owned();
} }
let rect = get_rect(glyph, x, y); let rect = get_rect(glyph, x, y);
@ -113,24 +200,16 @@ impl QuadRenderer {
unsafe { unsafe {
bind_mask_texture(glyph.tex_id); bind_mask_texture(glyph.tex_id);
gl::BindVertexArray(self.vao);
gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo);
gl::BufferSubData( gl::BufferSubData(
gl::ARRAY_BUFFER, gl::ARRAY_BUFFER,
0, 0,
(packed.len() * size_of::<PackedVertex>()) as isize, (packed.len() * size_of::<PackedVertex>()) as isize,
packed.as_ptr() as *const _ packed.as_ptr() as *const _
); );
gl::BindBuffer(gl::ARRAY_BUFFER, 0);
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, ptr::null()); gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, ptr::null());
gl::BindVertexArray(0);
gl::BindTexture(gl::TEXTURE_2D, 0); gl::BindTexture(gl::TEXTURE_2D, 0);
} }
self.program.deactivate();
} }
} }
@ -195,6 +274,11 @@ impl ShaderProgram {
assert!(color != gl::INVALID_VALUE as i32); assert!(color != gl::INVALID_VALUE as i32);
assert!(color != gl::INVALID_OPERATION as i32); assert!(color != gl::INVALID_OPERATION as i32);
// Initialize to known color (black)
unsafe {
gl::Uniform3i(color, 0, 0, 0);
}
let shader = ShaderProgram { let shader = ShaderProgram {
id: program, id: program,
u_projection: projection, u_projection: projection,

View File

@ -142,6 +142,11 @@ impl Term {
self.cursor.y self.cursor.y
} }
#[inline]
pub fn cursor(&self) -> Cursor {
self.cursor
}
/// Set character in current cursor position /// Set character in current cursor position
fn set_char(&mut self, c: char) { fn set_char(&mut self, c: char) {
let cell = &mut self.grid[self.cursor]; let cell = &mut self.grid[self.cursor];