test_shaders.rs (6072B)
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 use webrender::ShaderKind; 6 use webrender_build::shader::{ShaderFeatureFlags, ShaderVersion, build_shader_strings}; 7 use webrender_build::shader::get_shader_features; 8 use glsl_lang::ast::{InterpolationQualifierData, NodeContent, SingleDeclaration}; 9 use glsl_lang::ast::{StorageQualifierData, TranslationUnit, TypeSpecifierNonArrayData}; 10 use glsl_lang::ast::TypeQualifierSpecData; 11 use glsl_lang::parse::DefaultParse as _; 12 use glsl_lang::visitor::{Host, Visit, Visitor}; 13 14 /// Tests that a shader contains no flat scalar varyings. 15 /// These must be avoided on Adreno 3xx devices due to bug 1630356. 16 fn test_no_flat_scalar_varyings( 17 name: &str, 18 shader: &mut TranslationUnit, 19 _shader_kind: ShaderKind, 20 ) { 21 struct FlatScalarVaryingsVisitor { 22 shader_name: String, 23 } 24 25 impl Visitor for FlatScalarVaryingsVisitor { 26 fn visit_single_declaration(&mut self, declaration: &SingleDeclaration) -> Visit { 27 let is_scalar = matches!( 28 declaration.ty.ty.ty.content, 29 TypeSpecifierNonArrayData::Bool 30 | TypeSpecifierNonArrayData::Int 31 | TypeSpecifierNonArrayData::UInt 32 | TypeSpecifierNonArrayData::Float 33 | TypeSpecifierNonArrayData::Double 34 ); 35 36 let qualifiers = declaration 37 .ty 38 .qualifier 39 .as_ref() 40 .map(|q| q.qualifiers.as_slice()) 41 .unwrap_or(&[]); 42 43 let is_flat = qualifiers.contains( 44 &TypeQualifierSpecData::Interpolation(InterpolationQualifierData::Flat.into_node()) 45 .into_node(), 46 ); 47 48 assert!( 49 !(is_scalar && is_flat), 50 "{}: {} is a flat scalar varying", 51 self.shader_name, 52 &declaration.name.as_ref().unwrap() 53 ); 54 55 Visit::Parent 56 } 57 } 58 59 let mut visitor = FlatScalarVaryingsVisitor { 60 shader_name: name.to_string(), 61 }; 62 shader.visit(&mut visitor); 63 } 64 65 /// Tests that a shader's varyings have an explicit precision specifier. 66 /// Mali vendor tooling shows us that we are often varying-iterpolation bound, so using mediump 67 /// where possible helps alleviate this. By enforcing that varyings are given explicit precisions, 68 /// we ensure that highp is only used when necessary rather than just by default. 69 fn test_varying_explicit_precision( 70 name: &str, 71 shader: &mut TranslationUnit, 72 shader_kind: ShaderKind, 73 ) { 74 struct VaryingExplicitPrecisionVisitor { 75 shader_name: String, 76 shader_kind: ShaderKind, 77 } 78 79 impl Visitor for VaryingExplicitPrecisionVisitor { 80 fn visit_single_declaration(&mut self, declaration: &SingleDeclaration) -> Visit { 81 let qualifiers = declaration 82 .ty 83 .qualifier 84 .as_ref() 85 .map(|q| q.qualifiers.as_slice()) 86 .unwrap_or(&[]); 87 88 let is_varying = qualifiers.iter().any(|qualifier| { 89 match &qualifier.content { 90 TypeQualifierSpecData::Storage(storage) => match self.shader_kind { 91 ShaderKind::Vertex => storage.content == StorageQualifierData::Out, 92 ShaderKind::Fragment => storage.content == StorageQualifierData::In, 93 } 94 _ => false, 95 } 96 }); 97 98 let has_explicit_precision = qualifiers 99 .iter() 100 .any(|qualifier| matches!(qualifier.content, TypeQualifierSpecData::Precision(_))); 101 102 assert!( 103 !is_varying || has_explicit_precision, 104 "{}: {} is a varying without an explicit precision declared", 105 self.shader_name, 106 &declaration.name.as_ref().unwrap() 107 ); 108 109 Visit::Parent 110 } 111 } 112 113 let mut visitor = VaryingExplicitPrecisionVisitor { 114 shader_name: name.to_string(), 115 shader_kind, 116 }; 117 shader.visit(&mut visitor); 118 } 119 120 pub fn test_shaders() { 121 let mut flags = ShaderFeatureFlags::all(); 122 if cfg!(any(target_os = "windows", target_os = "android")) { 123 flags.remove(ShaderFeatureFlags::GL); 124 } else { 125 flags.remove(ShaderFeatureFlags::GLES); 126 } 127 // glsl-lang crate fails to parse advanced blend shaders 128 flags.remove(ShaderFeatureFlags::ADVANCED_BLEND_EQUATION); 129 // glsl-lang crate fails to parse texture external BT709 shaders 130 flags.remove(ShaderFeatureFlags::TEXTURE_EXTERNAL_BT709); 131 132 for (shader, configs) in get_shader_features(flags) { 133 for config in configs { 134 let name = if config.is_empty() { 135 shader.to_string() 136 } else { 137 format!("{}_{}", shader, config.replace(",", "_")) 138 }; 139 let vert_name = format!("{}.vert", name); 140 let frag_name = format!("{}.frag", name); 141 142 143 let features = config 144 .split(",") 145 .filter(|f| !f.is_empty()) 146 .collect::<Vec<_>>(); 147 148 let (vert_src, frag_src) = 149 build_shader_strings(ShaderVersion::Gles, &features, shader, &|f| { 150 webrender::get_unoptimized_shader_source(f, None) 151 }); 152 153 let mut vert = TranslationUnit::parse(&vert_src).unwrap(); 154 let mut frag = TranslationUnit::parse(&frag_src).unwrap(); 155 156 157 test_no_flat_scalar_varyings(&vert_name, &mut vert, ShaderKind::Vertex); 158 test_no_flat_scalar_varyings(&frag_name, &mut frag, ShaderKind::Fragment); 159 test_varying_explicit_precision(&vert_name, &mut vert, ShaderKind::Vertex); 160 test_varying_explicit_precision(&frag_name, &mut frag, ShaderKind::Fragment); 161 } 162 } 163 }