Bug 1535146 - Attempt to load non-startup shaders from disk cache when required. r=bholley

On startup some program binaries are loaded from disk into an
in-memory cache. When we call create_program() we check if the
required program is present in this cache, and if so we call
glProgramBinary(). This is done early on so that the driver can
perform any necessary work in the background.

There may however be binaries in the disk cache that have not yet been
loaded in to memory, in order not to slow down startup. This change
makes it so that we attempt to load missing binaries from disk during
link_program(). The reason we do not do this in create_program() is
because that would result in loading all shaders from disk during
startup, which we want to avoid. Loading these shaders may therefore
take slightly longer than if they'd been loaded at startup, but will
still be much faster than recompiling them from scratch, and startup
will remain quick.

If loading the shaders on startup had previously timed out, then we do
not attempt to load shaders on demand as the disk is probably too slow
for that to be useful.

Depends on D33954

Differential Revision: https://phabricator.services.mozilla.com/D33955

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Jamie Nicol 2019-07-09 16:09:15 +00:00
parent c9c015cbff
commit 191dd4f438
3 changed files with 48 additions and 21 deletions

View File

@ -7,7 +7,7 @@ use std::path::{PathBuf};
use std::rc::Rc;
use std::sync::Arc;
use webrender::{ProgramBinary, ProgramCache, ProgramCacheObserver};
use webrender::{ProgramBinary, ProgramCache, ProgramCacheObserver, ProgramSourceDigest};
use bincode;
use fxhash;
use nsstring::nsAString;
@ -83,6 +83,7 @@ fn get_cache_path_from_prof_path(prof_path: &nsAString) -> Option<PathBuf> {
struct WrProgramBinaryDiskCache {
cache_path: PathBuf,
workers: Arc<ThreadPool>,
cached_shader_filenames: Vec<OsString>,
}
// Magic number + version. Increment the version when the binary format changes.
@ -105,6 +106,7 @@ impl WrProgramBinaryDiskCache {
WrProgramBinaryDiskCache {
cache_path,
workers: Arc::clone(workers),
cached_shader_filenames: Vec::new(),
}
}
@ -184,6 +186,28 @@ impl WrProgramBinaryDiskCache {
}
}
pub fn try_load_shader_from_disk(&mut self, filename: &str, program_cache: &Rc<ProgramCache>) {
if let Some(index) = self.cached_shader_filenames.iter().position(|e| e == filename) {
let mut path = self.cache_path.clone();
path.push(filename);
self.cached_shader_filenames.swap_remove(index);
info!("Loading shader: {}", filename);
match deserialize_program_binary(&path) {
Ok(program) => {
program_cache.load_program_binary(program);
}
Err(err) => {
error!("shader-cache: Failed to deserialize program binary: {}", err);
}
};
} else {
info!("shader-cache: Program binary not found in disk cache");
}
}
pub fn try_load_startup_shaders_from_disk(&mut self, program_cache: &Rc<ProgramCache>) {
use std::time::{Instant};
let start = Instant::now();
@ -202,7 +226,7 @@ impl WrProgramBinaryDiskCache {
};
info!("Loaded startup shader whitelist in {:?}", start.elapsed());
let mut cached_shader_filenames = read_dir(&self.cache_path)
self.cached_shader_filenames = read_dir(&self.cache_path)
.unwrap()
.map(|e| e.unwrap().file_name())
.filter(|e| e != WHITELIST_FILENAME)
@ -210,25 +234,7 @@ impl WrProgramBinaryDiskCache {
// Load whitelisted program binaries if they exist
for entry in &whitelist {
if let Some(index) = cached_shader_filenames.iter().position(|e| e == entry.as_str()) {
let mut path = self.cache_path.clone();
path.push(entry);
cached_shader_filenames.swap_remove(index);
info!("Loading shader: {}", entry);
match deserialize_program_binary(&path) {
Ok(program) => {
program_cache.load_program_binary(program);
}
Err(err) => {
error!("shader-cache: Failed to deserialize program binary: {}", err);
}
};
} else {
info!("shader-cache: Program binary not found in disk cache");
}
self.try_load_shader_from_disk(&entry, program_cache);
let elapsed = start.elapsed();
info!("Loaded shader in {:?}", elapsed);
@ -236,7 +242,11 @@ impl WrProgramBinaryDiskCache {
(elapsed.subsec_nanos() / 1_000_000) as u64;
if elapsed_ms > MAX_LOAD_TIME_MS {
// Loading the startup shaders is taking too long, so bail out now.
// Additionally clear the list of remaining shaders cached on disk,
// so that we do not attempt to load any on demand during rendering.
error!("shader-cache: Timed out before finishing loads");
self.cached_shader_filenames.clear();
break;
}
}
@ -261,6 +271,11 @@ impl ProgramCacheObserver for WrProgramCacheObserver {
self.disk_cache.borrow_mut().update(entries);
}
fn try_load_shader_from_disk(&self, digest: &ProgramSourceDigest, program_cache: &Rc<ProgramCache>) {
let filename = format!("{}", digest);
self.disk_cache.borrow_mut().try_load_shader_from_disk(&filename, program_cache);
}
fn notify_program_binary_failed(&self, _program_binary: &Arc<ProgramBinary>) {
error!("shader-cache: Failed program_binary");
}

View File

@ -798,6 +798,7 @@ impl ProgramBinary {
/// The interfaces that an application can implement to handle ProgramCache update
pub trait ProgramCacheObserver {
fn update_disk_cache(&self, entries: Vec<Arc<ProgramBinary>>);
fn try_load_shader_from_disk(&self, digest: &ProgramSourceDigest, program_cache: &Rc<ProgramCache>);
fn notify_program_binary_failed(&self, program_binary: &Arc<ProgramBinary>);
}
@ -1676,6 +1677,16 @@ impl Device {
// See if we hit the binary shader cache
if let Some(ref cached_programs) = self.cached_programs {
// If the shader is not in the cache, attempt to load it from disk
if cached_programs.entries.borrow().get(&program.source_info.digest).is_none() {
if let Some(ref handler) = cached_programs.program_cache_handler {
handler.try_load_shader_from_disk(&program.source_info.digest, cached_programs);
if let Some(entry) = cached_programs.entries.borrow().get(&program.source_info.digest) {
self.gl.program_binary(program.id, entry.binary.format, &entry.binary.bytes);
}
}
}
if let Some(entry) = cached_programs.entries.borrow_mut().get_mut(&info.digest) {
let mut link_status = [0];
unsafe {

View File

@ -222,3 +222,4 @@ pub use crate::renderer::{
pub use crate::screen_capture::{AsyncScreenshotHandle, RecordedFrameHandle};
pub use crate::shade::{Shaders, WrShaders};
pub use api as webrender_api;
pub use webrender_build::shader::ProgramSourceDigest;