Add live-reload for shaders
Recompiling the entire program whenever a shader changes is slow, and it can interrupt flow. Shader reloads are essentially instantaneous now. If the new shader fails to compile, no state is changed; the previous program continues to be used.
This commit is contained in:
parent
c475c82c69
commit
f944b517fa
|
@ -8,6 +8,7 @@ dependencies = [
|
||||||
"gl_generator 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gl_generator 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"glutin 0.4.9 (git+https://github.com/jwilm/glutin?rev=c95e6973ace3cbf321123a64588b27f032675be9)",
|
"glutin 0.4.9 (git+https://github.com/jwilm/glutin?rev=c95e6973ace3cbf321123a64588b27f032675be9)",
|
||||||
"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)",
|
||||||
|
"notify 2.5.5 (git+https://github.com/jwilm/rsnotify?branch=add-ignore-op)",
|
||||||
"servo-fontconfig 0.2.0 (git+https://github.com/jwilm/rust-fontconfig)",
|
"servo-fontconfig 0.2.0 (git+https://github.com/jwilm/rust-fontconfig)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -21,6 +22,11 @@ name = "bitflags"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
@ -36,6 +42,16 @@ name = "byteorder"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cgl"
|
name = "cgl"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
|
@ -142,6 +158,14 @@ dependencies = [
|
||||||
"pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"pkg-config 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filetime"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "freetype-rs"
|
name = "freetype-rs"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
@ -172,6 +196,24 @@ dependencies = [
|
||||||
"winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fsevent"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"fsevent-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fsevent-sys"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gcc"
|
name = "gcc"
|
||||||
version = "0.3.28"
|
version = "0.3.28"
|
||||||
|
@ -242,6 +284,14 @@ dependencies = [
|
||||||
"x11-dl 2.4.0 (git+https://github.com/jwilm/x11-rs?rev=40f08df7b4408980b922b3c6e258c9c6765c2c24)",
|
"x11-dl 2.4.0 (git+https://github.com/jwilm/x11-rs?rev=40f08df7b4408980b922b3c6e258c9c6765c2c24)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inotify"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kernel32-sys"
|
name = "kernel32-sys"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -320,6 +370,73 @@ dependencies = [
|
||||||
"winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"miow 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"slab 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miow"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "net2"
|
||||||
|
version = "0.2.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "notify"
|
||||||
|
version = "2.5.5"
|
||||||
|
source = "git+https://github.com/jwilm/rsnotify?branch=add-ignore-op#0ca41a4807c427e6cf47d7e75735df62d2e86708"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"filetime 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"fsevent 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"fsevent-sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"inotify 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"walkdir 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num"
|
name = "num"
|
||||||
version = "0.1.32"
|
version = "0.1.32"
|
||||||
|
@ -477,6 +594,11 @@ dependencies = [
|
||||||
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "2.1.3"
|
version = "2.1.3"
|
||||||
|
@ -489,6 +611,16 @@ dependencies = [
|
||||||
"winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "time"
|
||||||
|
version = "0.1.35"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"libc 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "user32-sys"
|
name = "user32-sys"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -498,6 +630,15 @@ dependencies = [
|
||||||
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "walkdir"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wayland-client"
|
name = "wayland-client"
|
||||||
version = "0.5.12"
|
version = "0.5.12"
|
||||||
|
@ -560,6 +701,15 @@ name = "winapi-build"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ws2_32-sys"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "x11-dl"
|
name = "x11-dl"
|
||||||
version = "2.4.0"
|
version = "2.4.0"
|
||||||
|
|
|
@ -12,6 +12,7 @@ freetype-rs = "0.9.0"
|
||||||
libc = "*"
|
libc = "*"
|
||||||
cgmath = "0.7"
|
cgmath = "0.7"
|
||||||
euclid = "0.6"
|
euclid = "0.6"
|
||||||
|
notify = { git = "https://github.com/jwilm/rsnotify", branch = "add-ignore-op" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gl_generator = "0.5"
|
gl_generator = "0.5"
|
||||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -11,6 +11,7 @@ extern crate libc;
|
||||||
extern crate glutin;
|
extern crate glutin;
|
||||||
extern crate cgmath;
|
extern crate cgmath;
|
||||||
extern crate euclid;
|
extern crate euclid;
|
||||||
|
extern crate notify;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
@ -61,6 +62,7 @@ struct TermProps {
|
||||||
cell_height: f32,
|
cell_height: f32,
|
||||||
sep_y: f32,
|
sep_y: f32,
|
||||||
height: f32,
|
height: f32,
|
||||||
|
width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
@ -178,17 +180,18 @@ fn main() {
|
||||||
gl::Clear(gl::COLOR_BUFFER_BIT);
|
gl::Clear(gl::COLOR_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
width: width as f32,
|
||||||
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
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
|
||||||
renderer.render_grid(terminal.grid(), &glyph_cache, &props);
|
renderer.render_grid(terminal.grid(), &glyph_cache, &props);
|
||||||
|
|
||||||
|
@ -199,7 +202,7 @@ fn main() {
|
||||||
// Draw render timer
|
// 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 };
|
||||||
renderer.render_string(&timing[..], &glyph_cache, cell_width, &color);
|
renderer.render_string(&timing[..], &glyph_cache, &props, &color);
|
||||||
|
|
||||||
window.swap_buffers().unwrap();
|
window.swap_buffers().unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,32 @@
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{self, Read};
|
||||||
use std::mem::size_of;
|
use std::mem::size_of;
|
||||||
|
use std::path::{PathBuf, Path};
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::atomic::{Ordering, AtomicBool};
|
||||||
|
|
||||||
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 text::RasterizedGlyph;
|
use text::RasterizedGlyph;
|
||||||
use grid::Grid;
|
use grid::Grid;
|
||||||
use term;
|
use term;
|
||||||
|
|
||||||
use super::{Rgb, TermProps, GlyphCache};
|
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");
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
pub struct QuadRenderer {
|
pub struct QuadRenderer {
|
||||||
program: ShaderProgram,
|
program: ShaderProgram,
|
||||||
|
should_reload: Arc<AtomicBool>,
|
||||||
vao: GLuint,
|
vao: GLuint,
|
||||||
vbo: GLuint,
|
vbo: GLuint,
|
||||||
ebo: GLuint,
|
ebo: GLuint,
|
||||||
|
@ -39,7 +48,7 @@ pub struct PackedVertex {
|
||||||
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
|
||||||
pub fn new(width: u32, height: u32) -> QuadRenderer {
|
pub fn new(width: u32, height: u32) -> QuadRenderer {
|
||||||
let program = ShaderProgram::new(width, height);
|
let program = ShaderProgram::new(width, height).unwrap();
|
||||||
|
|
||||||
let mut vao: GLuint = 0;
|
let mut vao: GLuint = 0;
|
||||||
let mut vbo: GLuint = 0;
|
let mut vbo: GLuint = 0;
|
||||||
|
@ -88,8 +97,41 @@ impl QuadRenderer {
|
||||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let should_reload = Arc::new(AtomicBool::new(false));
|
||||||
|
let should_reload2 = should_reload.clone();
|
||||||
|
|
||||||
|
::std::thread::spawn(move || {
|
||||||
|
let (tx, rx) = ::std::sync::mpsc::channel();
|
||||||
|
let mut watcher = Watcher::new(tx).unwrap();
|
||||||
|
watcher.watch(TEXT_SHADER_F_PATH).expect("watch fragment shader");
|
||||||
|
watcher.watch(TEXT_SHADER_V_PATH).expect("watch vertex shader");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let event = rx.recv().expect("watcher event");
|
||||||
|
let ::notify::Event { path, op } = event;
|
||||||
|
|
||||||
|
if let Ok(op) = op {
|
||||||
|
if op.contains(op::RENAME) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if op.contains(op::IGNORED) {
|
||||||
|
if let Some(path) = path.as_ref() {
|
||||||
|
if let Err(err) = watcher.watch(path) {
|
||||||
|
println!("failed to establish watch on {:?}: {:?}", path, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is last event we see after saving in vim
|
||||||
|
should_reload2.store(true, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let mut renderer = QuadRenderer {
|
let mut renderer = QuadRenderer {
|
||||||
program: program,
|
program: program,
|
||||||
|
should_reload: should_reload,
|
||||||
vao: vao,
|
vao: vao,
|
||||||
vbo: vbo,
|
vbo: vbo,
|
||||||
ebo: ebo,
|
ebo: ebo,
|
||||||
|
@ -109,10 +151,10 @@ impl QuadRenderer {
|
||||||
pub fn render_string(&mut self,
|
pub fn render_string(&mut self,
|
||||||
s: &str,
|
s: &str,
|
||||||
glyph_cache: &GlyphCache,
|
glyph_cache: &GlyphCache,
|
||||||
cell_width: u32,
|
props: &TermProps,
|
||||||
color: &Rgb)
|
color: &Rgb)
|
||||||
{
|
{
|
||||||
self.prepare_render();
|
self.prepare_render(props);
|
||||||
|
|
||||||
let (mut x, mut y) = (800f32, 20f32);
|
let (mut x, mut y) = (800f32, 20f32);
|
||||||
|
|
||||||
|
@ -121,7 +163,7 @@ impl QuadRenderer {
|
||||||
self.render(glyph, x, y, color);
|
self.render(glyph, x, y, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
x += cell_width as f32 + 2f32;
|
x += props.cell_width as f32 + 2f32;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.finish_render();
|
self.finish_render();
|
||||||
|
@ -132,7 +174,7 @@ impl QuadRenderer {
|
||||||
glyph_cache: &GlyphCache,
|
glyph_cache: &GlyphCache,
|
||||||
props: &TermProps)
|
props: &TermProps)
|
||||||
{
|
{
|
||||||
self.prepare_render();
|
self.prepare_render(props);
|
||||||
if let Some(glyph) = glyph_cache.get(&term::CURSOR_SHAPE) {
|
if let Some(glyph) = glyph_cache.get(&term::CURSOR_SHAPE) {
|
||||||
let y = (props.cell_height + props.sep_y) * (cursor.y as f32);
|
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 x = (props.cell_width + props.sep_x) * (cursor.x as f32);
|
||||||
|
@ -146,7 +188,7 @@ 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();
|
self.prepare_render(props);
|
||||||
for i in 0..grid.rows() {
|
for i in 0..grid.rows() {
|
||||||
let row = &grid[i];
|
let row = &grid[i];
|
||||||
for j in 0..row.cols() {
|
for j in 0..row.cols() {
|
||||||
|
@ -166,7 +208,7 @@ impl QuadRenderer {
|
||||||
self.finish_render();
|
self.finish_render();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_render(&self) {
|
fn prepare_render(&mut self, props: &TermProps) {
|
||||||
unsafe {
|
unsafe {
|
||||||
self.program.activate();
|
self.program.activate();
|
||||||
|
|
||||||
|
@ -175,6 +217,10 @@ impl QuadRenderer {
|
||||||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
|
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
|
||||||
gl::ActiveTexture(gl::TEXTURE0);
|
gl::ActiveTexture(gl::TEXTURE0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.should_reload.load(Ordering::Relaxed) {
|
||||||
|
self.reload_shaders(props.width as u32, props.height as u32);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish_render(&mut self) {
|
fn finish_render(&mut self) {
|
||||||
|
@ -187,6 +233,30 @@ impl QuadRenderer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn reload_shaders(&mut self, width: u32, height: u32) {
|
||||||
|
self.should_reload.store(false, Ordering::Relaxed);
|
||||||
|
let program = match ShaderProgram::new(width, height) {
|
||||||
|
Ok(program) => program,
|
||||||
|
Err(err) => {
|
||||||
|
match err {
|
||||||
|
ShaderCreationError::Io(err) => {
|
||||||
|
println!("Error reading shader file: {}", err);
|
||||||
|
},
|
||||||
|
ShaderCreationError::Compile(path, log) => {
|
||||||
|
println!("Error compiling shader at {:?}", path);
|
||||||
|
io::copy(&mut log.as_bytes(), &mut io::stdout()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
self.active_tex = 0;
|
||||||
|
self.active_color = Rgb { r: 0, g: 0, b: 0 };
|
||||||
|
self.program = program;
|
||||||
|
}
|
||||||
|
|
||||||
fn render(&mut self, glyph: &Glyph, x: f32, y: f32, color: &Rgb) {
|
fn render(&mut self, glyph: &Glyph, x: f32, y: f32, color: &Rgb) {
|
||||||
if &self.active_color != color {
|
if &self.active_color != color {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -308,9 +378,10 @@ impl ShaderProgram {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(width: u32, height: u32) -> ShaderProgram {
|
pub fn new(width: u32, height: u32) -> Result<ShaderProgram, ShaderCreationError> {
|
||||||
let vertex_shader = ShaderProgram::create_vertex_shader();
|
let vertex_shader = ShaderProgram::create_shader(TEXT_SHADER_V_PATH, gl::VERTEX_SHADER)?;
|
||||||
let fragment_shader = ShaderProgram::create_fragment_shader();
|
let fragment_shader = ShaderProgram::create_shader(TEXT_SHADER_F_PATH,
|
||||||
|
gl::FRAGMENT_SHADER)?;
|
||||||
let program = ShaderProgram::create_program(vertex_shader, fragment_shader);
|
let program = ShaderProgram::create_program(vertex_shader, fragment_shader);
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -358,11 +429,10 @@ impl ShaderProgram {
|
||||||
}
|
}
|
||||||
shader.deactivate();
|
shader.deactivate();
|
||||||
|
|
||||||
shader
|
Ok(shader)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_program(vertex: GLuint, fragment: GLuint) -> GLuint {
|
fn create_program(vertex: GLuint, fragment: GLuint) -> GLuint {
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let program = gl::CreateProgram();
|
let program = gl::CreateProgram();
|
||||||
gl::AttachShader(program, vertex);
|
gl::AttachShader(program, vertex);
|
||||||
|
@ -379,41 +449,109 @@ impl ShaderProgram {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_fragment_shader() -> GLuint {
|
|
||||||
|
fn create_shader(path: &str, kind: GLenum) -> Result<GLuint, ShaderCreationError> {
|
||||||
|
let source = CString::new(read_file(path)?).unwrap();
|
||||||
|
let shader = unsafe {
|
||||||
|
let shader = gl::CreateShader(kind);
|
||||||
|
gl::ShaderSource(shader, 1, &source.as_ptr(), ptr::null());
|
||||||
|
gl::CompileShader(shader);
|
||||||
|
shader
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut success: GLint = 0;
|
||||||
unsafe {
|
unsafe {
|
||||||
let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
|
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success);
|
||||||
let fragment_source = CString::new(TEXT_SHADER_F).unwrap();
|
|
||||||
gl::ShaderSource(fragment_shader, 1, &fragment_source.as_ptr(), ptr::null());
|
|
||||||
gl::CompileShader(fragment_shader);
|
|
||||||
|
|
||||||
let mut success: GLint = 0;
|
|
||||||
gl::GetShaderiv(fragment_shader, gl::COMPILE_STATUS, &mut success);
|
|
||||||
|
|
||||||
if success != (gl::TRUE as GLint) {
|
|
||||||
panic!("failed to compiler fragment shader");
|
|
||||||
}
|
|
||||||
fragment_shader
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn create_vertex_shader() -> GLuint {
|
if success == (gl::TRUE as GLint) {
|
||||||
unsafe {
|
Ok(shader)
|
||||||
let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER);
|
} else {
|
||||||
let vertex_source = CString::new(TEXT_SHADER_V).unwrap();
|
// Read log
|
||||||
gl::ShaderSource(vertex_shader, 1, &vertex_source.as_ptr(), ptr::null());
|
let log = get_shader_info_log(shader);
|
||||||
gl::CompileShader(vertex_shader);
|
|
||||||
|
|
||||||
let mut success: GLint = 0;
|
// Cleanup
|
||||||
gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success);
|
unsafe { gl::DeleteShader(shader); }
|
||||||
|
|
||||||
if success != (gl::TRUE as GLint) {
|
Err(ShaderCreationError::Compile(PathBuf::from(path), log))
|
||||||
panic!("failed to compiler vertex shader");
|
|
||||||
}
|
|
||||||
vertex_shader
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_shader_info_log(shader: GLuint) -> String {
|
||||||
|
// Get expected log length
|
||||||
|
let mut max_length: GLint = 0;
|
||||||
|
unsafe {
|
||||||
|
gl::GetShaderiv(shader, 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::GetShaderInfoLog(shader, 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 read_file(path: &str) -> Result<String, io::Error> {
|
||||||
|
let mut f = File::open(path)?;
|
||||||
|
let mut buf = String::new();
|
||||||
|
f.read_to_string(&mut buf)?;
|
||||||
|
|
||||||
|
Ok(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum ShaderCreationError {
|
||||||
|
/// Error reading file
|
||||||
|
Io(io::Error),
|
||||||
|
|
||||||
|
/// Error compiling shader
|
||||||
|
Compile(PathBuf, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::error::Error for ShaderCreationError {
|
||||||
|
fn cause(&self) -> Option<&::std::error::Error> {
|
||||||
|
match *self {
|
||||||
|
ShaderCreationError::Io(ref err) => Some(err),
|
||||||
|
ShaderCreationError::Compile(_, _) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
match *self {
|
||||||
|
ShaderCreationError::Io(ref err) => err.description(),
|
||||||
|
ShaderCreationError::Compile(ref _path, ref s) => s.as_str(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::fmt::Display for ShaderCreationError {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
|
match *self {
|
||||||
|
ShaderCreationError::Io(ref err) => write!(f, "Error creating shader: {}", err),
|
||||||
|
ShaderCreationError::Compile(ref _path, ref s) => {
|
||||||
|
write!(f, "Error compiling shader: {}", s)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<io::Error> for ShaderCreationError {
|
||||||
|
fn from(val: io::Error) -> ShaderCreationError {
|
||||||
|
ShaderCreationError::Io(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Manages a single texture atlas
|
/// Manages a single texture atlas
|
||||||
///
|
///
|
||||||
/// The strategy for filling an atlas looks roughly like this:
|
/// The strategy for filling an atlas looks roughly like this:
|
||||||
|
|
Loading…
Reference in New Issue