aboutsummaryrefslogtreecommitdiff
path: root/src/renderer/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/renderer/mod.rs')
-rw-r--r--src/renderer/mod.rs214
1 files changed, 176 insertions, 38 deletions
diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs
index 2f364482..6161191f 100644
--- a/src/renderer/mod.rs
+++ b/src/renderer/mod.rs
@@ -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);
+ gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut success);
+ }
- let mut success: GLint = 0;
- gl::GetShaderiv(fragment_shader, gl::COMPILE_STATUS, &mut success);
+ if success == (gl::TRUE as GLint) {
+ Ok(shader)
+ } else {
+ // Read log
+ let log = get_shader_info_log(shader);
- if success != (gl::TRUE as GLint) {
- panic!("failed to compiler fragment shader");
- }
- fragment_shader
+ // Cleanup
+ unsafe { gl::DeleteShader(shader); }
+
+ Err(ShaderCreationError::Compile(PathBuf::from(path), log))
}
}
+}
- 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);
+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);
+ }
- let mut success: GLint = 0;
- gl::GetShaderiv(vertex_shader, gl::COMPILE_STATUS, &mut success);
+ // 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 _);
+ }
- if success != (gl::TRUE as GLint) {
- panic!("failed to compiler vertex shader");
- }
- vertex_shader
+ // 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: