diff --git a/examples/benchmark.rs b/examples/benchmark.rs index f7122310..e24f33da 100644 --- a/examples/benchmark.rs +++ b/examples/benchmark.rs @@ -27,6 +27,7 @@ use pathfinder::otf::Font; use pathfinder::rasterizer::{Rasterizer, RasterizerOptions}; use std::env; use std::os::raw::c_void; +use std::path::PathBuf; const ATLAS_SIZE: u32 = 2048; const WIDTH: u32 = 512; @@ -35,6 +36,8 @@ const HEIGHT: u32 = 384; const MIN_TIME_PER_SIZE: u64 = 300_000_000; const MAX_TIME_PER_SIZE: u64 = 3_000_000_000; +static SHADER_PATH: &'static str = "resources/shaders/"; + fn main() { let mut glfw = glfw::init(glfw::LOG_ERRORS).unwrap(); glfw.window_hint(WindowHint::ContextVersion(3, 3)); @@ -51,7 +54,11 @@ fn main() { let device = instance.open_device().unwrap(); let queue = device.create_queue().unwrap(); - let rasterizer_options = RasterizerOptions::from_env().unwrap(); + let mut rasterizer_options = RasterizerOptions::from_env().unwrap(); + if env::var("PATHFINDER_SHADER_PATH").is_err() { + rasterizer_options.shader_path = PathBuf::from(SHADER_PATH) + } + let rasterizer = Rasterizer::new(&instance, device, queue, rasterizer_options).unwrap(); for point_size in 6..201 { diff --git a/examples/generate-atlas.rs b/examples/generate-atlas.rs index dc9c334d..14921487 100644 --- a/examples/generate-atlas.rs +++ b/examples/generate-atlas.rs @@ -24,12 +24,16 @@ use pathfinder::coverage::CoverageBuffer; use pathfinder::outline::OutlineBuilder; use pathfinder::otf::Font; use pathfinder::rasterizer::{Rasterizer, RasterizerOptions}; +use std::env; use std::os::raw::c_void; +use std::path::PathBuf; const DEFAULT_POINT_SIZE: f32 = 24.0; const WIDTH: u32 = 512; const HEIGHT: u32 = 384; +static SHADER_PATH: &'static str = "resources/shaders/"; + fn main() { let index_arg = Arg::with_name("index").short("i") .long("index") @@ -60,7 +64,11 @@ fn main() { let device = instance.open_device().unwrap(); let queue = device.create_queue().unwrap(); - let rasterizer_options = RasterizerOptions::from_env().unwrap(); + let mut rasterizer_options = RasterizerOptions::from_env().unwrap(); + if env::var("PATHFINDER_SHADER_PATH").is_err() { + rasterizer_options.shader_path = PathBuf::from(SHADER_PATH) + } + let rasterizer = Rasterizer::new(&instance, device, queue, rasterizer_options).unwrap(); let file = Mmap::open_path(matches.value_of("FONT-FILE").unwrap(), Protection::Read).unwrap(); diff --git a/examples/lorem-ipsum.rs b/examples/lorem-ipsum.rs index 517b4dfb..9748dd06 100644 --- a/examples/lorem-ipsum.rs +++ b/examples/lorem-ipsum.rs @@ -27,11 +27,12 @@ use pathfinder::outline::{OutlineBuilder, Outlines}; use pathfinder::rasterizer::{DrawAtlasProfilingEvents, Rasterizer, RasterizerOptions}; use pathfinder::shaper; use std::char; +use std::env; use std::fs::File; use std::io::Read; use std::mem; use std::os::raw::c_void; -use std::path::Path; +use std::path::{Path, PathBuf}; const ATLAS_SIZE: u32 = 2048; const WIDTH: u32 = 640; @@ -45,6 +46,8 @@ const MAX_POINT_SIZE: f32 = 256.0; const FPS_DISPLAY_POINT_SIZE: f32 = 24.0; const FPS_PADDING: i32 = 6; +static SHADER_PATH: &'static str = "resources/shaders/"; + static FPS_BACKGROUND_COLOR: [f32; 4] = [0.0, 0.0, 0.0, 0.7]; static FPS_FOREGROUND_COLOR: [f32; 4] = [1.0, 1.0, 1.0, 1.0]; static TEXT_COLOR: [f32; 4] = [0.0, 0.0, 0.0, 1.0]; @@ -290,7 +293,11 @@ impl Renderer { let device = instance.open_device().unwrap(); let queue = device.create_queue().unwrap(); - let rasterizer_options = RasterizerOptions::from_env().unwrap(); + let mut rasterizer_options = RasterizerOptions::from_env().unwrap(); + if env::var("PATHFINDER_SHADER_PATH").is_err() { + rasterizer_options.shader_path = PathBuf::from(SHADER_PATH) + } + let rasterizer = Rasterizer::new(&instance, device, queue, rasterizer_options).unwrap(); let (composite_program, composite_position_attribute, composite_tex_coord_attribute); diff --git a/src/error.rs b/src/error.rs index f97fd090..fa6c1519 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,6 +12,7 @@ use compute_shader; use gl::types::GLenum; +use std::io; /// An OpenGL error with the given code. /// @@ -26,6 +27,9 @@ pub enum InitError { /// An OpenGL error occurred. GlError(GlError), + /// A shader could not be loaded. + ShaderUnreadable(io::Error), + /// Shader compilation failed. /// /// The first string specifies the type of shader (vertex, fragment, etc.); the second holds diff --git a/src/rasterizer.rs b/src/rasterizer.rs index 2092ddd2..19259981 100644 --- a/src/rasterizer.rs +++ b/src/rasterizer.rs @@ -25,19 +25,20 @@ use gl; use outline::{Outlines, Vertex}; use std::ascii::AsciiExt; use std::env; +use std::fs::File; +use std::io::Read; use std::mem; +use std::path::{Path, PathBuf}; use std::ptr; -// TODO(pcwalton): Don't force that these be compiled in. -static ACCUM_CL_SHADER: &'static str = include_str!("../resources/shaders/accum.cl"); -static ACCUM_COMPUTE_SHADER: &'static str = include_str!("../resources/shaders/accum.cs.glsl"); +static ACCUM_CL_SHADER_FILENAME: &'static str = "accum.cl"; +static ACCUM_COMPUTE_SHADER_FILENAME: &'static str = "accum.cs.glsl"; -static DRAW_VERTEX_SHADER: &'static str = include_str!("../resources/shaders/draw.vs.glsl"); -static DRAW_TESS_CONTROL_SHADER: &'static str = include_str!("../resources/shaders/draw.tcs.glsl"); -static DRAW_TESS_EVALUATION_SHADER: &'static str = - include_str!("../resources/shaders/draw.tes.glsl"); -static DRAW_GEOMETRY_SHADER: &'static str = include_str!("../resources/shaders/draw.gs.glsl"); -static DRAW_FRAGMENT_SHADER: &'static str = include_str!("../resources/shaders/draw.fs.glsl"); +static DRAW_VERTEX_SHADER_FILENAME: &'static str = "draw.vs.glsl"; +static DRAW_TESS_CONTROL_SHADER_FILENAME: &'static str = "draw.tcs.glsl"; +static DRAW_TESS_EVALUATION_SHADER_FILENAME: &'static str = "draw.tes.glsl"; +static DRAW_GEOMETRY_SHADER_FILENAME: &'static str = "draw.gs.glsl"; +static DRAW_FRAGMENT_SHADER_FILENAME: &'static str = "draw.fs.glsl"; /// A GPU rasterizer for glyphs. pub struct Rasterizer { @@ -92,27 +93,32 @@ impl Rasterizer { let vertex_shader = try!(compile_gl_shader(gl::VERTEX_SHADER, "Vertex shader", - DRAW_VERTEX_SHADER)); + DRAW_VERTEX_SHADER_FILENAME, + &options.shader_path)); gl::AttachShader(draw_program, vertex_shader); let fragment_shader = try!(compile_gl_shader(gl::FRAGMENT_SHADER, "Fragment shader", - DRAW_FRAGMENT_SHADER)); + DRAW_FRAGMENT_SHADER_FILENAME, + &options.shader_path)); gl::AttachShader(draw_program, fragment_shader); if options.force_geometry_shader { let geometry_shader = try!(compile_gl_shader(gl::GEOMETRY_SHADER, "Geometry shader", - DRAW_GEOMETRY_SHADER)); + DRAW_GEOMETRY_SHADER_FILENAME, + &options.shader_path)); gl::AttachShader(draw_program, geometry_shader); } else { let tess_control_shader = try!(compile_gl_shader(gl::TESS_CONTROL_SHADER, "Tessellation control shader", - DRAW_TESS_CONTROL_SHADER)); + DRAW_TESS_CONTROL_SHADER_FILENAME, + &options.shader_path)); gl::AttachShader(draw_program, tess_control_shader); let tess_evaluation_shader = try!(compile_gl_shader(gl::TESS_EVALUATION_SHADER, "Tessellation evaluation shader", - DRAW_TESS_EVALUATION_SHADER)); + DRAW_TESS_EVALUATION_SHADER_FILENAME, + &options.shader_path)); gl::AttachShader(draw_program, tess_evaluation_shader); } @@ -144,12 +150,25 @@ impl Rasterizer { // FIXME(pcwalton): Don't panic if this fails to compile; just return an error. let shading_language = instance.shading_language(); - let accum_source = match shading_language { - ShadingLanguage::Cl => ACCUM_CL_SHADER, - ShadingLanguage::Glsl => ACCUM_COMPUTE_SHADER, + let accum_filename = match shading_language { + ShadingLanguage::Cl => ACCUM_CL_SHADER_FILENAME, + ShadingLanguage::Glsl => ACCUM_COMPUTE_SHADER_FILENAME, }; - let accum_program = try!(device.create_program(accum_source) + let mut accum_path = options.shader_path.to_owned(); + accum_path.push(accum_filename); + + let mut accum_file = match File::open(&accum_path) { + Err(error) => return Err(InitError::ShaderUnreadable(error)), + Ok(file) => file, + }; + + let mut accum_source = String::new(); + if accum_file.read_to_string(&mut accum_source).is_err() { + return Err(InitError::CompileFailed("Compute shader", "Invalid UTF-8".to_string())) + } + + let accum_program = try!(device.create_program(&accum_source) .map_err(InitError::ComputeError)); Ok(Rasterizer { @@ -296,9 +315,25 @@ impl Rasterizer { } } -fn compile_gl_shader(shader_type: GLuint, description: &'static str, source: &str) +fn compile_gl_shader(shader_type: GLuint, + description: &'static str, + filename: &str, + shader_path: &Path) -> Result { unsafe { + let mut path = shader_path.to_owned(); + path.push(filename); + + let mut file = match File::open(&path) { + Err(error) => return Err(InitError::ShaderUnreadable(error)), + Ok(file) => file, + }; + + let mut source = String::new(); + if file.read_to_string(&mut source).is_err() { + return Err(InitError::CompileFailed(description, "Invalid UTF-8".to_string())) + } + let shader = gl::CreateShader(shader_type); gl::ShaderSource(shader, 1, &(source.as_ptr() as *const GLchar), &(source.len() as GLint)); gl::CompileShader(shader); @@ -338,8 +373,13 @@ fn check_gl_object_status(object: GLuint, } /// Options that control Pathfinder's behavior. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Debug)] pub struct RasterizerOptions { + /// The path to the shaders. + /// + /// If not specified, then the current directory is used. This is probably not what you want. + /// The corresponding environment variable is `PATHFINDER_SHADER_PATH`. + pub shader_path: PathBuf, /// If true, then a geometry shader is used instead of a tessellation shader. /// /// This will probably negatively impact performance. This should be considered a debugging @@ -353,6 +393,7 @@ pub struct RasterizerOptions { impl Default for RasterizerOptions { fn default() -> RasterizerOptions { RasterizerOptions { + shader_path: PathBuf::from("."), force_geometry_shader: false, } } @@ -369,6 +410,11 @@ impl RasterizerOptions { /// /// Environment variables not set cause their associated settings to take on default values. pub fn from_env() -> Result { + let shader_path = match env::var("PATHFINDER_SHADER_PATH") { + Ok(ref string) => PathBuf::from(string), + Err(_) => PathBuf::from("."), + }; + let force_geometry_shader = match env::var("PATHFINDER_FORCE_GEOMETRY_SHADER") { Ok(ref string) if string.eq_ignore_ascii_case("on") || string.eq_ignore_ascii_case("yes") || @@ -381,6 +427,7 @@ impl RasterizerOptions { }; Ok(RasterizerOptions { + shader_path: shader_path, force_geometry_shader: force_geometry_shader, }) }