Bug 1594128 - Only include shader sources once during parsing. r=gw
authorNicolas Silva <nsilva@mozilla.com>
Mon, 25 Nov 2019 20:01:47 +0000
changeset 503851 6762aab1845925b804e4f116cb169a5e614844fc
parent 503850 b5e15afce7ef635b367e6c624f7670c33c69d38b
child 503852 6386aa316da906a59f00cd2191105f4c1cefb833
push id36849
push userdvarga@mozilla.com
push dateTue, 26 Nov 2019 21:27:08 +0000
treeherdermozilla-central@bcbb45f7d4f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgw
bugs1594128
milestone72.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1594128 - Only include shader sources once during parsing. r=gw This chanes the shader parsing code to only inject #included shader sources once (the first time) if they are included multiple times. This will allow some extra flexibility needed by the multi-brush shader. Differential Revision: https://phabricator.services.mozilla.com/D53651
gfx/wr/webrender/build.rs
gfx/wr/webrender/src/device/gl.rs
gfx/wr/webrender_build/src/shader.rs
--- a/gfx/wr/webrender/build.rs
+++ b/gfx/wr/webrender/build.rs
@@ -31,17 +31,17 @@ fn write_shaders(glsl_files: Vec<PathBuf
         let shader_name = shader_name.replace(".glsl", "");
 
         // Compute a digest of the #include-expanded shader source. We store
         // this as a literal alongside the source string so that we don't need
         // to hash large strings at runtime.
         let mut hasher = Sha256::new();
         let base = glsl.parent().unwrap();
         assert!(base.is_dir());
-        parse_shader_source(
+        ShaderSourceParser::new().parse(
             Cow::Owned(shader_source_from_file(&glsl)),
             &|f| Cow::Owned(shader_source_from_file(&base.join(&format!("{}.glsl", f)))),
             &mut |s| hasher.input(s.as_bytes()),
         );
         let digest: ProgramSourceDigest = hasher.into();
 
         // Compute the shader path for insertion into the include_str!() macro.
         // This makes for more compact generated code than inserting the literal
--- a/gfx/wr/webrender/src/device/gl.rs
+++ b/gfx/wr/webrender/src/device/gl.rs
@@ -29,17 +29,17 @@ use std::{
     rc::Rc,
     slice,
     sync::Arc,
     sync::atomic::{AtomicUsize, Ordering},
     thread,
     time::Duration,
 };
 use webrender_build::shader::ProgramSourceDigest;
-use webrender_build::shader::{parse_shader_source, shader_source_from_file};
+use webrender_build::shader::{ShaderSourceParser, shader_source_from_file};
 
 /// Sequence number for frames, as tracked by the device layer.
 #[derive(Debug, Copy, Clone, PartialEq, Ord, Eq, PartialOrd)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct GpuFrameId(usize);
 
 /// Tracks the total number of GPU bytes allocated across all WebRender instances.
@@ -283,17 +283,21 @@ fn build_shader_prefix_string<F: FnMut(&
 
 /// Walks the main .glsl file, including any imports.
 fn build_shader_main_string<F: FnMut(&str)>(
     base_filename: &str,
     override_path: Option<&PathBuf>,
     output: &mut F,
 ) {
     let shared_source = get_shader_source(base_filename, override_path);
-    parse_shader_source(shared_source, &|f| get_shader_source(f, override_path), output);
+    ShaderSourceParser::new().parse(
+        shared_source,
+        &|f| get_shader_source(f, override_path),
+        output
+    );
 }
 
 pub trait FileWatcherHandler: Send {
     fn file_changed(&self, path: PathBuf);
 }
 
 impl VertexAttributeKind {
     fn size_in_bytes(&self) -> u32 {
--- a/gfx/wr/webrender_build/src/shader.rs
+++ b/gfx/wr/webrender_build/src/shader.rs
@@ -7,16 +7,17 @@
 //! This module is used during precompilation (build.rs) and regular compilation,
 //! so it has minimal dependencies.
 
 pub use sha2::{Digest, Sha256};
 use std::borrow::Cow;
 use std::fs::File;
 use std::io::Read;
 use std::path::Path;
+use std::collections::HashSet;
 
 #[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() {
@@ -31,35 +32,52 @@ impl From<Sha256> for ProgramSourceDiges
         let mut digest = Self::default();
         digest.0.copy_from_slice(hasher.result().as_slice());
         digest
     }
 }
 
 const SHADER_IMPORT: &str = "#include ";
 
-/// Parses a shader string for imports. Imports are recursively processed, and
-/// prepended to the output stream.
-pub fn parse_shader_source<F: FnMut(&str), G: Fn(&str) -> Cow<'static, str>>(
-    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(',');
+pub struct ShaderSourceParser {
+    included: HashSet<String>,
+}
+
+impl ShaderSourceParser {
+    pub fn new() -> Self {
+        ShaderSourceParser {
+            included: HashSet::new(),
+        }
+    }
 
-            // For each import, get the source, and recurse.
-            for import in imports {
-                let include = get_source(import);
-                parse_shader_source(include, get_source, output);
+    /// 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");
             }
-        } 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");
     let mut source = String::new();