webrender_build/
shader_features.rs

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
5use std::collections::HashMap;
6
7bitflags! {
8    #[derive(Default, Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord, Hash)]
9    pub struct ShaderFeatureFlags: u32 {
10        const GL = 1 << 0;
11        const GLES = 1 << 1;
12
13        const ADVANCED_BLEND_EQUATION = 1 << 8;
14        const DUAL_SOURCE_BLENDING = 1 << 9;
15        const DITHERING = 1 << 10;
16        const TEXTURE_EXTERNAL = 1 << 11;
17        const TEXTURE_EXTERNAL_ESSL1 = 1 << 12;
18        const TEXTURE_EXTERNAL_BT709 = 1 << 13;
19        const DEBUG = 1 << 14;
20    }
21}
22
23pub type ShaderFeatures = HashMap<&'static str, Vec<String>>;
24
25/// Builder for a list of features.
26#[derive(Clone)]
27struct FeatureList<'a> {
28    list: Vec<&'a str>,
29}
30
31impl<'a> FeatureList<'a> {
32    fn new() -> Self {
33        FeatureList {
34            list: Vec::new(),
35        }
36    }
37
38    fn add(&mut self, feature: &'a str) {
39        assert!(!feature.contains(','));
40        self.list.push(feature);
41    }
42
43    fn with(&self, feature: &'a str) -> Self {
44        let mut other = self.clone();
45        other.add(feature);
46        other
47    }
48
49    fn concat(&self, other: &Self) -> Self {
50        let mut list = self.list.clone();
51        list.extend_from_slice(&other.list);
52        FeatureList {
53            list
54        }
55    }
56
57    fn finish(&mut self) -> String {
58        self.list.sort_unstable();
59        self.list.join(",")
60    }
61}
62
63/// Computes available shaders and their features for the given feature flags.
64pub fn get_shader_features(flags: ShaderFeatureFlags) -> ShaderFeatures {
65    let mut shaders = ShaderFeatures::new();
66
67    // Clip shaders
68    shaders.insert("cs_clip_rectangle", vec![String::new(), "FAST_PATH".to_string()]);
69    shaders.insert("cs_clip_box_shadow", vec!["TEXTURE_2D".to_string()]);
70
71    // Cache shaders
72    shaders.insert("cs_blur", vec!["ALPHA_TARGET".to_string(), "COLOR_TARGET".to_string()]);
73
74    shaders.insert("ps_quad_mask", vec![String::new(), "FAST_PATH".to_string()]);
75
76    for name in &[
77        "cs_line_decoration",
78        "cs_fast_linear_gradient",
79        "cs_border_segment",
80        "cs_border_solid",
81        "cs_svg_filter",
82        "cs_svg_filter_node",
83    ] {
84        shaders.insert(name, vec![String::new()]);
85    }
86
87    for name in &[
88        "cs_linear_gradient",
89        "cs_radial_gradient",
90        "cs_conic_gradient",
91    ] {
92        let mut features = Vec::new();
93        features.push(String::new());
94        if flags.contains(ShaderFeatureFlags::DITHERING) {
95            features.push("DITHERING".to_string());
96        }
97        shaders.insert(name, features);
98    }
99
100    let mut base_prim_features = FeatureList::new();
101
102    // Brush shaders
103    let mut brush_alpha_features = base_prim_features.with("ALPHA_PASS");
104    for name in &["brush_solid", "brush_blend", "brush_mix_blend"] {
105        let features: Vec<String> = vec![
106            base_prim_features.finish(),
107            brush_alpha_features.finish(),
108            "DEBUG_OVERDRAW".to_string(),
109        ];
110        shaders.insert(name, features);
111    }
112
113    #[allow(clippy::single_element_loop)]
114    for name in &["brush_linear_gradient"] {
115        let mut list = FeatureList::new();
116        if flags.contains(ShaderFeatureFlags::DITHERING) {
117            list.add("DITHERING");
118        }
119        let features: Vec<String> = vec![
120            list.concat(&base_prim_features).finish(),
121            list.concat(&brush_alpha_features).finish(),
122            list.with("DEBUG_OVERDRAW").finish(),
123        ];
124        shaders.insert(name, features);
125    }
126
127    {
128        let features: Vec<String> = vec![
129            base_prim_features.finish(),
130            brush_alpha_features.finish(),
131            base_prim_features.with("ANTIALIASING").finish(),
132            brush_alpha_features.with("ANTIALIASING").finish(),
133            "ANTIALIASING,DEBUG_OVERDRAW".to_string(),
134            "DEBUG_OVERDRAW".to_string(),
135        ];
136        shaders.insert("brush_opacity", features);
137    }
138
139    // Image brush shaders
140    let mut texture_types = vec!["TEXTURE_2D"];
141    if flags.contains(ShaderFeatureFlags::GL) {
142        texture_types.push("TEXTURE_RECT");
143    }
144    if flags.contains(ShaderFeatureFlags::TEXTURE_EXTERNAL) {
145        texture_types.push("TEXTURE_EXTERNAL");
146    }
147    if flags.contains(ShaderFeatureFlags::TEXTURE_EXTERNAL_BT709) {
148        texture_types.push("TEXTURE_EXTERNAL_BT709");
149    }
150    let mut image_features: Vec<String> = Vec::new();
151    for texture_type in &texture_types {
152        let mut fast = FeatureList::new();
153        if !texture_type.is_empty() {
154            fast.add(texture_type);
155        }
156        image_features.push(fast.concat(&base_prim_features).finish());
157        image_features.push(fast.concat(&brush_alpha_features).finish());
158        image_features.push(fast.with("DEBUG_OVERDRAW").finish());
159        let mut slow = fast.clone();
160        slow.add("REPETITION");
161        slow.add("ANTIALIASING");
162        image_features.push(slow.concat(&base_prim_features).finish());
163        image_features.push(slow.concat(&brush_alpha_features).finish());
164        image_features.push(slow.with("DEBUG_OVERDRAW").finish());
165        if flags.contains(ShaderFeatureFlags::ADVANCED_BLEND_EQUATION) {
166            let advanced_blend_features = brush_alpha_features.with("ADVANCED_BLEND");
167            image_features.push(fast.concat(&advanced_blend_features).finish());
168            image_features.push(slow.concat(&advanced_blend_features).finish());
169        }
170        if flags.contains(ShaderFeatureFlags::DUAL_SOURCE_BLENDING) {
171            let dual_source_features = brush_alpha_features.with("DUAL_SOURCE_BLENDING");
172            image_features.push(fast.concat(&dual_source_features).finish());
173            image_features.push(slow.concat(&dual_source_features).finish());
174        }
175    }
176    shaders.insert("brush_image", image_features);
177
178    let mut composite_texture_types = texture_types.clone();
179    if flags.contains(ShaderFeatureFlags::TEXTURE_EXTERNAL_ESSL1) {
180        composite_texture_types.push("TEXTURE_EXTERNAL_ESSL1");
181    }
182    let mut composite_features: Vec<String> = Vec::new();
183    for texture_type in &composite_texture_types {
184        let base = texture_type.to_string();
185        composite_features.push(base);
186    }
187    shaders.insert("cs_scale", composite_features.clone());
188
189    // YUV image brush and composite shaders
190    let mut yuv_features: Vec<String> = Vec::new();
191    for texture_type in &texture_types {
192        let mut list = FeatureList::new();
193        if !texture_type.is_empty() {
194            list.add(texture_type);
195        }
196        list.add("YUV");
197        composite_features.push(list.finish());
198        yuv_features.push(list.concat(&base_prim_features).finish());
199        yuv_features.push(list.concat(&brush_alpha_features).finish());
200        yuv_features.push(list.with("DEBUG_OVERDRAW").finish());
201    }
202    shaders.insert("brush_yuv_image", yuv_features);
203
204    // Fast path composite shaders
205    for texture_type in &composite_texture_types {
206        let mut list = FeatureList::new();
207        if !texture_type.is_empty() {
208            list.add(texture_type);
209        }
210        list.add("FAST_PATH");
211        composite_features.push(list.finish());
212
213        // YUV shaders are not compatible with ESSL1
214        if *texture_type == "TEXTURE_EXTERNAL_ESSL1" {
215            continue;
216        }
217
218        list.add("YUV");
219        composite_features.push(list.finish());
220    }
221    shaders.insert("composite", composite_features);
222
223    // Prim shaders
224    let mut text_types = vec![""];
225    if flags.contains(ShaderFeatureFlags::DUAL_SOURCE_BLENDING) {
226        text_types.push("DUAL_SOURCE_BLENDING");
227    }
228    let mut text_features: Vec<String> = Vec::new();
229    for text_type in &text_types {
230        let mut list = base_prim_features.with("TEXTURE_2D");
231        if !text_type.is_empty() {
232            list.add(text_type);
233        }
234        let mut alpha_list = list.with("ALPHA_PASS");
235        text_features.push(alpha_list.finish());
236        text_features.push(alpha_list.with("GLYPH_TRANSFORM").finish());
237        text_features.push(list.with("DEBUG_OVERDRAW").finish());
238    }
239    shaders.insert("ps_text_run", text_features);
240
241    shaders.insert("ps_split_composite", vec![base_prim_features.finish()]);
242
243    shaders.insert("ps_quad_textured", vec![base_prim_features.finish()]);
244
245    shaders.insert("ps_quad_radial_gradient", vec![base_prim_features.finish()]);
246
247    shaders.insert("ps_quad_conic_gradient", vec![base_prim_features.finish()]);
248
249    shaders.insert("ps_clear", vec![base_prim_features.finish()]);
250
251    shaders.insert("ps_copy", vec![base_prim_features.finish()]);
252
253    if flags.contains(ShaderFeatureFlags::DEBUG) {
254        for name in &["debug_color", "debug_font"] {
255            shaders.insert(name, vec![String::new()]);
256        }
257    }
258
259    shaders
260}
261