diff options
Diffstat (limited to 'gfx/wr/webrender_build/src')
-rw-r--r-- | gfx/wr/webrender_build/src/lib.rs | 19 | ||||
-rw-r--r-- | gfx/wr/webrender_build/src/shader.rs | 240 | ||||
-rw-r--r-- | gfx/wr/webrender_build/src/shader_features.rs | 240 |
3 files changed, 499 insertions, 0 deletions
diff --git a/gfx/wr/webrender_build/src/lib.rs b/gfx/wr/webrender_build/src/lib.rs new file mode 100644 index 0000000000..23438a478e --- /dev/null +++ b/gfx/wr/webrender_build/src/lib.rs @@ -0,0 +1,19 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +#[macro_use] +extern crate bitflags; + +#[macro_use] +extern crate lazy_static; + +#[cfg(any(feature = "serde"))] +#[macro_use] +extern crate serde; + +pub mod shader; +pub mod shader_features; + +/// This must be known at build-time as the shaders depend on it. +pub const MAX_VERTEX_TEXTURE_WIDTH: usize = 1024; diff --git a/gfx/wr/webrender_build/src/shader.rs b/gfx/wr/webrender_build/src/shader.rs new file mode 100644 index 0000000000..5db5d738e7 --- /dev/null +++ b/gfx/wr/webrender_build/src/shader.rs @@ -0,0 +1,240 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +//! Functionality for managing source code for shaders. +//! +//! This module is used during precompilation (build.rs) and regular compilation, +//! so it has minimal dependencies. + +use std::borrow::Cow; +use std::fs::File; +use std::io::Read; +use std::path::Path; +use std::collections::HashSet; +use std::collections::hash_map::DefaultHasher; +use crate::MAX_VERTEX_TEXTURE_WIDTH; + +pub use crate::shader_features::*; + +lazy_static! { + static ref MAX_VERTEX_TEXTURE_WIDTH_STRING: String = MAX_VERTEX_TEXTURE_WIDTH.to_string(); +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum ShaderKind { + Vertex, + Fragment, +} + +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub enum ShaderVersion { + Gl, + Gles, +} + +impl ShaderVersion { + /// Return the full variant name, for use in code generation. + pub fn variant_name(&self) -> &'static str { + match self { + ShaderVersion::Gl => "ShaderVersion::Gl", + ShaderVersion::Gles => "ShaderVersion::Gles", + } + } +} + +#[derive(PartialEq, Eq, Hash, Debug, Clone, Default)] +#[cfg_attr(feature = "serialize_program", derive(Deserialize, Serialize))] +pub struct ProgramSourceDigest(u64); + +impl ::std::fmt::Display for ProgramSourceDigest { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "{:02x}", self.0) + } +} + +impl From<DefaultHasher> for ProgramSourceDigest { + fn from(hasher: DefaultHasher) -> Self { + use std::hash::Hasher; + ProgramSourceDigest(hasher.finish()) + } +} + +const SHADER_IMPORT: &str = "#include "; + +pub struct ShaderSourceParser { + included: HashSet<String>, +} + +impl ShaderSourceParser { + pub fn new() -> Self { + ShaderSourceParser { + included: HashSet::new(), + } + } + + /// Parses a shader string for imports. Imports are recursively processed, and + /// prepended to the output stream. + pub fn parse<F: FnMut(&str), G: Fn(&str) -> Cow<'static, str>>( + &mut self, + source: Cow<'static, str>, + get_source: &G, + output: &mut F, + ) { + for line in source.lines() { + if line.starts_with(SHADER_IMPORT) { + let imports = line[SHADER_IMPORT.len() ..].split(','); + + // For each import, get the source, and recurse. + for import in imports { + if self.included.insert(import.into()) { + let include = get_source(import); + self.parse(include, get_source, output); + } else { + output(&format!("// {} is already included\n", import)); + } + } + } else { + output(line); + output("\n"); + } + } + } +} + +/// Reads a shader source file from disk into a String. +pub fn shader_source_from_file(shader_path: &Path) -> String { + assert!(shader_path.exists(), "Shader not found {:?}", shader_path); + let mut source = String::new(); + File::open(&shader_path) + .expect("Shader not found") + .read_to_string(&mut source) + .unwrap(); + source +} + +/// Creates heap-allocated strings for both vertex and fragment shaders. +pub fn build_shader_strings<G: Fn(&str) -> Cow<'static, str>>( + gl_version: ShaderVersion, + features: &[&str], + base_filename: &str, + get_source: &G, +) -> (String, String) { + let mut vs_source = String::new(); + do_build_shader_string( + gl_version, + features, + ShaderKind::Vertex, + base_filename, + get_source, + |s| vs_source.push_str(s), + ); + + let mut fs_source = String::new(); + do_build_shader_string( + gl_version, + features, + ShaderKind::Fragment, + base_filename, + get_source, + |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. +pub fn do_build_shader_string<F: FnMut(&str), G: Fn(&str) -> Cow<'static, str>>( + gl_version: ShaderVersion, + features: &[&str], + kind: ShaderKind, + base_filename: &str, + get_source: &G, + mut output: F, +) { + build_shader_prefix_string(gl_version, features, kind, base_filename, &mut output); + build_shader_main_string(base_filename, get_source, &mut output); +} + +/// Walks the prefix section of the shader string, which manages the various +/// defines for features etc. +pub fn build_shader_prefix_string<F: FnMut(&str)>( + gl_version: ShaderVersion, + features: &[&str], + kind: ShaderKind, + base_filename: &str, + output: &mut F, +) { + // GLSL requires that the version number comes first. + let gl_version_string = match gl_version { + ShaderVersion::Gl => "#version 150\n", + ShaderVersion::Gles if features.contains(&"TEXTURE_EXTERNAL_ESSL1") => "#version 100\n", + ShaderVersion::Gles => "#version 300 es\n", + }; + output(gl_version_string); + + // Insert the shader name to make debugging easier. + output("// shader: "); + output(base_filename); + output(" "); + for (i, feature) in features.iter().enumerate() { + output(feature); + if i != features.len() - 1 { + output(","); + } + } + output("\n"); + + // Define a constant depending on whether we are compiling VS or FS. + let kind_string = match kind { + ShaderKind::Vertex => "#define WR_VERTEX_SHADER\n", + ShaderKind::Fragment => "#define WR_FRAGMENT_SHADER\n", + }; + output(kind_string); + + // detect which platform we're targeting + let is_macos = match std::env::var("CARGO_CFG_TARGET_OS") { + Ok(os) => os == "macos", + // if this is not called from build.rs (e.g. the gpu_cache_update shader or + // if the optimized shader pref is disabled) we want to use the runtime value + Err(_) => cfg!(target_os = "macos"), + }; + let is_android = match std::env::var("CARGO_CFG_TARGET_OS") { + Ok(os) => os == "android", + Err(_) => cfg!(target_os = "android"), + }; + if is_macos { + output("#define PLATFORM_MACOS\n"); + } else if is_android { + output("#define PLATFORM_ANDROID\n"); + } + + // Define a constant for the vertex texture width. + output("#define WR_MAX_VERTEX_TEXTURE_WIDTH "); + output(&MAX_VERTEX_TEXTURE_WIDTH_STRING); + output("U\n"); + + // Add any defines for features that were passed by the caller. + for feature in features { + assert!(!feature.is_empty()); + output("#define WR_FEATURE_"); + output(feature); + output("\n"); + } +} + +/// Walks the main .glsl file, including any imports. +pub fn build_shader_main_string<F: FnMut(&str), G: Fn(&str) -> Cow<'static, str>>( + base_filename: &str, + get_source: &G, + output: &mut F, +) { + let shared_source = get_source(base_filename); + ShaderSourceParser::new().parse( + shared_source, + &|f| get_source(f), + output + ); +} diff --git a/gfx/wr/webrender_build/src/shader_features.rs b/gfx/wr/webrender_build/src/shader_features.rs new file mode 100644 index 0000000000..db16cdf703 --- /dev/null +++ b/gfx/wr/webrender_build/src/shader_features.rs @@ -0,0 +1,240 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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 std::collections::HashMap; + +bitflags! { + #[derive(Default)] + pub struct ShaderFeatureFlags: u32 { + const GL = 1 << 0; + const GLES = 1 << 1; + + const ADVANCED_BLEND_EQUATION = 1 << 8; + const DUAL_SOURCE_BLENDING = 1 << 9; + const DITHERING = 1 << 10; + const TEXTURE_EXTERNAL = 1 << 11; + const TEXTURE_EXTERNAL_ESSL1 = 1 << 12; + const DEBUG = 1 << 13; + } +} + +pub type ShaderFeatures = HashMap<&'static str, Vec<String>>; + +/// Builder for a list of features. +#[derive(Clone)] +struct FeatureList<'a> { + list: Vec<&'a str>, +} + +impl<'a> FeatureList<'a> { + fn new() -> Self { + FeatureList { + list: Vec::new(), + } + } + + fn add(&mut self, feature: &'a str) { + assert!(!feature.contains(',')); + self.list.push(feature); + } + + fn with(&self, feature: &'a str) -> Self { + let mut other = self.clone(); + other.add(feature); + other + } + + fn concat(&self, other: &Self) -> Self { + let mut list = self.list.clone(); + list.extend_from_slice(&other.list); + FeatureList { + list + } + } + + fn finish(&mut self) -> String { + self.list.sort_unstable(); + self.list.join(",") + } +} + +/// Computes available shaders and their features for the given feature flags. +pub fn get_shader_features(flags: ShaderFeatureFlags) -> ShaderFeatures { + let mut shaders = ShaderFeatures::new(); + + // Clip shaders + shaders.insert("cs_clip_rectangle", vec![String::new(), "FAST_PATH".to_string()]); + shaders.insert("cs_clip_image", vec!["TEXTURE_2D".to_string()]); + shaders.insert("cs_clip_box_shadow", vec!["TEXTURE_2D".to_string()]); + + // Cache shaders + shaders.insert("cs_blur", vec!["ALPHA_TARGET".to_string(), "COLOR_TARGET".to_string()]); + + shaders.insert("ps_quad_mask", vec![String::new(), "FAST_PATH".to_string()]); + + for name in &[ + "cs_line_decoration", + "cs_fast_linear_gradient", + "cs_border_segment", + "cs_border_solid", + "cs_svg_filter", + ] { + shaders.insert(name, vec![String::new()]); + } + + for name in &[ + "cs_linear_gradient", + "cs_radial_gradient", + "cs_conic_gradient", + ] { + let mut features = Vec::new(); + features.push(String::new()); + if flags.contains(ShaderFeatureFlags::DITHERING) { + features.push("DITHERING".to_string()); + } + shaders.insert(name, features); + } + + let mut base_prim_features = FeatureList::new(); + + // Brush shaders + let mut brush_alpha_features = base_prim_features.with("ALPHA_PASS"); + for name in &["brush_solid", "brush_blend", "brush_mix_blend"] { + let mut features: Vec<String> = Vec::new(); + features.push(base_prim_features.finish()); + features.push(brush_alpha_features.finish()); + features.push("DEBUG_OVERDRAW".to_string()); + shaders.insert(name, features); + } + for name in &["brush_linear_gradient"] { + let mut features: Vec<String> = Vec::new(); + let mut list = FeatureList::new(); + if flags.contains(ShaderFeatureFlags::DITHERING) { + list.add("DITHERING"); + } + features.push(list.concat(&base_prim_features).finish()); + features.push(list.concat(&brush_alpha_features).finish()); + features.push(list.with("DEBUG_OVERDRAW").finish()); + shaders.insert(name, features); + } + + { + let mut features: Vec<String> = Vec::new(); + features.push(base_prim_features.finish()); + features.push(brush_alpha_features.finish()); + features.push(base_prim_features.with("ANTIALIASING").finish()); + features.push(brush_alpha_features.with("ANTIALIASING").finish()); + features.push("ANTIALIASING,DEBUG_OVERDRAW".to_string()); + features.push("DEBUG_OVERDRAW".to_string()); + shaders.insert("brush_opacity", features); + } + + // Image brush shaders + let mut texture_types = vec!["TEXTURE_2D"]; + if flags.contains(ShaderFeatureFlags::GL) { + texture_types.push("TEXTURE_RECT"); + } + if flags.contains(ShaderFeatureFlags::TEXTURE_EXTERNAL) { + texture_types.push("TEXTURE_EXTERNAL"); + } + let mut image_features: Vec<String> = Vec::new(); + for texture_type in &texture_types { + let mut fast = FeatureList::new(); + if !texture_type.is_empty() { + fast.add(texture_type); + } + image_features.push(fast.concat(&base_prim_features).finish()); + image_features.push(fast.concat(&brush_alpha_features).finish()); + image_features.push(fast.with("DEBUG_OVERDRAW").finish()); + let mut slow = fast.clone(); + slow.add("REPETITION"); + slow.add("ANTIALIASING"); + image_features.push(slow.concat(&base_prim_features).finish()); + image_features.push(slow.concat(&brush_alpha_features).finish()); + image_features.push(slow.with("DEBUG_OVERDRAW").finish()); + if flags.contains(ShaderFeatureFlags::ADVANCED_BLEND_EQUATION) { + let advanced_blend_features = brush_alpha_features.with("ADVANCED_BLEND"); + image_features.push(fast.concat(&advanced_blend_features).finish()); + image_features.push(slow.concat(&advanced_blend_features).finish()); + } + if flags.contains(ShaderFeatureFlags::DUAL_SOURCE_BLENDING) { + let dual_source_features = brush_alpha_features.with("DUAL_SOURCE_BLENDING"); + image_features.push(fast.concat(&dual_source_features).finish()); + image_features.push(slow.concat(&dual_source_features).finish()); + } + } + shaders.insert("brush_image", image_features); + + let mut composite_texture_types = texture_types.clone(); + if flags.contains(ShaderFeatureFlags::TEXTURE_EXTERNAL_ESSL1) { + composite_texture_types.push("TEXTURE_EXTERNAL_ESSL1"); + } + let mut composite_features: Vec<String> = Vec::new(); + for texture_type in &composite_texture_types { + let base = texture_type.to_string(); + composite_features.push(base); + } + shaders.insert("cs_scale", composite_features.clone()); + + // YUV image brush and composite shaders + let mut yuv_features: Vec<String> = Vec::new(); + for texture_type in &texture_types { + let mut list = FeatureList::new(); + if !texture_type.is_empty() { + list.add(texture_type); + } + list.add("YUV"); + composite_features.push(list.finish()); + yuv_features.push(list.concat(&base_prim_features).finish()); + yuv_features.push(list.concat(&brush_alpha_features).finish()); + yuv_features.push(list.with("DEBUG_OVERDRAW").finish()); + } + shaders.insert("brush_yuv_image", yuv_features); + + // Fast path composite shaders + for texture_type in &composite_texture_types { + let mut list = FeatureList::new(); + if !texture_type.is_empty() { + list.add(texture_type); + } + list.add("FAST_PATH"); + composite_features.push(list.finish()); + } + shaders.insert("composite", composite_features); + + // Prim shaders + let mut text_types = vec![""]; + if flags.contains(ShaderFeatureFlags::DUAL_SOURCE_BLENDING) { + text_types.push("DUAL_SOURCE_BLENDING"); + } + let mut text_features: Vec<String> = Vec::new(); + for text_type in &text_types { + let mut list = base_prim_features.with("TEXTURE_2D"); + if !text_type.is_empty() { + list.add(text_type); + } + let mut alpha_list = list.with("ALPHA_PASS"); + text_features.push(alpha_list.finish()); + text_features.push(alpha_list.with("GLYPH_TRANSFORM").finish()); + text_features.push(list.with("DEBUG_OVERDRAW").finish()); + } + shaders.insert("ps_text_run", text_features); + + shaders.insert("ps_split_composite", vec![base_prim_features.finish()]); + + shaders.insert("ps_quad_textured", vec![base_prim_features.finish()]); + + shaders.insert("ps_clear", vec![base_prim_features.finish()]); + + shaders.insert("ps_copy", vec![base_prim_features.finish()]); + + if flags.contains(ShaderFeatureFlags::DEBUG) { + for name in &["debug_color", "debug_font"] { + shaders.insert(name, vec![String::new()]); + } + } + + shaders +} + |