Bug 1594128 - Only include shader sources once during parsing. r=gw
authorNicolas Silva <nsilva@mozilla.com>
Wed, 27 Nov 2019 13:22:47 +0000
changeset 504184 a1138b7e56a3be562fe49e5b1dc755ed34ebf389
parent 504183 3d2b14d80fb3ba4e8064e105a817ffcec21f7adc
child 504185 9e1443b6c17c81bc5b074b4d33600a3717c55d8e
push id36858
push usercbrindusan@mozilla.com
push dateThu, 28 Nov 2019 16:06:38 +0000
treeherdermozilla-central@6a3ca2b832f6 [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();