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