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 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
33const DEFAULT_DISABLED_TEX_TYPES_WEBGL1: [GLenum; 2] = [
37 constants::FLOAT,
38 OESTextureHalfFloatConstants::HALF_FLOAT_OES,
39];
40
41const DEFAULT_NOT_FILTERABLE_TEX_TYPES: [GLenum; 2] = [
45 constants::FLOAT,
46 OESTextureHalfFloatConstants::HALF_FLOAT_OES,
47];
48
49const 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
58const DEFAULT_DISABLED_GET_PARAMETER_NAMES_WEBGL2: [GLenum; 1] =
62 [EXTTextureFilterAnisotropicConstants::MAX_TEXTURE_MAX_ANISOTROPY_EXT];
63
64const DEFAULT_DISABLED_GET_TEX_PARAMETER_NAMES_WEBGL1: [GLenum; 1] =
68 [EXTTextureFilterAnisotropicConstants::TEXTURE_MAX_ANISOTROPY_EXT];
69
70const DEFAULT_DISABLED_GET_TEX_PARAMETER_NAMES_WEBGL2: [GLenum; 1] =
74 [EXTTextureFilterAnisotropicConstants::TEXTURE_MAX_ANISOTROPY_EXT];
75
76const DEFAULT_DISABLED_GET_VERTEX_ATTRIB_NAMES_WEBGL1: [GLenum; 1] =
80 [ANGLEInstancedArraysConstants::VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE];
81
82#[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 hint_targets: FxHashSet<GLenum>,
92 disabled_get_parameter_names: FxHashSet<GLenum>,
94 disabled_get_tex_parameter_names: FxHashSet<GLenum>,
96 disabled_get_vertex_attrib_names: FxHashSet<GLenum>,
98 element_index_uint_enabled: bool,
100 blend_minmax_enabled: bool,
102 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#[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#[derive(Eq, Hash, MallocSizeOf, PartialEq)]
477struct TexFormatType(TexFormat, u32);