From 04ec19974cb0021d886cdd5f3ce1439144ffd96b Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Tue, 19 Jan 2016 12:02:27 +1000 Subject: [PATCH] Auto reload shaders while WR is running if they are saved. This makes prototyping / iteration a lot faster when working on shader changes that don't require a recompile. --- Cargo.toml | 4 + src/debug_render.rs | 4 +- src/device.rs | 361 +++++++++++++++++++++++++++++++++--------- src/frame.rs | 12 +- src/internal_types.rs | 2 + src/lib.rs | 2 + src/renderer.rs | 106 +++++++++---- 7 files changed, 372 insertions(+), 119 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index bdcb00fb05..f730395a25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,10 @@ git = "https://github.com/glennw/webrender_traits" git = "https://github.com/ecoal95/rust-offscreen-rendering-context" branch = "webrender" +[dependencies.notify] +git = "https://github.com/glennw/rsnotify.git" +branch = "inotify-modify" + [dependencies] gleam = "0.2" euclid = {version = "0.4", features = ["plugins"]} diff --git a/src/debug_render.rs b/src/debug_render.rs index 860974bab3..6b24b6a47a 100644 --- a/src/debug_render.rs +++ b/src/debug_render.rs @@ -25,8 +25,8 @@ pub struct DebugRenderer { impl DebugRenderer { pub fn new(device: &mut Device) -> DebugRenderer { - let font_program_id = device.create_program("debug_font.vs.glsl", "debug_font.fs.glsl"); - let color_program_id = device.create_program("debug_color.vs.glsl", "debug_color.fs.glsl"); + let font_program_id = device.create_program("debug_font"); + let color_program_id = device.create_program("debug_color"); let font_vao = device.create_vao(VertexFormat::DebugFont); let line_vao = device.create_vao(VertexFormat::DebugColor); diff --git a/src/device.rs b/src/device.rs index ab9f8fbb80..03b2c018c9 100644 --- a/src/device.rs +++ b/src/device.rs @@ -4,12 +4,15 @@ use gleam::gl; use internal_types::{PackedVertex, PackedVertexForTextureCacheUpdate, RenderTargetMode}; use internal_types::{TextureSampler, VertexAttribute}; use internal_types::{DebugFontVertex, DebugColorVertex}; +use notify::{self, Watcher}; use std::collections::HashMap; use std::collections::hash_state::DefaultState; use std::fs::File; +use std::io::Read; use std::path::PathBuf; use std::mem; -use std::io::Read; +use std::sync::mpsc::{channel, Sender}; +use std::thread; use webrender_traits::ImageFormat; #[cfg(not(any(target_os = "android", target_os = "gonk")))] @@ -56,6 +59,10 @@ pub enum VertexFormat { RasterOp, } +pub trait FileWatcherHandler : Send { + fn file_changed(&self, path: PathBuf); +} + impl VertexFormat { fn bind(&self) { match *self { @@ -310,6 +317,54 @@ impl Drop for Texture { struct Program { id: gl::GLuint, u_transform: gl::GLint, + vs_path: PathBuf, + fs_path: PathBuf, + vs_id: Option, + fs_id: Option, +} + +impl Program { + fn attach_and_bind_shaders(&mut self, + vs_id: gl::GLuint, + fs_id: gl::GLuint, + panic_on_fail: bool) -> bool { + gl::attach_shader(self.id, vs_id); + gl::attach_shader(self.id, fs_id); + + gl::bind_attrib_location(self.id, VertexAttribute::Position as gl::GLuint, "aPosition"); + gl::bind_attrib_location(self.id, VertexAttribute::Color as gl::GLuint, "aColor"); + gl::bind_attrib_location(self.id, + VertexAttribute::ColorTexCoord as gl::GLuint, + "aColorTexCoord"); + gl::bind_attrib_location(self.id, + VertexAttribute::MaskTexCoord as gl::GLuint, + "aMaskTexCoord"); + gl::bind_attrib_location(self.id, VertexAttribute::BorderRadii as gl::GLuint, "aBorderRadii"); + gl::bind_attrib_location(self.id, + VertexAttribute::BorderPosition as gl::GLuint, + "aBorderPosition"); + gl::bind_attrib_location(self.id, VertexAttribute::BlurRadius as gl::GLuint, "aBlurRadius"); + gl::bind_attrib_location(self.id, + VertexAttribute::DestTextureSize as gl::GLuint, + "aDestTextureSize"); + gl::bind_attrib_location(self.id, + VertexAttribute::SourceTextureSize as gl::GLuint, + "aSourceTextureSize"); + gl::bind_attrib_location(self.id, VertexAttribute::Misc as gl::GLuint, "aMisc"); + + gl::link_program(self.id); + if gl::get_program_iv(self.id, gl::LINK_STATUS) == (0 as gl::GLint) { + println!("Failed to link shader program: {}", gl::get_program_info_log(self.id)); + gl::detach_shader(self.id, vs_id); + gl::detach_shader(self.id, fs_id); + if panic_on_fail { + panic!("-- Program link failed - exiting --"); + } + false + } else { + true + } + } } impl Drop for Program { @@ -385,6 +440,78 @@ impl VertexUsageHint { #[derive(Copy, Clone, Debug)] pub struct UniformLocation(gl::GLint); +impl UniformLocation { + pub fn invalid() -> UniformLocation { + UniformLocation(-1) + } +} + +enum FileWatcherCmd { + AddWatch(PathBuf), + Exit, +} + +struct FileWatcherThread { + api_tx: Sender, +} + +impl FileWatcherThread { + fn new(handler: Box) -> FileWatcherThread { + let (api_tx, api_rx) = channel(); + + thread::spawn(move || { + + let (watch_tx, watch_rx) = channel(); + + enum Request { + Watcher(notify::Event), + Command(FileWatcherCmd), + } + + let mut file_watcher: notify::RecommendedWatcher = notify::Watcher::new(watch_tx).unwrap(); + + loop { + let request = { + let receiver_from_api = &api_rx; + let receiver_from_watcher = &watch_rx; + select! { + msg = receiver_from_api.recv() => Request::Command(msg.unwrap()), + msg = receiver_from_watcher.recv() => Request::Watcher(msg.unwrap()) + } + }; + + match request { + Request::Watcher(event) => { + handler.file_changed(event.path.unwrap()); + } + Request::Command(cmd) => { + match cmd { + FileWatcherCmd::AddWatch(path) => { + file_watcher.watch(path).ok(); + } + FileWatcherCmd::Exit => { + break; + } + } + } + } + } + }); + + FileWatcherThread { + api_tx: api_tx, + } + } + + fn exit(&self) { + self.api_tx.send(FileWatcherCmd::Exit).ok(); + } + + fn add_watch(&self, path: PathBuf) { + self.api_tx.send(FileWatcherCmd::AddWatch(path)).ok(); + } +} + pub struct Device { // device state bound_color_texture: TextureId, @@ -408,6 +535,7 @@ pub struct Device { // misc. vertex_shader_preamble: String, fragment_shader_preamble: String, + file_watcher: FileWatcherThread, // Used on android only #[allow(dead_code)] @@ -415,18 +543,24 @@ pub struct Device { } impl Device { - pub fn new(resource_path: PathBuf, device_pixel_ratio: f32) -> Device { + pub fn new(resource_path: PathBuf, + device_pixel_ratio: f32, + file_changed_handler: Box) -> Device { + let file_watcher = FileWatcherThread::new(file_changed_handler); + let mut path = resource_path.clone(); path.push(VERTEX_SHADER_PREAMBLE); let mut f = File::open(&path).unwrap(); let mut vertex_shader_preamble = String::new(); f.read_to_string(&mut vertex_shader_preamble).unwrap(); + file_watcher.add_watch(path); let mut path = resource_path.clone(); path.push(FRAGMENT_SHADER_PREAMBLE); let mut f = File::open(&path).unwrap(); let mut fragment_shader_preamble = String::new(); f.read_to_string(&mut fragment_shader_preamble).unwrap(); + file_watcher.add_watch(path); Device { resource_path: resource_path, @@ -449,17 +583,15 @@ impl Device { fragment_shader_preamble: fragment_shader_preamble, next_vao_id: 1, + file_watcher: file_watcher, } } - pub fn compile_shader(filename: &str, + pub fn compile_shader(path: &PathBuf, shader_type: gl::GLenum, - resource_path: &PathBuf, - shader_preamble: &str) - -> gl::GLuint { - let mut path = resource_path.clone(); - path.push(filename); - + shader_preamble: &str, + panic_on_fail: bool) + -> Option { println!("compile {:?}", path); let mut f = File::open(&path).unwrap(); @@ -472,10 +604,15 @@ impl Device { gl::shader_source(id, &[&source[..]]); gl::compile_shader(id); if gl::get_shader_iv(id, gl::COMPILE_STATUS) == (0 as gl::GLint) { - panic!("Failed to compile shader: {}", gl::get_shader_info_log(id)); - } + println!("Failed to compile shader: {}", gl::get_shader_info_log(id)); + if panic_on_fail { + panic!("-- Shader compile failed - exiting --"); + } - id + None + } else { + Some(id) + } } pub fn begin_frame(&mut self) { @@ -746,91 +883,157 @@ impl Device { texture.fbo_ids.clear(); } - pub fn create_program(&mut self, - vs_filename: &str, - fs_filename: &str) -> ProgramId { + pub fn create_program(&mut self, base_filename: &str) -> ProgramId { debug_assert!(self.inside_frame); let pid = gl::create_program(); + let mut vs_path = self.resource_path.clone(); + vs_path.push(&format!("{}.vs.glsl", base_filename)); + self.file_watcher.add_watch(vs_path.clone()); + + let mut fs_path = self.resource_path.clone(); + fs_path.push(&format!("{}.fs.glsl", base_filename)); + self.file_watcher.add_watch(fs_path.clone()); + + let program = Program { + id: pid, + u_transform: -1, + vs_path: vs_path, + fs_path: fs_path, + vs_id: None, + fs_id: None, + }; + + let program_id = ProgramId(pid); + + debug_assert!(self.programs.contains_key(&program_id) == false); + self.programs.insert(program_id, program); + + self.load_program(program_id, true); + + program_id + } + + fn load_program(&mut self, + program_id: ProgramId, + panic_on_fail: bool) { + debug_assert!(self.inside_frame); + + let program = self.programs.get_mut(&program_id).unwrap(); + // todo(gw): store shader ids so they can be freed! - let vs_id = Device::compile_shader(vs_filename, + let vs_id = Device::compile_shader(&program.vs_path, gl::VERTEX_SHADER, - &self.resource_path, - &*self.vertex_shader_preamble); - let fs_id = Device::compile_shader(fs_filename, + &*self.vertex_shader_preamble, + panic_on_fail); + let fs_id = Device::compile_shader(&program.fs_path, gl::FRAGMENT_SHADER, - &self.resource_path, - &*self.fragment_shader_preamble); - - gl::attach_shader(pid, vs_id); - gl::attach_shader(pid, fs_id); + &*self.fragment_shader_preamble, + panic_on_fail); - gl::bind_attrib_location(pid, VertexAttribute::Position as gl::GLuint, "aPosition"); - gl::bind_attrib_location(pid, VertexAttribute::Color as gl::GLuint, "aColor"); - gl::bind_attrib_location(pid, - VertexAttribute::ColorTexCoord as gl::GLuint, - "aColorTexCoord"); - gl::bind_attrib_location(pid, - VertexAttribute::MaskTexCoord as gl::GLuint, - "aMaskTexCoord"); - gl::bind_attrib_location(pid, VertexAttribute::BorderRadii as gl::GLuint, "aBorderRadii"); - gl::bind_attrib_location(pid, - VertexAttribute::BorderPosition as gl::GLuint, - "aBorderPosition"); - gl::bind_attrib_location(pid, VertexAttribute::BlurRadius as gl::GLuint, "aBlurRadius"); - gl::bind_attrib_location(pid, - VertexAttribute::DestTextureSize as gl::GLuint, - "aDestTextureSize"); - gl::bind_attrib_location(pid, - VertexAttribute::SourceTextureSize as gl::GLuint, - "aSourceTextureSize"); - gl::bind_attrib_location(pid, VertexAttribute::Misc as gl::GLuint, "aMisc"); + match (vs_id, fs_id) { + (Some(vs_id), None) => { + println!("FAILED to load fs - falling back to previous!"); + gl::delete_shader(vs_id); + } + (None, Some(fs_id)) => { + println!("FAILED to load vs - falling back to previous!"); + gl::delete_shader(fs_id); + } + (None, None) => { + println!("FAILED to load vs/fs - falling back to previous!"); + } + (Some(vs_id), Some(fs_id)) => { + if let Some(vs_id) = program.vs_id { + gl::detach_shader(program.id, vs_id); + } - gl::link_program(pid); - if gl::get_program_iv(pid, gl::LINK_STATUS) == (0 as gl::GLint) { - panic!("Failed to compile shader program: {}", gl::get_program_info_log(pid)); - } + if let Some(fs_id) = program.fs_id { + gl::detach_shader(program.id, fs_id); + } - let u_transform = gl::get_uniform_location(pid, "uTransform"); + if program.attach_and_bind_shaders(vs_id, fs_id, panic_on_fail) { + if let Some(vs_id) = program.vs_id { + gl::delete_shader(vs_id); + } - let program_id = ProgramId(pid); + if let Some(fs_id) = program.fs_id { + gl::delete_shader(fs_id); + } - let program = Program { - id: pid, - u_transform: u_transform, - }; + program.vs_id = Some(vs_id); + program.fs_id = Some(fs_id); + } else { + let vs_id = program.vs_id.unwrap(); + let fs_id = program.fs_id.unwrap(); + program.attach_and_bind_shaders(vs_id, fs_id, true); + } - debug_assert!(self.programs.contains_key(&program_id) == false); - self.programs.insert(program_id, program); + program.u_transform = gl::get_uniform_location(program.id, "uTransform"); - program_id.bind(); - let u_diffuse = gl::get_uniform_location(pid, "sDiffuse"); - if u_diffuse != -1 { - gl::uniform_1i(u_diffuse, TextureSampler::Color as i32); - } - let u_mask = gl::get_uniform_location(pid, "sMask"); - if u_mask != -1 { - gl::uniform_1i(u_mask, TextureSampler::Mask as i32); + program_id.bind(); + let u_diffuse = gl::get_uniform_location(program.id, "sDiffuse"); + if u_diffuse != -1 { + gl::uniform_1i(u_diffuse, TextureSampler::Color as i32); + } + let u_mask = gl::get_uniform_location(program.id, "sMask"); + if u_mask != -1 { + gl::uniform_1i(u_mask, TextureSampler::Mask as i32); + } + let u_diffuse2d = gl::get_uniform_location(program.id, "sDiffuse2D"); + if u_diffuse2d != -1 { + gl::uniform_1i(u_diffuse2d, TextureSampler::Color as i32); + } + let u_mask2d = gl::get_uniform_location(program.id, "sMask2D"); + if u_mask2d != -1 { + gl::uniform_1i(u_mask2d, TextureSampler::Mask as i32); + } + let u_device_pixel_ratio = gl::get_uniform_location(program.id, "uDevicePixelRatio"); + if u_device_pixel_ratio != -1 { + gl::uniform_1f(u_device_pixel_ratio, self.device_pixel_ratio); + } + } } - let u_diffuse2d = gl::get_uniform_location(pid, "sDiffuse2D"); - if u_diffuse2d != -1 { - gl::uniform_1i(u_diffuse2d, TextureSampler::Color as i32); + } + + pub fn refresh_shader(&mut self, path: PathBuf) { + let mut vs_preamble_path = self.resource_path.clone(); + vs_preamble_path.push(VERTEX_SHADER_PREAMBLE); + + let mut fs_preamble_path = self.resource_path.clone(); + fs_preamble_path.push(FRAGMENT_SHADER_PREAMBLE); + + let mut refresh_all = false; + + if path == vs_preamble_path { + let mut f = File::open(&vs_preamble_path).unwrap(); + self.vertex_shader_preamble = String::new(); + f.read_to_string(&mut self.vertex_shader_preamble).unwrap(); + refresh_all = true; } - let u_mask2d = gl::get_uniform_location(pid, "sMask2D"); - if u_mask2d != -1 { - gl::uniform_1i(u_mask2d, TextureSampler::Mask as i32); + + if path == fs_preamble_path { + let mut f = File::open(&fs_preamble_path).unwrap(); + self.fragment_shader_preamble = String::new(); + f.read_to_string(&mut self.fragment_shader_preamble).unwrap(); + refresh_all = true; } - let u_device_pixel_ratio = gl::get_uniform_location(pid, "uDevicePixelRatio"); - if u_device_pixel_ratio != -1 { - gl::uniform_1f(u_device_pixel_ratio, self.device_pixel_ratio); + + let mut programs_to_update = Vec::new(); + + for (program_id, program) in &mut self.programs { + if refresh_all || program.vs_path == path || program.fs_path == path { + programs_to_update.push(*program_id) + } } - program_id + for program_id in programs_to_update { + self.load_program(program_id, false); + } } pub fn get_uniform_location(&self, program_id: ProgramId, name: &str) -> UniformLocation { - debug_assert!(self.inside_frame); let ProgramId(program_id) = program_id; UniformLocation(gl::get_uniform_location(program_id, name)) } @@ -890,7 +1093,7 @@ impl Device { gl::uniform_matrix_4fv(location, false, &floats); } - pub fn set_uniforms(&self, program: &Program, transform: &Matrix4) { + fn set_uniforms(&self, program: &Program, transform: &Matrix4) { debug_assert!(self.inside_frame); gl::uniform_matrix_4fv(program.u_transform, false, &transform.to_array()); } @@ -1153,3 +1356,9 @@ impl Device { gl::use_program(0); } } + +impl Drop for Device { + fn drop(&mut self) { + self.file_watcher.exit(); + } +} diff --git a/src/frame.rs b/src/frame.rs index 7a543302e3..e155f0e85c 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -835,12 +835,12 @@ impl Frame { } } - pub fn flatten(&mut self, - scene_item: SceneItemKind, - parent_info: &FlattenInfo, - context: &mut FlattenContext, - target: &mut RenderTarget, - level: i32) { + fn flatten(&mut self, + scene_item: SceneItemKind, + parent_info: &FlattenInfo, + context: &mut FlattenContext, + target: &mut RenderTarget, + level: i32) { let _pf = util::ProfileScope::new(" flatten"); let stacking_context = match scene_item { diff --git a/src/internal_types.rs b/src/internal_types.rs index b9a6bc6128..858a1e16c8 100644 --- a/src/internal_types.rs +++ b/src/internal_types.rs @@ -7,6 +7,7 @@ use freelist::{FreeListItem, FreeListItemId}; use profiler::BackendProfileCounters; use std::collections::HashMap; use std::collections::hash_state::DefaultState; +use std::path::PathBuf; use std::sync::Arc; use texture_cache::TextureCacheItem; use util::{self, RectVaryings}; @@ -425,6 +426,7 @@ impl RendererFrame { pub enum ResultMsg { UpdateTextureCache(TextureUpdateList), UpdateBatches(BatchUpdateList), + RefreshShader(PathBuf), NewFrame(RendererFrame, BackendProfileCounters), } diff --git a/src/lib.rs b/src/lib.rs index 3358ad5565..cf22e15e6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![feature(hashmap_hasher)] #![feature(slice_patterns, step_by, convert, zero_one)] +#![feature(mpsc_select)] #[macro_use] extern crate lazy_static; @@ -57,6 +58,7 @@ extern crate euclid; extern crate fnv; extern crate gleam; extern crate ipc_channel; +extern crate notify; extern crate scoped_threadpool; extern crate time; extern crate webrender_traits; diff --git a/src/renderer.rs b/src/renderer.rs index efb83cc9af..c8592d1f63 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,7 +1,7 @@ use batch::{RasterBatch, VertexBufferId}; use debug_render::DebugRenderer; use device::{Device, ProgramId, TextureId, UniformLocation, VertexFormat}; -use device::{TextureFilter, VAOId, VertexUsageHint}; +use device::{TextureFilter, VAOId, VertexUsageHint, FileWatcherHandler}; use euclid::{Rect, Matrix4, Point2D, Size2D}; use fnv::FnvHasher; use gleam::gl; @@ -20,7 +20,7 @@ use std::f32; use std::mem; use std::path::PathBuf; use std::sync::{Arc, Mutex}; -use std::sync::mpsc::{channel, Receiver}; +use std::sync::mpsc::{channel, Receiver, Sender}; use std::thread; use tessellator::BorderCornerTessellation; use texture_cache::{BorderType, TextureCache, TextureInsertOp}; @@ -86,11 +86,25 @@ struct RenderContext { device_pixel_ratio: f32, } +struct FileWatcher { + notifier: Arc>>>, + result_tx: Sender, +} + +impl FileWatcherHandler for FileWatcher { + fn file_changed(&self, path: PathBuf) { + self.result_tx.send(ResultMsg::RefreshShader(path)).ok(); + let mut notifier = self.notifier.lock(); + notifier.as_mut().unwrap().as_mut().unwrap().new_frame_ready(); + } +} + pub struct Renderer { result_rx: Receiver, device: Device, pending_texture_updates: Vec, pending_batch_updates: Vec, + pending_shader_updates: Vec, current_frame: Option, device_pixel_ratio: f32, vertex_buffers: HashMap>, @@ -120,7 +134,6 @@ pub struct Renderer { notifier: Arc>>>, viewport_size: Size2D, - //framebuffer_size: Size2D, enable_profiler: bool, debug: DebugRenderer, @@ -133,7 +146,6 @@ pub struct Renderer { impl Renderer { pub fn new(width: u32, height: u32, - //framebuffer_size: &Size2D, device_pixel_ratio: f32, resource_path: PathBuf, enable_aa: bool, @@ -142,30 +154,25 @@ impl Renderer { let (result_tx, result_rx) = channel(); let initial_viewport = Rect::new(Point2D::zero(), Size2D::new(width as i32, height as i32)); + let notifier = Arc::new(Mutex::new(None)); - let mut device = Device::new(resource_path, device_pixel_ratio); - device.begin_frame(); - - let quad_program_id = device.create_program("quad.vs.glsl", "quad.fs.glsl"); - let blit_program_id = device.create_program("blit.vs.glsl", "blit.fs.glsl"); - let border_program_id = device.create_program("border.vs.glsl", "border.fs.glsl"); - let blend_program_id = device.create_program("blend.vs.glsl", "blend.fs.glsl"); - let filter_program_id = device.create_program("filter.vs.glsl", "filter.fs.glsl"); - let box_shadow_program_id = device.create_program("box_shadow.vs.glsl", - "box_shadow.fs.glsl"); - let blur_program_id = device.create_program("blur.vs.glsl", "blur.fs.glsl"); - - let u_quad_transform_array = device.get_uniform_location(quad_program_id, "uMatrixPalette"); - let u_quad_offset_array = device.get_uniform_location(quad_program_id, "uOffsets"); - let u_tile_params = device.get_uniform_location(quad_program_id, "uTileParams"); - let u_clip_rects = device.get_uniform_location(quad_program_id, "uClipRects"); - let u_atlas_params = device.get_uniform_location(quad_program_id, "uAtlasParams"); - - let u_blend_params = device.get_uniform_location(blend_program_id, "uBlendParams"); + let file_watch_handler = FileWatcher { + result_tx: result_tx.clone(), + notifier: notifier.clone(), + }; - let u_filter_params = device.get_uniform_location(filter_program_id, "uFilterParams"); + let mut device = Device::new(resource_path, + device_pixel_ratio, + Box::new(file_watch_handler)); + device.begin_frame(); - let u_direction = device.get_uniform_location(blur_program_id, "uDirection"); + let quad_program_id = device.create_program("quad"); + let blit_program_id = device.create_program("blit"); + let border_program_id = device.create_program("border"); + let blend_program_id = device.create_program("blend"); + let filter_program_id = device.create_program("filter"); + let box_shadow_program_id = device.create_program("box_shadow"); + let blur_program_id = device.create_program("blur"); let texture_ids = device.create_texture_ids(1024); let mut texture_cache = TextureCache::new(texture_ids); @@ -206,7 +213,6 @@ impl Renderer { device.end_frame(); - let notifier = Arc::new(Mutex::new(None)); let backend_notifier = notifier.clone(); // We need a reference to the webrender context from the render backend in order to share @@ -227,7 +233,7 @@ impl Renderer { backend.run(); }); - let renderer = Renderer { + let mut renderer = Renderer { result_rx: result_rx, device: device, current_frame: None, @@ -235,6 +241,7 @@ impl Renderer { raster_batches: Vec::new(), pending_texture_updates: Vec::new(), pending_batch_updates: Vec::new(), + pending_shader_updates: Vec::new(), border_program_id: border_program_id, device_pixel_ratio: device_pixel_ratio, blend_program_id: blend_program_id, @@ -243,14 +250,14 @@ impl Renderer { blit_program_id: blit_program_id, box_shadow_program_id: box_shadow_program_id, blur_program_id: blur_program_id, - u_blend_params: u_blend_params, - u_filter_params: u_filter_params, - u_direction: u_direction, - u_quad_offset_array: u_quad_offset_array, - u_quad_transform_array: u_quad_transform_array, - u_atlas_params: u_atlas_params, - u_tile_params: u_tile_params, - u_clip_rects: u_clip_rects, + u_blend_params: UniformLocation::invalid(), + u_filter_params: UniformLocation::invalid(), + u_direction: UniformLocation::invalid(), + u_quad_offset_array: UniformLocation::invalid(), + u_quad_transform_array: UniformLocation::invalid(), + u_atlas_params: UniformLocation::invalid(), + u_tile_params: UniformLocation::invalid(), + u_clip_rects: UniformLocation::invalid(), notifier: notifier, viewport_size: Size2D::new(width, height), debug: debug_renderer, @@ -261,11 +268,24 @@ impl Renderer { last_time: 0, }; + renderer.update_uniform_locations(); + let sender = RenderApiSender::new(api_tx); (renderer, sender) } + fn update_uniform_locations(&mut self) { + self.u_quad_transform_array = self.device.get_uniform_location(self.quad_program_id, "uMatrixPalette"); + self.u_quad_offset_array = self.device.get_uniform_location(self.quad_program_id, "uOffsets"); + self.u_tile_params = self.device.get_uniform_location(self.quad_program_id, "uTileParams"); + self.u_clip_rects = self.device.get_uniform_location(self.quad_program_id, "uClipRects"); + self.u_atlas_params = self.device.get_uniform_location(self.quad_program_id, "uAtlasParams"); + self.u_blend_params = self.device.get_uniform_location(self.blend_program_id, "uBlendParams"); + self.u_filter_params = self.device.get_uniform_location(self.filter_program_id, "uFilterParams"); + self.u_direction = self.device.get_uniform_location(self.blur_program_id, "uDirection"); + } + pub fn set_render_notifier(&self, notifier: Box) { let mut notifier_arc = self.notifier.lock().unwrap(); *notifier_arc = Some(notifier); @@ -291,6 +311,9 @@ impl Renderer { self.backend_profile_counters = profile_counters; self.current_frame = Some(frame); } + ResultMsg::RefreshShader(path) => { + self.pending_shader_updates.push(path); + } } } } @@ -300,6 +323,7 @@ impl Renderer { profile_timers.total_time.profile(|| { self.device.begin_frame(); + self.update_shaders(); self.update_texture_cache(); self.update_batches(); self.draw_frame(); @@ -353,6 +377,18 @@ impl Renderer { } } + fn update_shaders(&mut self) { + let update_uniforms = !self.pending_shader_updates.is_empty(); + + for path in self.pending_shader_updates.drain(..) { + self.device.refresh_shader(path); + } + + if update_uniforms { + self.update_uniform_locations(); + } + } + fn update_texture_cache(&mut self) { let mut pending_texture_updates = mem::replace(&mut self.pending_texture_updates, vec![]); for update_list in pending_texture_updates.drain(..) {