Bug 1604615 - Use optimized shader source in webrender. r=jrmuizel

Add a gecko pref "gfx.webrender.use-optimized-shaders". If enabled,
then when attempting to compile a webrender shader first look for the
optimized source. If the optimized source is not present, emit a
warning and fall back to the unoptimized source.

Use the optimized source by default in wrench, and add the flag
"--use-unoptimized-shaders" to override this.

Differential Revision: https://phabricator.services.mozilla.com/D70033
This commit is contained in:
Jamie Nicol 2020-04-21 10:32:15 +00:00
Родитель f548e9a284
Коммит d9a1b3bbde
11 изменённых файлов: 151 добавлений и 46 удалений

Просмотреть файл

@ -45,6 +45,7 @@ class gfxVarReceiver;
_(UseWebRenderTripleBufferingWin, bool, false) \
_(UseWebRenderCompositor, bool, false) \
_(UseWebRenderProgramBinaryDisk, bool, false) \
_(UseWebRenderOptimizedShaders, bool, false) \
_(UseWebRenderMultithreading, bool, false) \
_(WebRenderMaxPartialPresentRects, int32_t, 0) \
_(WebRenderDebugFlags, int32_t, 0) \

Просмотреть файл

@ -125,6 +125,10 @@ const char* gfx_wr_resource_path_override() {
return resourcePath;
}
bool gfx_wr_use_optimized_shaders() {
return mozilla::gfx::gfxVars::UseWebRenderOptimizedShaders();
}
void gfx_critical_note(const char* msg) { gfxCriticalNote << msg; }
void gfx_critical_error(const char* msg) { gfxCriticalError() << msg; }

Просмотреть файл

@ -3032,6 +3032,10 @@ void gfxPlatform::InitWebRenderConfig() {
gfxConfig::IsEnabled(Feature::WEBRENDER));
}
if (StaticPrefs::gfx_webrender_use_optimized_shaders_AtStartup()) {
gfxVars::SetUseWebRenderOptimizedShaders(gfxConfig::IsEnabled(Feature::WEBRENDER));
}
if (Preferences::GetBool("gfx.webrender.software", false)) {
gfxVars::SetUseSoftwareWebRender(gfxConfig::IsEnabled(Feature::WEBRENDER));
}

Просмотреть файл

@ -510,6 +510,7 @@ extern "C" {
// by commenting out the path that adds an external image ID
fn gfx_use_wrench() -> bool;
fn gfx_wr_resource_path_override() -> *const c_char;
fn gfx_wr_use_optimized_shaders() -> bool;
// TODO: make gfx_critical_error() work.
// We still have problem to pass the error message from render/render_backend
// thread to main thread now.
@ -1142,6 +1143,8 @@ fn wr_device_new(gl_context: *mut c_void, pc: Option<&mut WrProgramCache>) -> De
}
};
let use_optimized_shaders = unsafe { gfx_wr_use_optimized_shaders() };
let cached_programs = match pc {
Some(cached_programs) => Some(Rc::clone(cached_programs.rc_get())),
None => None,
@ -1150,6 +1153,7 @@ fn wr_device_new(gl_context: *mut c_void, pc: Option<&mut WrProgramCache>) -> De
Device::new(
gl,
resource_override_path,
use_optimized_shaders,
upload_method,
cached_programs,
false,
@ -1427,6 +1431,7 @@ pub extern "C" fn wr_window_new(
}
}
},
use_optimized_shaders: unsafe { gfx_wr_use_optimized_shaders() },
renderer_id: Some(window_id.0),
upload_method,
scene_builder_hooks: Some(Box::new(APZCallbacks::new(window_id))),

Просмотреть файл

@ -23,6 +23,7 @@ bool is_glcontext_gles(void* glcontext_ptr);
bool is_glcontext_angle(void* glcontext_ptr);
bool gfx_use_wrench();
const char* gfx_wr_resource_path_override();
bool gfx_wr_use_optimized_shaders();
void gfx_critical_note(const char* msg);
void gfx_critical_error(const char* msg);
void gecko_printf_stderr_output(const char* msg);

Просмотреть файл

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use super::super::shader_source::UNOPTIMIZED_SHADERS;
use super::super::shader_source::{OPTIMIZED_SHADERS, UNOPTIMIZED_SHADERS};
use api::{ColorF, ImageDescriptor, ImageFormat, MemoryReport};
use api::{MixBlendMode, TextureTarget, VoidPtrToSizeFn};
use api::units::*;
@ -662,10 +662,17 @@ pub struct VBOId(gl::GLuint);
#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
struct IBOId(gl::GLuint);
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[derive(Clone, Debug)]
enum ProgramSourceType {
Unoptimized,
Optimized(ShaderVersion),
}
#[derive(Clone, Debug)]
pub struct ProgramSourceInfo {
base_filename: &'static str,
features: Vec<&'static str>,
source_type: ProgramSourceType,
digest: ProgramSourceDigest,
}
@ -677,72 +684,131 @@ impl ProgramSourceInfo {
) -> 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.
//
// Note that we cheat a bit to make the hashing more efficient. First,
// the only difference between the vertex and fragment shader is a
// single deterministic define, so we don't need to hash both. Second,
// we precompute the digest of the expanded source file at build time,
// and then just hash that digest here.
// will always be needed, whereas the source is rarely needed.
use std::collections::hash_map::DefaultHasher;
use std::hash::Hasher;
// Setup.
let mut hasher = DefaultHasher::new();
let version = get_shader_version(&*device.gl());
let override_path = device.resource_override_path.as_ref();
let source_and_digest = UNOPTIMIZED_SHADERS.get(&name).expect("Shader not found");
let gl_version = get_shader_version(&*device.gl());
// Hash the renderer name.
hasher.write(device.capabilities.renderer_name.as_bytes());
// Hash the prefix string.
build_shader_prefix_string(
version,
&features,
ShaderKind::Vertex,
&name,
&mut |s| hasher.write(s.as_bytes()),
);
let full_name = &Self::full_name(name, features);
// Hash the shader file contents. We use a precomputed digest, and
// verify it in debug builds.
if override_path.is_some() || cfg!(debug_assertions) {
let mut h = DefaultHasher::new();
build_shader_main_string(
&name,
&|f| get_unoptimized_shader_source(f, override_path),
&mut |s| h.write(s.as_bytes())
);
let d: ProgramSourceDigest = h.into();
let digest = format!("{}", d);
debug_assert!(override_path.is_some() || digest == source_and_digest.digest);
hasher.write(digest.as_bytes());
let optimized_source = if device.use_optimized_shaders {
OPTIMIZED_SHADERS.get(&(gl_version, full_name)).or_else(|| {
warn!("Missing optimized shader source for {}", full_name);
None
})
} else {
hasher.write(source_and_digest.digest.as_bytes());
None
};
let source_type = match optimized_source {
Some(source_and_digest) => {
// Optimized shader sources are used as-is, without any run-time processing.
// The vertex and fragment shaders are different, so must both be hashed.
// We use the hashes that were computed at build time, and verify it in debug builds.
if cfg!(debug_assertions) {
let mut h = DefaultHasher::new();
h.write(source_and_digest.vert_source.as_bytes());
h.write(source_and_digest.frag_source.as_bytes());
let d: ProgramSourceDigest = h.into();
let digest = d.to_string();
debug_assert_eq!(digest, source_and_digest.digest);
hasher.write(digest.as_bytes());
} else {
hasher.write(source_and_digest.digest.as_bytes());
}
ProgramSourceType::Optimized(gl_version)
}
None => {
// For non-optimized sources 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.
//
// Note that we cheat a bit to make the hashing more efficient. First, the only
// difference between the vertex and fragment shader is a single deterministic
// define, so we don't need to hash both. Second, we precompute the digest of the
// expanded source file at build time, and then just hash that digest here.
let override_path = device.resource_override_path.as_ref();
let source_and_digest = UNOPTIMIZED_SHADERS.get(&name).expect("Shader not found");
// Hash the prefix string.
build_shader_prefix_string(
gl_version,
&features,
ShaderKind::Vertex,
&name,
&mut |s| hasher.write(s.as_bytes()),
);
// Hash the shader file contents. We use a precomputed digest, and
// verify it in debug builds.
if override_path.is_some() || cfg!(debug_assertions) {
let mut h = DefaultHasher::new();
build_shader_main_string(
&name,
&|f| get_unoptimized_shader_source(f, override_path),
&mut |s| h.write(s.as_bytes())
);
let d: ProgramSourceDigest = h.into();
let digest = format!("{}", d);
debug_assert!(override_path.is_some() || digest == source_and_digest.digest);
hasher.write(digest.as_bytes());
} else {
hasher.write(source_and_digest.digest.as_bytes());
}
ProgramSourceType::Unoptimized
}
};
// Finish.
ProgramSourceInfo {
base_filename: name,
features: features.to_vec(),
source_type,
digest: hasher.into(),
}
}
fn compute_source(&self, device: &Device, kind: ShaderKind) -> String {
let mut src = String::new();
device.build_shader_string(
&self.features,
kind,
self.base_filename,
|s| src.push_str(s),
);
src
let full_name = Self::full_name(self.base_filename, &self.features);
match self.source_type {
ProgramSourceType::Optimized(gl_version) => {
let shader = OPTIMIZED_SHADERS
.get(&(gl_version, &full_name))
.unwrap_or_else(|| panic!("Missing optimized shader source for {}", full_name));
match kind {
ShaderKind::Vertex => shader.vert_source.to_string(),
ShaderKind::Fragment => shader.frag_source.to_string(),
}
},
ProgramSourceType::Unoptimized => {
let mut src = String::new();
device.build_shader_string(
&self.features,
kind,
self.base_filename,
|s| src.push_str(s),
);
src
}
}
}
fn full_name(base_filename: &'static str, features: &[&'static str]) -> String {
if features.is_empty() {
base_filename.to_string()
} else {
format!("{}_{}", base_filename, features.join("_"))
}
}
}
@ -1015,6 +1081,9 @@ pub struct Device {
// resources
resource_override_path: Option<PathBuf>,
/// Whether to use shaders that have been optimized at build time.
use_optimized_shaders: bool,
max_texture_size: i32,
max_texture_layers: u32,
cached_programs: Option<Rc<ProgramCache>>,
@ -1250,6 +1319,7 @@ impl Device {
pub fn new(
mut gl: Rc<dyn gl::Gl>,
resource_override_path: Option<PathBuf>,
use_optimized_shaders: bool,
upload_method: UploadMethod,
cached_programs: Option<Rc<ProgramCache>>,
allow_pixel_local_storage_support: bool,
@ -1408,7 +1478,8 @@ impl Device {
),
};
let (depth_format, upload_method) = if renderer_name.starts_with("Software WebRender") {
let is_software_webrender = renderer_name.starts_with("Software WebRender");
let (depth_format, upload_method) = if is_software_webrender {
(gl::DEPTH_COMPONENT16, UploadMethod::Immediate)
} else {
(gl::DEPTH_COMPONENT24, upload_method)
@ -1449,6 +1520,9 @@ impl Device {
gl::GlType::Gles => supports_extension(&extensions,"GL_EXT_blend_func_extended"),
};
// Software webrender relies on the unoptimized shader source.
let use_optimized_shaders = use_optimized_shaders && !is_software_webrender;
// On the android emulator, glShaderSource can crash if the source
// strings are not null-terminated. See bug 1591945.
let requires_null_terminated_shader_source = is_emulator;
@ -1479,6 +1553,7 @@ impl Device {
gl,
base_gl: None,
resource_override_path,
use_optimized_shaders,
upload_method,
inside_frame: false,

Просмотреть файл

@ -2192,6 +2192,7 @@ impl Renderer {
let mut device = Device::new(
gl,
options.resource_override_path.clone(),
options.use_optimized_shaders,
options.upload_method.clone(),
options.cached_programs.take(),
options.allow_pixel_local_storage_support,
@ -6713,6 +6714,8 @@ bitflags! {
pub struct RendererOptions {
pub device_pixel_ratio: f32,
pub resource_override_path: Option<PathBuf>,
/// Whether to use shaders that have been optimized at build time.
pub use_optimized_shaders: bool,
pub enable_aa: bool,
pub enable_dithering: bool,
pub max_recorded_profiles: usize,
@ -6783,6 +6786,7 @@ impl Default for RendererOptions {
RendererOptions {
device_pixel_ratio: 1.0,
resource_override_path: None,
use_optimized_shaders: false,
enable_aa: true,
enable_dithering: false,
debug_flags: DebugFlags::empty(),

Просмотреть файл

@ -21,6 +21,9 @@ args:
long: shaders
help: Override path for shaders
takes_value: true
- use_unoptimized_shaders:
long: use-unoptimized-shaders
help: Use unoptimized shaders rather than the shaders optimized at build-time
- rebuild:
short: r
long: rebuild

Просмотреть файл

@ -657,6 +657,7 @@ fn main() {
&mut window,
events_loop.as_mut().map(|el| el.create_proxy()),
res_path,
!args.is_present("use_unoptimized_shaders"),
dp_ratio,
save_type,
dim,

Просмотреть файл

@ -232,6 +232,7 @@ impl Wrench {
window: &mut WindowWrapper,
proxy: Option<EventsLoopProxy>,
shader_override_path: Option<PathBuf>,
use_optimized_shaders: bool,
dp_ratio: f32,
save_type: Option<SaveType>,
size: DeviceIntSize,
@ -274,6 +275,7 @@ impl Wrench {
let opts = webrender::RendererOptions {
device_pixel_ratio: dp_ratio,
resource_override_path: shader_override_path,
use_optimized_shaders,
recorder,
enable_subpixel_aa: !no_subpixel_aa,
debug_flags,

Просмотреть файл

@ -4151,6 +4151,11 @@
value: @IS_NIGHTLY_BUILD@
mirror: once
- name: gfx.webrender.use-optimized-shaders
type: bool
value: true
mirror: once
#ifdef NIGHTLY_BUILD
# Keep this pref hidden on non-nightly builds to avoid people accidentally
# turning it on.