mirror of
https://github.com/alacritty/alacritty.git
synced 2024-11-25 14:05:41 -05:00
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
4 changed files with 342 additions and 50 deletions
150
Cargo.lock
generated
150
Cargo.lock
generated
|
@ -8,6 +8,7 @@ dependencies = [
|
|||
"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)",
|
||||
"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)",
|
||||
]
|
||||
|
||||
|
@ -21,6 +22,11 @@ name = "bitflags"
|
|||
version = "0.3.3"
|
||||
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]]
|
||||
name = "bitflags"
|
||||
version = "0.6.0"
|
||||
|
@ -36,6 +42,16 @@ name = "byteorder"
|
|||
version = "0.5.3"
|
||||
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]]
|
||||
name = "cgl"
|
||||
version = "0.1.5"
|
||||
|
@ -142,6 +158,14 @@ dependencies = [
|
|||
"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]]
|
||||
name = "freetype-rs"
|
||||
version = "0.9.0"
|
||||
|
@ -172,6 +196,24 @@ dependencies = [
|
|||
"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]]
|
||||
name = "gcc"
|
||||
version = "0.3.28"
|
||||
|
@ -242,6 +284,14 @@ dependencies = [
|
|||
"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]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
|
@ -320,6 +370,73 @@ dependencies = [
|
|||
"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]]
|
||||
name = "num"
|
||||
version = "0.1.32"
|
||||
|
@ -477,6 +594,11 @@ dependencies = [
|
|||
"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]]
|
||||
name = "tempfile"
|
||||
version = "2.1.3"
|
||||
|
@ -489,6 +611,16 @@ dependencies = [
|
|||
"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]]
|
||||
name = "user32-sys"
|
||||
version = "0.1.2"
|
||||
|
@ -498,6 +630,15 @@ dependencies = [
|
|||
"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]]
|
||||
name = "wayland-client"
|
||||
version = "0.5.12"
|
||||
|
@ -560,6 +701,15 @@ name = "winapi-build"
|
|||
version = "0.1.1"
|
||||
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]]
|
||||
name = "x11-dl"
|
||||
version = "2.4.0"
|
||||
|
|
|
@ -12,6 +12,7 @@ freetype-rs = "0.9.0"
|
|||
libc = "*"
|
||||
cgmath = "0.7"
|
||||
euclid = "0.6"
|
||||
notify = { git = "https://github.com/jwilm/rsnotify", branch = "add-ignore-op" }
|
||||
|
||||
[build-dependencies]
|
||||
gl_generator = "0.5"
|
||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -11,6 +11,7 @@ extern crate libc;
|
|||
extern crate glutin;
|
||||
extern crate cgmath;
|
||||
extern crate euclid;
|
||||
extern crate notify;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
@ -61,6 +62,7 @@ struct TermProps {
|
|||
cell_height: f32,
|
||||
sep_y: f32,
|
||||
height: f32,
|
||||
width: f32,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
@ -178,17 +180,18 @@ fn main() {
|
|||
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 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
|
||||
renderer.render_grid(terminal.grid(), &glyph_cache, &props);
|
||||
|
||||
|
@ -199,7 +202,7 @@ fn main() {
|
|||
// Draw render timer
|
||||
let timing = format!("{:.3} usec", meter.average());
|
||||
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();
|
||||
}
|
||||
|
|
|
@ -1,23 +1,32 @@
|
|||
use std::ffi::CString;
|
||||
use std::fs::File;
|
||||
use std::io::{self, Read};
|
||||
use std::mem::size_of;
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{Ordering, AtomicBool};
|
||||
|
||||
use cgmath::{self, Matrix};
|
||||
use euclid::{Rect, Size2D, Point2D};
|
||||
use gl::types::*;
|
||||
use gl;
|
||||
|
||||
use notify::{Watcher as WatcherApi, RecommendedWatcher as Watcher, op};
|
||||
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_F: &'static str = include_str!("../../res/text.f.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_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 {
|
||||
program: ShaderProgram,
|
||||
should_reload: Arc<AtomicBool>,
|
||||
vao: GLuint,
|
||||
vbo: GLuint,
|
||||
ebo: GLuint,
|
||||
|
@ -39,7 +48,7 @@ pub struct PackedVertex {
|
|||
impl QuadRenderer {
|
||||
// TODO should probably hand this a transform instead of width/height
|
||||
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 vbo: GLuint = 0;
|
||||
|
@ -88,8 +97,41 @@ impl QuadRenderer {
|
|||
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 {
|
||||
program: program,
|
||||
should_reload: should_reload,
|
||||
vao: vao,
|
||||
vbo: vbo,
|
||||
ebo: ebo,
|
||||
|
@ -109,10 +151,10 @@ impl QuadRenderer {
|
|||
pub fn render_string(&mut self,
|
||||
s: &str,
|
||||
glyph_cache: &GlyphCache,
|
||||
cell_width: u32,
|
||||
props: &TermProps,
|
||||
color: &Rgb)
|
||||
{
|
||||
self.prepare_render();
|
||||
self.prepare_render(props);
|
||||
|
||||
let (mut x, mut y) = (800f32, 20f32);
|
||||
|
||||
|
@ -121,7 +163,7 @@ impl QuadRenderer {
|
|||
self.render(glyph, x, y, color);
|
||||
}
|
||||
|
||||
x += cell_width as f32 + 2f32;
|
||||
x += props.cell_width as f32 + 2f32;
|
||||
}
|
||||
|
||||
self.finish_render();
|
||||
|
@ -132,7 +174,7 @@ impl QuadRenderer {
|
|||
glyph_cache: &GlyphCache,
|
||||
props: &TermProps)
|
||||
{
|
||||
self.prepare_render();
|
||||
self.prepare_render(props);
|
||||
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);
|
||||
|
@ -146,7 +188,7 @@ impl QuadRenderer {
|
|||
}
|
||||
|
||||
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() {
|
||||
let row = &grid[i];
|
||||
for j in 0..row.cols() {
|
||||
|
@ -166,7 +208,7 @@ impl QuadRenderer {
|
|||
self.finish_render();
|
||||
}
|
||||
|
||||
fn prepare_render(&self) {
|
||||
fn prepare_render(&mut self, props: &TermProps) {
|
||||
unsafe {
|
||||
self.program.activate();
|
||||
|
||||
|
@ -175,6 +217,10 @@ impl QuadRenderer {
|
|||
gl::BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
|
||||
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) {
|
||||
|
@ -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) {
|
||||
if &self.active_color != color {
|
||||
unsafe {
|
||||
|
@ -308,9 +378,10 @@ impl ShaderProgram {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new(width: u32, height: u32) -> ShaderProgram {
|
||||
let vertex_shader = ShaderProgram::create_vertex_shader();
|
||||
let fragment_shader = ShaderProgram::create_fragment_shader();
|
||||
pub fn new(width: u32, height: u32) -> Result<ShaderProgram, ShaderCreationError> {
|
||||
let vertex_shader = ShaderProgram::create_shader(TEXT_SHADER_V_PATH, gl::VERTEX_SHADER)?;
|
||||
let fragment_shader = ShaderProgram::create_shader(TEXT_SHADER_F_PATH,
|
||||
gl::FRAGMENT_SHADER)?;
|
||||
let program = ShaderProgram::create_program(vertex_shader, fragment_shader);
|
||||
|
||||
unsafe {
|
||||
|
@ -358,11 +429,10 @@ impl ShaderProgram {
|
|||
}
|
||||
shader.deactivate();
|
||||
|
||||
shader
|
||||
Ok(shader)
|
||||
}
|
||||
|
||||
fn create_program(vertex: GLuint, fragment: GLuint) -> GLuint {
|
||||
|
||||
unsafe {
|
||||
let program = gl::CreateProgram();
|
||||
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 {
|
||||
let fragment_shader = gl::CreateShader(gl::FRAGMENT_SHADER);
|
||||
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
|
||||
gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success);
|
||||
}
|
||||
}
|
||||
|
||||
fn create_vertex_shader() -> GLuint {
|
||||
unsafe {
|
||||
let vertex_shader = gl::CreateShader(gl::VERTEX_SHADER);
|
||||
let vertex_source = CString::new(TEXT_SHADER_V).unwrap();
|
||||
gl::ShaderSource(vertex_shader, 1, &vertex_source.as_ptr(), ptr::null());
|
||||
gl::CompileShader(vertex_shader);
|
||||
if success == (gl::TRUE as GLint) {
|
||||
Ok(shader)
|
||||
} else {
|
||||
// Read log
|
||||
let log = get_shader_info_log(shader);
|
||||
|
||||
let mut success: GLint = 0;
|
||||
gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success);
|
||||
// Cleanup
|
||||
unsafe { gl::DeleteShader(shader); }
|
||||
|
||||
if success != (gl::TRUE as GLint) {
|
||||
panic!("failed to compiler vertex shader");
|
||||
}
|
||||
vertex_shader
|
||||
Err(ShaderCreationError::Compile(PathBuf::from(path), log))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
///
|
||||
/// The strategy for filling an atlas looks roughly like this:
|
||||
|
|
Loading…
Reference in a new issue