summaryrefslogtreecommitdiffstats
path: root/gfx/wr/webrender_build/src
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/wr/webrender_build/src')
-rw-r--r--gfx/wr/webrender_build/src/lib.rs19
-rw-r--r--gfx/wr/webrender_build/src/shader.rs240
-rw-r--r--gfx/wr/webrender_build/src/shader_features.rs240
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
+}
+