tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

shader.rs (7155B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 //! Functionality for managing source code for shaders.
      6 //!
      7 //! This module is used during precompilation (build.rs) and regular compilation,
      8 //! so it has minimal dependencies.
      9 
     10 use std::borrow::Cow;
     11 use std::fs::File;
     12 use std::io::Read;
     13 use std::path::Path;
     14 use std::collections::HashSet;
     15 use std::collections::hash_map::DefaultHasher;
     16 use crate::MAX_VERTEX_TEXTURE_WIDTH;
     17 
     18 pub use crate::shader_features::*;
     19 
     20 lazy_static! {
     21    static ref MAX_VERTEX_TEXTURE_WIDTH_STRING: String = MAX_VERTEX_TEXTURE_WIDTH.to_string();
     22 }
     23 
     24 #[derive(Clone, Copy, Debug, PartialEq)]
     25 pub enum ShaderKind {
     26    Vertex,
     27    Fragment,
     28 }
     29 
     30 #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
     31 pub enum ShaderVersion {
     32    Gl,
     33    Gles,
     34 }
     35 
     36 impl ShaderVersion {
     37    /// Return the full variant name, for use in code generation.
     38    pub fn variant_name(&self) -> &'static str {
     39        match self {
     40            ShaderVersion::Gl => "ShaderVersion::Gl",
     41            ShaderVersion::Gles => "ShaderVersion::Gles",
     42        }
     43    }
     44 }
     45 
     46 #[derive(PartialEq, Eq, Hash, Debug, Clone, Default)]
     47 #[cfg_attr(feature = "serialize_program", derive(Deserialize, Serialize))]
     48 pub struct ProgramSourceDigest(u64);
     49 
     50 impl ::std::fmt::Display for ProgramSourceDigest {
     51    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
     52        write!(f, "{:02x}", self.0)
     53    }
     54 }
     55 
     56 impl From<DefaultHasher> for ProgramSourceDigest {
     57    fn from(hasher: DefaultHasher) -> Self {
     58        use std::hash::Hasher;
     59        ProgramSourceDigest(hasher.finish())
     60    }
     61 }
     62 
     63 const SHADER_IMPORT: &str = "#include ";
     64 
     65 pub struct ShaderSourceParser {
     66    included: HashSet<String>,
     67 }
     68 
     69 impl ShaderSourceParser {
     70    pub fn new() -> Self {
     71        ShaderSourceParser {
     72            included: HashSet::new(),
     73        }
     74    }
     75 
     76    /// Parses a shader string for imports. Imports are recursively processed, and
     77    /// prepended to the output stream.
     78    pub fn parse<F: FnMut(&str), G: Fn(&str) -> Cow<'static, str>>(
     79        &mut self,
     80        source: Cow<'static, str>,
     81        get_source: &G,
     82        output: &mut F,
     83    ) {
     84        for line in source.lines() {
     85            if let Some(imports) = line.strip_prefix(SHADER_IMPORT) {
     86                // For each import, get the source, and recurse.
     87                for import in imports.split(',') {
     88                    if self.included.insert(import.into()) {
     89                        let include = get_source(import);
     90                        self.parse(include, get_source, output);
     91                    } else {
     92                        output(&format!("// {} is already included\n", import));
     93                    }
     94                }
     95            } else {
     96                output(line);
     97                output("\n");
     98            }
     99        }
    100    }
    101 }
    102 
    103 /// Reads a shader source file from disk into a String.
    104 pub fn shader_source_from_file(shader_path: &Path) -> String {
    105    assert!(shader_path.exists(), "Shader not found {:?}", shader_path);
    106    let mut source = String::new();
    107    File::open(shader_path)
    108        .expect("Shader not found")
    109        .read_to_string(&mut source)
    110        .unwrap();
    111    source
    112 }
    113 
    114 /// Creates heap-allocated strings for both vertex and fragment shaders.
    115 pub fn build_shader_strings<G: Fn(&str) -> Cow<'static, str>>(
    116    gl_version: ShaderVersion,
    117    features: &[&str],
    118    base_filename: &str,
    119    get_source: &G,
    120 ) -> (String, String) {
    121   let mut vs_source = String::new();
    122   do_build_shader_string(
    123       gl_version,
    124       features,
    125       ShaderKind::Vertex,
    126       base_filename,
    127       get_source,
    128       |s| vs_source.push_str(s),
    129   );
    130 
    131   let mut fs_source = String::new();
    132   do_build_shader_string(
    133       gl_version,
    134       features,
    135       ShaderKind::Fragment,
    136       base_filename,
    137       get_source,
    138       |s| fs_source.push_str(s),
    139   );
    140 
    141   (vs_source, fs_source)
    142 }
    143 
    144 /// Walks the given shader string and applies the output to the provided
    145 /// callback. Assuming an override path is not used, does no heap allocation
    146 /// and no I/O.
    147 pub fn do_build_shader_string<F: FnMut(&str), G: Fn(&str) -> Cow<'static, str>>(
    148   gl_version: ShaderVersion,
    149   features: &[&str],
    150   kind: ShaderKind,
    151   base_filename: &str,
    152   get_source: &G,
    153   mut output: F,
    154 ) {
    155   build_shader_prefix_string(gl_version, features, kind, base_filename, &mut output);
    156   build_shader_main_string(base_filename, get_source, &mut output);
    157 }
    158 
    159 /// Walks the prefix section of the shader string, which manages the various
    160 /// defines for features etc.
    161 pub fn build_shader_prefix_string<F: FnMut(&str)>(
    162   gl_version: ShaderVersion,
    163   features: &[&str],
    164   kind: ShaderKind,
    165   base_filename: &str,
    166   output: &mut F,
    167 ) {
    168    // GLSL requires that the version number comes first.
    169    let gl_version_string = match gl_version {
    170        ShaderVersion::Gl => "#version 150\n",
    171        ShaderVersion::Gles if features.contains(&"TEXTURE_EXTERNAL_ESSL1") => "#version 100\n",
    172        ShaderVersion::Gles => "#version 300 es\n",
    173    };
    174    output(gl_version_string);
    175 
    176    // Insert the shader name to make debugging easier.
    177    output("// shader: ");
    178    output(base_filename);
    179    output(" ");
    180    for (i, feature) in features.iter().enumerate() {
    181        output(feature);
    182        if i != features.len() - 1 {
    183            output(",");
    184        }
    185    }
    186    output("\n");
    187 
    188    // Define a constant depending on whether we are compiling VS or FS.
    189    let kind_string = match kind {
    190        ShaderKind::Vertex => "#define WR_VERTEX_SHADER\n",
    191        ShaderKind::Fragment => "#define WR_FRAGMENT_SHADER\n",
    192    };
    193    output(kind_string);
    194 
    195    // detect which platform we're targeting
    196    let is_macos = match std::env::var("CARGO_CFG_TARGET_OS") {
    197        Ok(os) => os == "macos",
    198        // if this is not called from build.rs (e.g. if the optimized shader
    199        // pref is disabled) we want to use the runtime value
    200        Err(_) => cfg!(target_os = "macos"),
    201    };
    202    let is_android = match std::env::var("CARGO_CFG_TARGET_OS") {
    203        Ok(os) => os == "android",
    204        Err(_) => cfg!(target_os = "android"),
    205    };
    206    if is_macos {
    207        output("#define PLATFORM_MACOS\n");
    208    } else if is_android {
    209        output("#define PLATFORM_ANDROID\n");
    210    }
    211 
    212    // Define a constant for the vertex texture width.
    213    output("#define WR_MAX_VERTEX_TEXTURE_WIDTH ");
    214    output(&MAX_VERTEX_TEXTURE_WIDTH_STRING);
    215    output("U\n");
    216 
    217    // Add any defines for features that were passed by the caller.
    218    for feature in features {
    219        assert!(!feature.is_empty());
    220        output("#define WR_FEATURE_");
    221        output(feature);
    222        output("\n");
    223    }
    224 }
    225 
    226 /// Walks the main .glsl file, including any imports.
    227 pub fn build_shader_main_string<F: FnMut(&str), G: Fn(&str) -> Cow<'static, str>>(
    228   base_filename: &str,
    229   get_source: &G,
    230   output: &mut F,
    231 ) {
    232   let shared_source = get_source(base_filename);
    233   ShaderSourceParser::new().parse(
    234       shared_source,
    235       &|f| get_source(f),
    236       output
    237   );
    238 }