diff --git a/Cargo.lock b/Cargo.lock index 4902d5d086d42..3d9575a780314 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2956,7 +2956,6 @@ dependencies = [ "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 3d284ebaa7943..7c347077e5e45 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -704,7 +704,6 @@ WebRenderMemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport, helper.Report(aReport.images, "resource-cache/images"); helper.Report(aReport.rasterized_blobs, "resource-cache/rasterized-blobs"); - helper.Report(aReport.shader_cache, "shader-cache"); // GPU Memory. helper.ReportTexture(aReport.gpu_cache_textures, "gpu-cache"); @@ -714,6 +713,9 @@ WebRenderMemoryReporter::CollectReports(nsIHandleReportCallback* aHandleReport, helper.ReportTexture(aReport.depth_target_textures, "depth-targets"); helper.ReportTexture(aReport.swap_chain, "swap-chains"); + // Total GPU bytes, for sanity-checking the above. + helper.ReportTotalGPUBytes(aReport.total_gpu_bytes_allocated); + FinishAsyncMemoryReport(); }, [](mozilla::ipc::ResponseRejectReason aReason) { diff --git a/gfx/webrender_bindings/RenderThread.cpp b/gfx/webrender_bindings/RenderThread.cpp index aadc6592e0b84..be2b2945ecea0 100644 --- a/gfx/webrender_bindings/RenderThread.cpp +++ b/gfx/webrender_bindings/RenderThread.cpp @@ -30,8 +30,6 @@ using namespace mozilla; static already_AddRefed CreateGLContext(); -MOZ_DEFINE_MALLOC_SIZE_OF(WebRenderRendererMallocSizeOf) - namespace mozilla { namespace wr { @@ -130,18 +128,14 @@ void RenderThread::DoAccumulateMemoryReport( MemoryReport aReport, const RefPtr& aPromise) { MOZ_ASSERT(IsInRenderThread()); + MOZ_ASSERT(aReport.total_gpu_bytes_allocated == 0); for (auto& r : mRenderers) { r.second->AccumulateMemoryReport(&aReport); } - // Note memory used by the shader cache, which is shared across all WR - // instances. - MOZ_ASSERT(aReport.shader_cache == 0); - if (mProgramCache) { - aReport.shader_cache = wr_program_cache_report_memory( - mProgramCache->Raw(), &WebRenderRendererMallocSizeOf); - } + // Note total gpu bytes allocated across all WR instances. + aReport.total_gpu_bytes_allocated += wr_total_gpu_bytes_allocated(); aPromise->Resolve(aReport, __func__); } diff --git a/gfx/webrender_bindings/RendererOGL.cpp b/gfx/webrender_bindings/RendererOGL.cpp index a6124405f0eb7..be6ce2152f90f 100644 --- a/gfx/webrender_bindings/RendererOGL.cpp +++ b/gfx/webrender_bindings/RendererOGL.cpp @@ -208,6 +208,7 @@ void RendererOGL::AccumulateMemoryReport(MemoryReport* aReport) { BytesPerPixel(SurfaceFormat::B8G8R8A8) * (mCompositor->UseTripleBuffering() ? 3 : 2); aReport->swap_chain += swapChainSize; + aReport->total_gpu_bytes_allocated += swapChainSize; } static void DoNotifyWebRenderError(layers::CompositorBridgeParent* aBridge, diff --git a/gfx/webrender_bindings/src/bindings.rs b/gfx/webrender_bindings/src/bindings.rs index 0cc92a7b99a62..3d5e6b4e53116 100644 --- a/gfx/webrender_bindings/src/bindings.rs +++ b/gfx/webrender_bindings/src/bindings.rs @@ -675,6 +675,11 @@ pub unsafe extern "C" fn wr_renderer_accumulate_memory_report(renderer: &mut Ren *report += renderer.report_memory(); } +#[no_mangle] +pub unsafe extern "C" fn wr_total_gpu_bytes_allocated() -> usize { + ::webrender::total_gpu_bytes_allocated() +} + // cbindgen doesn't support tuples, so we have a little struct instead, with // an Into implementation to convert from the tuple to the struct. #[repr(C)] @@ -2794,11 +2799,3 @@ pub unsafe extern "C" fn wr_shaders_delete(shaders: *mut WrShaders, gl_context: } // let shaders go out of scope and get dropped } - -#[no_mangle] -pub unsafe extern "C" fn wr_program_cache_report_memory( - cache: *const WrProgramCache, - size_of_op: VoidPtrToSizeFn, - ) -> usize { - (*cache).program_cache.report_memory(size_of_op) -} diff --git a/gfx/webrender_bindings/src/program_cache.rs b/gfx/webrender_bindings/src/program_cache.rs index b0585f227eb1e..d7a08e16404d1 100644 --- a/gfx/webrender_bindings/src/program_cache.rs +++ b/gfx/webrender_bindings/src/program_cache.rs @@ -11,32 +11,27 @@ use bincode; use fxhash; use nsstring::nsAString; use rayon::ThreadPool; +use uuid::Uuid; const MAX_LOAD_TIME_MS: u64 = 400; +const MAX_CACHED_PROGRAM_COUNT: u32 = 15; fn deserialize_program_binary(path: &PathBuf) -> Result, Error> { let mut buf = vec![]; let mut file = File::open(path)?; file.read_to_end(&mut buf)?; - if buf.len() <= 8 + 4 { + if buf.len() <= 8 { return Err(Error::new(ErrorKind::InvalidData, "File size is too small")); } - let magic = &buf[0 .. 4]; - let hash = &buf[4 .. 8 + 4]; - let data = &buf[8 + 4 ..]; - - // Check if magic + version are correct. - let mv:u32 = bincode::deserialize(&magic).unwrap(); - if mv != MAGIC_AND_VERSION { - return Err(Error::new(ErrorKind::InvalidData, "File data is invalid (magic+version)")); - } + let hash = &buf[0 .. 8]; + let data = &buf[8 ..]; // Check if hash is correct let hash:u64 = bincode::deserialize(&hash).unwrap(); let hash_data = fxhash::hash64(&data); if hash != hash_data { - return Err(Error::new(ErrorKind::InvalidData, "File data is invalid (hash)")); + return Err(Error::new(ErrorKind::InvalidData, "File data is invalid")); } // Deserialize ProgramBinary @@ -83,125 +78,140 @@ fn get_cache_path_from_prof_path(prof_path: &nsAString) -> Option { } struct WrProgramBinaryDiskCache { - cache_path: PathBuf, + cache_path: Option, + program_count: u32, + is_enabled: bool, workers: Arc, } -// Magic number + version. Increment the version when the binary format changes. -const MAGIC: u32 = 0xB154AD30; // BI-SHADE + version. -const VERSION: u32 = 2; -const MAGIC_AND_VERSION: u32 = MAGIC + VERSION; - -/// Helper to convert a closure returning a `Result` to one that returns void. -/// This allows the enclosed code to use the question-mark operator in a -/// context where the calling function doesn't expect a `Result`. -#[allow(unused_must_use)] -fn result_to_void Result<(), ()>>(f: F) { f(); } - impl WrProgramBinaryDiskCache { #[allow(dead_code)] - fn new(cache_path: PathBuf, workers: &Arc) -> Self { - WrProgramBinaryDiskCache { + fn new(prof_path: &nsAString, workers: &Arc) -> Self { + let cache_path = get_cache_path_from_prof_path(prof_path); + let is_enabled = cache_path.is_some(); + let workers = Arc::clone(workers); + + WrProgramBinaryDiskCache{ cache_path, - workers: Arc::clone(workers), + program_count: 0, + is_enabled, + workers, } } - /// Updates the on-disk cache to contain exactly the entries specified. - fn update(&mut self, entries: Vec>) { - info!("Updating on-disk shader cache"); - - // Compute the digests in string form. - let mut entries: Vec<(String, Arc)> = - entries.into_iter().map(|e| (format!("{}", e.source_digest()), e)).collect(); - - // For each file in the current directory, check if it corresponds to - // an entry we're supposed to write. If so, we don't need to write the - // entry. If not, we delete the file. - for existing in read_dir(&self.cache_path).unwrap().filter_map(|f| f.ok()) { - let pos = existing.file_name().to_str() - .and_then(|digest| entries.iter().position(|x| x.0 == digest)); - if let Some(p) = pos { - info!("Found existing shader: {}", existing.file_name().to_string_lossy()); - entries.swap_remove(p); - } else { - self.workers.spawn(move || { - info!("Removing shader: {}", existing.file_name().to_string_lossy()); - ::std::fs::remove_file(existing.path()) - .unwrap_or_else(|e| error!("shader-cache: Failed to remove shader: {:?}", e)); - }); - } + fn notify_binary_added(&mut self, program_binary: &Arc) { + if !self.is_enabled { + return; } - // Write the remaining entries to disk on a worker thread. - for entry in entries.into_iter() { - let (file_name, program_binary) = entry; - let file_path = self.cache_path.join(&file_name); + if let Some(ref cache_path) = self.cache_path { + if let Err(_) = create_dir_all(&cache_path) { + error!("failed to create dir for shader disk cache"); + return; + } - self.workers.spawn(move || result_to_void(move || { - info!("Writing shader: {}", file_name); + self.program_count += 1; + if self.program_count > MAX_CACHED_PROGRAM_COUNT { + // Disable disk cache to avoid storing more shader programs to disk + self.is_enabled = false; + return; + } - use std::time::{Instant}; - let start = Instant::now(); + // Use uuid for file name + let uuid1 = Uuid::new_v4(); + let file_name = uuid1.hyphenated().to_string(); + let program_binary = Arc::clone(program_binary); + let file_path = cache_path.join(&file_name); - let data: Vec = bincode::serialize(&*program_binary) - .map_err(|e| error!("shader-cache: Failed to serialize: {}", e))?; + let program_count = self.program_count; - let mut file = File::create(&file_path) - .map_err(|e| error!("shader-cache: Failed to create file: {}", e))?; + // Save to disk on worker thread + self.workers.spawn(move || { + + use std::time::{Instant}; + let start = Instant::now(); - // Write magic + version. - let mv = MAGIC_AND_VERSION; - let mv = bincode::serialize(&mv).unwrap(); - assert!(mv.len() == 4); - file.write_all(&mv) - .map_err(|e| error!("shader-cache: Failed to write magic + version: {}", e))?; + let data: Vec = match bincode::serialize(&*program_binary) { + Ok(data) => data, + Err(err) => { + error!("Failed to serialize program binary error: {}", err); + return; + } + }; + + let mut file = match File::create(&file_path) { + Ok(file) => file, + Err(err) => { + error!("Unable to create file for program binary error: {}", err); + return; + } + }; // Write hash let hash = fxhash::hash64(&data); let hash = bincode::serialize(&hash).unwrap(); assert!(hash.len() == 8); - file.write_all(&hash) - .map_err(|e| error!("shader-cache: Failed to write hash: {}", e))?; + match file.write_all(&hash) { + Err(err) => { + error!("Failed to write hash to file error: {}", err); + } + _ => {}, + }; // Write serialized data - file.write_all(&data) - .map_err(|e| error!("shader-cache: Failed to write program binary: {}", e))?; - - info!("Wrote shader {} in {:?}", file_name, start.elapsed()); - Ok(()) - })); + match file.write_all(&data) { + Err(err) => { + error!("Failed to write program binary to file error: {}", err); + } + _ => {}, + }; + + let elapsed = start.elapsed(); + info!("notify_binary_added: {} ms program_count {}", + (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64, program_count); + + }); } } pub fn try_load_from_disk(&mut self, program_cache: &Rc) { - use std::time::{Instant}; - let start = Instant::now(); - - // Load program binaries if exist - for entry in read_dir(&self.cache_path).unwrap() { - let entry = entry.unwrap(); - let path = entry.path(); + if !self.is_enabled { + return; + } - info!("Loading shader: {}", entry.file_name().to_string_lossy()); + if let Some(ref cache_path) = self.cache_path { + use std::time::{Instant}; + let start = Instant::now(); - match deserialize_program_binary(&path) { - Ok(program) => { - program_cache.load_program_binary(program); + // Load program binaries if exist + if cache_path.exists() && cache_path.is_dir() { + for entry in read_dir(cache_path).unwrap() { + let entry = entry.unwrap(); + let path = entry.path(); + + info!("loading shader file"); + + match deserialize_program_binary(&path) { + Ok(program) => { + program_cache.load_program_binary(program); + } + Err(err) => { + error!("Failed to desriralize program binary error: {}", err); + } + }; + + self.program_count += 1; + + let elapsed = start.elapsed(); + let elapsed_ms = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64; + info!("deserialize_program_binary: {} ms program_count {}", elapsed_ms, self.program_count); + + if self.program_count > MAX_CACHED_PROGRAM_COUNT || elapsed_ms > MAX_LOAD_TIME_MS { + // Disable disk cache to avoid storing more shader programs to disk + self.is_enabled = false; + break; + } } - Err(err) => { - error!("shader-cache: Failed to deserialize program binary: {}", err); - } - }; - - let elapsed = start.elapsed(); - info!("Loaded shader in {:?}", elapsed); - let elapsed_ms = (elapsed.as_secs() * 1_000) + - (elapsed.subsec_nanos() / 1_000_000) as u64; - - if elapsed_ms > MAX_LOAD_TIME_MS { - error!("shader-cache: Timed out before finishing loads"); - break; } } } @@ -221,38 +231,30 @@ impl WrProgramCacheObserver { } impl ProgramCacheObserver for WrProgramCacheObserver { - fn update_disk_cache(&self, entries: Vec>) { - self.disk_cache.borrow_mut().update(entries); + fn notify_binary_added(&self, program_binary: &Arc) { + self.disk_cache.borrow_mut().notify_binary_added(program_binary); } fn notify_program_binary_failed(&self, _program_binary: &Arc) { - error!("shader-cache: Failed program_binary"); + error!("Failed program_binary"); } } pub struct WrProgramCache { - pub program_cache: Rc, + program_cache: Rc, disk_cache: Option>>, } impl WrProgramCache { pub fn new(prof_path: &nsAString, workers: &Arc) -> Self { - let cache_path = get_cache_path_from_prof_path(prof_path); - let use_disk_cache = cache_path.as_ref().map_or(false, |p| create_dir_all(p).is_ok()); - let (disk_cache, program_cache_observer) = if use_disk_cache { - let cache = Rc::new(RefCell::new(WrProgramBinaryDiskCache::new(cache_path.unwrap(), workers))); - let obs = Box::new(WrProgramCacheObserver::new(Rc::clone(&cache))) as - Box; - (Some(cache), Some(obs)) - } else { - (None, None) - }; - let program_cache = ProgramCache::new(program_cache_observer); + let disk_cache = Rc::new(RefCell::new(WrProgramBinaryDiskCache::new(prof_path, workers))); + let program_cache_observer = Box::new(WrProgramCacheObserver::new(Rc::clone(&disk_cache))); + let program_cache = ProgramCache::new(Some(program_cache_observer)); WrProgramCache { program_cache, - disk_cache: disk_cache, + disk_cache: Some(disk_cache), } } @@ -264,7 +266,7 @@ impl WrProgramCache { if let Some(ref disk_cache) = self.disk_cache { disk_cache.borrow_mut().try_load_from_disk(&self.program_cache); } else { - error!("shader-cache: Shader disk cache is not supported"); + error!("Shader disk cache is not supported"); } } } @@ -276,8 +278,12 @@ pub fn remove_disk_cache(prof_path: &nsAString) -> Result<(), Error> { if let Some(cache_path) = get_cache_path_from_prof_path(prof_path) { if cache_path.exists() { let start = Instant::now(); + remove_dir_all(&cache_path)?; - info!("removed all disk cache shaders in {:?}", start.elapsed()); + + let elapsed = start.elapsed(); + let elapsed_ms = (elapsed.as_secs() * 1_000) + (elapsed.subsec_nanos() / 1_000_000) as u64; + info!("remove_disk_cache: {} ms", elapsed_ms); } } Ok(()) diff --git a/gfx/webrender_bindings/webrender_ffi_generated.h b/gfx/webrender_bindings/webrender_ffi_generated.h index cafd75a003ded..2da5db1a1e517 100644 --- a/gfx/webrender_bindings/webrender_ffi_generated.h +++ b/gfx/webrender_bindings/webrender_ffi_generated.h @@ -539,13 +539,13 @@ struct MemoryReport { uintptr_t fonts; uintptr_t images; uintptr_t rasterized_blobs; - uintptr_t shader_cache; uintptr_t gpu_cache_textures; uintptr_t vertex_data_textures; uintptr_t render_target_textures; uintptr_t texture_cache_textures; uintptr_t depth_target_textures; uintptr_t swap_chain; + uintptr_t total_gpu_bytes_allocated; bool operator==(const MemoryReport& aOther) const { return primitive_stores == aOther.primitive_stores && @@ -557,13 +557,13 @@ struct MemoryReport { fonts == aOther.fonts && images == aOther.images && rasterized_blobs == aOther.rasterized_blobs && - shader_cache == aOther.shader_cache && gpu_cache_textures == aOther.gpu_cache_textures && vertex_data_textures == aOther.vertex_data_textures && render_target_textures == aOther.render_target_textures && texture_cache_textures == aOther.texture_cache_textures && depth_target_textures == aOther.depth_target_textures && - swap_chain == aOther.swap_chain; + swap_chain == aOther.swap_chain && + total_gpu_bytes_allocated == aOther.total_gpu_bytes_allocated; } }; @@ -1008,12 +1008,6 @@ struct MutByteSlice { } }; -// A C function that takes a pointer to a heap allocation and returns its size. -// -// This is borrowed from the malloc_size_of crate, upon which we want to avoid -// a dependency from WebRender. -using VoidPtrToSizeFn = uintptr_t(*)(const void*); - struct RendererStats { uintptr_t total_draw_calls; uintptr_t alpha_target_count; @@ -1122,6 +1116,12 @@ struct WrOpacityProperty { } }; +// A C function that takes a pointer to a heap allocation and returns its size. +// +// This is borrowed from the malloc_size_of crate, upon which we want to avoid +// a dependency from WebRender. +using VoidPtrToSizeFn = uintptr_t(*)(const void*); + extern "C" { extern void AddBlobFont(WrFontInstanceKey aInstanceKey, @@ -1656,11 +1656,6 @@ WrProgramCache *wr_program_cache_new(const nsAString *aProfPath, WrThreadPool *aThreadPool) WR_FUNC; -WR_INLINE -uintptr_t wr_program_cache_report_memory(const WrProgramCache *aCache, - VoidPtrToSizeFn aSizeOfOp) -WR_FUNC; - WR_INLINE void wr_renderer_accumulate_memory_report(Renderer *aRenderer, MemoryReport *aReport) @@ -1861,6 +1856,10 @@ WR_INLINE WrThreadPool *wr_thread_pool_new() WR_FUNC; +WR_INLINE +uintptr_t wr_total_gpu_bytes_allocated() +WR_FUNC; + WR_INLINE void wr_transaction_append_transform_properties(Transaction *aTxn, const WrTransformProperty *aTransformArray, diff --git a/gfx/wr/Cargo.lock b/gfx/wr/Cargo.lock index 8a41d5153726c..2dad0c2a43b18 100644 --- a/gfx/wr/Cargo.lock +++ b/gfx/wr/Cargo.lock @@ -33,11 +33,6 @@ dependencies = [ "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "arrayref" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "arrayvec" version = "0.4.7" @@ -94,20 +89,6 @@ name = "block" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "block-buffer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "byte-tools" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "byteorder" version = "1.2.3" @@ -322,14 +303,6 @@ dependencies = [ "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "digest" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "direct-composition" version = "0.1.0" @@ -407,11 +380,6 @@ dependencies = [ "pkg-config 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "fnv" version = "1.0.6" @@ -484,14 +452,6 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "generic-array" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "gif" version = "0.10.0" @@ -1246,17 +1206,6 @@ name = "sha1" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "sha2" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", - "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "shared_library" version = "0.1.8" @@ -1394,11 +1343,6 @@ dependencies = [ "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "typenum" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "ucd-util" version = "0.1.1" @@ -1560,7 +1504,6 @@ dependencies = [ "ron 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", - "sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "thread_profiler 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1760,7 +1703,6 @@ dependencies = [ "checksum android_glue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum app_units 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9dadc668390b373e73e4abbfc1f07238b09a25858f2f39c06cebc6d8e141d774" -"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" "checksum atty 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4a1aa4c24c0718a250f0681885c1af91419d242f29eb8f2ab28502d80dbd1" "checksum base64 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "85415d2594767338a74a30c1d370b2f3262ec1b4ed2d7bba5b3faf4de40467d9" @@ -1769,8 +1711,6 @@ dependencies = [ "checksum bit-vec 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "02b4ff8b16e6076c3e14220b39fbc1fabb6737522281a388998046859400895f" "checksum bitflags 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d0c54bb8f454c567f21197eefcdbf5679d0bd99f2ddbe52e84c77061952e6789" "checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -"checksum block-buffer 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a076c298b9ecdb530ed9d967e74a6027d6a7478924520acddcddc24c1c8ab3ab" -"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40" "checksum byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "74c0b906e9446b0a2e4f760cdb3fa4b2c48cdc6db8766a845c54b6ff063fd2e9" "checksum bytes 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "2f1d50c876fb7545f5f289cd8b2aee3f359d073ae819eed5d6373638e2c61e59" "checksum cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)" = "0ebb87d1116151416c0cf66a0e3fb6430cccd120fd6300794b4dfaa050ac40ba" @@ -1795,7 +1735,6 @@ dependencies = [ "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" "checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" "checksum deflate 0.7.18 (registry+https://github.com/rust-lang/crates.io-index)" = "32c8120d981901a9970a3a1c97cf8b630e0fa8c3ca31e75b6fd6fd5f9f427b31" -"checksum digest 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "03b072242a8cbaf9c145665af9d250c59af3b958f83ed6824e13533cf76d5b90" "checksum dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a" "checksum downcast-rs 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "18df8ce4470c189d18aa926022da57544f31e154631eb4cfe796aea97051fe6c" "checksum dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "09c3753c3db574d215cba4ea76018483895d7bff25a31b49ba45db21c48e50ab" @@ -1804,7 +1743,6 @@ dependencies = [ "checksum env_logger 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0e6e40ebb0e66918a37b38c7acab4e10d299e0463fe2af5d29b9cc86710cfd2a" "checksum euclid 0.19.3 (registry+https://github.com/rust-lang/crates.io-index)" = "600657e7e5c03bfbccdc68721bc3b5abcb761553973387124eae9c9e4f02c210" "checksum expat-sys 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c470ccb972f2088549b023db8029ed9da9426f5affbf9b62efff7009ab8ed5b1" -"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum font-loader 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fd330f40acb3016432cbfa4c54b3d6e6e893a538df79d8df8fd8c26e21c36aaa" "checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" @@ -1814,7 +1752,6 @@ dependencies = [ "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" "checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518" -"checksum generic-array 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ef25c5683767570c2bbd7deba372926a55eaae9982d7726ee2a1050239d45b9d" "checksum gif 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff3414b424657317e708489d2857d9575f4403698428b040b609b9d1c1a84a2c" "checksum gl_generator 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0ffaf173cf76c73a73e080366bf556b4776ece104b06961766ff11449f38604" "checksum gl_generator 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a795170cbd85b5a7baa58d6d7525cae6a03e486859860c220f7ebbbdd379d0a" @@ -1902,7 +1839,6 @@ dependencies = [ "checksum servo-fontconfig-sys 4.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "38b494f03009ee81914b0e7d387ad7c145cafcd69747c2ec89b0e17bb94f303a" "checksum servo-freetype-sys 4.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9232032c2e85118c0282c6562c84cab12316e655491ba0a5d1905b2320060d1b" "checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" -"checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" "checksum shared_library 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8254bf098ce4d8d7cc7cc6de438c5488adc5297e5b7ffef88816c0a91bd289c1" "checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" "checksum smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "26df3bb03ca5eac2e64192b723d51f56c1b1e0860e7c766281f4598f181acdc8" @@ -1918,7 +1854,6 @@ dependencies = [ "checksum thread_profiler 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5920e77802b177479ab5795767fa48e68f61b2f516c2ac0041e2978dd8efe483" "checksum tiff 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a2cc6c4fd13cb1cfd20abdb196e794ceccb29371855b7e7f575945f920a5b3c2" "checksum time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "d825be0eb33fda1a7e68012d51e9c7f451dc1a69391e7fdc197060bb8c56667b" -"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6a0180bc61fc5a987082bfa111f4cc95c4caff7f9799f3e46df09163a937aa25" diff --git a/gfx/wr/webrender/Cargo.toml b/gfx/wr/webrender/Cargo.toml index d2d7020a32462..c299a2ddab69d 100644 --- a/gfx/wr/webrender/Cargo.toml +++ b/gfx/wr/webrender/Cargo.toml @@ -37,7 +37,6 @@ rayon = "1" ron = { optional = true, version = "0.1.7" } serde = { optional = true, version = "1.0", features = ["serde_derive"] } serde_json = { optional = true, version = "1.0" } -sha2 = "0.7" smallvec = "0.6" thread_profiler = "0.1.1" time = "0.1" diff --git a/gfx/wr/webrender/src/debug_render.rs b/gfx/wr/webrender/src/debug_render.rs index a11afbd4c3868..2f2e769041203 100644 --- a/gfx/wr/webrender/src/debug_render.rs +++ b/gfx/wr/webrender/src/debug_render.rs @@ -108,7 +108,7 @@ impl DebugRenderer { pub fn new(device: &mut Device) -> Result { let font_program = device.create_program_linked( "debug_font", - String::new(), + "", &DESC_FONT, )?; device.bind_program(&font_program); @@ -116,7 +116,7 @@ impl DebugRenderer { let color_program = device.create_program_linked( "debug_color", - String::new(), + "", &DESC_COLOR, )?; diff --git a/gfx/wr/webrender/src/device/gl.rs b/gfx/wr/webrender/src/device/gl.rs index 823cd2c4acb31..1e14d5e63522e 100644 --- a/gfx/wr/webrender/src/device/gl.rs +++ b/gfx/wr/webrender/src/device/gl.rs @@ -6,24 +6,20 @@ use super::super::shader_source; use api::{ColorF, ImageFormat, MemoryReport}; use api::{DeviceIntPoint, DeviceIntRect, DeviceIntSize}; use api::TextureTarget; -use api::VoidPtrToSizeFn; #[cfg(any(feature = "debug_renderer", feature="capture"))] use api::ImageDescriptor; use euclid::Transform3D; use gleam::gl; use internal_types::{FastHashMap, LayerIndex, RenderTargetInfo}; use log::Level; -use sha2::{Digest, Sha256}; use smallvec::SmallVec; -use std::borrow::Cow; -use std::cell::{Cell, RefCell}; +use std::cell::RefCell; use std::cmp; use std::collections::hash_map::Entry; use std::fs::File; use std::io::Read; use std::marker::PhantomData; use std::mem; -use std::os::raw::c_void; use std::ops::Add; use std::path::PathBuf; use std::ptr; @@ -184,7 +180,7 @@ fn get_shader_version(gl: &gl::Gl) -> &'static str { // Get a shader string by name, from the built in resources or // an override path, if supplied. -fn get_shader_source(shader_name: &str, base_path: &Option) -> Option> { +fn get_shader_source(shader_name: &str, base_path: &Option) -> Option { if let Some(ref base) = *base_path { let shader_path = base.join(&format!("{}.glsl", shader_name)); if shader_path.exists() { @@ -193,18 +189,18 @@ fn get_shader_source(shader_name: &str, base_path: &Option) -> Option(source: Cow<'static, str>, base_path: &Option, output: &mut F) { +// prepended to the list of outputs. +fn parse_shader_source(source: String, base_path: &Option, output: &mut String) { for line in source.lines() { if line.starts_with(SHADER_IMPORT) { let imports = line[SHADER_IMPORT.len() ..].split(','); @@ -216,72 +212,50 @@ fn parse_shader_source(source: Cow<'static, str>, base_path: &Op } } } else { - output(line); - output("\n"); + output.push_str(line); + output.push_str("\n"); } } } -/// Creates heap-allocated strings for both vertex and fragment shaders. Public -/// to be accessible to tests. pub fn build_shader_strings( - gl_version_string: &str, - features: &str, - base_filename: &str, - override_path: &Option, -) -> (String, String) { - let mut vs_source = String::new(); - do_build_shader_string( - gl_version_string, - features, - SHADER_KIND_VERTEX, - base_filename, - override_path, - |s| vs_source.push_str(s), - ); - - let mut fs_source = String::new(); - do_build_shader_string( - gl_version_string, - features, - SHADER_KIND_FRAGMENT, - base_filename, - override_path, - |s| fs_source.push_str(s), - ); - - (vs_source, fs_source) -} - -/// Walks the given shader string and applies the output to the provided -/// callback. Assuming an override path is not used, does no heap allocation -/// and no I/O. -fn do_build_shader_string( gl_version_string: &str, features: &str, - kind: &str, base_filename: &str, override_path: &Option, - mut output: F, -) { +) -> (String, String) { + // Construct a list of strings to be passed to the shader compiler. + let mut vs_source = String::new(); + let mut fs_source = String::new(); + // GLSL requires that the version number comes first. - output(gl_version_string); + vs_source.push_str(gl_version_string); + fs_source.push_str(gl_version_string); // Insert the shader name to make debugging easier. let name_string = format!("// {}\n", base_filename); - output(&name_string); + vs_source.push_str(&name_string); + fs_source.push_str(&name_string); // Define a constant depending on whether we are compiling VS or FS. - output(kind); + vs_source.push_str(SHADER_KIND_VERTEX); + fs_source.push_str(SHADER_KIND_FRAGMENT); // Add any defines that were passed by the caller. - output(features); + vs_source.push_str(features); + fs_source.push_str(features); // Parse the main .glsl file, including any imports // and append them to the list of sources. + let mut shared_result = String::new(); if let Some(shared_source) = get_shader_source(base_filename, override_path) { - parse_shader_source(shared_source, override_path, &mut output); + parse_shader_source(shared_source, override_path, &mut shared_result); } + + vs_source.push_str(&shared_result); + fs_source.push_str(&shared_result); + + (vs_source, fs_source) } pub trait FileWatcherHandler: Send { @@ -612,17 +586,23 @@ impl Drop for Texture { } } +/// Temporary state retained by a program when it +/// is created, discarded when it is linked. +struct ProgramInitState { + base_filename: String, + sources: ProgramSources, +} + pub struct Program { id: gl::GLuint, u_transform: gl::GLint, u_mode: gl::GLint, - source_info: ProgramSourceInfo, - is_initialized: bool, + init_state: Option, } impl Program { pub fn is_initialized(&self) -> bool { - self.is_initialized + self.init_state.is_none() } } @@ -691,125 +671,54 @@ pub struct VBOId(gl::GLuint); #[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)] struct IBOId(gl::GLuint); -#[derive(PartialEq, Eq, Hash, Debug, Clone, Default)] -#[cfg_attr(feature = "serialize_program", derive(Deserialize, Serialize))] -pub struct ProgramSourceDigest([u8; 32]); - -impl ::std::fmt::Display for ProgramSourceDigest { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - for byte in self.0.iter() { - f.write_fmt(format_args!("{:02x}", byte))?; - } - Ok(()) - } -} - #[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub struct ProgramSourceInfo { - base_filename: &'static str, - features: String, - digest: ProgramSourceDigest, +#[cfg_attr(feature = "serialize_program", derive(Deserialize, Serialize))] +pub struct ProgramSources { + renderer_name: String, + vs_source: String, + fs_source: String, } -impl ProgramSourceInfo { - fn new( - device: &Device, - base_filename: &'static str, - features: String, - ) -> Self { - // Compute the digest. Assuming the device has a `ProgramCache`, this - // will always be needed, whereas the source is rarely needed. As such, - // we compute the hash by walking the static strings in the same order - // as we would when concatenating the source, to avoid heap-allocating - // in the common case. - - // Construct the hasher. - let mut hasher = Sha256::new(); - - // Hash the renderer name. - hasher.input(device.renderer_name.as_bytes()); - - // Hash the vertex shader. - device.build_shader_string( - &features, - SHADER_KIND_VERTEX, - &base_filename, - |s| hasher.input(s.as_bytes()), - ); - - // Hash the fragment shader. - device.build_shader_string( - &features, - SHADER_KIND_FRAGMENT, - base_filename, - |s| hasher.input(s.as_bytes()), - ); - - // Finish. - let mut digest = ProgramSourceDigest::default(); - digest.0.copy_from_slice(hasher.result().as_slice()); - - ProgramSourceInfo { - base_filename, - features, - digest, +impl ProgramSources { + fn new(renderer_name: String, vs_source: String, fs_source: String) -> Self { + ProgramSources { + renderer_name, + vs_source, + fs_source, } } - - fn compute_source(&self, device: &Device, kind: &str) -> String { - let mut src = String::new(); - device.build_shader_string( - &self.features, - kind, - self.base_filename, - |s| src.push_str(s), - ); - src - } } #[cfg_attr(feature = "serialize_program", derive(Deserialize, Serialize))] pub struct ProgramBinary { - bytes: Vec, + binary: Vec, format: gl::GLenum, - source_digest: ProgramSourceDigest, + #[cfg(feature = "serialize_program")] + sources: ProgramSources, } impl ProgramBinary { - fn new(bytes: Vec, + #[allow(unused_variables)] + fn new(binary: Vec, format: gl::GLenum, - source_digest: ProgramSourceDigest) -> Self { + sources: &ProgramSources) -> Self { ProgramBinary { - bytes, + binary, format, - source_digest, + #[cfg(feature = "serialize_program")] + sources: sources.clone(), } } - - /// Returns a reference to the source digest hash. - pub fn source_digest(&self) -> &ProgramSourceDigest { - &self.source_digest - } } /// The interfaces that an application can implement to handle ProgramCache update pub trait ProgramCacheObserver { - fn update_disk_cache(&self, entries: Vec>); + fn notify_binary_added(&self, program_binary: &Arc); fn notify_program_binary_failed(&self, program_binary: &Arc); } -struct ProgramCacheEntry { - /// The binary. - binary: Arc, - /// True if the binary has been linked, i.e. used for rendering. - linked: bool, -} - pub struct ProgramCache { - entries: RefCell>, - - /// True if we've already updated the disk cache with the shaders used during startup. - updated_disk_cache: Cell, + binaries: RefCell>>, /// Optional trait object that allows the client /// application to handle ProgramCache updating @@ -820,47 +729,17 @@ impl ProgramCache { pub fn new(program_cache_observer: Option>) -> Rc { Rc::new( ProgramCache { - entries: RefCell::new(FastHashMap::default()), - updated_disk_cache: Cell::new(false), + binaries: RefCell::new(FastHashMap::default()), program_cache_handler: program_cache_observer, } ) } - - /// Notify that we've rendered the first few frames, and that the shaders - /// we've loaded correspond to the shaders needed during startup, and thus - /// should be the ones cached to disk. - fn startup_complete(&self) { - if self.updated_disk_cache.get() { - return; - } - - if let Some(ref handler) = self.program_cache_handler { - let active_shaders = self.entries.borrow().values() - .filter(|e| e.linked).map(|e| e.binary.clone()) - .collect::>(); - handler.update_disk_cache(active_shaders); - self.updated_disk_cache.set(true); - } - } - /// Load ProgramBinary to ProgramCache. /// The function is typically used to load ProgramBinary from disk. #[cfg(feature = "serialize_program")] pub fn load_program_binary(&self, program_binary: Arc) { - let digest = program_binary.source_digest.clone(); - let entry = ProgramCacheEntry { - binary: program_binary, - linked: false, - }; - self.entries.borrow_mut().insert(digest, entry); - } - - /// Returns the number of bytes allocated for shaders in the cache. - pub fn report_memory(&self, op: VoidPtrToSizeFn) -> usize { - self.entries.borrow().values() - .map(|e| unsafe { op(e.binary.bytes.as_ptr() as *const c_void ) }) - .sum() + let sources = program_binary.sources.clone(); + self.binaries.borrow_mut().insert(sources, program_binary); } } @@ -1463,28 +1342,91 @@ impl Device { } /// Link a program, attaching the supplied vertex format. - /// - /// If `create_program()` finds a binary shader on disk, it will kick - /// off linking immediately, which some drivers (notably ANGLE) run - /// in parallel on background threads. As such, this function should - /// ideally be run sometime later, to give the driver time to do that - /// before blocking due to an API call accessing the shader. - /// - /// This generally means that the first run of the application will have - /// to do a bunch of blocking work to compile the shader from source, but - /// subsequent runs should load quickly. + /// Ideally, this should be run some time after the program + /// is created. This gives some drivers time to compile the + /// shader on a background thread, before blocking due to + /// an API call accessing the shader. pub fn link_program( &mut self, program: &mut Program, descriptor: &VertexDescriptor, ) -> Result<(), ShaderError> { - assert!(!program.is_initialized()); - let mut build_program = true; - let info = &program.source_info; + if let Some(init_state) = program.init_state.take() { + let mut build_program = true; + + // See if we hit the binary shader cache + if let Some(ref cached_programs) = self.cached_programs { + if let Some(binary) = cached_programs.binaries.borrow().get(&init_state.sources) { + let mut link_status = [0]; + unsafe { + self.gl.get_program_iv(program.id, gl::LINK_STATUS, &mut link_status); + } + if link_status[0] == 0 { + let error_log = self.gl.get_program_info_log(program.id); + error!( + "Failed to load a program object with a program binary: {} renderer {}\n{}", + &init_state.base_filename, + self.renderer_name, + error_log + ); + if let Some(ref program_cache_handler) = cached_programs.program_cache_handler { + program_cache_handler.notify_program_binary_failed(&binary); + } + } else { + build_program = false; + } + } + } + + // If not, we need to do a normal compile + link pass. + if build_program { + // Compile the vertex shader + let vs_id = + match Device::compile_shader(&*self.gl, &init_state.base_filename, gl::VERTEX_SHADER, &init_state.sources.vs_source) { + Ok(vs_id) => vs_id, + Err(err) => return Err(err), + }; + + // Compile the fragment shader + let fs_id = + match Device::compile_shader(&*self.gl, &init_state.base_filename, gl::FRAGMENT_SHADER, &init_state.sources.fs_source) { + Ok(fs_id) => fs_id, + Err(err) => { + self.gl.delete_shader(vs_id); + return Err(err); + } + }; + + // Attach shaders + self.gl.attach_shader(program.id, vs_id); + self.gl.attach_shader(program.id, fs_id); + + // Bind vertex attributes + for (i, attr) in descriptor + .vertex_attributes + .iter() + .chain(descriptor.instance_attributes.iter()) + .enumerate() + { + self.gl + .bind_attrib_location(program.id, i as gl::GLuint, attr.name); + } + + if self.cached_programs.is_some() { + self.gl.program_parameter_i(program.id, gl::PROGRAM_BINARY_RETRIEVABLE_HINT, gl::TRUE as gl::GLint); + } + + // Link! + self.gl.link_program(program.id); + + // GL recommends detaching and deleting shaders once the link + // is complete (whether successful or not). This allows the driver + // to free any memory associated with the parsing and compilation. + self.gl.detach_shader(program.id, vs_id); + self.gl.detach_shader(program.id, fs_id); + self.gl.delete_shader(vs_id); + self.gl.delete_shader(fs_id); - // See if we hit the binary shader cache - if let Some(ref cached_programs) = self.cached_programs { - if let Some(entry) = cached_programs.entries.borrow_mut().get_mut(&info.digest) { let mut link_status = [0]; unsafe { self.gl.get_program_iv(program.id, gl::LINK_STATUS, &mut link_status); @@ -1492,111 +1434,39 @@ impl Device { if link_status[0] == 0 { let error_log = self.gl.get_program_info_log(program.id); error!( - "Failed to load a program object with a program binary: {} renderer {}\n{}", - &info.base_filename, - self.renderer_name, - error_log + "Failed to link shader program: {}\n{}", + &init_state.base_filename, + error_log ); - if let Some(ref program_cache_handler) = cached_programs.program_cache_handler { - program_cache_handler.notify_program_binary_failed(&entry.binary); - } - } else { - entry.linked = true; - build_program = false; + self.gl.delete_program(program.id); + return Err(ShaderError::Link(init_state.base_filename.clone(), error_log)); } - } - } - // If not, we need to do a normal compile + link pass. - if build_program { - // Compile the vertex shader - let vs_source = info.compute_source(self, SHADER_KIND_VERTEX); - let vs_id = match Device::compile_shader(&*self.gl, &info.base_filename, gl::VERTEX_SHADER, &vs_source) { - Ok(vs_id) => vs_id, - Err(err) => return Err(err), - }; - - // Compile the fragment shader - let fs_source = info.compute_source(self, SHADER_KIND_FRAGMENT); - let fs_id = - match Device::compile_shader(&*self.gl, &info.base_filename, gl::FRAGMENT_SHADER, &fs_source) { - Ok(fs_id) => fs_id, - Err(err) => { - self.gl.delete_shader(vs_id); - return Err(err); - } - }; - - // Attach shaders - self.gl.attach_shader(program.id, vs_id); - self.gl.attach_shader(program.id, fs_id); - - // Bind vertex attributes - for (i, attr) in descriptor - .vertex_attributes - .iter() - .chain(descriptor.instance_attributes.iter()) - .enumerate() - { - self.gl - .bind_attrib_location(program.id, i as gl::GLuint, attr.name); - } - - if self.cached_programs.is_some() { - self.gl.program_parameter_i(program.id, gl::PROGRAM_BINARY_RETRIEVABLE_HINT, gl::TRUE as gl::GLint); - } - - // Link! - self.gl.link_program(program.id); - - // GL recommends detaching and deleting shaders once the link - // is complete (whether successful or not). This allows the driver - // to free any memory associated with the parsing and compilation. - self.gl.detach_shader(program.id, vs_id); - self.gl.detach_shader(program.id, fs_id); - self.gl.delete_shader(vs_id); - self.gl.delete_shader(fs_id); - - let mut link_status = [0]; - unsafe { - self.gl.get_program_iv(program.id, gl::LINK_STATUS, &mut link_status); - } - if link_status[0] == 0 { - let error_log = self.gl.get_program_info_log(program.id); - error!( - "Failed to link shader program: {}\n{}", - &info.base_filename, - error_log - ); - self.gl.delete_program(program.id); - return Err(ShaderError::Link(info.base_filename.to_owned(), error_log)); - } - - if let Some(ref cached_programs) = self.cached_programs { - if !cached_programs.entries.borrow().contains_key(&info.digest) { - let (buffer, format) = self.gl.get_program_binary(program.id); - if buffer.len() > 0 { - let entry = ProgramCacheEntry { - binary: Arc::new(ProgramBinary::new(buffer, format, info.digest.clone())), - linked: true, - }; - cached_programs.entries.borrow_mut().insert(info.digest.clone(), entry); + if let Some(ref cached_programs) = self.cached_programs { + if !cached_programs.binaries.borrow().contains_key(&init_state.sources) { + let (buffer, format) = self.gl.get_program_binary(program.id); + if buffer.len() > 0 { + let program_binary = Arc::new(ProgramBinary::new(buffer, format, &init_state.sources)); + if let Some(ref program_cache_handler) = cached_programs.program_cache_handler { + program_cache_handler.notify_binary_added(&program_binary); + } + cached_programs.binaries.borrow_mut().insert(init_state.sources, program_binary); + } } } } - } - // If we get here, the link succeeded, so get the uniforms. - program.is_initialized = true; - program.u_transform = self.gl.get_uniform_location(program.id, "uTransform"); - program.u_mode = self.gl.get_uniform_location(program.id, "uMode"); + // If we get here, the link succeeded, so get the uniforms. + program.u_transform = self.gl.get_uniform_location(program.id, "uTransform"); + program.u_mode = self.gl.get_uniform_location(program.id, "uMode"); + } Ok(()) } pub fn bind_program(&mut self, program: &Program) { debug_assert!(self.inside_frame); - debug_assert!(program.is_initialized()); + debug_assert!(program.init_state.is_none()); if self.bound_program != program.id { self.gl.use_program(program.id); @@ -1995,8 +1865,8 @@ impl Device { /// Create a shader program and link it immediately. pub fn create_program_linked( &mut self, - base_filename: &'static str, - features: String, + base_filename: &str, + features: &str, descriptor: &VertexDescriptor, ) -> Result { let mut program = self.create_program(base_filename, features)?; @@ -2004,59 +1874,54 @@ impl Device { Ok(program) } - /// Create a shader program. This does minimal amount of work to start - /// loading a binary shader. If a binary shader is found, we invoke - /// glProgramBinary, which, at least on ANGLE, will load and link the - /// binary on a background thread. This can speed things up later when - /// we invoke `link_program()`. + /// Create a shader program. This does minimal amount of work + /// to start loading a binary shader. The main part of the + /// work is done in link_program. pub fn create_program( &mut self, - base_filename: &'static str, - features: String, + base_filename: &str, + features: &str, ) -> Result { debug_assert!(self.inside_frame); - let source_info = ProgramSourceInfo::new(self, base_filename, features); + let gl_version_string = get_shader_version(&*self.gl); + + let (vs_source, fs_source) = build_shader_strings( + gl_version_string, + features, + base_filename, + &self.resource_override_path, + ); + + let sources = ProgramSources::new(self.renderer_name.clone(), vs_source, fs_source); // Create program let pid = self.gl.create_program(); // Attempt to load a cached binary if possible. if let Some(ref cached_programs) = self.cached_programs { - if let Some(entry) = cached_programs.entries.borrow().get(&source_info.digest) { - self.gl.program_binary(pid, entry.binary.format, &entry.binary.bytes); + if let Some(binary) = cached_programs.binaries.borrow().get(&sources) { + self.gl.program_binary(pid, binary.format, &binary.binary); } } + // Set up the init state that will be used in link_program. + let init_state = Some(ProgramInitState { + base_filename: base_filename.to_owned(), + sources, + }); + // Use 0 for the uniforms as they are initialized by link_program. let program = Program { id: pid, u_transform: 0, u_mode: 0, - source_info, - is_initialized: false, + init_state, }; Ok(program) } - fn build_shader_string( - &self, - features: &str, - kind: &str, - base_filename: &str, - output: F, - ) { - do_build_shader_string( - get_shader_version(&*self.gl), - features, - kind, - base_filename, - &self.resource_override_path, - output, - ) - } - pub fn bind_shader_samplers(&mut self, program: &Program, bindings: &[(&'static str, S)]) where S: Into + Copy, @@ -2567,15 +2432,6 @@ impl Device { self.gl.active_texture(gl::TEXTURE0); self.frame_id.0 += 1; - - // Declare startup complete after the first ten frames. This number is - // basically a heuristic, which dictates how early a shader needs to be - // used in order to be cached to disk. - if self.frame_id.0 == 10 { - if let Some(ref cache) = self.cached_programs { - cache.startup_complete(); - } - } } pub fn clear_target( diff --git a/gfx/wr/webrender/src/lib.rs b/gfx/wr/webrender/src/lib.rs index 9bd30b1df2d30..fc6582219cdf9 100644 --- a/gfx/wr/webrender/src/lib.rs +++ b/gfx/wr/webrender/src/lib.rs @@ -179,7 +179,6 @@ extern crate rayon; extern crate ron; #[cfg(feature = "debugger")] extern crate serde_json; -extern crate sha2; extern crate smallvec; extern crate time; #[cfg(feature = "debugger")] @@ -195,8 +194,8 @@ pub extern crate webrender_api; #[doc(hidden)] pub use device::{build_shader_strings, ReadPixelsFormat, UploadMethod, VertexUsageHint}; -pub use device::{ProgramBinary, ProgramCache, ProgramCacheObserver}; -pub use device::Device; +pub use device::{ProgramBinary, ProgramCache, ProgramCacheObserver, ProgramSources}; +pub use device::{Device, total_gpu_bytes_allocated}; pub use frame_builder::ChasePrimitive; pub use renderer::{AsyncPropertySampler, CpuProfile, DebugFlags, OutputImageHandler, RendererKind}; pub use renderer::{ExternalImage, ExternalImageHandler, ExternalImageSource, GpuProfile}; diff --git a/gfx/wr/webrender/src/renderer.rs b/gfx/wr/webrender/src/renderer.rs index 7d880b86ea14d..66c0298659eeb 100644 --- a/gfx/wr/webrender/src/renderer.rs +++ b/gfx/wr/webrender/src/renderer.rs @@ -1123,7 +1123,7 @@ impl GpuCacheTexture { let bus = if use_scatter { let program = device.create_program_linked( "gpu_cache_update", - String::new(), + "", &desc::GPU_CACHE_UPDATE, )?; let buf_position = device.create_vbo(); diff --git a/gfx/wr/webrender/src/shade.rs b/gfx/wr/webrender/src/shade.rs index 738742b0e0413..ba004d6b32dbc 100644 --- a/gfx/wr/webrender/src/shade.rs +++ b/gfx/wr/webrender/src/shade.rs @@ -424,7 +424,7 @@ fn create_prim_shader( debug!("PrimShader {}", name); - device.create_program(name, prefix) + device.create_program(name, &prefix) } fn create_clip_shader(name: &'static str, device: &mut Device) -> Result { @@ -435,7 +435,7 @@ fn create_clip_shader(name: &'static str, device: &mut Device) -> Result