Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "WebGLContext.h"
7 : #include "WebGLContextUtils.h"
8 : #include "WebGLExtensions.h"
9 : #include "gfxPrefs.h"
10 : #include "GLContext.h"
11 :
12 : #include "nsString.h"
13 : #include "mozilla/Preferences.h"
14 : #include "mozilla/dom/BindingDeclarations.h"
15 : #include "AccessCheck.h"
16 :
17 : namespace mozilla {
18 :
19 : /*static*/ const char*
20 0 : WebGLContext::GetExtensionString(WebGLExtensionID ext)
21 : {
22 : typedef EnumeratedArray<WebGLExtensionID, WebGLExtensionID::Max,
23 : const char*> names_array_t;
24 :
25 0 : static names_array_t sExtensionNamesEnumeratedArray;
26 : static bool initialized = false;
27 :
28 0 : if (!initialized) {
29 0 : initialized = true;
30 :
31 : #define WEBGL_EXTENSION_IDENTIFIER(x) \
32 : sExtensionNamesEnumeratedArray[WebGLExtensionID::x] = #x;
33 :
34 0 : WEBGL_EXTENSION_IDENTIFIER(ANGLE_instanced_arrays)
35 0 : WEBGL_EXTENSION_IDENTIFIER(EXT_blend_minmax)
36 0 : WEBGL_EXTENSION_IDENTIFIER(EXT_color_buffer_float)
37 0 : WEBGL_EXTENSION_IDENTIFIER(EXT_color_buffer_half_float)
38 0 : WEBGL_EXTENSION_IDENTIFIER(EXT_frag_depth)
39 0 : WEBGL_EXTENSION_IDENTIFIER(EXT_shader_texture_lod)
40 0 : WEBGL_EXTENSION_IDENTIFIER(EXT_sRGB)
41 0 : WEBGL_EXTENSION_IDENTIFIER(EXT_texture_filter_anisotropic)
42 0 : WEBGL_EXTENSION_IDENTIFIER(EXT_disjoint_timer_query)
43 0 : WEBGL_EXTENSION_IDENTIFIER(MOZ_debug)
44 0 : WEBGL_EXTENSION_IDENTIFIER(OES_element_index_uint)
45 0 : WEBGL_EXTENSION_IDENTIFIER(OES_standard_derivatives)
46 0 : WEBGL_EXTENSION_IDENTIFIER(OES_texture_float)
47 0 : WEBGL_EXTENSION_IDENTIFIER(OES_texture_float_linear)
48 0 : WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float)
49 0 : WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float_linear)
50 0 : WEBGL_EXTENSION_IDENTIFIER(OES_vertex_array_object)
51 0 : WEBGL_EXTENSION_IDENTIFIER(WEBGL_color_buffer_float)
52 0 : WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_astc)
53 0 : WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_atc)
54 0 : WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_etc)
55 0 : WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_etc1)
56 0 : WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_pvrtc)
57 0 : WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_s3tc)
58 0 : WEBGL_EXTENSION_IDENTIFIER(WEBGL_compressed_texture_s3tc_srgb)
59 0 : WEBGL_EXTENSION_IDENTIFIER(WEBGL_debug_renderer_info)
60 0 : WEBGL_EXTENSION_IDENTIFIER(WEBGL_debug_shaders)
61 0 : WEBGL_EXTENSION_IDENTIFIER(WEBGL_depth_texture)
62 0 : WEBGL_EXTENSION_IDENTIFIER(WEBGL_draw_buffers)
63 0 : WEBGL_EXTENSION_IDENTIFIER(WEBGL_lose_context)
64 :
65 : #undef WEBGL_EXTENSION_IDENTIFIER
66 : }
67 :
68 0 : return sExtensionNamesEnumeratedArray[ext];
69 : }
70 :
71 : bool
72 0 : WebGLContext::IsExtensionEnabled(WebGLExtensionID ext) const
73 : {
74 0 : return mExtensions[ext];
75 : }
76 :
77 0 : bool WebGLContext::IsExtensionSupported(dom::CallerType callerType,
78 : WebGLExtensionID ext) const
79 : {
80 0 : bool allowPrivilegedExts = false;
81 :
82 : // Chrome contexts need access to debug information even when
83 : // webgl.disable-extensions is set. This is used in the graphics
84 : // section of about:support
85 0 : if (callerType == dom::CallerType::System) {
86 0 : allowPrivilegedExts = true;
87 : }
88 :
89 0 : if (gfxPrefs::WebGLPrivilegedExtensionsEnabled()) {
90 0 : allowPrivilegedExts = true;
91 : }
92 :
93 0 : if (allowPrivilegedExts) {
94 0 : switch (ext) {
95 : case WebGLExtensionID::MOZ_debug:
96 0 : return true;
97 : case WebGLExtensionID::WEBGL_debug_renderer_info:
98 0 : return true;
99 : case WebGLExtensionID::WEBGL_debug_shaders:
100 0 : return true;
101 : default:
102 : // For warnings-as-errors.
103 0 : break;
104 : }
105 : }
106 :
107 0 : return IsExtensionSupported(ext);
108 : }
109 :
110 : bool
111 0 : WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const
112 : {
113 0 : if (mDisableExtensions)
114 0 : return false;
115 :
116 : // Extensions for both WebGL 1 and 2.
117 0 : switch (ext) {
118 : // In alphabetical order
119 : // EXT_
120 : case WebGLExtensionID::EXT_disjoint_timer_query:
121 0 : return WebGLExtensionDisjointTimerQuery::IsSupported(this);
122 : case WebGLExtensionID::EXT_texture_filter_anisotropic:
123 0 : return gl->IsExtensionSupported(gl::GLContext::EXT_texture_filter_anisotropic);
124 :
125 : // OES_
126 : case WebGLExtensionID::OES_texture_float_linear:
127 0 : return gl->IsSupported(gl::GLFeature::texture_float_linear);
128 :
129 : // WEBGL_
130 : case WebGLExtensionID::WEBGL_compressed_texture_astc:
131 0 : return WebGLExtensionCompressedTextureASTC::IsSupported(this);
132 : case WebGLExtensionID::WEBGL_compressed_texture_atc:
133 0 : return gl->IsExtensionSupported(gl::GLContext::AMD_compressed_ATC_texture);
134 : case WebGLExtensionID::WEBGL_compressed_texture_etc:
135 0 : return gl->IsSupported(gl::GLFeature::ES3_compatibility) &&
136 0 : !gl->IsANGLE();
137 : case WebGLExtensionID::WEBGL_compressed_texture_etc1:
138 0 : return gl->IsExtensionSupported(gl::GLContext::OES_compressed_ETC1_RGB8_texture) &&
139 0 : !gl->IsANGLE();
140 : case WebGLExtensionID::WEBGL_compressed_texture_pvrtc:
141 0 : return gl->IsExtensionSupported(gl::GLContext::IMG_texture_compression_pvrtc);
142 : case WebGLExtensionID::WEBGL_compressed_texture_s3tc:
143 0 : return WebGLExtensionCompressedTextureS3TC::IsSupported(this);
144 : case WebGLExtensionID::WEBGL_compressed_texture_s3tc_srgb:
145 0 : return WebGLExtensionCompressedTextureS3TC::IsSupported(this) &&
146 0 : gl->IsExtensionSupported(gl::GLContext::EXT_texture_sRGB);
147 : case WebGLExtensionID::WEBGL_debug_renderer_info:
148 0 : return Preferences::GetBool("webgl.enable-debug-renderer-info", false);
149 :
150 : case WebGLExtensionID::WEBGL_lose_context:
151 : // We always support this extension.
152 0 : return true;
153 :
154 : default:
155 : // For warnings-as-errors.
156 0 : break;
157 : }
158 :
159 0 : if (IsWebGL2()) {
160 : // WebGL2-only extensions
161 0 : switch (ext) {
162 : // EXT_
163 : case WebGLExtensionID::EXT_color_buffer_float:
164 0 : return WebGLExtensionEXTColorBufferFloat::IsSupported(this);
165 :
166 : default:
167 : // For warnings-as-errors.
168 0 : break;
169 : }
170 : } else {
171 : // WebGL1-only extensions
172 0 : switch (ext) {
173 : // ANGLE_
174 : case WebGLExtensionID::ANGLE_instanced_arrays:
175 0 : return WebGLExtensionInstancedArrays::IsSupported(this);
176 :
177 : // EXT_
178 : case WebGLExtensionID::EXT_blend_minmax:
179 0 : return WebGLExtensionBlendMinMax::IsSupported(this);
180 : case WebGLExtensionID::EXT_color_buffer_half_float:
181 0 : return WebGLExtensionColorBufferHalfFloat::IsSupported(this);
182 : case WebGLExtensionID::EXT_frag_depth:
183 0 : return WebGLExtensionFragDepth::IsSupported(this);
184 : case WebGLExtensionID::EXT_shader_texture_lod:
185 0 : return gl->IsSupported(gl::GLFeature::shader_texture_lod);
186 : case WebGLExtensionID::EXT_sRGB:
187 0 : return WebGLExtensionSRGB::IsSupported(this);
188 :
189 : // OES_
190 : case WebGLExtensionID::OES_element_index_uint:
191 0 : return gl->IsSupported(gl::GLFeature::element_index_uint);
192 : case WebGLExtensionID::OES_standard_derivatives:
193 0 : return gl->IsSupported(gl::GLFeature::standard_derivatives);
194 : case WebGLExtensionID::OES_texture_float:
195 0 : return WebGLExtensionTextureFloat::IsSupported(this);
196 : case WebGLExtensionID::OES_texture_half_float:
197 0 : return WebGLExtensionTextureHalfFloat::IsSupported(this);
198 : case WebGLExtensionID::OES_texture_half_float_linear:
199 0 : return gl->IsSupported(gl::GLFeature::texture_half_float_linear);
200 :
201 : case WebGLExtensionID::OES_vertex_array_object:
202 0 : return true;
203 :
204 : // WEBGL_
205 : case WebGLExtensionID::WEBGL_color_buffer_float:
206 0 : return WebGLExtensionColorBufferFloat::IsSupported(this);
207 : case WebGLExtensionID::WEBGL_depth_texture:
208 : // WEBGL_depth_texture supports DEPTH_STENCIL textures
209 0 : if (!gl->IsSupported(gl::GLFeature::packed_depth_stencil))
210 0 : return false;
211 :
212 0 : return gl->IsSupported(gl::GLFeature::depth_texture) ||
213 0 : gl->IsExtensionSupported(gl::GLContext::ANGLE_depth_texture);
214 : case WebGLExtensionID::WEBGL_draw_buffers:
215 0 : return WebGLExtensionDrawBuffers::IsSupported(this);
216 : default:
217 : // For warnings-as-errors.
218 0 : break;
219 : }
220 :
221 0 : if (gfxPrefs::WebGLDraftExtensionsEnabled()) {
222 : /*
223 : switch (ext) {
224 : default:
225 : // For warnings-as-errors.
226 : break;
227 : }
228 : */
229 : }
230 : }
231 :
232 0 : return false;
233 : }
234 :
235 : static bool
236 0 : CompareWebGLExtensionName(const nsACString& name, const char* other)
237 : {
238 0 : return name.Equals(other, nsCaseInsensitiveCStringComparator());
239 : }
240 :
241 : WebGLExtensionBase*
242 0 : WebGLContext::EnableSupportedExtension(dom::CallerType callerType,
243 : WebGLExtensionID ext)
244 : {
245 0 : if (!IsExtensionEnabled(ext)) {
246 0 : if (!IsExtensionSupported(callerType, ext))
247 0 : return nullptr;
248 :
249 0 : EnableExtension(ext);
250 : }
251 :
252 0 : return mExtensions[ext];
253 : }
254 :
255 : void
256 0 : WebGLContext::GetExtension(JSContext* cx,
257 : const nsAString& wideName,
258 : JS::MutableHandle<JSObject*> retval,
259 : dom::CallerType callerType,
260 : ErrorResult& rv)
261 : {
262 0 : retval.set(nullptr);
263 :
264 0 : if (IsContextLost())
265 0 : return;
266 :
267 0 : NS_LossyConvertUTF16toASCII name(wideName);
268 :
269 0 : WebGLExtensionID ext = WebGLExtensionID::Unknown;
270 :
271 : // step 1: figure what extension is wanted
272 0 : for (size_t i = 0; i < size_t(WebGLExtensionID::Max); i++) {
273 0 : WebGLExtensionID extension = WebGLExtensionID(i);
274 :
275 0 : if (CompareWebGLExtensionName(name, GetExtensionString(extension))) {
276 0 : ext = extension;
277 0 : break;
278 : }
279 : }
280 :
281 0 : if (ext == WebGLExtensionID::Unknown) {
282 : // We keep backward compatibility for these deprecated vendor-prefixed
283 : // alias. Do not add new ones anymore. Hide it behind the
284 : // webgl.enable-draft-extensions flag instead.
285 :
286 0 : if (CompareWebGLExtensionName(name, "MOZ_WEBGL_lose_context")) {
287 0 : ext = WebGLExtensionID::WEBGL_lose_context;
288 :
289 0 : } else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_s3tc")) {
290 0 : ext = WebGLExtensionID::WEBGL_compressed_texture_s3tc;
291 :
292 0 : } else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_atc")) {
293 0 : ext = WebGLExtensionID::WEBGL_compressed_texture_atc;
294 :
295 0 : } else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_compressed_texture_pvrtc")) {
296 0 : ext = WebGLExtensionID::WEBGL_compressed_texture_pvrtc;
297 :
298 0 : } else if (CompareWebGLExtensionName(name, "MOZ_WEBGL_depth_texture")) {
299 0 : ext = WebGLExtensionID::WEBGL_depth_texture;
300 : }
301 :
302 0 : if (ext != WebGLExtensionID::Unknown) {
303 0 : GenerateWarning("getExtension('%s'): MOZ_ prefixed WebGL extension"
304 : " strings are deprecated. Support for them will be"
305 : " removed in the future. Use unprefixed extension"
306 : " strings. To get draft extensions, set the"
307 : " webgl.enable-draft-extensions preference.",
308 0 : name.get());
309 : }
310 : }
311 :
312 0 : if (ext == WebGLExtensionID::Unknown)
313 0 : return;
314 :
315 : // step 2: check if the extension is supported
316 0 : if (!IsExtensionSupported(callerType, ext))
317 0 : return;
318 :
319 : // step 3: if the extension hadn't been previously been created, create it now, thus enabling it
320 0 : WebGLExtensionBase* extObj = EnableSupportedExtension(callerType, ext);
321 0 : if (!extObj)
322 0 : return;
323 :
324 : // Step 4: Enable any implied extensions.
325 0 : switch (ext) {
326 : case WebGLExtensionID::OES_texture_float:
327 : EnableSupportedExtension(callerType,
328 0 : WebGLExtensionID::WEBGL_color_buffer_float);
329 0 : break;
330 :
331 : case WebGLExtensionID::OES_texture_half_float:
332 : EnableSupportedExtension(callerType,
333 0 : WebGLExtensionID::EXT_color_buffer_half_float);
334 0 : break;
335 :
336 : default:
337 0 : break;
338 : }
339 :
340 0 : retval.set(WebGLObjectAsJSObject(cx, extObj, rv));
341 : }
342 :
343 : void
344 0 : WebGLContext::EnableExtension(WebGLExtensionID ext)
345 : {
346 0 : MOZ_ASSERT(IsExtensionEnabled(ext) == false);
347 :
348 0 : WebGLExtensionBase* obj = nullptr;
349 0 : switch (ext) {
350 : // ANGLE_
351 : case WebGLExtensionID::ANGLE_instanced_arrays:
352 0 : obj = new WebGLExtensionInstancedArrays(this);
353 0 : break;
354 :
355 : // EXT_
356 : case WebGLExtensionID::EXT_blend_minmax:
357 0 : obj = new WebGLExtensionBlendMinMax(this);
358 0 : break;
359 : case WebGLExtensionID::EXT_color_buffer_float:
360 0 : obj = new WebGLExtensionEXTColorBufferFloat(this);
361 0 : break;
362 : case WebGLExtensionID::EXT_color_buffer_half_float:
363 0 : obj = new WebGLExtensionColorBufferHalfFloat(this);
364 0 : break;
365 : case WebGLExtensionID::EXT_disjoint_timer_query:
366 0 : obj = new WebGLExtensionDisjointTimerQuery(this);
367 0 : break;
368 : case WebGLExtensionID::EXT_frag_depth:
369 0 : obj = new WebGLExtensionFragDepth(this);
370 0 : break;
371 : case WebGLExtensionID::EXT_shader_texture_lod:
372 0 : obj = new WebGLExtensionShaderTextureLod(this);
373 0 : break;
374 : case WebGLExtensionID::EXT_sRGB:
375 0 : obj = new WebGLExtensionSRGB(this);
376 0 : break;
377 : case WebGLExtensionID::EXT_texture_filter_anisotropic:
378 0 : obj = new WebGLExtensionTextureFilterAnisotropic(this);
379 0 : break;
380 :
381 : // MOZ_
382 : case WebGLExtensionID::MOZ_debug:
383 0 : obj = new WebGLExtensionMOZDebug(this);
384 0 : break;
385 :
386 : // OES_
387 : case WebGLExtensionID::OES_element_index_uint:
388 0 : obj = new WebGLExtensionElementIndexUint(this);
389 0 : break;
390 : case WebGLExtensionID::OES_standard_derivatives:
391 0 : obj = new WebGLExtensionStandardDerivatives(this);
392 0 : break;
393 : case WebGLExtensionID::OES_texture_float:
394 0 : obj = new WebGLExtensionTextureFloat(this);
395 0 : break;
396 : case WebGLExtensionID::OES_texture_float_linear:
397 0 : obj = new WebGLExtensionTextureFloatLinear(this);
398 0 : break;
399 : case WebGLExtensionID::OES_texture_half_float:
400 0 : obj = new WebGLExtensionTextureHalfFloat(this);
401 0 : break;
402 : case WebGLExtensionID::OES_texture_half_float_linear:
403 0 : obj = new WebGLExtensionTextureHalfFloatLinear(this);
404 0 : break;
405 : case WebGLExtensionID::OES_vertex_array_object:
406 0 : obj = new WebGLExtensionVertexArray(this);
407 0 : break;
408 :
409 : // WEBGL_
410 : case WebGLExtensionID::WEBGL_color_buffer_float:
411 0 : obj = new WebGLExtensionColorBufferFloat(this);
412 0 : break;
413 : case WebGLExtensionID::WEBGL_compressed_texture_astc:
414 0 : obj = new WebGLExtensionCompressedTextureASTC(this);
415 0 : break;
416 : case WebGLExtensionID::WEBGL_compressed_texture_atc:
417 0 : obj = new WebGLExtensionCompressedTextureATC(this);
418 0 : break;
419 : case WebGLExtensionID::WEBGL_compressed_texture_etc:
420 0 : obj = new WebGLExtensionCompressedTextureES3(this);
421 0 : break;
422 : case WebGLExtensionID::WEBGL_compressed_texture_etc1:
423 0 : obj = new WebGLExtensionCompressedTextureETC1(this);
424 0 : break;
425 : case WebGLExtensionID::WEBGL_compressed_texture_pvrtc:
426 0 : obj = new WebGLExtensionCompressedTexturePVRTC(this);
427 0 : break;
428 : case WebGLExtensionID::WEBGL_compressed_texture_s3tc:
429 0 : obj = new WebGLExtensionCompressedTextureS3TC(this);
430 0 : break;
431 : case WebGLExtensionID::WEBGL_compressed_texture_s3tc_srgb:
432 0 : obj = new WebGLExtensionCompressedTextureS3TC_SRGB(this);
433 0 : break;
434 : case WebGLExtensionID::WEBGL_debug_renderer_info:
435 0 : obj = new WebGLExtensionDebugRendererInfo(this);
436 0 : break;
437 : case WebGLExtensionID::WEBGL_debug_shaders:
438 0 : obj = new WebGLExtensionDebugShaders(this);
439 0 : break;
440 : case WebGLExtensionID::WEBGL_depth_texture:
441 0 : obj = new WebGLExtensionDepthTexture(this);
442 0 : break;
443 : case WebGLExtensionID::WEBGL_draw_buffers:
444 0 : obj = new WebGLExtensionDrawBuffers(this);
445 0 : break;
446 : case WebGLExtensionID::WEBGL_lose_context:
447 0 : obj = new WebGLExtensionLoseContext(this);
448 0 : break;
449 :
450 : default:
451 0 : MOZ_ASSERT(false, "should not get there.");
452 : }
453 :
454 0 : mExtensions[ext] = obj;
455 0 : }
456 :
457 : void
458 0 : WebGLContext::GetSupportedExtensions(dom::Nullable< nsTArray<nsString> >& retval,
459 : dom::CallerType callerType)
460 : {
461 0 : retval.SetNull();
462 0 : if (IsContextLost())
463 0 : return;
464 :
465 0 : nsTArray<nsString>& arr = retval.SetValue();
466 :
467 0 : for (size_t i = 0; i < size_t(WebGLExtensionID::Max); i++) {
468 0 : WebGLExtensionID extension = WebGLExtensionID(i);
469 :
470 0 : if (IsExtensionSupported(callerType, extension)) {
471 0 : const char* extStr = GetExtensionString(extension);
472 0 : arr.AppendElement(NS_ConvertUTF8toUTF16(extStr));
473 : }
474 : }
475 :
476 : /**
477 : * We keep backward compatibility for these deprecated vendor-prefixed
478 : * alias. Do not add new ones anymore. Hide it behind the
479 : * webgl.enable-draft-extensions flag instead.
480 : */
481 0 : if (IsExtensionSupported(callerType, WebGLExtensionID::WEBGL_lose_context))
482 0 : arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_lose_context"));
483 0 : if (IsExtensionSupported(callerType,
484 : WebGLExtensionID::WEBGL_compressed_texture_s3tc))
485 0 : arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_s3tc"));
486 0 : if (IsExtensionSupported(callerType,
487 : WebGLExtensionID::WEBGL_compressed_texture_atc))
488 0 : arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_atc"));
489 0 : if (IsExtensionSupported(callerType,
490 : WebGLExtensionID::WEBGL_compressed_texture_pvrtc))
491 0 : arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_compressed_texture_pvrtc"));
492 0 : if (IsExtensionSupported(callerType,
493 : WebGLExtensionID::WEBGL_depth_texture))
494 0 : arr.AppendElement(NS_LITERAL_STRING("MOZ_WEBGL_depth_texture"));
495 : }
496 :
497 : } // namespace mozilla
|