script/dom/webgl/extensions/
extensions.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 https://mozilla.org/MPL/2.0/. */
4
5use std::collections::HashMap;
6use std::iter::FromIterator;
7use std::ptr::NonNull;
8
9use canvas_traits::webgl::{GlType, TexFormat, WebGLSLVersion, WebGLVersion};
10use js::jsapi::JSObject;
11use malloc_size_of::MallocSizeOf;
12use rustc_hash::{FxHashMap, FxHashSet};
13use script_bindings::str::DOMString;
14type GLenum = u32;
15
16use super::wrapper::{TypedWebGLExtensionWrapper, WebGLExtensionWrapper};
17use super::{WebGLExtension, WebGLExtensionSpec, ext};
18use crate::dom::bindings::cell::DomRefCell;
19use crate::dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants;
20use crate::dom::bindings::codegen::Bindings::EXTTextureFilterAnisotropicBinding::EXTTextureFilterAnisotropicConstants;
21use crate::dom::bindings::codegen::Bindings::OESStandardDerivativesBinding::OESStandardDerivativesConstants;
22use crate::dom::bindings::codegen::Bindings::OESTextureHalfFloatBinding::OESTextureHalfFloatConstants;
23use crate::dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::OESVertexArrayObjectConstants;
24use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
25use crate::dom::bindings::trace::JSTraceable;
26use crate::dom::webgl::extensions::extcolorbufferhalffloat::EXTColorBufferHalfFloat;
27use crate::dom::webgl::extensions::oestexturefloat::OESTextureFloat;
28use crate::dom::webgl::extensions::oestexturehalffloat::OESTextureHalfFloat;
29use crate::dom::webgl::extensions::webglcolorbufferfloat::WEBGLColorBufferFloat;
30use crate::dom::webgl::webglrenderingcontext::WebGLRenderingContext;
31use crate::dom::webgl::webgltexture::TexCompression;
32
33// Data types that are implemented for texImage2D and texSubImage2D in a WebGL 1.0 context
34// but must trigger a InvalidValue error until the related WebGL Extensions are enabled.
35// Example: https://www.khronos.org/registry/webgl/extensions/OES_texture_float/
36const DEFAULT_DISABLED_TEX_TYPES_WEBGL1: [GLenum; 2] = [
37    constants::FLOAT,
38    OESTextureHalfFloatConstants::HALF_FLOAT_OES,
39];
40
41// Data types that are implemented for textures in WebGLRenderingContext
42// but not allowed to use with linear filtering until the related WebGL Extensions are enabled.
43// Example: https://www.khronos.org/registry/webgl/extensions/OES_texture_float_linear/
44const DEFAULT_NOT_FILTERABLE_TEX_TYPES: [GLenum; 2] = [
45    constants::FLOAT,
46    OESTextureHalfFloatConstants::HALF_FLOAT_OES,
47];
48
49// Param names that are implemented for glGetParameter in a WebGL 1.0 context
50// but must trigger a InvalidEnum error until the related WebGL Extensions are enabled.
51// Example: https://www.khronos.org/registry/webgl/extensions/OES_standard_derivatives/
52const DEFAULT_DISABLED_GET_PARAMETER_NAMES_WEBGL1: [GLenum; 3] = [
53    EXTTextureFilterAnisotropicConstants::MAX_TEXTURE_MAX_ANISOTROPY_EXT,
54    OESStandardDerivativesConstants::FRAGMENT_SHADER_DERIVATIVE_HINT_OES,
55    OESVertexArrayObjectConstants::VERTEX_ARRAY_BINDING_OES,
56];
57
58// Param names that are implemented for glGetParameter in a WebGL 2.0 context
59// but must trigger a InvalidEnum error until the related WebGL Extensions are enabled.
60// Example: https://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/
61const DEFAULT_DISABLED_GET_PARAMETER_NAMES_WEBGL2: [GLenum; 1] =
62    [EXTTextureFilterAnisotropicConstants::MAX_TEXTURE_MAX_ANISOTROPY_EXT];
63
64// Param names that are implemented for glGetTexParameter in a WebGL 1.0 context
65// but must trigger a InvalidEnum error until the related WebGL Extensions are enabled.
66// Example: https://www.khronos.org/registry/webgl/extensions/OES_standard_derivatives/
67const DEFAULT_DISABLED_GET_TEX_PARAMETER_NAMES_WEBGL1: [GLenum; 1] =
68    [EXTTextureFilterAnisotropicConstants::TEXTURE_MAX_ANISOTROPY_EXT];
69
70// Param names that are implemented for glGetTexParameter in a WebGL 2.0 context
71// but must trigger a InvalidEnum error until the related WebGL Extensions are enabled.
72// Example: https://www.khronos.org/registry/webgl/extensions/EXT_texture_filter_anisotropic/
73const DEFAULT_DISABLED_GET_TEX_PARAMETER_NAMES_WEBGL2: [GLenum; 1] =
74    [EXTTextureFilterAnisotropicConstants::TEXTURE_MAX_ANISOTROPY_EXT];
75
76// Param names that are implemented for glGetVertexAttrib in a WebGL 1.0 context
77// but must trigger a InvalidEnum error until the related WebGL Extensions are enabled.
78// Example: https://www.khronos.org/registry/webgl/extensions/ANGLE_instanced_arrays/
79const DEFAULT_DISABLED_GET_VERTEX_ATTRIB_NAMES_WEBGL1: [GLenum; 1] =
80    [ANGLEInstancedArraysConstants::VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE];
81
82/// WebGL features that are enabled/disabled by WebGL Extensions.
83#[derive(JSTraceable, MallocSizeOf)]
84struct WebGLExtensionFeatures {
85    gl_extensions: FxHashSet<String>,
86    disabled_tex_types: FxHashSet<GLenum>,
87    not_filterable_tex_types: FxHashSet<GLenum>,
88    #[no_trace]
89    effective_tex_internal_formats: FxHashMap<TexFormatType, TexFormat>,
90    /// WebGL Hint() targets enabled by extensions.
91    hint_targets: FxHashSet<GLenum>,
92    /// WebGL GetParameter() names enabled by extensions.
93    disabled_get_parameter_names: FxHashSet<GLenum>,
94    /// WebGL GetTexParameter() names enabled by extensions.
95    disabled_get_tex_parameter_names: FxHashSet<GLenum>,
96    /// WebGL GetAttribVertex() names enabled by extensions.
97    disabled_get_vertex_attrib_names: FxHashSet<GLenum>,
98    /// WebGL OES_element_index_uint extension.
99    element_index_uint_enabled: bool,
100    /// WebGL EXT_blend_minmax extension.
101    blend_minmax_enabled: bool,
102    /// WebGL supported texture compression formats enabled by extensions.
103    tex_compression_formats: FxHashMap<GLenum, TexCompression>,
104}
105
106impl WebGLExtensionFeatures {
107    fn new(webgl_version: WebGLVersion) -> Self {
108        let (
109            disabled_tex_types,
110            disabled_get_parameter_names,
111            disabled_get_tex_parameter_names,
112            disabled_get_vertex_attrib_names,
113            not_filterable_tex_types,
114            element_index_uint_enabled,
115            blend_minmax_enabled,
116        ) = match webgl_version {
117            WebGLVersion::WebGL1 => (
118                DEFAULT_DISABLED_TEX_TYPES_WEBGL1.iter().cloned().collect(),
119                DEFAULT_DISABLED_GET_PARAMETER_NAMES_WEBGL1
120                    .iter()
121                    .cloned()
122                    .collect(),
123                DEFAULT_DISABLED_GET_TEX_PARAMETER_NAMES_WEBGL1
124                    .iter()
125                    .cloned()
126                    .collect(),
127                DEFAULT_DISABLED_GET_VERTEX_ATTRIB_NAMES_WEBGL1
128                    .iter()
129                    .cloned()
130                    .collect(),
131                DEFAULT_NOT_FILTERABLE_TEX_TYPES.iter().cloned().collect(),
132                false,
133                false,
134            ),
135            WebGLVersion::WebGL2 => (
136                Default::default(),
137                DEFAULT_DISABLED_GET_PARAMETER_NAMES_WEBGL2
138                    .iter()
139                    .cloned()
140                    .collect(),
141                DEFAULT_DISABLED_GET_TEX_PARAMETER_NAMES_WEBGL2
142                    .iter()
143                    .cloned()
144                    .collect(),
145                Default::default(),
146                Default::default(),
147                true,
148                true,
149            ),
150        };
151        Self {
152            gl_extensions: Default::default(),
153            disabled_tex_types,
154            not_filterable_tex_types,
155            effective_tex_internal_formats: Default::default(),
156            hint_targets: Default::default(),
157            disabled_get_parameter_names,
158            disabled_get_tex_parameter_names,
159            disabled_get_vertex_attrib_names,
160            element_index_uint_enabled,
161            blend_minmax_enabled,
162            tex_compression_formats: Default::default(),
163        }
164    }
165}
166
167/// Handles the list of implemented, supported and enabled WebGL extensions.
168#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
169#[derive(JSTraceable, MallocSizeOf)]
170pub(crate) struct WebGLExtensions {
171    extensions: DomRefCell<HashMap<String, Box<dyn WebGLExtensionWrapper>>>,
172    features: DomRefCell<WebGLExtensionFeatures>,
173    #[no_trace]
174    webgl_version: WebGLVersion,
175    #[no_trace]
176    api_type: GlType,
177    #[no_trace]
178    glsl_version: WebGLSLVersion,
179}
180
181impl WebGLExtensions {
182    pub(crate) fn new(
183        webgl_version: WebGLVersion,
184        api_type: GlType,
185        glsl_version: WebGLSLVersion,
186    ) -> WebGLExtensions {
187        Self {
188            extensions: DomRefCell::new(HashMap::new()),
189            features: DomRefCell::new(WebGLExtensionFeatures::new(webgl_version)),
190            webgl_version,
191            api_type,
192            glsl_version,
193        }
194    }
195
196    pub(crate) fn init_once<F>(&self, cb: F)
197    where
198        F: FnOnce() -> String,
199    {
200        if self.extensions.borrow().is_empty() {
201            let gl_str = cb();
202            self.features.borrow_mut().gl_extensions =
203                FxHashSet::from_iter(gl_str.split(&[',', ' '][..]).map(|s| s.into()));
204            self.register_all_extensions();
205        }
206    }
207
208    pub(crate) fn register<T: 'static + WebGLExtension + JSTraceable + MallocSizeOf>(&self) {
209        let name = T::name().to_uppercase();
210        self.extensions
211            .borrow_mut()
212            .insert(name, Box::new(TypedWebGLExtensionWrapper::<T>::new()));
213    }
214
215    pub(crate) fn get_supported_extensions(&self) -> Vec<&'static str> {
216        self.extensions
217            .borrow()
218            .iter()
219            .filter(|v| {
220                if let WebGLExtensionSpec::Specific(version) = v.1.spec() {
221                    if self.webgl_version != version {
222                        return false;
223                    }
224                }
225                v.1.is_supported(self)
226            })
227            .map(|ref v| v.1.name())
228            .collect()
229    }
230
231    pub(crate) fn get_or_init_extension(
232        &self,
233        name: &DOMString,
234        ctx: &WebGLRenderingContext,
235    ) -> Option<NonNull<JSObject>> {
236        let name = name.to_uppercase();
237        self.extensions.borrow().get(&name).and_then(|extension| {
238            if extension.is_supported(self) {
239                Some(extension.instance_or_init(ctx, self))
240            } else {
241                None
242            }
243        })
244    }
245
246    pub(crate) fn is_enabled<T>(&self) -> bool
247    where
248        T: 'static + WebGLExtension + JSTraceable + MallocSizeOf,
249    {
250        let name = T::name().to_uppercase();
251        self.extensions
252            .borrow()
253            .get(&name)
254            .is_some_and(|ext| ext.is_enabled())
255    }
256
257    pub(crate) fn supports_gl_extension(&self, name: &str) -> bool {
258        self.features.borrow().gl_extensions.contains(name)
259    }
260
261    pub(crate) fn supports_any_gl_extension(&self, names: &[&str]) -> bool {
262        let features = self.features.borrow();
263        names
264            .iter()
265            .any(|name| features.gl_extensions.contains(*name))
266    }
267
268    pub(crate) fn supports_all_gl_extension(&self, names: &[&str]) -> bool {
269        let features = self.features.borrow();
270        names
271            .iter()
272            .all(|name| features.gl_extensions.contains(*name))
273    }
274
275    pub(crate) fn enable_tex_type(&self, data_type: GLenum) {
276        self.features
277            .borrow_mut()
278            .disabled_tex_types
279            .remove(&data_type);
280    }
281
282    pub(crate) fn is_tex_type_enabled(&self, data_type: GLenum) -> bool {
283        !self
284            .features
285            .borrow()
286            .disabled_tex_types
287            .contains(&data_type)
288    }
289
290    pub(crate) fn add_effective_tex_internal_format(
291        &self,
292        source_internal_format: TexFormat,
293        source_data_type: u32,
294        effective_internal_format: TexFormat,
295    ) {
296        let format = TexFormatType(source_internal_format, source_data_type);
297        self.features
298            .borrow_mut()
299            .effective_tex_internal_formats
300            .insert(format, effective_internal_format);
301    }
302
303    pub(crate) fn get_effective_tex_internal_format(
304        &self,
305        source_internal_format: TexFormat,
306        source_data_type: u32,
307    ) -> TexFormat {
308        let format = TexFormatType(source_internal_format, source_data_type);
309        *(self
310            .features
311            .borrow()
312            .effective_tex_internal_formats
313            .get(&format)
314            .unwrap_or(&source_internal_format))
315    }
316
317    pub(crate) fn enable_filterable_tex_type(&self, text_data_type: GLenum) {
318        self.features
319            .borrow_mut()
320            .not_filterable_tex_types
321            .remove(&text_data_type);
322    }
323
324    pub(crate) fn is_filterable(&self, text_data_type: u32) -> bool {
325        !self
326            .features
327            .borrow()
328            .not_filterable_tex_types
329            .contains(&text_data_type)
330    }
331
332    pub(crate) fn enable_hint_target(&self, name: GLenum) {
333        self.features.borrow_mut().hint_targets.insert(name);
334    }
335
336    pub(crate) fn is_hint_target_enabled(&self, name: GLenum) -> bool {
337        self.features.borrow().hint_targets.contains(&name)
338    }
339
340    pub(crate) fn enable_get_parameter_name(&self, name: GLenum) {
341        self.features
342            .borrow_mut()
343            .disabled_get_parameter_names
344            .remove(&name);
345    }
346
347    pub(crate) fn is_get_parameter_name_enabled(&self, name: GLenum) -> bool {
348        !self
349            .features
350            .borrow()
351            .disabled_get_parameter_names
352            .contains(&name)
353    }
354
355    pub(crate) fn enable_get_tex_parameter_name(&self, name: GLenum) {
356        self.features
357            .borrow_mut()
358            .disabled_get_tex_parameter_names
359            .remove(&name);
360    }
361
362    pub(crate) fn is_get_tex_parameter_name_enabled(&self, name: GLenum) -> bool {
363        !self
364            .features
365            .borrow()
366            .disabled_get_tex_parameter_names
367            .contains(&name)
368    }
369
370    pub(crate) fn enable_get_vertex_attrib_name(&self, name: GLenum) {
371        self.features
372            .borrow_mut()
373            .disabled_get_vertex_attrib_names
374            .remove(&name);
375    }
376
377    pub(crate) fn is_get_vertex_attrib_name_enabled(&self, name: GLenum) -> bool {
378        !self
379            .features
380            .borrow()
381            .disabled_get_vertex_attrib_names
382            .contains(&name)
383    }
384
385    pub(crate) fn add_tex_compression_formats(&self, formats: &[TexCompression]) {
386        let formats: FxHashMap<GLenum, TexCompression> = formats
387            .iter()
388            .map(|&compression| (compression.format.as_gl_constant(), compression))
389            .collect();
390
391        self.features
392            .borrow_mut()
393            .tex_compression_formats
394            .extend(formats.iter());
395    }
396
397    pub(crate) fn get_tex_compression_format(&self, format_id: GLenum) -> Option<TexCompression> {
398        self.features
399            .borrow()
400            .tex_compression_formats
401            .get(&format_id)
402            .cloned()
403    }
404
405    pub(crate) fn get_tex_compression_ids(&self) -> Vec<GLenum> {
406        self.features
407            .borrow()
408            .tex_compression_formats
409            .keys()
410            .copied()
411            .collect()
412    }
413
414    fn register_all_extensions(&self) {
415        self.register::<ext::angleinstancedarrays::ANGLEInstancedArrays>();
416        self.register::<ext::extblendminmax::EXTBlendMinmax>();
417        self.register::<ext::extcolorbufferhalffloat::EXTColorBufferHalfFloat>();
418        self.register::<ext::extfragdepth::EXTFragDepth>();
419        self.register::<ext::extshadertexturelod::EXTShaderTextureLod>();
420        self.register::<ext::exttexturefilteranisotropic::EXTTextureFilterAnisotropic>();
421        self.register::<ext::oeselementindexuint::OESElementIndexUint>();
422        self.register::<ext::oesstandardderivatives::OESStandardDerivatives>();
423        self.register::<ext::oestexturefloat::OESTextureFloat>();
424        self.register::<ext::oestexturefloatlinear::OESTextureFloatLinear>();
425        self.register::<ext::oestexturehalffloat::OESTextureHalfFloat>();
426        self.register::<ext::oestexturehalffloatlinear::OESTextureHalfFloatLinear>();
427        self.register::<ext::oesvertexarrayobject::OESVertexArrayObject>();
428        self.register::<ext::webglcolorbufferfloat::WEBGLColorBufferFloat>();
429        self.register::<ext::webglcompressedtextureetc1::WEBGLCompressedTextureETC1>();
430        self.register::<ext::webglcompressedtextures3tc::WEBGLCompressedTextureS3TC>();
431    }
432
433    pub(crate) fn enable_element_index_uint(&self) {
434        self.features.borrow_mut().element_index_uint_enabled = true;
435    }
436
437    pub(crate) fn is_element_index_uint_enabled(&self) -> bool {
438        self.features.borrow().element_index_uint_enabled
439    }
440
441    pub(crate) fn enable_blend_minmax(&self) {
442        self.features.borrow_mut().blend_minmax_enabled = true;
443    }
444
445    pub(crate) fn is_blend_minmax_enabled(&self) -> bool {
446        self.features.borrow().blend_minmax_enabled
447    }
448
449    pub(crate) fn is_float_buffer_renderable(&self) -> bool {
450        self.is_enabled::<WEBGLColorBufferFloat>() || self.is_enabled::<OESTextureFloat>()
451    }
452
453    pub(crate) fn is_min_glsl_version_satisfied(&self, min_glsl_version: WebGLSLVersion) -> bool {
454        self.glsl_version >= min_glsl_version
455    }
456
457    pub(crate) fn is_half_float_buffer_renderable(&self) -> bool {
458        self.is_enabled::<EXTColorBufferHalfFloat>() || self.is_enabled::<OESTextureHalfFloat>()
459    }
460
461    pub(crate) fn effective_type(&self, type_: u32) -> u32 {
462        if type_ == OESTextureHalfFloatConstants::HALF_FLOAT_OES &&
463            !self.supports_gl_extension("GL_OES_texture_half_float")
464        {
465            return glow::HALF_FLOAT;
466        }
467        type_
468    }
469
470    pub(crate) fn is_gles(&self) -> bool {
471        self.api_type == GlType::Gles
472    }
473}
474
475// Helper structs
476#[derive(Eq, Hash, MallocSizeOf, PartialEq)]
477struct TexFormatType(TexFormat, u32);