diff options
Diffstat (limited to 'gfx/wr/wrench/src/test_shaders.rs')
-rw-r--r-- | gfx/wr/wrench/src/test_shaders.rs | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/gfx/wr/wrench/src/test_shaders.rs b/gfx/wr/wrench/src/test_shaders.rs new file mode 100644 index 0000000000..9e6492538a --- /dev/null +++ b/gfx/wr/wrench/src/test_shaders.rs @@ -0,0 +1,161 @@ +/* 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 webrender::ShaderKind; +use webrender_build::shader::{ShaderFeatureFlags, ShaderVersion, build_shader_strings}; +use webrender_build::shader::get_shader_features; +use glsl_lang::ast::{InterpolationQualifierData, NodeContent, SingleDeclaration}; +use glsl_lang::ast::{StorageQualifierData, TranslationUnit, TypeSpecifierNonArrayData}; +use glsl_lang::ast::TypeQualifierSpecData; +use glsl_lang::parse::DefaultParse as _; +use glsl_lang::visitor::{Host, Visit, Visitor}; + +/// Tests that a shader contains no flat scalar varyings. +/// These must be avoided on Adreno 3xx devices due to bug 1630356. +fn test_no_flat_scalar_varyings( + name: &str, + shader: &mut TranslationUnit, + _shader_kind: ShaderKind, +) { + struct FlatScalarVaryingsVisitor { + shader_name: String, + } + + impl Visitor for FlatScalarVaryingsVisitor { + fn visit_single_declaration(&mut self, declaration: &SingleDeclaration) -> Visit { + let is_scalar = matches!( + declaration.ty.ty.ty.content, + TypeSpecifierNonArrayData::Bool + | TypeSpecifierNonArrayData::Int + | TypeSpecifierNonArrayData::UInt + | TypeSpecifierNonArrayData::Float + | TypeSpecifierNonArrayData::Double + ); + + let qualifiers = declaration + .ty + .qualifier + .as_ref() + .map(|q| q.qualifiers.as_slice()) + .unwrap_or(&[]); + + let is_flat = qualifiers.contains( + &TypeQualifierSpecData::Interpolation(InterpolationQualifierData::Flat.into_node()) + .into_node(), + ); + + assert!( + !(is_scalar && is_flat), + "{}: {} is a flat scalar varying", + self.shader_name, + &declaration.name.as_ref().unwrap() + ); + + Visit::Parent + } + } + + let mut visitor = FlatScalarVaryingsVisitor { + shader_name: name.to_string(), + }; + shader.visit(&mut visitor); +} + +/// Tests that a shader's varyings have an explicit precision specifier. +/// Mali vendor tooling shows us that we are often varying-iterpolation bound, so using mediump +/// where possible helps alleviate this. By enforcing that varyings are given explicit precisions, +/// we ensure that highp is only used when necessary rather than just by default. +fn test_varying_explicit_precision( + name: &str, + shader: &mut TranslationUnit, + shader_kind: ShaderKind, +) { + struct VaryingExplicitPrecisionVisitor { + shader_name: String, + shader_kind: ShaderKind, + } + + impl Visitor for VaryingExplicitPrecisionVisitor { + fn visit_single_declaration(&mut self, declaration: &SingleDeclaration) -> Visit { + let qualifiers = declaration + .ty + .qualifier + .as_ref() + .map(|q| q.qualifiers.as_slice()) + .unwrap_or(&[]); + + let is_varying = qualifiers.iter().any(|qualifier| { + match &qualifier.content { + TypeQualifierSpecData::Storage(storage) => match self.shader_kind { + ShaderKind::Vertex => storage.content == StorageQualifierData::Out, + ShaderKind::Fragment => storage.content == StorageQualifierData::In, + } + _ => false, + } + }); + + let has_explicit_precision = qualifiers + .iter() + .any(|qualifier| matches!(qualifier.content, TypeQualifierSpecData::Precision(_))); + + assert!( + !is_varying || has_explicit_precision, + "{}: {} is a varying without an explicit precision declared", + self.shader_name, + &declaration.name.as_ref().unwrap() + ); + + Visit::Parent + } + } + + let mut visitor = VaryingExplicitPrecisionVisitor { + shader_name: name.to_string(), + shader_kind, + }; + shader.visit(&mut visitor); +} + +pub fn test_shaders() { + let mut flags = ShaderFeatureFlags::all(); + if cfg!(any(target_os = "windows", target_os = "android")) { + flags.remove(ShaderFeatureFlags::GL); + } else { + flags.remove(ShaderFeatureFlags::GLES); + } + // glsl-lang crate fails to parse advanced blend shaders + flags.remove(ShaderFeatureFlags::ADVANCED_BLEND_EQUATION); + + for (shader, configs) in get_shader_features(flags) { + for config in configs { + let name = if config.is_empty() { + shader.to_string() + } else { + format!("{}_{}", shader, config.replace(",", "_")) + }; + let vert_name = format!("{}.vert", name); + let frag_name = format!("{}.frag", name); + + + let features = config + .split(",") + .filter(|f| !f.is_empty()) + .collect::<Vec<_>>(); + + let (vert_src, frag_src) = + build_shader_strings(ShaderVersion::Gles, &features, shader, &|f| { + webrender::get_unoptimized_shader_source(f, None) + }); + + let mut vert = TranslationUnit::parse(&vert_src).unwrap(); + let mut frag = TranslationUnit::parse(&frag_src).unwrap(); + + + test_no_flat_scalar_varyings(&vert_name, &mut vert, ShaderKind::Vertex); + test_no_flat_scalar_varyings(&frag_name, &mut frag, ShaderKind::Fragment); + test_varying_explicit_precision(&vert_name, &mut vert, ShaderKind::Vertex); + test_varying_explicit_precision(&frag_name, &mut frag, ShaderKind::Fragment); + } + } +} |