Optimize Rendering with batched draw calls
Draw calls are now batched for performance. Render times on git log at the default size are now ~200usec.
This commit is contained in:
parent
4fdd5280f1
commit
1f3f9add49
|
@ -2,6 +2,7 @@
|
||||||
name = "alacritty"
|
name = "alacritty"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"cgmath 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"cgmath 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"euclid 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"euclid 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"freetype-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"freetype-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -17,6 +18,15 @@ name = "android_glue"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.3.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"nodrop 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"odds 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
|
@ -418,6 +428,14 @@ dependencies = [
|
||||||
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nodrop"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"odds 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "notify"
|
name = "notify"
|
||||||
version = "2.5.5"
|
version = "2.5.5"
|
||||||
|
@ -512,6 +530,11 @@ dependencies = [
|
||||||
"malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "odds"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "osmesa-sys"
|
name = "osmesa-sys"
|
||||||
version = "0.0.5"
|
version = "0.0.5"
|
||||||
|
|
|
@ -13,6 +13,7 @@ libc = "*"
|
||||||
cgmath = "0.7"
|
cgmath = "0.7"
|
||||||
euclid = "0.6"
|
euclid = "0.6"
|
||||||
notify = { git = "https://github.com/jwilm/rsnotify", branch = "add-ignore-op" }
|
notify = { git = "https://github.com/jwilm/rsnotify", branch = "add-ignore-op" }
|
||||||
|
arrayvec = "0.3"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gl_generator = "0.5"
|
gl_generator = "0.5"
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
#version 330 core
|
#version 330 core
|
||||||
in vec2 TexCoords;
|
in vec2 TexCoords;
|
||||||
|
flat in int InstanceId;
|
||||||
|
|
||||||
layout(location = 0, index = 0) out vec4 color;
|
layout(location = 0, index = 0) out vec4 color;
|
||||||
layout(location = 0, index = 1) out vec4 alphaMask;
|
layout(location = 0, index = 1) out vec4 alphaMask;
|
||||||
|
|
||||||
uniform sampler2D mask;
|
uniform sampler2D mask;
|
||||||
uniform ivec3 textColor;
|
uniform ivec3 textColor[32];
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
|
int i = InstanceId;
|
||||||
alphaMask = vec4(texture(mask, TexCoords).rgb, 1.0);
|
alphaMask = vec4(texture(mask, TexCoords).rgb, 1.0);
|
||||||
vec3 textColorF = vec3(textColor) / vec3(255.0, 255.0, 255.0);
|
vec3 textColorF = vec3(textColor[i]) / vec3(255.0, 255.0, 255.0);
|
||||||
color = vec4(textColorF, 1.0);
|
color = vec4(textColorF, 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
layout (location = 0) in vec2 position;
|
layout (location = 0) in vec2 position;
|
||||||
|
|
||||||
out vec2 TexCoords;
|
out vec2 TexCoords;
|
||||||
|
flat out int InstanceId;
|
||||||
|
|
||||||
// Terminal properties
|
// Terminal properties
|
||||||
uniform vec2 termDim;
|
uniform vec2 termDim;
|
||||||
|
@ -9,33 +10,37 @@ uniform vec2 cellDim;
|
||||||
uniform vec2 cellSep;
|
uniform vec2 cellSep;
|
||||||
|
|
||||||
// Cell properties
|
// Cell properties
|
||||||
uniform vec2 gridCoords;
|
uniform vec2 gridCoords[32];
|
||||||
|
|
||||||
// glyph properties
|
// glyph properties
|
||||||
uniform vec2 glyphScale;
|
uniform vec2 glyphScale[32];
|
||||||
uniform vec2 glyphOffset;
|
uniform vec2 glyphOffset[32];
|
||||||
|
|
||||||
// uv mapping
|
// uv mapping
|
||||||
uniform vec2 uvScale;
|
uniform vec2 uvScale[32];
|
||||||
uniform vec2 uvOffset;
|
uniform vec2 uvOffset[32];
|
||||||
|
|
||||||
// Orthographic projection
|
// Orthographic projection
|
||||||
uniform mat4 projection;
|
uniform mat4 projection;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
|
int i = gl_InstanceID;
|
||||||
|
|
||||||
// Position of cell from top-left
|
// Position of cell from top-left
|
||||||
vec2 cellPosition = (cellDim + cellSep) * gridCoords;
|
vec2 cellPosition = (cellDim + cellSep) * gridCoords[i];
|
||||||
|
|
||||||
// Invert Y since framebuffer origin is bottom-left
|
// Invert Y since framebuffer origin is bottom-left
|
||||||
cellPosition.y = termDim.y - cellPosition.y - cellDim.y;
|
cellPosition.y = termDim.y - cellPosition.y - cellDim.y;
|
||||||
|
|
||||||
// Glyphs are offset within their cell; account for y-flip
|
// Glyphs are offset within their cell; account for y-flip
|
||||||
vec2 cellOffset = vec2(glyphOffset.x, glyphOffset.y - glyphScale.y);
|
vec2 cellOffset = vec2(glyphOffset[i].x,
|
||||||
|
glyphOffset[i].y - glyphScale[i].y);
|
||||||
|
|
||||||
// position coordinates are normalized on [0, 1]
|
// position coordinates are normalized on [0, 1]
|
||||||
vec2 finalPosition = glyphScale * position + cellPosition + cellOffset;
|
vec2 finalPosition = glyphScale[i] * position + cellPosition + cellOffset;
|
||||||
|
|
||||||
gl_Position = projection * vec4(finalPosition.xy, 0.0, 1.0);
|
gl_Position = projection * vec4(finalPosition.xy, 0.0, 1.0);
|
||||||
TexCoords = vec2(position.x, 1 - position.y) * uvScale + uvOffset;
|
TexCoords = vec2(position.x, 1 - position.y) * uvScale[i] + uvOffset[i];
|
||||||
|
InstanceId = i;
|
||||||
}
|
}
|
||||||
|
|
10
src/main.rs
10
src/main.rs
|
@ -12,6 +12,7 @@ extern crate glutin;
|
||||||
extern crate cgmath;
|
extern crate cgmath;
|
||||||
extern crate euclid;
|
extern crate euclid;
|
||||||
extern crate notify;
|
extern crate notify;
|
||||||
|
extern crate arrayvec;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
@ -190,15 +191,6 @@ fn main() {
|
||||||
width: width as f32,
|
width: width as f32,
|
||||||
};
|
};
|
||||||
|
|
||||||
let props = TermProps {
|
|
||||||
cell_width: cell_width as f32,
|
|
||||||
cell_height: cell_height as f32,
|
|
||||||
sep_x: sep_x as f32,
|
|
||||||
sep_y: sep_y as f32,
|
|
||||||
height: height as f32,
|
|
||||||
width: width as f32,
|
|
||||||
};
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let _sampler = meter.sampler();
|
let _sampler = meter.sampler();
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,13 @@ use std::ptr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{Ordering, AtomicBool};
|
use std::sync::atomic::{Ordering, AtomicBool};
|
||||||
|
|
||||||
|
use arrayvec::ArrayVec;
|
||||||
use cgmath::{self, Matrix};
|
use cgmath::{self, Matrix};
|
||||||
use euclid::{Rect, Size2D, Point2D};
|
use euclid::{Rect, Size2D, Point2D};
|
||||||
use gl::types::*;
|
use gl::types::*;
|
||||||
use gl;
|
use gl;
|
||||||
use notify::{Watcher as WatcherApi, RecommendedWatcher as Watcher, op};
|
use notify::{Watcher as WatcherApi, RecommendedWatcher as Watcher, op};
|
||||||
|
|
||||||
use text::RasterizedGlyph;
|
use text::RasterizedGlyph;
|
||||||
use grid::Grid;
|
use grid::Grid;
|
||||||
use term;
|
use term;
|
||||||
|
@ -21,6 +23,19 @@ use super::{Rgb, TermProps, GlyphCache};
|
||||||
static TEXT_SHADER_F_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.f.glsl");
|
static TEXT_SHADER_F_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.f.glsl");
|
||||||
static TEXT_SHADER_V_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.v.glsl");
|
static TEXT_SHADER_V_PATH: &'static str = concat!(env!("CARGO_MANIFEST_DIR"), "/res/text.v.glsl");
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Glyph {
|
||||||
|
tex_id: GLuint,
|
||||||
|
top: f32,
|
||||||
|
left: f32,
|
||||||
|
width: f32,
|
||||||
|
height: f32,
|
||||||
|
uv_bot: f32,
|
||||||
|
uv_left: f32,
|
||||||
|
uv_width: f32,
|
||||||
|
uv_height: f32,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct QuadRenderer {
|
pub struct QuadRenderer {
|
||||||
program: ShaderProgram,
|
program: ShaderProgram,
|
||||||
should_reload: Arc<AtomicBool>,
|
should_reload: Arc<AtomicBool>,
|
||||||
|
@ -32,13 +47,111 @@ pub struct QuadRenderer {
|
||||||
active_tex: GLuint,
|
active_tex: GLuint,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct PackedVertex {
|
pub struct PackedVertex {
|
||||||
x: f32,
|
x: f32,
|
||||||
y: f32,
|
y: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ElementIndex {
|
||||||
|
col: u32, // x
|
||||||
|
row: u32, // y
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Batch {
|
||||||
|
tex: GLuint,
|
||||||
|
coords: ArrayVec<[Point2D<f32>; BATCH_MAX]>,
|
||||||
|
color: ArrayVec<[RgbUpload; BATCH_MAX]>,
|
||||||
|
glyph_scale: ArrayVec<[Point2D<f32>; BATCH_MAX]>,
|
||||||
|
glyph_offset: ArrayVec<[Point2D<f32>; BATCH_MAX]>,
|
||||||
|
uv_scale: ArrayVec<[Point2D<f32>; BATCH_MAX]>,
|
||||||
|
uv_offset: ArrayVec<[Point2D<f32>; BATCH_MAX]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct RgbUpload {
|
||||||
|
r: i32,
|
||||||
|
g: i32,
|
||||||
|
b: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Rgb> for RgbUpload {
|
||||||
|
#[inline]
|
||||||
|
fn from(color: Rgb) -> RgbUpload {
|
||||||
|
RgbUpload {
|
||||||
|
r: color.r as i32,
|
||||||
|
g: color.g as i32,
|
||||||
|
b: color.b as i32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Batch {
|
||||||
|
pub fn new() -> Batch {
|
||||||
|
Batch {
|
||||||
|
tex: 0,
|
||||||
|
coords: ArrayVec::new(),
|
||||||
|
color: ArrayVec::new(),
|
||||||
|
glyph_scale: ArrayVec::new(),
|
||||||
|
glyph_offset: ArrayVec::new(),
|
||||||
|
uv_scale: ArrayVec::new(),
|
||||||
|
uv_offset: ArrayVec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_item(&mut self, row: f32, col: f32, color: Rgb, glyph: &Glyph) {
|
||||||
|
if self.is_empty() {
|
||||||
|
self.tex = glyph.tex_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.coords.push(Point2D::new(col, row));
|
||||||
|
self.color.push(RgbUpload::from(color));
|
||||||
|
self.glyph_scale.push(Point2D::new(glyph.width, glyph.height));
|
||||||
|
self.glyph_offset.push(Point2D::new(glyph.left, glyph.top));
|
||||||
|
self.uv_scale.push(Point2D::new(glyph.uv_width, glyph.uv_height));
|
||||||
|
self.uv_offset.push(Point2D::new(glyph.uv_left, glyph.uv_bot));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn full(&self) -> bool {
|
||||||
|
self.capacity() == self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.color.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn capacity(&self) -> usize {
|
||||||
|
BATCH_MAX
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.len() == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.tex = 0;
|
||||||
|
self.coords.clear();
|
||||||
|
self.color.clear();
|
||||||
|
self.glyph_scale.clear();
|
||||||
|
self.glyph_offset.clear();
|
||||||
|
self.uv_scale.clear();
|
||||||
|
self.uv_offset.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&mut self, renderer: &mut QuadRenderer) {
|
||||||
|
renderer.render_batch(self);
|
||||||
|
self.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maximum items to be drawn in a batch.
|
||||||
|
const BATCH_MAX: usize = 32usize;
|
||||||
|
|
||||||
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
|
||||||
|
@ -155,12 +268,24 @@ impl QuadRenderer {
|
||||||
|
|
||||||
let row = 40.0;
|
let row = 40.0;
|
||||||
let mut col = 100.0;
|
let mut col = 100.0;
|
||||||
|
|
||||||
|
let mut batch = Batch::new();
|
||||||
|
|
||||||
for c in s.chars() {
|
for c in s.chars() {
|
||||||
if let Some(glyph) = glyph_cache.get(&c) {
|
if let Some(glyph) = glyph_cache.get(&c) {
|
||||||
self.render(glyph, row, col, color, c);
|
batch.add_item(row, col, *color, glyph);
|
||||||
}
|
}
|
||||||
|
|
||||||
col += 1.0;
|
col += 1.0;
|
||||||
|
|
||||||
|
// Render batch and clear if it's full
|
||||||
|
if batch.full() {
|
||||||
|
batch.render(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !batch.is_empty() {
|
||||||
|
batch.render(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finish_render();
|
self.finish_render();
|
||||||
|
@ -172,9 +297,11 @@ impl QuadRenderer {
|
||||||
props: &TermProps)
|
props: &TermProps)
|
||||||
{
|
{
|
||||||
self.prepare_render(props);
|
self.prepare_render(props);
|
||||||
|
|
||||||
if let Some(glyph) = glyph_cache.get(&term::CURSOR_SHAPE) {
|
if let Some(glyph) = glyph_cache.get(&term::CURSOR_SHAPE) {
|
||||||
self.render(glyph, cursor.y as f32, cursor.x as f32,
|
let mut batch = Batch::new();
|
||||||
&term::DEFAULT_FG, term::CURSOR_SHAPE);
|
batch.add_item(cursor.y as f32, cursor.x as f32, term::DEFAULT_FG, glyph);
|
||||||
|
batch.render(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finish_render();
|
self.finish_render();
|
||||||
|
@ -183,6 +310,9 @@ impl QuadRenderer {
|
||||||
pub fn render_grid(&mut self, grid: &Grid, glyph_cache: &GlyphCache, props: &TermProps) {
|
pub fn render_grid(&mut self, grid: &Grid, glyph_cache: &GlyphCache, props: &TermProps) {
|
||||||
self.prepare_render(props);
|
self.prepare_render(props);
|
||||||
|
|
||||||
|
// All draws are batched
|
||||||
|
let mut batch = Batch::new();
|
||||||
|
|
||||||
for (i, row) in grid.rows().enumerate() {
|
for (i, row) in grid.rows().enumerate() {
|
||||||
for (j, cell) in row.cells().enumerate() {
|
for (j, cell) in row.cells().enumerate() {
|
||||||
// Skip empty cells
|
// Skip empty cells
|
||||||
|
@ -190,13 +320,23 @@ impl QuadRenderer {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render if glyph is loaded
|
// Add cell to batch if the glyph is laoded
|
||||||
if let Some(glyph) = glyph_cache.get(&cell.c) {
|
if let Some(glyph) = glyph_cache.get(&cell.c) {
|
||||||
self.render(glyph, i as f32, j as f32, &cell.fg, cell.c);
|
batch.add_item(i as f32, j as f32, cell.fg, glyph);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render batch and clear if it's full
|
||||||
|
if batch.full() {
|
||||||
|
batch.render(self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Could have some data in a batch still; render it.
|
||||||
|
if !batch.is_empty() {
|
||||||
|
batch.render(self);
|
||||||
|
}
|
||||||
|
|
||||||
self.finish_render();
|
self.finish_render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,27 +390,20 @@ impl QuadRenderer {
|
||||||
self.program = program;
|
self.program = program;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self, glyph: &Glyph, row: f32, col: f32, color: &Rgb, c: char) {
|
fn render_batch(&mut self, batch: &Batch) {
|
||||||
if &self.active_color != color {
|
self.program.set_uniforms(batch);
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.program.set_glyph_uniforms(row, col, glyph);
|
// Bind texture if necessary
|
||||||
|
if self.active_tex != batch.tex {
|
||||||
|
unsafe {
|
||||||
|
gl::BindTexture(gl::TEXTURE_2D, batch.tex);
|
||||||
|
}
|
||||||
|
self.active_tex = batch.tex;
|
||||||
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// Bind texture if it changed
|
let count = batch.len() as GLsizei;
|
||||||
if self.active_tex != glyph.tex_id {
|
gl::DrawElementsInstanced(gl::TRIANGLES, 6, gl::UNSIGNED_INT, ptr::null(), count);
|
||||||
gl::BindTexture(gl::TEXTURE_2D, glyph.tex_id);
|
|
||||||
self.active_tex = glyph.tex_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
gl::DrawElements(gl::TRIANGLES, 6, gl::UNSIGNED_INT, ptr::null());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,7 +492,7 @@ pub struct ShaderProgram {
|
||||||
u_glyph_scale: GLint,
|
u_glyph_scale: GLint,
|
||||||
|
|
||||||
/// Glyph offset
|
/// Glyph offset
|
||||||
u_glyph_offest: GLint,
|
u_glyph_offset: GLint,
|
||||||
|
|
||||||
/// Atlas scale
|
/// Atlas scale
|
||||||
u_uv_scale: GLint,
|
u_uv_scale: GLint,
|
||||||
|
@ -420,7 +553,7 @@ impl ShaderProgram {
|
||||||
|
|
||||||
assert_uniform_valid!(projection, color, term_dim, cell_dim, cell_sep);
|
assert_uniform_valid!(projection, color, term_dim, cell_dim, cell_sep);
|
||||||
|
|
||||||
let (cell_coord, glyph_scale, glyph_offest, uv_scale, uv_offset) = unsafe {
|
let (cell_coord, glyph_scale, glyph_offset, uv_scale, uv_offset) = unsafe {
|
||||||
(
|
(
|
||||||
gl::GetUniformLocation(program, cptr!(b"gridCoords\0")),
|
gl::GetUniformLocation(program, cptr!(b"gridCoords\0")),
|
||||||
gl::GetUniformLocation(program, cptr!(b"glyphScale\0")),
|
gl::GetUniformLocation(program, cptr!(b"glyphScale\0")),
|
||||||
|
@ -430,7 +563,7 @@ impl ShaderProgram {
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
assert_uniform_valid!(cell_coord, glyph_scale, glyph_offest, uv_scale, uv_offset);
|
assert_uniform_valid!(cell_coord, glyph_scale, glyph_offset, uv_scale, uv_offset);
|
||||||
|
|
||||||
// Initialize to known color (black)
|
// Initialize to known color (black)
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -446,7 +579,7 @@ impl ShaderProgram {
|
||||||
u_cell_sep: cell_sep,
|
u_cell_sep: cell_sep,
|
||||||
u_cell_coord: cell_coord,
|
u_cell_coord: cell_coord,
|
||||||
u_glyph_scale: glyph_scale,
|
u_glyph_scale: glyph_scale,
|
||||||
u_glyph_offest: glyph_offest,
|
u_glyph_offset: glyph_offset,
|
||||||
u_uv_scale: uv_scale,
|
u_uv_scale: uv_scale,
|
||||||
u_uv_offset: uv_offset,
|
u_uv_offset: uv_offset,
|
||||||
};
|
};
|
||||||
|
@ -475,13 +608,15 @@ impl ShaderProgram {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_glyph_uniforms(&self, row: f32, col: f32, glyph: &Glyph) {
|
fn set_uniforms(&self, batch: &Batch) {
|
||||||
|
let len = batch.len();
|
||||||
unsafe {
|
unsafe {
|
||||||
gl::Uniform2f(self.u_cell_coord, col, row); // col = x; row = y
|
gl::Uniform2fv(self.u_cell_coord, len as i32, batch.coords.as_ptr() as *const _);
|
||||||
gl::Uniform2f(self.u_glyph_scale, glyph.width, glyph.height);
|
gl::Uniform2fv(self.u_glyph_scale, len as i32, batch.glyph_scale.as_ptr() as *const _);
|
||||||
gl::Uniform2f(self.u_glyph_offest, glyph.left, glyph.top);
|
gl::Uniform2fv(self.u_glyph_offset, len as i32, batch.glyph_offset.as_ptr() as *const _);
|
||||||
gl::Uniform2f(self.u_uv_scale, glyph.uv_width, glyph.uv_height);
|
gl::Uniform2fv(self.u_uv_scale, len as i32, batch.uv_scale.as_ptr() as *const _);
|
||||||
gl::Uniform2f(self.u_uv_offset, glyph.uv_left, glyph.uv_bot);
|
gl::Uniform2fv(self.u_uv_offset, len as i32, batch.uv_offset.as_ptr() as *const _);
|
||||||
|
gl::Uniform3iv(self.u_color, len as i32, batch.color.as_ptr() as *const _);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -496,6 +631,7 @@ impl ShaderProgram {
|
||||||
gl::GetProgramiv(program, gl::LINK_STATUS, &mut success);
|
gl::GetProgramiv(program, gl::LINK_STATUS, &mut success);
|
||||||
|
|
||||||
if success != (gl::TRUE as GLint) {
|
if success != (gl::TRUE as GLint) {
|
||||||
|
println!("{}", get_program_info_log(program));
|
||||||
panic!("failed to link shader program");
|
panic!("failed to link shader program");
|
||||||
}
|
}
|
||||||
program
|
program
|
||||||
|
@ -531,6 +667,29 @@ impl ShaderProgram {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_program_info_log(program: GLuint) -> String {
|
||||||
|
// Get expected log length
|
||||||
|
let mut max_length: GLint = 0;
|
||||||
|
unsafe {
|
||||||
|
gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut max_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the info log
|
||||||
|
let mut actual_length: GLint = 0;
|
||||||
|
let mut buf: Vec<u8> = Vec::with_capacity(max_length as usize);
|
||||||
|
unsafe {
|
||||||
|
gl::GetProgramInfoLog(program, max_length, &mut actual_length, buf.as_mut_ptr() as *mut _);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a string
|
||||||
|
unsafe {
|
||||||
|
buf.set_len(actual_length as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX should we expect opengl to return garbage?
|
||||||
|
String::from_utf8(buf).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
fn get_shader_info_log(shader: GLuint) -> String {
|
fn get_shader_info_log(shader: GLuint) -> String {
|
||||||
// Get expected log length
|
// Get expected log length
|
||||||
let mut max_length: GLint = 0;
|
let mut max_length: GLint = 0;
|
||||||
|
@ -759,16 +918,3 @@ impl Atlas {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Glyph {
|
|
||||||
tex_id: GLuint,
|
|
||||||
top: f32,
|
|
||||||
left: f32,
|
|
||||||
width: f32,
|
|
||||||
height: f32,
|
|
||||||
uv_bot: f32,
|
|
||||||
uv_left: f32,
|
|
||||||
uv_width: f32,
|
|
||||||
uv_height: f32,
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue