From 28bfc2461954d6b2d0fae0cbd5bdb7ba1d69326f Mon Sep 17 00:00:00 2001 From: Zakor Gyula Date: Wed, 4 Jul 2018 13:33:07 +0200 Subject: [PATCH 1/2] Genereate shaders in build script with the help of a .ron file --- webrender/Cargo.toml | 4 + webrender/build.rs | 163 +++++++++++++++++++++++++++++++++++++- webrender/res/shaders.ron | 130 ++++++++++++++++++++++++++++++ 3 files changed, 295 insertions(+), 2 deletions(-) create mode 100644 webrender/res/shaders.ron diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml index b02c0492c2..9b49b57fb2 100644 --- a/webrender/Cargo.toml +++ b/webrender/Cargo.toml @@ -74,3 +74,7 @@ dwrote = "0.4.1" core-foundation = "0.6" core-graphics = "0.14" core-text = { version = "10", default-features = false } + +[build-dependencies] +ron = "0.1.7" +serde = { version = "1.0", features = ["serde_derive"] } diff --git a/webrender/build.rs b/webrender/build.rs index 53b7450e8f..098d3f5cec 100644 --- a/webrender/build.rs +++ b/webrender/build.rs @@ -2,11 +2,30 @@ * 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/. */ +extern crate ron; +#[macro_use] +extern crate serde; + +use ron::de; use std::env; use std::fs::{canonicalize, read_dir, File}; use std::io::prelude::*; +use std::collections::HashMap; use std::path::{Path, PathBuf}; +const SHADER_IMPORT: &str = "#include "; +const SHADER_KIND_FRAGMENT: &str = "#define WR_FRAGMENT_SHADER\n"; +const SHADER_KIND_VERTEX: &str = "#define WR_VERTEX_SHADER\n"; +const SHADER_PREFIX: &str = "#define WR_MAX_VERTEX_TEXTURE_WIDTH 1024\n"; + +const SHADER_VERSION: &str = "#version 150\n"; + +#[derive(Deserialize)] +struct Shader { + name: String, + feature_sets: Vec, +} + fn write_shaders(glsl_files: Vec, shader_file_path: &Path) { let mut shader_file = File::create(shader_file_path).unwrap(); @@ -39,6 +58,145 @@ fn write_shaders(glsl_files: Vec, shader_file_path: &Path) { write!(shader_file, "}}\n").unwrap(); } +fn create_map(glsl_files: Vec) -> HashMap { + let mut shader_map: HashMap = HashMap::new(); + + for glsl in glsl_files { + let shader_name = glsl.file_name().unwrap().to_str().unwrap(); + // strip .glsl + let shader_name = shader_name.replace(".glsl", ""); + let full_path = canonicalize(&glsl).unwrap(); + let full_name = full_path.as_os_str().to_str().unwrap(); + // if someone is building on a network share, I'm sorry. + let full_name = full_name.replace("\\\\?\\", ""); + let full_name = full_name.replace("\\", "/"); + shader_map.insert(shader_name.clone(), full_name.clone()); + } + shader_map +} + +// Get a shader string by name +fn get_shader_source(shader_name: &str, shaders: &HashMap) -> String { + let shader_file_path = Path::new(&shaders[shader_name]); + let mut shader_source_file = + File::open(shader_file_path).expect(&format!("Unable to open shader file: {:?}", shader_file_path)); + let mut source = String::new(); + shader_source_file.read_to_string(&mut source).unwrap(); + source +} + +// Parse a shader string for imports. Imports are recursively processed, and +// prepended to the list of outputs. +fn parse_shader_source(source: String, shaders: &HashMap, output: &mut String) { + 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 { + let include = get_shader_source(import, shaders); + parse_shader_source(include, shaders, output); + } + } else { + output.push_str(line); + output.push_str("\n"); + } + } +} + +fn build_shader_strings( + gl_version_string: &str, + features: &str, + base_filename: &str, + shaders: &HashMap, +) -> (String, String) { + // Construct a list of strings to be passed to the shader compiler. + let mut vs_source = String::new(); + let mut fs_source = String::new(); + + // GLSL requires that the version number comes first. + vs_source.push_str(gl_version_string); + fs_source.push_str(gl_version_string); + + // Insert the shader name to make debugging easier. + let name_string = format!("// {}\n", base_filename); + vs_source.push_str(&name_string); + fs_source.push_str(&name_string); + + // Define a constant depending on whether we are compiling VS or FS. + vs_source.push_str(SHADER_KIND_VERTEX); + fs_source.push_str(SHADER_KIND_FRAGMENT); + + // Add any defines that were passed by the caller. + vs_source.push_str(features); + fs_source.push_str(features); + + // Parse the main .glsl file, including any imports + // and append them to the list of sources. + let mut shared_result = String::new(); + let shared_source = get_shader_source(base_filename, shaders); + parse_shader_source(shared_source, shaders, &mut shared_result); + + vs_source.push_str(&shared_result); + fs_source.push_str(&shared_result); + + (vs_source, fs_source) +} + +fn generate_shaders(out_dir: &str, shaders: HashMap) { + let file = File::open("res/shaders.ron").expect("Unable to open shaders.ron"); + let shader_configs: Vec = de::from_reader(file).expect("Unable to deserialize shaders.ron"); + + for shader in shader_configs { + let mut feature_variants: Vec = Vec::new(); + + // Building up possible permutations of features + for feature_set in shader.feature_sets { + if feature_variants.is_empty() { + feature_variants = feature_set.split(',').map(|s| s.to_owned()).collect(); + } else { + let prev_variants: Vec = feature_variants.drain(..).collect(); + for variant in prev_variants.iter() { + for feature in feature_set.split(',') { + feature_variants.push(format!("{},{}", variant, feature)); + } + } + } + } + + // Creating a shader file for each permutation + for variant in feature_variants { + let mut features = String::new(); + + features.push_str(SHADER_PREFIX); + features.push_str(format!("//Source: {}.glsl\n", shader.name).as_str()); + + let mut file_name_postfix = String::new(); + for feature in variant.split(',') { + if !feature.is_empty() { + features.push_str(&format!("#define WR_FEATURE_{}\n", feature)); + file_name_postfix + .push_str(&format!("_{}", feature.to_lowercase().as_str())); + } + } + + let (mut vs_source, mut fs_source) = + build_shader_strings(SHADER_VERSION, &features, &shader.name, &shaders); + + let (vs_file_path, fs_file_path) = ( + Path::new(out_dir).join(format!("{}{}.vert", &shader.name, &file_name_postfix)), + Path::new(out_dir).join(format!("{}{}.frag", &shader.name, &file_name_postfix)), + ); + let (mut vs_file, mut fs_file) = ( + File::create(vs_file_path).unwrap(), + File::create(fs_file_path).unwrap(), + ); + write!(vs_file, "{}", vs_source).unwrap(); + write!(fs_file, "{}", fs_source).unwrap(); + } + } +} + fn main() { let out_dir = env::var("OUT_DIR").unwrap_or("out".to_owned()); @@ -56,10 +214,11 @@ fn main() { glsl_files.push(path.to_owned()); } } - // Sort the file list so that the shaders.rs file is filled // deterministically. glsl_files.sort_by(|a, b| a.file_name().cmp(&b.file_name())); - write_shaders(glsl_files, &shaders_file); + write_shaders(glsl_files.clone(), &shaders_file); + let shaders_map = create_map(glsl_files); + generate_shaders(&out_dir, shaders_map); } diff --git a/webrender/res/shaders.ron b/webrender/res/shaders.ron new file mode 100644 index 0000000000..1a7e6ca75e --- /dev/null +++ b/webrender/res/shaders.ron @@ -0,0 +1,130 @@ +[ + ( + name: "cs_blur", + feature_sets: [ + "ALPHA_TARGET,COLOR_TARGET", // cs_blur_a8 and cs_blur_rgba8 + ], + ),// [0] + ( + name: "cs_border_segment", + feature_sets: [ + "", + ], + ),// [1] + ( + name: "brush_solid", + feature_sets: [ + ",ALPHA_PASS", // Brush shader feature + ], + ),// [2] + ( + name: "brush_image", + feature_sets: [ + ",TEXTURE_2D,TEXTURE_RECT,TEXTURE_EXTERNAL",// Image buffer kinds + ",ALPHA_PASS", // Brush shader feature + ",DUAL_SOURCE_BLENDING", // Optional brush shader feature + ], + ),// [3] + ( + name: "brush_blend", + feature_sets: [ + ",ALPHA_PASS", // Brush shader feature + ], + ),// [4] + ( + name: "brush_mix_blend", + feature_sets: [ + ",ALPHA_PASS", // Brush shader feature + ], + ),// [5] + ( + name: "brush_yuv_image", + feature_sets: [ + ",TEXTURE_2D,TEXTURE_RECT,TEXTURE_EXTERNAL",// Image buffer kinds + "YUV_NV12,YUV_PLANAR,YUV_INTERLEAVED", // Yuv formats + "YUV_REC601,YUV_REC709", // Yuv color spaces + ",ALPHA_PASS", // Brush shader feature + ], + ),// [6] + ( + name: "brush_radial_gradient", + feature_sets: [ + ",DITHERING", // Dither feature + ",ALPHA_PASS", // Brush shader feature + ], + ),// [7] + ( + name: "brush_linear_gradient", + feature_sets: [ + ",DITHERING", // Dither feature + ",ALPHA_PASS", // Brush shader feature + ], + ),// [8] + ( + name: "cs_clip_rectangle", + feature_sets: [ + "TRANSFORM", // ClipCache shader feature + ], + ),// [9] + ( + name: "cs_clip_box_shadow", + feature_sets: [ + "TRANSFORM", // ClipCache shader feature + ], + ),// [10] + ( + name: "cs_clip_image", + feature_sets: [ + "TRANSFORM", // ClipCache shader feature + ], + ),// [11] + ( + name: "cs_clip_line", + feature_sets: [ + "TRANSFORM", // ClipCache shader feature + ], + ),// [12] + ( + name: "ps_text_run", + feature_sets: [ + ",DUAL_SOURCE_BLENDING", // ps_text_run_dual_source + ",GLYPH_TRANSFORM", // TextShader feature + ], + ),// [13] + ( + name: "ps_split_composite", + feature_sets: [ + "", + ], + ),// [14] + ( + name: "debug_color", + feature_sets: [ + "", + ], + ),// [15] + ( + name: "debug_font", + feature_sets: [ + "", + ], + ),// [16] + ( + name: "gpu_cache_update", + feature_sets: [ + "", + ], + ),// [17] + ( + name: "pf_vector_cover", + feature_sets: [ + "TEXTURE_2D", + ], + ),// [18] + ( + name: "pf_vector_stencil", + feature_sets: [ + "TEXTURE_2D", + ], + ),// [19] +] From 1c4f2daefdcc128861a6a8c88aabba9344ede998 Mon Sep 17 00:00:00 2001 From: Zakor Gyula Date: Fri, 6 Jul 2018 10:10:04 +0200 Subject: [PATCH 2/2] Load shaders from files instead of building them --- webrender/Cargo.toml | 2 + webrender/build.rs | 38 ------ webrender/src/debug_render.rs | 4 +- webrender/src/device/gl.rs | 121 ++++++----------- webrender/src/lib.rs | 8 +- webrender/src/renderer.rs | 2 +- webrender/src/shade.rs | 20 +-- webrender/tests/angle_shader_validation.rs | 151 +++++++-------------- 8 files changed, 102 insertions(+), 244 deletions(-) diff --git a/webrender/Cargo.toml b/webrender/Cargo.toml index 9b49b57fb2..2e8741ef11 100644 --- a/webrender/Cargo.toml +++ b/webrender/Cargo.toml @@ -63,6 +63,8 @@ optional = true [dev-dependencies] mozangle = "0.1" +ron = "0.1.7" +serde = { version = "1.0", features = ["serde_derive"] } [target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies] freetype = { version = "0.4", default-features = false } diff --git a/webrender/build.rs b/webrender/build.rs index 098d3f5cec..04e3c31a5f 100644 --- a/webrender/build.rs +++ b/webrender/build.rs @@ -26,38 +26,6 @@ struct Shader { feature_sets: Vec, } -fn write_shaders(glsl_files: Vec, shader_file_path: &Path) { - let mut shader_file = File::create(shader_file_path).unwrap(); - - write!(shader_file, "/// AUTO GENERATED BY build.rs\n\n").unwrap(); - write!(shader_file, "use std::collections::HashMap;\n").unwrap(); - write!(shader_file, "lazy_static! {{\n").unwrap(); - write!( - shader_file, - " pub static ref SHADERS: HashMap<&'static str, &'static str> = {{\n" - ).unwrap(); - write!(shader_file, " let mut h = HashMap::new();\n").unwrap(); - for glsl in glsl_files { - let shader_name = glsl.file_name().unwrap().to_str().unwrap(); - // strip .glsl - let shader_name = shader_name.replace(".glsl", ""); - let full_path = canonicalize(&glsl).unwrap(); - let full_name = full_path.as_os_str().to_str().unwrap(); - // if someone is building on a network share, I'm sorry. - let full_name = full_name.replace("\\\\?\\", ""); - let full_name = full_name.replace("\\", "/"); - write!( - shader_file, - " h.insert(\"{}\", include_str!(\"{}\"));\n", - shader_name, - full_name - ).unwrap(); - } - write!(shader_file, " h\n").unwrap(); - write!(shader_file, " }};\n").unwrap(); - write!(shader_file, "}}\n").unwrap(); -} - fn create_map(glsl_files: Vec) -> HashMap { let mut shader_map: HashMap = HashMap::new(); @@ -200,7 +168,6 @@ fn generate_shaders(out_dir: &str, shaders: HashMap) { fn main() { let out_dir = env::var("OUT_DIR").unwrap_or("out".to_owned()); - let shaders_file = Path::new(&out_dir).join("shaders.rs"); let mut glsl_files = vec![]; println!("cargo:rerun-if-changed=res"); @@ -214,11 +181,6 @@ fn main() { glsl_files.push(path.to_owned()); } } - // Sort the file list so that the shaders.rs file is filled - // deterministically. - glsl_files.sort_by(|a, b| a.file_name().cmp(&b.file_name())); - - write_shaders(glsl_files.clone(), &shaders_file); let shaders_map = create_map(glsl_files); generate_shaders(&out_dir, shaders_map); } diff --git a/webrender/src/debug_render.rs b/webrender/src/debug_render.rs index 03161b5ef9..96992e7e2e 100644 --- a/webrender/src/debug_render.rs +++ b/webrender/src/debug_render.rs @@ -105,11 +105,11 @@ pub struct DebugRenderer { impl DebugRenderer { pub fn new(device: &mut Device) -> Self { - let font_program = device.create_program("debug_font", "", &DESC_FONT).unwrap(); + let font_program = device.create_program("debug_font", &[], &DESC_FONT).unwrap(); device.bind_shader_samplers(&font_program, &[("sColor0", DebugSampler::Font)]); let color_program = device - .create_program("debug_color", "", &DESC_COLOR) + .create_program("debug_color", &[], &DESC_COLOR) .unwrap(); let font_vao = device.create_vao(&DESC_FONT); diff --git a/webrender/src/device/gl.rs b/webrender/src/device/gl.rs index 5ce6b00b40..a87f4c70d8 100644 --- a/webrender/src/device/gl.rs +++ b/webrender/src/device/gl.rs @@ -2,7 +2,6 @@ * 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 super::super::shader_source; use api::{ColorF, ImageFormat}; use api::{DeviceIntPoint, DeviceIntRect, DeviceUintRect, DeviceUintSize}; use api::TextureTarget; @@ -54,10 +53,6 @@ const GL_FORMAT_BGRA_GLES: gl::GLuint = gl::BGRA_EXT; const SHADER_VERSION_GL: &str = "#version 150\n"; const SHADER_VERSION_GLES: &str = "#version 300 es\n"; -const SHADER_KIND_VERTEX: &str = "#define WR_VERTEX_SHADER\n"; -const SHADER_KIND_FRAGMENT: &str = "#define WR_FRAGMENT_SHADER\n"; -const SHADER_IMPORT: &str = "#include "; - pub struct TextureSlot(pub usize); // In some places we need to temporarily bind a texture to any slot. @@ -147,83 +142,51 @@ fn get_shader_version(gl: &gl::Gl) -> &'static str { } } -// Get a shader string by name, from the built in resources or -// an override path, if supplied. -fn get_shader_source(shader_name: &str, base_path: &Option) -> Option { - if let Some(ref base) = *base_path { - let shader_path = base.join(&format!("{}.glsl", shader_name)); - if shader_path.exists() { - let mut source = String::new(); - File::open(&shader_path) - .unwrap() - .read_to_string(&mut source) - .unwrap(); - return Some(source); - } - } - - shader_source::SHADERS - .get(shader_name) - .map(|s| s.to_string()) -} - -// Parse a shader string for imports. Imports are recursively processed, and -// prepended to the list of outputs. -fn parse_shader_source(source: String, base_path: &Option, output: &mut String) { - 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 let Some(include) = get_shader_source(import, base_path) { - parse_shader_source(include, base_path, output); - } - } - } else { - output.push_str(line); - output.push_str("\n"); - } - } -} - -pub fn build_shader_strings( +pub fn load_shader_sources( gl_version_string: &str, - features: &str, + features: &[&str], base_filename: &str, override_path: &Option, ) -> (String, String) { - // Construct a list of strings to be passed to the shader compiler. - let mut vs_source = String::new(); - let mut fs_source = String::new(); - // GLSL requires that the version number comes first. - vs_source.push_str(gl_version_string); - fs_source.push_str(gl_version_string); - // Insert the shader name to make debugging easier. - let name_string = format!("// {}\n", base_filename); - vs_source.push_str(&name_string); - fs_source.push_str(&name_string); + let (vs_path, fs_path) = { + let mut postfix = String::new(); + for feature in features { + if !feature.is_empty() { + postfix.push_str(&format!("_{}", &feature.to_lowercase())); + } + } - // Define a constant depending on whether we are compiling VS or FS. - vs_source.push_str(SHADER_KIND_VERTEX); - fs_source.push_str(SHADER_KIND_FRAGMENT); + let mut file_name = base_filename.to_owned(); + file_name.push_str(&postfix); - // Add any defines that were passed by the caller. - vs_source.push_str(features); - fs_source.push_str(features); + let mut vs_path = match *override_path { + Some(ref p) => PathBuf::from(p), + None => PathBuf::from(env!("OUT_DIR")), + }; - // Parse the main .glsl file, including any imports - // and append them to the list of sources. - let mut shared_result = String::new(); - if let Some(shared_source) = get_shader_source(base_filename, override_path) { - parse_shader_source(shared_source, override_path, &mut shared_result); - } + vs_path.push(file_name); + let mut fs_path = vs_path.clone(); + vs_path.set_extension("vert"); + fs_path.set_extension("frag"); + (vs_path, fs_path) - vs_source.push_str(&shared_result); - fs_source.push_str(&shared_result); + }; + let mut vs_file = + File::open(vs_path.as_path()).expect(&format!("Unable to open shader file: {:?}", vs_path)); + let mut vs_source = String::new(); + vs_file.read_to_string(&mut vs_source).unwrap(); + + let mut fs_file = + File::open(fs_path.as_path()).expect(&format!("Unable to open shader file: {:?}", fs_path)); + let mut fs_source = String::new(); + fs_file.read_to_string(&mut fs_source).unwrap(); + if gl_version_string == SHADER_VERSION_GLES { + vs_source = vs_source.replacen(SHADER_VERSION_GL, SHADER_VERSION_GLES, 1); + fs_source = fs_source.replacen(SHADER_VERSION_GL, SHADER_VERSION_GLES, 1); + } (vs_source, fs_source) } @@ -1412,19 +1375,17 @@ impl Device { pub fn create_program( &mut self, base_filename: &str, - features: &str, + features: &[&'static str], descriptor: &VertexDescriptor, ) -> Result { debug_assert!(self.inside_frame); - let gl_version_string = get_shader_version(&*self.gl); - - let (vs_source, fs_source) = build_shader_strings( - gl_version_string, - features, - base_filename, - &self.resource_override_path, - ); + let (vs_source, fs_source) = + load_shader_sources( + get_shader_version(&*self.gl), + features, + base_filename, + &self.resource_override_path); let sources = ProgramSources::new(self.renderer_name.clone(), vs_source, fs_source); diff --git a/webrender/src/lib.rs b/webrender/src/lib.rs index c53b2078af..02dc43758b 100644 --- a/webrender/src/lib.rs +++ b/webrender/src/lib.rs @@ -44,7 +44,7 @@ they're nestable. extern crate bitflags; #[macro_use] extern crate cfg_if; -#[macro_use] +#[cfg_attr(target_os = "windows", macro_use)] extern crate lazy_static; #[macro_use] extern crate log; @@ -103,10 +103,6 @@ mod texture_cache; mod tiling; mod util; -mod shader_source { - include!(concat!(env!("OUT_DIR"), "/shaders.rs")); -} - pub use record::{ApiRecordingReceiver, BinaryRecorder, WEBRENDER_RECORDING_HEADER}; mod platform { @@ -179,7 +175,7 @@ extern crate png; pub extern crate webrender_api; #[doc(hidden)] -pub use device::{build_shader_strings, ReadPixelsFormat, UploadMethod, VertexUsageHint}; +pub use device::{load_shader_sources, ReadPixelsFormat, UploadMethod, VertexUsageHint}; pub use device::{ProgramBinary, ProgramCache, ProgramCacheObserver, ProgramSources}; pub use frame_builder::ChasePrimitive; pub use renderer::{AsyncPropertySampler, CpuProfile, DebugFlags, OutputImageHandler, RendererKind}; diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 3958eb0ce6..0a64fe0657 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -966,7 +966,7 @@ impl CacheTexture { let bus = if use_scatter { let program = device - .create_program("gpu_cache_update", "", &desc::GPU_CACHE_UPDATE)?; + .create_program("gpu_cache_update", &[], &desc::GPU_CACHE_UPDATE)?; let buf_position = device.create_vbo(); let buf_value = device.create_vbo(); //Note: the vertex attributes have to be supplied in the same order diff --git a/webrender/src/shade.rs b/webrender/src/shade.rs index 7549f92ff2..6a9618f2a8 100644 --- a/webrender/src/shade.rs +++ b/webrender/src/shade.rs @@ -12,7 +12,6 @@ use euclid::{Transform3D}; use glyph_rasterizer::GlyphFormat; use renderer::{ desc, - MAX_VERTEX_TEXTURE_WIDTH, BlendMode, ImageBufferKind, RendererError, RendererOptions, TextureSampler, VertexArrayKind, }; @@ -319,15 +318,6 @@ fn create_prim_shader( features: &[&'static str], vertex_format: VertexArrayKind, ) -> Result { - let mut prefix = format!( - "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}\n", - MAX_VERTEX_TEXTURE_WIDTH - ); - - for feature in features { - prefix.push_str(&format!("#define WR_FEATURE_{}\n", feature)); - } - debug!("PrimShader {}", name); let vertex_descriptor = match vertex_format { @@ -339,7 +329,7 @@ fn create_prim_shader( VertexArrayKind::Border => desc::BORDER, }; - let program = device.create_program(name, &prefix, &vertex_descriptor); + let program = device.create_program(name, &features, &vertex_descriptor); if let Ok(ref program) = program { device.bind_shader_samplers( @@ -365,15 +355,9 @@ fn create_prim_shader( } fn create_clip_shader(name: &'static str, device: &mut Device) -> Result { - let prefix = format!( - "#define WR_MAX_VERTEX_TEXTURE_WIDTH {}\n - #define WR_FEATURE_TRANSFORM\n", - MAX_VERTEX_TEXTURE_WIDTH - ); - debug!("ClipShader {}", name); - let program = device.create_program(name, &prefix, &desc::CLIP); + let program = device.create_program(name, &["TRANSFORM"], &desc::CLIP); if let Ok(ref program) = program { device.bind_shader_samplers( diff --git a/webrender/tests/angle_shader_validation.rs b/webrender/tests/angle_shader_validation.rs index 5c0db40c7d..58bd5f5a36 100644 --- a/webrender/tests/angle_shader_validation.rs +++ b/webrender/tests/angle_shader_validation.rs @@ -3,102 +3,30 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ extern crate mozangle; +extern crate ron; +#[macro_use] +extern crate serde; extern crate webrender; use mozangle::shaders::{BuiltInResources, Output, ShaderSpec, ShaderValidator}; +use ron::de; +use std::fs::File; +use std::path::PathBuf; // from glslang const FRAGMENT_SHADER: u32 = 0x8B30; const VERTEX_SHADER: u32 = 0x8B31; -struct Shader { - name: &'static str, - features: &'static [&'static str], -} +const VERSION_STRING: &str = "#version 300 es\n"; -const SHADER_PREFIX: &str = "#define WR_MAX_VERTEX_TEXTURE_WIDTH 1024\n"; - -const BRUSH_FEATURES: &[&str] = &["", "ALPHA_PASS"]; -const CLIP_FEATURES: &[&str] = &["TRANSFORM"]; -const CACHE_FEATURES: &[&str] = &[""]; -const GRADIENT_FEATURES: &[&str] = &[ "", "DITHERING", "ALPHA_PASS", "DITHERING,ALPHA_PASS" ]; -const PRIM_FEATURES: &[&str] = &[""]; - -const SHADERS: &[Shader] = &[ - // Clip mask shaders - Shader { - name: "cs_clip_rectangle", - features: CLIP_FEATURES, - }, - Shader { - name: "cs_clip_image", - features: CLIP_FEATURES, - }, - Shader { - name: "cs_clip_box_shadow", - features: CLIP_FEATURES, - }, - Shader { - name: "cs_clip_line", - features: CLIP_FEATURES, - }, - // Cache shaders - Shader { - name: "cs_blur", - features: &[ "ALPHA_TARGET", "COLOR_TARGET" ], - }, - Shader { - name: "cs_border_segment", - features: CACHE_FEATURES, - }, - // Prim shaders - Shader { - name: "ps_split_composite", - features: PRIM_FEATURES, - }, - Shader { - name: "ps_text_run", - features: &[ "", "GLYPH_TRANSFORM" ], - }, - // Brush shaders - Shader { - name: "brush_yuv_image", - features: &[ - "", - "YUV_NV12", - "YUV_PLANAR", - "YUV_INTERLEAVED", - "TEXTURE_2D,YUV_NV12", - "YUV_NV12,ALPHA_PASS", - ], - }, - Shader { - name: "brush_solid", - features: BRUSH_FEATURES, - }, - Shader { - name: "brush_image", - features: BRUSH_FEATURES, - }, - Shader { - name: "brush_blend", - features: BRUSH_FEATURES, - }, - Shader { - name: "brush_mix_blend", - features: BRUSH_FEATURES, - }, - Shader { - name: "brush_radial_gradient", - features: GRADIENT_FEATURES, - }, - Shader { - name: "brush_linear_gradient", - features: GRADIENT_FEATURES, - }, -]; +// Extensions required by these features are not supported by the angle shader validator. +const EXCLUDED_FEATURES: &'static[&'static str] = &["DUAL_SOURCE_BLENDING", "TEXTURE_EXTERNAL", "TEXTURE_RECT"]; -const VERSION_STRING: &str = "#version 300 es\n"; +#[derive(Deserialize)] +struct Shader { + name: String, + feature_sets: Vec, +} #[test] fn validate_shaders() { @@ -111,25 +39,49 @@ fn validate_shaders() { let fs_validator = ShaderValidator::new(FRAGMENT_SHADER, ShaderSpec::Gles3, Output::Essl, &resources).unwrap(); - for shader in SHADERS { - for config in shader.features { - let mut features = String::new(); - features.push_str(SHADER_PREFIX); - - for feature in config.split(",") { - features.push_str(&format!("#define WR_FEATURE_{}\n", feature)); + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + path.push("res"); + path.push("shaders.ron"); + + let file = File::open(&path).expect("Unable to open shaders.ron"); + let shaders: Vec = de::from_reader(file).expect("Unable to deserialize shaders.ron"); + + for shader in shaders { + let mut feature_variants: Vec = Vec::new(); + + // Building up possible permutations of features + for feature_set in shader.feature_sets { + if feature_variants.is_empty() { + feature_variants = feature_set.split(',').map(|s| s.to_owned()).collect(); + feature_variants.retain(|f| !EXCLUDED_FEATURES.contains(&f.as_str())); + } else { + let prev_variants: Vec = feature_variants.drain(..).collect(); + for variant in prev_variants.iter() { + for feature in feature_set.split(',') { + if !EXCLUDED_FEATURES.contains(&feature) { + feature_variants.push(format!("{},{}", variant, feature)); + } + } + } } + } + for variant in feature_variants { + let features = variant.split(",").collect::>(); let (vs, fs) = - webrender::build_shader_strings(VERSION_STRING, &features, shader.name, &None); + webrender::load_shader_sources(VERSION_STRING, + &features, + &shader.name, + &None); + + validate(&vs_validator, &shader.name, &features, vs); + validate(&fs_validator, &shader.name, &features, fs); - validate(&vs_validator, shader.name, vs); - validate(&fs_validator, shader.name, fs); } } } -fn validate(validator: &ShaderValidator, name: &str, source: String) { +fn validate(validator: &ShaderValidator, name: &str, features: &[&str], source: String) { // Check for each `switch` to have a `default`, see // https://github.com/servo/webrender/wiki/Driver-issues#lack-of-default-case-in-a-switch assert_eq!(source.matches("switch").count(), source.matches("default:").count(), @@ -137,12 +89,13 @@ fn validate(validator: &ShaderValidator, name: &str, source: String) { // Run Angle validator match validator.compile_and_translate(&[&source]) { Ok(_) => { - println!("Shader translated succesfully: {}", name); + println!("Shader translated succesfully: {}, features: {:?}", name, features); } Err(_) => { panic!( - "Shader compilation failed: {}\n{}", + "Shader compilation failed: {}, features: {:?}\n{}", name, + features, validator.info_log() ); }