1 /* -*- Mode: C++; tab-width: 20; 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
8 #include <algorithm>
9 #include <queue>
10
11 #include "AccessCheck.h"
12 #include "gfxContext.h"
13 #include "gfxCrashReporterUtils.h"
14 #include "gfxPattern.h"
15 #include "gfxPrefs.h"
16 #include "gfxUtils.h"
17 #include "MozFramebuffer.h"
18 #include "GLBlitHelper.h"
19 #include "GLContext.h"
20 #include "GLContextProvider.h"
21 #include "GLReadTexImageHelper.h"
22 #include "GLScreenBuffer.h"
23 #include "ImageContainer.h"
24 #include "ImageEncoder.h"
25 #include "Layers.h"
26 #include "LayerUserData.h"
27 #include "mozilla/dom/BindingUtils.h"
28 #include "mozilla/dom/Event.h"
29 #include "mozilla/dom/HTMLVideoElement.h"
30 #include "mozilla/dom/ImageData.h"
31 #include "mozilla/dom/WebGLContextEvent.h"
32 #include "mozilla/EnumeratedArrayCycleCollection.h"
33 #include "mozilla/Preferences.h"
34 #include "mozilla/ProcessPriorityManager.h"
35 #include "mozilla/ScopeExit.h"
36 #include "mozilla/Services.h"
37 #include "mozilla/Telemetry.h"
38 #include "nsContentUtils.h"
39 #include "nsDisplayList.h"
40 #include "nsError.h"
41 #include "nsIClassInfoImpl.h"
42 #include "nsIConsoleService.h"
43 #include "nsIDOMEvent.h"
44 #include "nsIGfxInfo.h"
45 #include "nsIObserverService.h"
46 #include "nsIVariant.h"
47 #include "nsIWidget.h"
48 #include "nsIXPConnect.h"
49 #include "nsServiceManagerUtils.h"
50 #include "SVGObserverUtils.h"
51 #include "prenv.h"
52 #include "ScopedGLHelpers.h"
53 #include "VRManagerChild.h"
54 #include "mozilla/layers/TextureClientSharedSurface.h"
55 #include "mozilla/layers/WebRenderUserData.h"
56 #include "mozilla/layers/WebRenderCanvasRenderer.h"
57
58 // Local
59 #include "CanvasUtils.h"
60 #include "WebGL1Context.h"
61 #include "WebGLActiveInfo.h"
62 #include "WebGLBuffer.h"
63 #include "WebGLContextLossHandler.h"
64 #include "WebGLContextUtils.h"
65 #include "WebGLExtensions.h"
66 #include "WebGLFramebuffer.h"
67 #include "WebGLMemoryTracker.h"
68 #include "WebGLObjectModel.h"
69 #include "WebGLProgram.h"
70 #include "WebGLQuery.h"
71 #include "WebGLSampler.h"
72 #include "WebGLShader.h"
73 #include "WebGLSync.h"
74 #include "WebGLTransformFeedback.h"
75 #include "WebGLVertexArray.h"
76 #include "WebGLVertexAttribData.h"
77
78 #ifdef MOZ_WIDGET_COCOA
79 #include "nsCocoaFeatures.h"
80 #endif
81
82 #ifdef XP_WIN
83 #include "WGLLibrary.h"
84 #endif
85
86 // Generated
87 #include "mozilla/dom/WebGLRenderingContextBinding.h"
88
89 namespace mozilla {
90
91 using namespace mozilla::dom;
92 using namespace mozilla::gfx;
93 using namespace mozilla::gl;
94 using namespace mozilla::layers;
95
WebGLContextOptions()96 WebGLContextOptions::WebGLContextOptions()
97 : alpha(true),
98 depth(true),
99 stencil(false),
100 premultipliedAlpha(true),
101 antialias(true),
102 preserveDrawingBuffer(false),
103 failIfMajorPerformanceCaveat(false) {
104 // Set default alpha state based on preference.
105 if (gfxPrefs::WebGLDefaultNoAlpha()) alpha = false;
106 }
107
WebGLContext()108 WebGLContext::WebGLContext()
109 : WebGLContextUnchecked(nullptr),
110 mMaxPerfWarnings(gfxPrefs::WebGLMaxPerfWarnings()),
111 mNumPerfWarnings(0),
112 mMaxAcceptableFBStatusInvals(
113 gfxPrefs::WebGLMaxAcceptableFBStatusInvals()),
114 mDataAllocGLCallCount(0),
115 mBypassShaderValidation(false),
116 mEmptyTFO(0),
117 mContextLossHandler(this),
118 mNeedsFakeNoAlpha(false),
119 mNeedsFakeNoDepth(false),
120 mNeedsFakeNoStencil(false),
121 mAllowFBInvalidation(gfxPrefs::WebGLFBInvalidation()),
122 mMsaaSamples(gfxPrefs::WebGLMsaaSamples()) {
123 mGeneration = 0;
124 mInvalidated = false;
125 mCapturedFrameInvalidated = false;
126 mShouldPresent = true;
127 mResetLayer = true;
128 mOptionsFrozen = false;
129 mDisableExtensions = false;
130 mIsMesa = false;
131 mEmitContextLostErrorOnce = false;
132 mWebGLError = 0;
133 mUnderlyingGLError = 0;
134
135 mContextLostErrorSet = false;
136
137 mViewportX = 0;
138 mViewportY = 0;
139 mViewportWidth = 0;
140 mViewportHeight = 0;
141
142 mDitherEnabled = 1;
143 mRasterizerDiscardEnabled = 0; // OpenGL ES 3.0 spec p244
144 mScissorTestEnabled = 0;
145 mStencilTestEnabled = 0;
146
147 if (NS_IsMainThread()) {
148 // XXX mtseng: bug 709490, not thread safe
149 WebGLMemoryTracker::AddWebGLContext(this);
150 }
151
152 mAllowContextRestore = true;
153 mLastLossWasSimulated = false;
154 mContextStatus = ContextNotLost;
155 mLoseContextOnMemoryPressure = false;
156 mCanLoseContextInForeground = true;
157 mRestoreWhenVisible = false;
158
159 mAlreadyGeneratedWarnings = 0;
160 mAlreadyWarnedAboutFakeVertexAttrib0 = false;
161 mAlreadyWarnedAboutViewportLargerThanDest = false;
162
163 mMaxWarnings = gfxPrefs::WebGLMaxWarningsPerContext();
164 if (mMaxWarnings < -1) {
165 GenerateWarning(
166 "webgl.max-warnings-per-context size is too large (seems like a "
167 "negative value wrapped)");
168 mMaxWarnings = 0;
169 }
170
171 mLastUseIndex = 0;
172
173 mDisableFragHighP = false;
174
175 mDrawCallsSinceLastFlush = 0;
176 }
177
~WebGLContext()178 WebGLContext::~WebGLContext() {
179 RemovePostRefreshObserver();
180
181 DestroyResourcesAndContext();
182 if (NS_IsMainThread()) {
183 // XXX mtseng: bug 709490, not thread safe
184 WebGLMemoryTracker::RemoveWebGLContext(this);
185 }
186 }
187
188 template <typename T>
ClearLinkedList(LinkedList<T> & list)189 void ClearLinkedList(LinkedList<T>& list) {
190 while (!list.isEmpty()) {
191 list.getLast()->DeleteOnce();
192 }
193 }
194
DestroyResourcesAndContext()195 void WebGLContext::DestroyResourcesAndContext() {
196 if (!gl) return;
197
198 mDefaultFB = nullptr;
199 mResolvedDefaultFB = nullptr;
200
201 mBound2DTextures.Clear();
202 mBoundCubeMapTextures.Clear();
203 mBound3DTextures.Clear();
204 mBound2DArrayTextures.Clear();
205 mBoundSamplers.Clear();
206 mBoundArrayBuffer = nullptr;
207 mBoundCopyReadBuffer = nullptr;
208 mBoundCopyWriteBuffer = nullptr;
209 mBoundPixelPackBuffer = nullptr;
210 mBoundPixelUnpackBuffer = nullptr;
211 mBoundUniformBuffer = nullptr;
212 mCurrentProgram = nullptr;
213 mActiveProgramLinkInfo = nullptr;
214 mBoundDrawFramebuffer = nullptr;
215 mBoundReadFramebuffer = nullptr;
216 mBoundRenderbuffer = nullptr;
217 mBoundVertexArray = nullptr;
218 mDefaultVertexArray = nullptr;
219 mBoundTransformFeedback = nullptr;
220 mDefaultTransformFeedback = nullptr;
221
222 mQuerySlot_SamplesPassed = nullptr;
223 mQuerySlot_TFPrimsWritten = nullptr;
224 mQuerySlot_TimeElapsed = nullptr;
225
226 mIndexedUniformBufferBindings.clear();
227
228 if (mAvailabilityRunnable) {
229 mAvailabilityRunnable->Run();
230 }
231
232 //////
233
234 ClearLinkedList(mBuffers);
235 ClearLinkedList(mFramebuffers);
236 ClearLinkedList(mPrograms);
237 ClearLinkedList(mQueries);
238 ClearLinkedList(mRenderbuffers);
239 ClearLinkedList(mSamplers);
240 ClearLinkedList(mShaders);
241 ClearLinkedList(mSyncs);
242 ClearLinkedList(mTextures);
243 ClearLinkedList(mTransformFeedbacks);
244 ClearLinkedList(mVertexArrays);
245
246 //////
247
248 if (mEmptyTFO) {
249 gl->fDeleteTransformFeedbacks(1, &mEmptyTFO);
250 mEmptyTFO = 0;
251 }
252
253 //////
254
255 mFakeBlack_2D_0000 = nullptr;
256 mFakeBlack_2D_0001 = nullptr;
257 mFakeBlack_CubeMap_0000 = nullptr;
258 mFakeBlack_CubeMap_0001 = nullptr;
259 mFakeBlack_3D_0000 = nullptr;
260 mFakeBlack_3D_0001 = nullptr;
261 mFakeBlack_2D_Array_0000 = nullptr;
262 mFakeBlack_2D_Array_0001 = nullptr;
263
264 if (mFakeVertexAttrib0BufferObject) {
265 gl->fDeleteBuffers(1, &mFakeVertexAttrib0BufferObject);
266 mFakeVertexAttrib0BufferObject = 0;
267 }
268
269 // disable all extensions except "WEBGL_lose_context". see bug #927969
270 // spec: http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
271 for (size_t i = 0; i < size_t(WebGLExtensionID::Max); ++i) {
272 WebGLExtensionID extension = WebGLExtensionID(i);
273
274 if (!IsExtensionEnabled(extension) ||
275 (extension == WebGLExtensionID::WEBGL_lose_context))
276 continue;
277
278 mExtensions[extension]->MarkLost();
279 mExtensions[extension] = nullptr;
280 }
281
282 // We just got rid of everything, so the context had better
283 // have been going away.
284 if (GLContext::ShouldSpew()) {
285 printf_stderr("--- WebGL context destroyed: %p\n", gl.get());
286 }
287
288 MOZ_ASSERT(gl);
289 gl->MarkDestroyed();
290 mGL_OnlyClearInDestroyResourcesAndContext = nullptr;
291 MOZ_ASSERT(!gl);
292 }
293
Invalidate()294 void WebGLContext::Invalidate() {
295 if (!mCanvasElement) return;
296
297 mCapturedFrameInvalidated = true;
298
299 if (mInvalidated) return;
300
301 SVGObserverUtils::InvalidateDirectRenderingObservers(mCanvasElement);
302
303 mInvalidated = true;
304 mCanvasElement->InvalidateCanvasContent(nullptr);
305 }
306
OnVisibilityChange()307 void WebGLContext::OnVisibilityChange() {
308 if (!IsContextLost()) {
309 return;
310 }
311
312 if (!mRestoreWhenVisible || mLastLossWasSimulated) {
313 return;
314 }
315
316 ForceRestoreContext();
317 }
318
OnMemoryPressure()319 void WebGLContext::OnMemoryPressure() {
320 bool shouldLoseContext = mLoseContextOnMemoryPressure;
321
322 if (!mCanLoseContextInForeground &&
323 ProcessPriorityManager::CurrentProcessIsForeground()) {
324 shouldLoseContext = false;
325 }
326
327 if (shouldLoseContext) ForceLoseContext();
328 }
329
330 //
331 // nsICanvasRenderingContextInternal
332 //
333
334 NS_IMETHODIMP
SetContextOptions(JSContext * cx,JS::Handle<JS::Value> options,ErrorResult & aRvForDictionaryInit)335 WebGLContext::SetContextOptions(JSContext* cx, JS::Handle<JS::Value> options,
336 ErrorResult& aRvForDictionaryInit) {
337 if (options.isNullOrUndefined() && mOptionsFrozen) return NS_OK;
338
339 WebGLContextAttributes attributes;
340 if (!attributes.Init(cx, options)) {
341 aRvForDictionaryInit.Throw(NS_ERROR_UNEXPECTED);
342 return NS_ERROR_UNEXPECTED;
343 }
344
345 WebGLContextOptions newOpts;
346
347 newOpts.stencil = attributes.mStencil;
348 newOpts.depth = attributes.mDepth;
349 newOpts.premultipliedAlpha = attributes.mPremultipliedAlpha;
350 newOpts.antialias = attributes.mAntialias;
351 newOpts.preserveDrawingBuffer = attributes.mPreserveDrawingBuffer;
352 newOpts.failIfMajorPerformanceCaveat =
353 attributes.mFailIfMajorPerformanceCaveat;
354
355 if (attributes.mAlpha.WasPassed()) newOpts.alpha = attributes.mAlpha.Value();
356
357 // Don't do antialiasing if we've disabled MSAA.
358 if (!gfxPrefs::MSAALevel()) newOpts.antialias = false;
359
360 #if 0
361 GenerateWarning("aaHint: %d stencil: %d depth: %d alpha: %d premult: %d preserve: %d\n",
362 newOpts.antialias ? 1 : 0,
363 newOpts.stencil ? 1 : 0,
364 newOpts.depth ? 1 : 0,
365 newOpts.alpha ? 1 : 0,
366 newOpts.premultipliedAlpha ? 1 : 0,
367 newOpts.preserveDrawingBuffer ? 1 : 0);
368 #endif
369
370 if (mOptionsFrozen && newOpts != mOptions) {
371 // Error if the options are already frozen, and the ones that were asked for
372 // aren't the same as what they were originally.
373 return NS_ERROR_FAILURE;
374 }
375
376 mOptions = newOpts;
377 return NS_OK;
378 }
379
380 /* So there are a number of points of failure here. We might fail based
381 * on EGL vs. WGL, or we might fail to alloc a too-large size, or we
382 * might not be able to create a context with a certain combo of context
383 * creation attribs.
384 *
385 * We don't want to test the complete fallback matrix. (for now, at
386 * least) Instead, attempt creation in this order:
387 * 1. By platform API. (e.g. EGL vs. WGL)
388 * 2. By context creation attribs.
389 * 3. By size.
390 *
391 * That is, try to create headless contexts based on the platform API.
392 * Next, create dummy-sized backbuffers for the contexts with the right
393 * caps. Finally, resize the backbuffer to an acceptable size given the
394 * requested size.
395 */
396
IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo> & gfxInfo,int32_t feature,nsCString * const out_blacklistId)397 static bool IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
398 int32_t feature,
399 nsCString* const out_blacklistId) {
400 int32_t status;
401 if (!NS_SUCCEEDED(gfxUtils::ThreadSafeGetFeatureStatus(
402 gfxInfo, feature, *out_blacklistId, &status))) {
403 return false;
404 }
405
406 return status != nsIGfxInfo::FEATURE_STATUS_OK;
407 }
408
HasAcceleratedLayers(const nsCOMPtr<nsIGfxInfo> & gfxInfo)409 static bool HasAcceleratedLayers(const nsCOMPtr<nsIGfxInfo>& gfxInfo) {
410 int32_t status;
411
412 nsCString discardFailureId;
413 gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
414 nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
415 discardFailureId, &status);
416 if (status) return true;
417 gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
418 nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
419 discardFailureId, &status);
420 if (status) return true;
421 gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
422 nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
423 discardFailureId, &status);
424 if (status) return true;
425 gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
426 nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
427 discardFailureId, &status);
428 if (status) return true;
429 gfxUtils::ThreadSafeGetFeatureStatus(
430 gfxInfo, nsIGfxInfo::FEATURE_OPENGL_LAYERS, discardFailureId, &status);
431 if (status) return true;
432
433 return false;
434 }
435
PopulateCapFallbackQueue(const gl::SurfaceCaps & baseCaps,std::queue<gl::SurfaceCaps> * out_fallbackCaps)436 static void PopulateCapFallbackQueue(
437 const gl::SurfaceCaps& baseCaps,
438 std::queue<gl::SurfaceCaps>* out_fallbackCaps) {
439 out_fallbackCaps->push(baseCaps);
440 }
441
BaseCaps(const WebGLContextOptions & options,WebGLContext * webgl)442 static gl::SurfaceCaps BaseCaps(const WebGLContextOptions& options,
443 WebGLContext* webgl) {
444 gl::SurfaceCaps baseCaps;
445
446 baseCaps.color = true;
447 baseCaps.alpha = true;
448 baseCaps.antialias = false;
449 baseCaps.depth = false;
450 baseCaps.stencil = false;
451 baseCaps.premultAlpha = options.premultipliedAlpha;
452 baseCaps.preserve = options.preserveDrawingBuffer;
453
454 if (!baseCaps.alpha) {
455 baseCaps.premultAlpha = true;
456 }
457
458 if (!gfxPrefs::WebGLForceMSAA()) {
459 const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
460
461 nsCString blocklistId;
462 if (IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_MSAA,
463 &blocklistId)) {
464 webgl->GenerateWarning(
465 "Disallowing antialiased backbuffers due"
466 " to blacklisting.");
467 baseCaps.antialias = false;
468 }
469 }
470
471 return baseCaps;
472 }
473
474 ////////////////////////////////////////
475
CreateGLWithEGL(const gl::SurfaceCaps & caps,gl::CreateContextFlags flags,WebGLContext * webgl,std::vector<WebGLContext::FailureReason> * const out_failReasons)476 static already_AddRefed<gl::GLContext> CreateGLWithEGL(
477 const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
478 WebGLContext* webgl,
479 std::vector<WebGLContext::FailureReason>* const out_failReasons) {
480 const gfx::IntSize dummySize(16, 16);
481 nsCString failureId;
482 RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(
483 dummySize, caps, flags, &failureId);
484 if (gl && gl->IsANGLE()) {
485 gl = nullptr;
486 }
487
488 if (!gl) {
489 out_failReasons->push_back(WebGLContext::FailureReason(
490 failureId, "Error during EGL OpenGL init."));
491 return nullptr;
492 }
493
494 return gl.forget();
495 }
496
CreateGLWithANGLE(const gl::SurfaceCaps & caps,gl::CreateContextFlags flags,WebGLContext * webgl,std::vector<WebGLContext::FailureReason> * const out_failReasons)497 static already_AddRefed<GLContext> CreateGLWithANGLE(
498 const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
499 WebGLContext* webgl,
500 std::vector<WebGLContext::FailureReason>* const out_failReasons) {
501 const gfx::IntSize dummySize(16, 16);
502 nsCString failureId;
503 RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(
504 dummySize, caps, flags, &failureId);
505 if (gl && !gl->IsANGLE()) {
506 gl = nullptr;
507 }
508
509 if (!gl) {
510 out_failReasons->push_back(WebGLContext::FailureReason(
511 failureId, "Error during ANGLE OpenGL init."));
512 return nullptr;
513 }
514
515 return gl.forget();
516 }
517
CreateGLWithDefault(const gl::SurfaceCaps & caps,gl::CreateContextFlags flags,WebGLContext * webgl,std::vector<WebGLContext::FailureReason> * const out_failReasons)518 static already_AddRefed<gl::GLContext> CreateGLWithDefault(
519 const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
520 WebGLContext* webgl,
521 std::vector<WebGLContext::FailureReason>* const out_failReasons) {
522 const gfx::IntSize dummySize(16, 16);
523 nsCString failureId;
524 RefPtr<GLContext> gl = gl::GLContextProvider::CreateOffscreen(
525 dummySize, caps, flags, &failureId);
526 if (gl && gl->IsANGLE()) {
527 gl = nullptr;
528 }
529
530 if (!gl) {
531 out_failReasons->push_back(WebGLContext::FailureReason(
532 failureId, "Error during native OpenGL init."));
533 return nullptr;
534 }
535
536 return gl.forget();
537 }
538
539 ////////////////////////////////////////
540
CreateAndInitGLWith(FnCreateGL_T fnCreateGL,const gl::SurfaceCaps & baseCaps,gl::CreateContextFlags flags,std::vector<FailureReason> * const out_failReasons)541 bool WebGLContext::CreateAndInitGLWith(
542 FnCreateGL_T fnCreateGL, const gl::SurfaceCaps& baseCaps,
543 gl::CreateContextFlags flags,
544 std::vector<FailureReason>* const out_failReasons) {
545 std::queue<gl::SurfaceCaps> fallbackCaps;
546 PopulateCapFallbackQueue(baseCaps, &fallbackCaps);
547
548 MOZ_RELEASE_ASSERT(!gl, "GFX: Already have a context.");
549 RefPtr<gl::GLContext> potentialGL;
550 while (!fallbackCaps.empty()) {
551 const gl::SurfaceCaps& caps = fallbackCaps.front();
552 potentialGL = fnCreateGL(caps, flags, this, out_failReasons);
553 if (potentialGL) break;
554
555 fallbackCaps.pop();
556 }
557 if (!potentialGL) {
558 out_failReasons->push_back(FailureReason(
559 "FEATURE_FAILURE_WEBGL_EXHAUSTED_CAPS", "Exhausted GL driver caps."));
560 return false;
561 }
562
563 FailureReason reason;
564
565 mGL_OnlyClearInDestroyResourcesAndContext = potentialGL;
566 MOZ_RELEASE_ASSERT(gl);
567 if (!InitAndValidateGL(&reason)) {
568 DestroyResourcesAndContext();
569 MOZ_RELEASE_ASSERT(!gl);
570
571 // The fail reason here should be specific enough for now.
572 out_failReasons->push_back(reason);
573 return false;
574 }
575
576 return true;
577 }
578
CreateAndInitGL(bool forceEnabled,std::vector<FailureReason> * const out_failReasons)579 bool WebGLContext::CreateAndInitGL(
580 bool forceEnabled, std::vector<FailureReason>* const out_failReasons) {
581 // Can't use WebGL in headless mode.
582 if (gfxPlatform::IsHeadless()) {
583 FailureReason reason;
584 reason.info =
585 "Can't use WebGL in headless mode (https://bugzil.la/1375585).";
586 out_failReasons->push_back(reason);
587 GenerateWarning("%s", reason.info.BeginReading());
588 return false;
589 }
590
591 // WebGL2 is separately blocked:
592 if (IsWebGL2()) {
593 const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
594 const auto feature = nsIGfxInfo::FEATURE_WEBGL2;
595
596 FailureReason reason;
597 if (IsFeatureInBlacklist(gfxInfo, feature, &reason.key)) {
598 reason.info =
599 "Refused to create WebGL2 context because of blacklist"
600 " entry: ";
601 reason.info.Append(reason.key);
602 out_failReasons->push_back(reason);
603 GenerateWarning("%s", reason.info.BeginReading());
604 return false;
605 }
606 }
607
608 const gl::SurfaceCaps baseCaps = BaseCaps(mOptions, this);
609 gl::CreateContextFlags flags = (gl::CreateContextFlags::NO_VALIDATION |
610 gl::CreateContextFlags::PREFER_ROBUSTNESS);
611 bool tryNativeGL = true;
612 bool tryANGLE = false;
613
614 if (forceEnabled) {
615 flags |= gl::CreateContextFlags::FORCE_ENABLE_HARDWARE;
616 }
617
618 if (IsWebGL2()) {
619 flags |= gl::CreateContextFlags::PREFER_ES3;
620 } else if (!gfxPrefs::WebGL1AllowCoreProfile()) {
621 flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
622 }
623
624 #ifdef XP_MACOSX
625 const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
626 nsString vendorID, deviceID;
627
628 // Avoid crash for Intel HD Graphics 3000 on OSX. (Bug 1413269)
629 gfxInfo->GetAdapterVendorID(vendorID);
630 gfxInfo->GetAdapterDeviceID(deviceID);
631 if (vendorID.EqualsLiteral("0x8086") &&
632 (deviceID.EqualsLiteral("0x0116") || deviceID.EqualsLiteral("0x0126"))) {
633 flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
634 }
635 #endif
636 //////
637
638 const bool useEGL = PR_GetEnv("MOZ_WEBGL_FORCE_EGL");
639
640 #ifdef XP_WIN
641 tryNativeGL = false;
642 tryANGLE = true;
643
644 if (gfxPrefs::WebGLDisableWGL()) {
645 tryNativeGL = false;
646 }
647
648 if (gfxPrefs::WebGLDisableANGLE() || PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL") ||
649 useEGL) {
650 tryNativeGL = true;
651 tryANGLE = false;
652 }
653 #endif
654
655 if (tryNativeGL && !forceEnabled) {
656 const nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
657 const auto feature = nsIGfxInfo::FEATURE_WEBGL_OPENGL;
658
659 FailureReason reason;
660 if (IsFeatureInBlacklist(gfxInfo, feature, &reason.key)) {
661 reason.info =
662 "Refused to create native OpenGL context because of blacklist"
663 " entry: ";
664 reason.info.Append(reason.key);
665
666 out_failReasons->push_back(reason);
667
668 GenerateWarning("%s", reason.info.BeginReading());
669 tryNativeGL = false;
670 }
671 }
672
673 //////
674
675 if (tryNativeGL) {
676 if (useEGL)
677 return CreateAndInitGLWith(CreateGLWithEGL, baseCaps, flags,
678 out_failReasons);
679
680 if (CreateAndInitGLWith(CreateGLWithDefault, baseCaps, flags,
681 out_failReasons))
682 return true;
683 }
684
685 //////
686
687 if (tryANGLE) {
688 // Force enable alpha channel to make sure ANGLE use correct framebuffer
689 // format
690 auto angleCaps = baseCaps;
691 angleCaps.alpha = true;
692 return CreateAndInitGLWith(CreateGLWithANGLE, angleCaps, flags,
693 out_failReasons);
694 }
695
696 //////
697
698 out_failReasons->push_back(
699 FailureReason("FEATURE_FAILURE_WEBGL_EXHAUSTED_DRIVERS",
700 "Exhausted GL driver options."));
701 return false;
702 }
703
704 // Fallback for resizes:
705
EnsureDefaultFB(const char * const funcName)706 bool WebGLContext::EnsureDefaultFB(const char* const funcName) {
707 if (mDefaultFB) {
708 MOZ_ASSERT(mDefaultFB->mSize == mRequestedSize);
709 return true;
710 }
711
712 const bool depthStencil = mOptions.depth || mOptions.stencil;
713 auto attemptSize = mRequestedSize;
714
715 while (attemptSize.width || attemptSize.height) {
716 attemptSize.width = std::max(attemptSize.width, 1);
717 attemptSize.height = std::max(attemptSize.height, 1);
718
719 [&]() {
720 if (mOptions.antialias) {
721 MOZ_ASSERT(!mDefaultFB);
722 mDefaultFB =
723 MozFramebuffer::Create(gl, attemptSize, mMsaaSamples, depthStencil);
724 if (mDefaultFB) return;
725 if (mOptionsFrozen) return;
726 }
727
728 MOZ_ASSERT(!mDefaultFB);
729 mDefaultFB = MozFramebuffer::Create(gl, attemptSize, 0, depthStencil);
730 }();
731
732 if (mDefaultFB) break;
733
734 attemptSize.width /= 2;
735 attemptSize.height /= 2;
736 }
737
738 if (!mDefaultFB) {
739 GenerateWarning("%s: Backbuffer resize failed. Losing context.", funcName);
740 ForceLoseContext();
741 return false;
742 }
743
744 mDefaultFB_IsInvalid = true;
745
746 if (mDefaultFB->mSize != mRequestedSize) {
747 GenerateWarning(
748 "Requested size %dx%d was too large, but resize"
749 " to %dx%d succeeded.",
750 mRequestedSize.width, mRequestedSize.height, mDefaultFB->mSize.width,
751 mDefaultFB->mSize.height);
752 }
753 mRequestedSize = mDefaultFB->mSize;
754 return true;
755 }
756
ThrowEvent_WebGLContextCreationError(const nsACString & text)757 void WebGLContext::ThrowEvent_WebGLContextCreationError(
758 const nsACString& text) {
759 RefPtr<EventTarget> target = mCanvasElement;
760 if (!target && mOffscreenCanvas) {
761 target = mOffscreenCanvas;
762 } else if (!target) {
763 GenerateWarning("Failed to create WebGL context: %s", text.BeginReading());
764 return;
765 }
766
767 const auto kEventName = NS_LITERAL_STRING("webglcontextcreationerror");
768
769 WebGLContextEventInit eventInit;
770 // eventInit.mCancelable = true; // The spec says this, but it's silly.
771 eventInit.mStatusMessage = NS_ConvertASCIItoUTF16(text);
772
773 const RefPtr<WebGLContextEvent> event =
774 WebGLContextEvent::Constructor(target, kEventName, eventInit);
775 event->SetTrusted(true);
776
777 bool didPreventDefault;
778 target->DispatchEvent(event, &didPreventDefault);
779
780 //////
781
782 GenerateWarning("Failed to create WebGL context: %s", text.BeginReading());
783 }
784
785 NS_IMETHODIMP
SetDimensions(int32_t signedWidth,int32_t signedHeight)786 WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight) {
787 if (signedWidth < 0 || signedHeight < 0) {
788 if (!gl) {
789 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID,
790 NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_SIZE"));
791 }
792 GenerateWarning(
793 "Canvas size is too large (seems like a negative value wrapped)");
794 return NS_ERROR_OUT_OF_MEMORY;
795 }
796
797 uint32_t width = signedWidth;
798 uint32_t height = signedHeight;
799
800 // Early success return cases
801
802 // May have a OffscreenCanvas instead of an HTMLCanvasElement
803 if (GetCanvas()) GetCanvas()->InvalidateCanvas();
804
805 // Zero-sized surfaces can cause problems.
806 if (width == 0) width = 1;
807
808 if (height == 0) height = 1;
809
810 // If we already have a gl context, then we just need to resize it
811 if (gl) {
812 if (uint32_t(mRequestedSize.width) == width &&
813 uint32_t(mRequestedSize.height) == height) {
814 return NS_OK;
815 }
816
817 if (IsContextLost()) return NS_OK;
818
819 // If we've already drawn, we should commit the current buffer.
820 PresentScreenBuffer();
821
822 if (IsContextLost()) {
823 GenerateWarning("WebGL context was lost due to swap failure.");
824 return NS_OK;
825 }
826
827 // Kill our current default fb(s), for later lazy allocation.
828 mRequestedSize = {width, height};
829 mDefaultFB = nullptr;
830
831 mResetLayer = true;
832 return NS_OK;
833 }
834
835 nsCString failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_UNKOWN");
836 auto autoTelemetry = mozilla::MakeScopeExit([&] {
837 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID, failureId);
838 });
839
840 // End of early return cases.
841 // At this point we know that we're not just resizing an existing context,
842 // we are initializing a new context.
843
844 // if we exceeded either the global or the per-principal limit for WebGL
845 // contexts, lose the oldest-used context now to free resources. Note that we
846 // can't do that in the WebGLContext constructor as we don't have a canvas
847 // element yet there. Here is the right place to do so, as we are about to
848 // create the OpenGL context and that is what can fail if we already have too
849 // many.
850 LoseOldestWebGLContextIfLimitExceeded();
851
852 // We're going to create an entirely new context. If our
853 // generation is not 0 right now (that is, if this isn't the first
854 // context we're creating), we may have to dispatch a context lost
855 // event.
856
857 // If incrementing the generation would cause overflow,
858 // don't allow it. Allowing this would allow us to use
859 // resource handles created from older context generations.
860 if (!(mGeneration + 1).isValid()) {
861 // exit without changing the value of mGeneration
862 failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_TOO_MANY");
863 const nsLiteralCString text("Too many WebGL contexts created this run.");
864 ThrowEvent_WebGLContextCreationError(text);
865 return NS_ERROR_FAILURE;
866 }
867
868 // increment the generation number - Do this early because later
869 // in CreateOffscreenGL(), "default" objects are created that will
870 // pick up the old generation.
871 ++mGeneration;
872
873 bool disabled = gfxPrefs::WebGLDisabled();
874
875 // TODO: When we have software webgl support we should use that instead.
876 disabled |= gfxPlatform::InSafeMode();
877
878 if (disabled) {
879 if (gfxPlatform::InSafeMode()) {
880 failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_SAFEMODE");
881 } else {
882 failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_DISABLED");
883 }
884 const nsLiteralCString text("WebGL is currently disabled.");
885 ThrowEvent_WebGLContextCreationError(text);
886 return NS_ERROR_FAILURE;
887 }
888
889 if (gfxPrefs::WebGLDisableFailIfMajorPerformanceCaveat()) {
890 mOptions.failIfMajorPerformanceCaveat = false;
891 }
892
893 if (mOptions.failIfMajorPerformanceCaveat) {
894 nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
895 if (!HasAcceleratedLayers(gfxInfo)) {
896 failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_PERF_CAVEAT");
897 const nsLiteralCString text(
898 "failIfMajorPerformanceCaveat: Compositor is not"
899 " hardware-accelerated.");
900 ThrowEvent_WebGLContextCreationError(text);
901 return NS_ERROR_FAILURE;
902 }
903 }
904
905 // Alright, now let's start trying.
906 bool forceEnabled = gfxPrefs::WebGLForceEnabled();
907 ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
908
909 MOZ_ASSERT(!gl);
910 std::vector<FailureReason> failReasons;
911 if (!CreateAndInitGL(forceEnabled, &failReasons)) {
912 nsCString text("WebGL creation failed: ");
913 for (const auto& cur : failReasons) {
914 // Don't try to accumulate using an empty key if |cur.key| is empty.
915 if (cur.key.IsEmpty()) {
916 Telemetry::Accumulate(
917 Telemetry::CANVAS_WEBGL_FAILURE_ID,
918 NS_LITERAL_CSTRING("FEATURE_FAILURE_REASON_UNKNOWN"));
919 } else {
920 Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_FAILURE_ID, cur.key);
921 }
922
923 text.AppendASCII("\n* ");
924 text.Append(cur.info);
925 }
926 failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_REASON");
927 ThrowEvent_WebGLContextCreationError(text);
928 return NS_ERROR_FAILURE;
929 }
930 MOZ_ASSERT(gl);
931
932 if (mOptions.failIfMajorPerformanceCaveat) {
933 if (gl->IsWARP()) {
934 DestroyResourcesAndContext();
935 MOZ_ASSERT(!gl);
936
937 failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_PERF_WARP");
938 const nsLiteralCString text(
939 "failIfMajorPerformanceCaveat: Driver is not"
940 " hardware-accelerated.");
941 ThrowEvent_WebGLContextCreationError(text);
942 return NS_ERROR_FAILURE;
943 }
944
945 #ifdef XP_WIN
946 if (gl->GetContextType() == gl::GLContextType::WGL &&
947 !gl::sWGLLib.HasDXInterop2()) {
948 DestroyResourcesAndContext();
949 MOZ_ASSERT(!gl);
950
951 failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_DXGL_INTEROP2");
952 const nsLiteralCString text("Caveat: WGL without DXGLInterop2.");
953 ThrowEvent_WebGLContextCreationError(text);
954 return NS_ERROR_FAILURE;
955 }
956 #endif
957 }
958
959 MOZ_ASSERT(!mDefaultFB);
960 mRequestedSize = {width, height};
961 if (!EnsureDefaultFB("context initialization")) {
962 MOZ_ASSERT(!gl);
963
964 failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_BACKBUFFER");
965 const nsLiteralCString text("Initializing WebGL backbuffer failed.");
966 ThrowEvent_WebGLContextCreationError(text);
967 return NS_ERROR_FAILURE;
968 }
969
970 if (GLContext::ShouldSpew()) {
971 printf_stderr("--- WebGL context created: %p\n", gl.get());
972 }
973
974 // Update our internal stuff:
975 mOptions.antialias = bool(mDefaultFB->mSamples);
976
977 if (!mOptions.alpha) {
978 // We always have alpha.
979 mNeedsFakeNoAlpha = true;
980 }
981
982 if (mOptions.depth || mOptions.stencil) {
983 // We always have depth+stencil if we have either.
984 if (!mOptions.depth) {
985 mNeedsFakeNoDepth = true;
986 }
987 if (!mOptions.stencil) {
988 mNeedsFakeNoStencil = true;
989 }
990 }
991
992 mNeedsFakeNoStencil_UserFBs = false;
993 #ifdef MOZ_WIDGET_COCOA
994 if (!nsCocoaFeatures::IsAtLeastVersion(10, 12) &&
995 gl->Vendor() == GLVendor::Intel) {
996 mNeedsFakeNoStencil_UserFBs = true;
997 }
998 #endif
999
1000 mResetLayer = true;
1001 mOptionsFrozen = true;
1002
1003 //////
1004 // Initial setup.
1005
1006 gl->mImplicitMakeCurrent = true;
1007
1008 const auto& size = mDefaultFB->mSize;
1009
1010 mViewportX = mViewportY = 0;
1011 mViewportWidth = size.width;
1012 mViewportHeight = size.height;
1013 gl->fViewport(mViewportX, mViewportY, mViewportWidth, mViewportHeight);
1014
1015 gl->fScissor(0, 0, size.width, size.height);
1016
1017 //////
1018 // Check everything
1019
1020 AssertCachedBindings();
1021 AssertCachedGlobalState();
1022
1023 mShouldPresent = true;
1024
1025 //////
1026
1027 reporter.SetSuccessful();
1028
1029 failureId = NS_LITERAL_CSTRING("SUCCESS");
1030
1031 gl->ResetSyncCallCount("WebGLContext Initialization");
1032 return NS_OK;
1033 }
1034
LoseOldestWebGLContextIfLimitExceeded()1035 void WebGLContext::LoseOldestWebGLContextIfLimitExceeded() {
1036 const auto maxWebGLContexts = gfxPrefs::WebGLMaxContexts();
1037 auto maxWebGLContextsPerPrincipal = gfxPrefs::WebGLMaxContextsPerPrincipal();
1038
1039 // maxWebGLContextsPerPrincipal must be less than maxWebGLContexts
1040 MOZ_ASSERT(maxWebGLContextsPerPrincipal <= maxWebGLContexts);
1041 maxWebGLContextsPerPrincipal =
1042 std::min(maxWebGLContextsPerPrincipal, maxWebGLContexts);
1043
1044 if (!NS_IsMainThread()) {
1045 // XXX mtseng: bug 709490, WebGLMemoryTracker is not thread safe.
1046 return;
1047 }
1048
1049 // it's important to update the index on a new context before losing old
1050 // contexts, otherwise new unused contexts would all have index 0 and we
1051 // couldn't distinguish older ones when choosing which one to lose first.
1052 UpdateLastUseIndex();
1053
1054 WebGLMemoryTracker::ContextsArrayType& contexts =
1055 WebGLMemoryTracker::Contexts();
1056
1057 // quick exit path, should cover a majority of cases
1058 if (contexts.Length() <= maxWebGLContextsPerPrincipal) return;
1059
1060 // note that here by "context" we mean "non-lost context". See the check for
1061 // IsContextLost() below. Indeed, the point of this function is to maybe lose
1062 // some currently non-lost context.
1063
1064 uint64_t oldestIndex = UINT64_MAX;
1065 uint64_t oldestIndexThisPrincipal = UINT64_MAX;
1066 const WebGLContext* oldestContext = nullptr;
1067 const WebGLContext* oldestContextThisPrincipal = nullptr;
1068 size_t numContexts = 0;
1069 size_t numContextsThisPrincipal = 0;
1070
1071 for (size_t i = 0; i < contexts.Length(); ++i) {
1072 // don't want to lose ourselves.
1073 if (contexts[i] == this) continue;
1074
1075 if (contexts[i]->IsContextLost()) continue;
1076
1077 if (!contexts[i]->GetCanvas()) {
1078 // Zombie context: the canvas is already destroyed, but something else
1079 // (typically the compositor) is still holding on to the context.
1080 // Killing zombies is a no-brainer.
1081 const_cast<WebGLContext*>(contexts[i])->LoseContext();
1082 continue;
1083 }
1084
1085 numContexts++;
1086 if (contexts[i]->mLastUseIndex < oldestIndex) {
1087 oldestIndex = contexts[i]->mLastUseIndex;
1088 oldestContext = contexts[i];
1089 }
1090
1091 nsIPrincipal* ourPrincipal = GetCanvas()->NodePrincipal();
1092 nsIPrincipal* theirPrincipal = contexts[i]->GetCanvas()->NodePrincipal();
1093 bool samePrincipal;
1094 nsresult rv = ourPrincipal->Equals(theirPrincipal, &samePrincipal);
1095 if (NS_SUCCEEDED(rv) && samePrincipal) {
1096 numContextsThisPrincipal++;
1097 if (contexts[i]->mLastUseIndex < oldestIndexThisPrincipal) {
1098 oldestIndexThisPrincipal = contexts[i]->mLastUseIndex;
1099 oldestContextThisPrincipal = contexts[i];
1100 }
1101 }
1102 }
1103
1104 if (numContextsThisPrincipal > maxWebGLContextsPerPrincipal) {
1105 GenerateWarning(
1106 "Exceeded %u live WebGL contexts for this principal, losing the "
1107 "least recently used one.",
1108 maxWebGLContextsPerPrincipal);
1109 MOZ_ASSERT(oldestContextThisPrincipal); // if we reach this point, this
1110 // can't be null
1111 const_cast<WebGLContext*>(oldestContextThisPrincipal)->LoseContext();
1112 } else if (numContexts > maxWebGLContexts) {
1113 GenerateWarning(
1114 "Exceeded %u live WebGL contexts, losing the least "
1115 "recently used one.",
1116 maxWebGLContexts);
1117 MOZ_ASSERT(oldestContext); // if we reach this point, this can't be null
1118 const_cast<WebGLContext*>(oldestContext)->LoseContext();
1119 }
1120 }
1121
GetImageBuffer(int32_t * out_format)1122 UniquePtr<uint8_t[]> WebGLContext::GetImageBuffer(int32_t* out_format) {
1123 *out_format = 0;
1124
1125 // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
1126 gfxAlphaType any;
1127 RefPtr<SourceSurface> snapshot = GetSurfaceSnapshot(&any);
1128 if (!snapshot) return nullptr;
1129
1130 RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
1131
1132 return gfxUtils::GetImageBuffer(dataSurface, mOptions.premultipliedAlpha,
1133 out_format);
1134 }
1135
1136 NS_IMETHODIMP
GetInputStream(const char * mimeType,const char16_t * encoderOptions,nsIInputStream ** out_stream)1137 WebGLContext::GetInputStream(const char* mimeType,
1138 const char16_t* encoderOptions,
1139 nsIInputStream** out_stream) {
1140 NS_ASSERTION(gl, "GetInputStream on invalid context?");
1141 if (!gl) return NS_ERROR_FAILURE;
1142
1143 // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
1144 gfxAlphaType any;
1145 RefPtr<SourceSurface> snapshot = GetSurfaceSnapshot(&any);
1146 if (!snapshot) return NS_ERROR_FAILURE;
1147
1148 RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
1149 return gfxUtils::GetInputStream(dataSurface, mOptions.premultipliedAlpha,
1150 mimeType, encoderOptions, out_stream);
1151 }
1152
UpdateLastUseIndex()1153 void WebGLContext::UpdateLastUseIndex() {
1154 static CheckedInt<uint64_t> sIndex = 0;
1155
1156 sIndex++;
1157
1158 // should never happen with 64-bit; trying to handle this would be riskier
1159 // than not handling it as the handler code would never get exercised.
1160 if (!sIndex.isValid())
1161 MOZ_CRASH("Can't believe it's been 2^64 transactions already!");
1162 mLastUseIndex = sIndex.value();
1163 }
1164
1165 static uint8_t gWebGLLayerUserData;
1166
1167 class WebGLContextUserData : public LayerUserData {
1168 public:
WebGLContextUserData(HTMLCanvasElement * canvas)1169 explicit WebGLContextUserData(HTMLCanvasElement* canvas) : mCanvas(canvas) {}
1170
1171 /* PreTransactionCallback gets called by the Layers code every time the
1172 * WebGL canvas is going to be composited.
1173 */
PreTransactionCallback(void * data)1174 static void PreTransactionCallback(void* data) {
1175 WebGLContext* webgl = static_cast<WebGLContext*>(data);
1176
1177 // Prepare the context for composition
1178 webgl->BeginComposition();
1179 }
1180
1181 /** DidTransactionCallback gets called by the Layers code everytime the WebGL
1182 * canvas gets composite, so it really is the right place to put actions that
1183 * have to be performed upon compositing
1184 */
DidTransactionCallback(void * data)1185 static void DidTransactionCallback(void* data) {
1186 WebGLContext* webgl = static_cast<WebGLContext*>(data);
1187
1188 // Clean up the context after composition
1189 webgl->EndComposition();
1190 }
1191
1192 private:
1193 RefPtr<HTMLCanvasElement> mCanvas;
1194 };
1195
GetCanvasLayer(nsDisplayListBuilder * builder,Layer * oldLayer,LayerManager * manager)1196 already_AddRefed<layers::Layer> WebGLContext::GetCanvasLayer(
1197 nsDisplayListBuilder* builder, Layer* oldLayer, LayerManager* manager) {
1198 if (!mResetLayer && oldLayer && oldLayer->HasUserData(&gWebGLLayerUserData)) {
1199 RefPtr<layers::Layer> ret = oldLayer;
1200 return ret.forget();
1201 }
1202
1203 RefPtr<CanvasLayer> canvasLayer = manager->CreateCanvasLayer();
1204 if (!canvasLayer) {
1205 NS_WARNING("CreateCanvasLayer returned null!");
1206 return nullptr;
1207 }
1208
1209 WebGLContextUserData* userData = nullptr;
1210 if (builder->IsPaintingToWindow() && mCanvasElement) {
1211 userData = new WebGLContextUserData(mCanvasElement);
1212 }
1213
1214 canvasLayer->SetUserData(&gWebGLLayerUserData, userData);
1215
1216 CanvasRenderer* canvasRenderer = canvasLayer->CreateOrGetCanvasRenderer();
1217 if (!InitializeCanvasRenderer(builder, canvasRenderer)) return nullptr;
1218
1219 if (!gl) {
1220 NS_WARNING("GLContext is null!");
1221 return nullptr;
1222 }
1223
1224 uint32_t flags = gl->Caps().alpha ? 0 : Layer::CONTENT_OPAQUE;
1225 canvasLayer->SetContentFlags(flags);
1226
1227 mResetLayer = false;
1228
1229 return canvasLayer.forget();
1230 }
1231
UpdateWebRenderCanvasData(nsDisplayListBuilder * aBuilder,WebRenderCanvasData * aCanvasData)1232 bool WebGLContext::UpdateWebRenderCanvasData(nsDisplayListBuilder* aBuilder,
1233 WebRenderCanvasData* aCanvasData) {
1234 CanvasRenderer* renderer = aCanvasData->GetCanvasRenderer();
1235
1236 if (!mResetLayer && renderer) {
1237 return true;
1238 }
1239
1240 renderer = aCanvasData->CreateCanvasRenderer();
1241 if (!InitializeCanvasRenderer(aBuilder, renderer)) {
1242 // Clear CanvasRenderer of WebRenderCanvasData
1243 aCanvasData->ClearCanvasRenderer();
1244 return false;
1245 }
1246
1247 MOZ_ASSERT(renderer);
1248 mResetLayer = false;
1249 return true;
1250 }
1251
InitializeCanvasRenderer(nsDisplayListBuilder * aBuilder,CanvasRenderer * aRenderer)1252 bool WebGLContext::InitializeCanvasRenderer(nsDisplayListBuilder* aBuilder,
1253 CanvasRenderer* aRenderer) {
1254 if (IsContextLost()) return false;
1255
1256 CanvasInitializeData data;
1257 if (aBuilder->IsPaintingToWindow() && mCanvasElement) {
1258 // Make the layer tell us whenever a transaction finishes (including
1259 // the current transaction), so we can clear our invalidation state and
1260 // start invalidating again. We need to do this for the layer that is
1261 // being painted to a window (there shouldn't be more than one at a time,
1262 // and if there is, flushing the invalidation state more often than
1263 // necessary is harmless).
1264
1265 // The layer will be destroyed when we tear down the presentation
1266 // (at the latest), at which time this userData will be destroyed,
1267 // releasing the reference to the element.
1268 // The userData will receive DidTransactionCallbacks, which flush the
1269 // the invalidation state to indicate that the canvas is up to date.
1270 data.mPreTransCallback = WebGLContextUserData::PreTransactionCallback;
1271 data.mPreTransCallbackData = this;
1272 data.mDidTransCallback = WebGLContextUserData::DidTransactionCallback;
1273 data.mDidTransCallbackData = this;
1274 }
1275
1276 data.mSize = DrawingBufferSize("InitializeCanvasRenderer");
1277 data.mHasAlpha = mOptions.alpha;
1278 data.mIsGLAlphaPremult = IsPremultAlpha() || !data.mHasAlpha;
1279 data.mGLContext = gl;
1280
1281 aRenderer->Initialize(data);
1282 aRenderer->SetDirty();
1283 return true;
1284 }
1285
GetCompositorBackendType() const1286 layers::LayersBackend WebGLContext::GetCompositorBackendType() const {
1287 if (mCanvasElement) {
1288 return mCanvasElement->GetCompositorBackendType();
1289 } else if (mOffscreenCanvas) {
1290 return mOffscreenCanvas->GetCompositorBackendType();
1291 }
1292
1293 return LayersBackend::LAYERS_NONE;
1294 }
1295
GetOwnerDoc() const1296 nsIDocument* WebGLContext::GetOwnerDoc() const {
1297 MOZ_ASSERT(mCanvasElement);
1298 if (!mCanvasElement) {
1299 return nullptr;
1300 }
1301 return mCanvasElement->OwnerDoc();
1302 }
1303
Commit()1304 void WebGLContext::Commit() {
1305 if (mOffscreenCanvas) {
1306 mOffscreenCanvas->CommitFrameToCompositor();
1307 }
1308 }
1309
GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas> & retval)1310 void WebGLContext::GetCanvas(
1311 Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval) {
1312 if (mCanvasElement) {
1313 MOZ_RELEASE_ASSERT(!mOffscreenCanvas, "GFX: Canvas is offscreen.");
1314
1315 if (mCanvasElement->IsInNativeAnonymousSubtree()) {
1316 retval.SetNull();
1317 } else {
1318 retval.SetValue().SetAsHTMLCanvasElement() = mCanvasElement;
1319 }
1320 } else if (mOffscreenCanvas) {
1321 retval.SetValue().SetAsOffscreenCanvas() = mOffscreenCanvas;
1322 } else {
1323 retval.SetNull();
1324 }
1325 }
1326
GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes> & retval)1327 void WebGLContext::GetContextAttributes(
1328 dom::Nullable<dom::WebGLContextAttributes>& retval) {
1329 retval.SetNull();
1330 if (IsContextLost()) return;
1331
1332 dom::WebGLContextAttributes& result = retval.SetValue();
1333
1334 result.mAlpha.Construct(mOptions.alpha);
1335 result.mDepth = mOptions.depth;
1336 result.mStencil = mOptions.stencil;
1337 result.mAntialias = mOptions.antialias;
1338 result.mPremultipliedAlpha = mOptions.premultipliedAlpha;
1339 result.mPreserveDrawingBuffer = mOptions.preserveDrawingBuffer;
1340 result.mFailIfMajorPerformanceCaveat = mOptions.failIfMajorPerformanceCaveat;
1341 }
1342
ForceClearFramebufferWithDefaultValues(const GLbitfield clearBits,const bool fakeNoAlpha) const1343 void WebGLContext::ForceClearFramebufferWithDefaultValues(
1344 const GLbitfield clearBits, const bool fakeNoAlpha) const {
1345 const bool initializeColorBuffer =
1346 bool(clearBits & LOCAL_GL_COLOR_BUFFER_BIT);
1347 const bool initializeDepthBuffer =
1348 bool(clearBits & LOCAL_GL_DEPTH_BUFFER_BIT);
1349 const bool initializeStencilBuffer =
1350 bool(clearBits & LOCAL_GL_STENCIL_BUFFER_BIT);
1351
1352 // Fun GL fact: No need to worry about the viewport here, glViewport is just
1353 // setting up a coordinates transformation, it doesn't affect glClear at all.
1354 AssertCachedGlobalState();
1355
1356 // Prepare GL state for clearing.
1357 if (mScissorTestEnabled) {
1358 gl->fDisable(LOCAL_GL_SCISSOR_TEST);
1359 }
1360
1361 if (initializeColorBuffer) {
1362 DoColorMask(0x0f);
1363
1364 if (fakeNoAlpha) {
1365 gl->fClearColor(0.0f, 0.0f, 0.0f, 1.0f);
1366 } else {
1367 gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f);
1368 }
1369 }
1370
1371 if (initializeDepthBuffer) {
1372 gl->fDepthMask(1);
1373 gl->fClearDepth(1.0f);
1374 }
1375
1376 if (initializeStencilBuffer) {
1377 // "The clear operation always uses the front stencil write mask
1378 // when clearing the stencil buffer."
1379 gl->fStencilMaskSeparate(LOCAL_GL_FRONT, 0xffffffff);
1380 gl->fStencilMaskSeparate(LOCAL_GL_BACK, 0xffffffff);
1381 gl->fClearStencil(0);
1382 }
1383
1384 if (mRasterizerDiscardEnabled) {
1385 gl->fDisable(LOCAL_GL_RASTERIZER_DISCARD);
1386 }
1387
1388 // Do the clear!
1389 gl->fClear(clearBits);
1390
1391 // And reset!
1392 if (mScissorTestEnabled) {
1393 gl->fEnable(LOCAL_GL_SCISSOR_TEST);
1394 }
1395
1396 if (mRasterizerDiscardEnabled) {
1397 gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD);
1398 }
1399
1400 // Restore GL state after clearing.
1401 if (initializeColorBuffer) {
1402 gl->fClearColor(mColorClearValue[0], mColorClearValue[1],
1403 mColorClearValue[2], mColorClearValue[3]);
1404 }
1405
1406 if (initializeDepthBuffer) {
1407 gl->fDepthMask(mDepthWriteMask);
1408 gl->fClearDepth(mDepthClearValue);
1409 }
1410
1411 if (initializeStencilBuffer) {
1412 gl->fStencilMaskSeparate(LOCAL_GL_FRONT, mStencilWriteMaskFront);
1413 gl->fStencilMaskSeparate(LOCAL_GL_BACK, mStencilWriteMaskBack);
1414 gl->fClearStencil(mStencilClearValue);
1415 }
1416 }
1417
OnEndOfFrame() const1418 void WebGLContext::OnEndOfFrame() const {
1419 if (gfxPrefs::WebGLSpewFrameAllocs()) {
1420 GeneratePerfWarning("[webgl.perf.spew-frame-allocs] %" PRIu64
1421 " data allocations this frame.",
1422 mDataAllocGLCallCount);
1423 }
1424 mDataAllocGLCallCount = 0;
1425 gl->ResetSyncCallCount("WebGLContext PresentScreenBuffer");
1426 }
1427
BlitBackbufferToCurDriverFB() const1428 void WebGLContext::BlitBackbufferToCurDriverFB() const {
1429 DoColorMask(0x0f);
1430
1431 if (mScissorTestEnabled) {
1432 gl->fDisable(LOCAL_GL_SCISSOR_TEST);
1433 }
1434
1435 [&]() {
1436 const auto& size = mDefaultFB->mSize;
1437
1438 if (gl->IsSupported(GLFeature::framebuffer_blit)) {
1439 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, mDefaultFB->mFB);
1440 gl->fBlitFramebuffer(0, 0, size.width, size.height, 0, 0, size.width,
1441 size.height, LOCAL_GL_COLOR_BUFFER_BIT,
1442 LOCAL_GL_NEAREST);
1443 return;
1444 }
1445 if (mDefaultFB->mSamples &&
1446 gl->IsExtensionSupported(GLContext::APPLE_framebuffer_multisample)) {
1447 gl->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER, mDefaultFB->mFB);
1448 gl->fResolveMultisampleFramebufferAPPLE();
1449 return;
1450 }
1451
1452 gl->BlitHelper()->DrawBlitTextureToFramebuffer(mDefaultFB->ColorTex(), size,
1453 size);
1454 }();
1455
1456 if (mScissorTestEnabled) {
1457 gl->fEnable(LOCAL_GL_SCISSOR_TEST);
1458 }
1459 }
1460
1461 // For an overview of how WebGL compositing works, see:
1462 // https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing
PresentScreenBuffer()1463 bool WebGLContext::PresentScreenBuffer() {
1464 if (IsContextLost()) return false;
1465
1466 if (!mShouldPresent) return false;
1467
1468 if (!ValidateAndInitFB("Present", nullptr)) return false;
1469
1470 const auto& screen = gl->Screen();
1471 if (screen->Size() != mDefaultFB->mSize &&
1472 !screen->Resize(mDefaultFB->mSize)) {
1473 GenerateWarning("screen->Resize failed. Losing context.");
1474 ForceLoseContext();
1475 return false;
1476 }
1477
1478 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
1479 BlitBackbufferToCurDriverFB();
1480
1481 #ifdef DEBUG
1482 if (!mOptions.alpha) {
1483 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
1484 uint32_t pixel = 3;
1485 gl->fReadPixels(0, 0, 1, 1, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, &pixel);
1486 MOZ_ASSERT((pixel & 0xff000000) == 0xff000000);
1487 }
1488 #endif
1489
1490 if (!screen->PublishFrame(screen->Size())) {
1491 GenerateWarning("PublishFrame failed. Losing context.");
1492 ForceLoseContext();
1493 return false;
1494 }
1495
1496 if (!mOptions.preserveDrawingBuffer) {
1497 if (gl->IsSupported(gl::GLFeature::invalidate_framebuffer)) {
1498 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
1499 const GLenum attachments[] = {LOCAL_GL_COLOR_ATTACHMENT0};
1500 gl->fInvalidateFramebuffer(LOCAL_GL_FRAMEBUFFER, 1, attachments);
1501 }
1502 mDefaultFB_IsInvalid = true;
1503 }
1504 mResolvedDefaultFB = nullptr;
1505
1506 mShouldPresent = false;
1507 OnEndOfFrame();
1508
1509 return true;
1510 }
1511
1512 // Prepare the context for capture before compositing
BeginComposition()1513 void WebGLContext::BeginComposition() {
1514 // Present our screenbuffer, if needed.
1515 PresentScreenBuffer();
1516 mDrawCallsSinceLastFlush = 0;
1517 }
1518
1519 // Clean up the context after captured for compositing
EndComposition()1520 void WebGLContext::EndComposition() {
1521 // Mark ourselves as no longer invalidated.
1522 MarkContextClean();
1523 UpdateLastUseIndex();
1524 }
1525
DummyReadFramebufferOperation(const char * funcName)1526 void WebGLContext::DummyReadFramebufferOperation(const char* funcName) {
1527 if (!mBoundReadFramebuffer) return; // Infallible.
1528
1529 const auto status = mBoundReadFramebuffer->CheckFramebufferStatus(funcName);
1530
1531 if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
1532 ErrorInvalidFramebufferOperation("%s: Framebuffer must be complete.",
1533 funcName);
1534 }
1535 }
1536
Has64BitTimestamps() const1537 bool WebGLContext::Has64BitTimestamps() const {
1538 // 'sync' provides glGetInteger64v either by supporting ARB_sync, GL3+, or
1539 // GLES3+.
1540 return gl->IsSupported(GLFeature::sync);
1541 }
1542
CheckContextLost(GLContext * gl,bool * const out_isGuilty)1543 static bool CheckContextLost(GLContext* gl, bool* const out_isGuilty) {
1544 MOZ_ASSERT(gl);
1545 MOZ_ASSERT(out_isGuilty);
1546
1547 bool isEGL = gl->GetContextType() == gl::GLContextType::EGL;
1548
1549 GLenum resetStatus = LOCAL_GL_NO_ERROR;
1550 if (gl->IsSupported(GLFeature::robustness)) {
1551 gl->MakeCurrent();
1552 resetStatus = gl->fGetGraphicsResetStatus();
1553 } else if (isEGL) {
1554 // Simulate a ARB_robustness guilty context loss for when we
1555 // get an EGL_CONTEXT_LOST error. It may not actually be guilty,
1556 // but we can't make any distinction.
1557 if (!gl->MakeCurrent(true) && gl->IsContextLost()) {
1558 resetStatus = LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB;
1559 }
1560 }
1561
1562 if (resetStatus == LOCAL_GL_NO_ERROR) {
1563 *out_isGuilty = false;
1564 return false;
1565 }
1566
1567 // Assume guilty unless we find otherwise!
1568 bool isGuilty = true;
1569 switch (resetStatus) {
1570 case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB:
1571 // Either nothing wrong, or not our fault.
1572 isGuilty = false;
1573 break;
1574 case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB:
1575 NS_WARNING(
1576 "WebGL content on the page definitely caused the graphics"
1577 " card to reset.");
1578 break;
1579 case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB:
1580 NS_WARNING(
1581 "WebGL content on the page might have caused the graphics"
1582 " card to reset");
1583 // If we can't tell, assume guilty.
1584 break;
1585 default:
1586 MOZ_ASSERT(false, "Unreachable.");
1587 // If we do get here, let's pretend to be guilty as an escape plan.
1588 break;
1589 }
1590
1591 if (isGuilty) {
1592 NS_WARNING(
1593 "WebGL context on this page is considered guilty, and will"
1594 " not be restored.");
1595 }
1596
1597 *out_isGuilty = isGuilty;
1598 return true;
1599 }
1600
TryToRestoreContext()1601 bool WebGLContext::TryToRestoreContext() {
1602 if (NS_FAILED(SetDimensions(mRequestedSize.width, mRequestedSize.height)))
1603 return false;
1604
1605 return true;
1606 }
1607
RunContextLossTimer()1608 void WebGLContext::RunContextLossTimer() { mContextLossHandler.RunTimer(); }
1609
1610 class UpdateContextLossStatusTask : public CancelableRunnable {
1611 RefPtr<WebGLContext> mWebGL;
1612
1613 public:
UpdateContextLossStatusTask(WebGLContext * webgl)1614 explicit UpdateContextLossStatusTask(WebGLContext* webgl)
1615 : CancelableRunnable("UpdateContextLossStatusTask"), mWebGL(webgl) {}
1616
Run()1617 NS_IMETHOD Run() override {
1618 if (mWebGL) mWebGL->UpdateContextLossStatus();
1619
1620 return NS_OK;
1621 }
1622
Cancel()1623 nsresult Cancel() override {
1624 mWebGL = nullptr;
1625 return NS_OK;
1626 }
1627 };
1628
EnqueueUpdateContextLossStatus()1629 void WebGLContext::EnqueueUpdateContextLossStatus() {
1630 nsCOMPtr<nsIRunnable> task = new UpdateContextLossStatusTask(this);
1631 NS_DispatchToCurrentThread(task);
1632 }
1633
1634 // We use this timer for many things. Here are the things that it is activated
1635 // for:
1636 // 1) If a script is using the MOZ_WEBGL_lose_context extension.
1637 // 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
1638 // CONTEXT_LOST_WEBGL error has been triggered.
1639 // 3) If we are using ANGLE, or anything that supports ARB_robustness, query the
1640 // GPU periodically to see if the reset status bit has been set.
1641 // In all of these situations, we use this timer to send the script context lost
1642 // and restored events asynchronously. For example, if it triggers a context
1643 // loss, the webglcontextlost event will be sent to it the next time the
1644 // robustness timer fires.
1645 // Note that this timer mechanism is not used unless one of these 3 criteria are
1646 // met.
1647 // At a bare minimum, from context lost to context restores, it would take 3
1648 // full timer iterations: detection, webglcontextlost, webglcontextrestored.
UpdateContextLossStatus()1649 void WebGLContext::UpdateContextLossStatus() {
1650 if (!mCanvasElement && !mOffscreenCanvas) {
1651 // the canvas is gone. That happens when the page was closed before we got
1652 // this timer event. In this case, there's nothing to do here, just don't
1653 // crash.
1654 return;
1655 }
1656 if (mContextStatus == ContextNotLost) {
1657 // We don't know that we're lost, but we might be, so we need to
1658 // check. If we're guilty, don't allow restores, though.
1659
1660 bool isGuilty = true;
1661 MOZ_ASSERT(gl); // Shouldn't be missing gl if we're NotLost.
1662 bool isContextLost = CheckContextLost(gl, &isGuilty);
1663
1664 if (isContextLost) {
1665 if (isGuilty) mAllowContextRestore = false;
1666
1667 ForceLoseContext();
1668 }
1669
1670 // Fall through.
1671 }
1672
1673 if (mContextStatus == ContextLostAwaitingEvent) {
1674 // The context has been lost and we haven't yet triggered the
1675 // callback, so do that now.
1676 const auto kEventName = NS_LITERAL_STRING("webglcontextlost");
1677 const bool kCanBubble = true;
1678 const bool kIsCancelable = true;
1679 bool useDefaultHandler;
1680
1681 if (mCanvasElement) {
1682 nsContentUtils::DispatchTrustedEvent(
1683 mCanvasElement->OwnerDoc(), static_cast<nsIContent*>(mCanvasElement),
1684 kEventName, kCanBubble, kIsCancelable, &useDefaultHandler);
1685 } else {
1686 // OffscreenCanvas case
1687 RefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
1688 event->InitEvent(kEventName, kCanBubble, kIsCancelable);
1689 event->SetTrusted(true);
1690 mOffscreenCanvas->DispatchEvent(event, &useDefaultHandler);
1691 }
1692
1693 // We sent the callback, so we're just 'regular lost' now.
1694 mContextStatus = ContextLost;
1695 // If we're told to use the default handler, it means the script
1696 // didn't bother to handle the event. In this case, we shouldn't
1697 // auto-restore the context.
1698 if (useDefaultHandler) mAllowContextRestore = false;
1699
1700 // Fall through.
1701 }
1702
1703 if (mContextStatus == ContextLost) {
1704 // Context is lost, and we've already sent the callback. We
1705 // should try to restore the context if we're both allowed to,
1706 // and supposed to.
1707
1708 // Are we allowed to restore the context?
1709 if (!mAllowContextRestore) return;
1710
1711 // If we're only simulated-lost, we shouldn't auto-restore, and
1712 // instead we should wait for restoreContext() to be called.
1713 if (mLastLossWasSimulated) return;
1714
1715 // Restore when the app is visible
1716 if (mRestoreWhenVisible) return;
1717
1718 ForceRestoreContext();
1719 return;
1720 }
1721
1722 if (mContextStatus == ContextLostAwaitingRestore) {
1723 // Context is lost, but we should try to restore it.
1724
1725 if (!mAllowContextRestore) {
1726 // We might decide this after thinking we'd be OK restoring
1727 // the context, so downgrade.
1728 mContextStatus = ContextLost;
1729 return;
1730 }
1731
1732 if (!TryToRestoreContext()) {
1733 // Failed to restore. Try again later.
1734 mContextLossHandler.RunTimer();
1735 return;
1736 }
1737
1738 // Revival!
1739 mContextStatus = ContextNotLost;
1740
1741 if (mCanvasElement) {
1742 nsContentUtils::DispatchTrustedEvent(
1743 mCanvasElement->OwnerDoc(), static_cast<nsIContent*>(mCanvasElement),
1744 NS_LITERAL_STRING("webglcontextrestored"), true, true);
1745 } else {
1746 RefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
1747 event->InitEvent(NS_LITERAL_STRING("webglcontextrestored"), true, true);
1748 event->SetTrusted(true);
1749 bool unused;
1750 mOffscreenCanvas->DispatchEvent(event, &unused);
1751 }
1752
1753 mEmitContextLostErrorOnce = true;
1754 return;
1755 }
1756 }
1757
ForceLoseContext(bool simulateLosing)1758 void WebGLContext::ForceLoseContext(bool simulateLosing) {
1759 printf_stderr("WebGL(%p)::ForceLoseContext\n", this);
1760 MOZ_ASSERT(!IsContextLost());
1761 mContextStatus = ContextLostAwaitingEvent;
1762 mContextLostErrorSet = false;
1763
1764 // Burn it all!
1765 DestroyResourcesAndContext();
1766 mLastLossWasSimulated = simulateLosing;
1767
1768 // Queue up a task, since we know the status changed.
1769 EnqueueUpdateContextLossStatus();
1770 }
1771
ForceRestoreContext()1772 void WebGLContext::ForceRestoreContext() {
1773 printf_stderr("WebGL(%p)::ForceRestoreContext\n", this);
1774 mContextStatus = ContextLostAwaitingRestore;
1775 mAllowContextRestore = true; // Hey, you did say 'force'.
1776
1777 // Queue up a task, since we know the status changed.
1778 EnqueueUpdateContextLossStatus();
1779 }
1780
GetSurfaceSnapshot(gfxAlphaType * const out_alphaType)1781 already_AddRefed<mozilla::gfx::SourceSurface> WebGLContext::GetSurfaceSnapshot(
1782 gfxAlphaType* const out_alphaType) {
1783 if (!gl) return nullptr;
1784
1785 if (!BindDefaultFBForRead("GetSurfaceSnapshot")) return nullptr;
1786
1787 const auto surfFormat =
1788 mOptions.alpha ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
1789 const auto& size = mDefaultFB->mSize;
1790 RefPtr<DataSourceSurface> surf;
1791 surf = Factory::CreateDataSourceSurfaceWithStride(size, surfFormat,
1792 size.width * 4);
1793 if (NS_WARN_IF(!surf)) return nullptr;
1794
1795 ReadPixelsIntoDataSurface(gl, surf);
1796
1797 gfxAlphaType alphaType;
1798 if (!mOptions.alpha) {
1799 alphaType = gfxAlphaType::Opaque;
1800 } else if (mOptions.premultipliedAlpha) {
1801 alphaType = gfxAlphaType::Premult;
1802 } else {
1803 alphaType = gfxAlphaType::NonPremult;
1804 }
1805
1806 if (out_alphaType) {
1807 *out_alphaType = alphaType;
1808 } else {
1809 // Expects Opaque or Premult
1810 if (alphaType == gfxAlphaType::NonPremult) {
1811 gfxUtils::PremultiplyDataSurface(surf, surf);
1812 }
1813 }
1814
1815 RefPtr<DrawTarget> dt = Factory::CreateDrawTarget(
1816 gfxPlatform::GetPlatform()->GetSoftwareBackend(), size,
1817 SurfaceFormat::B8G8R8A8);
1818 if (!dt) return nullptr;
1819
1820 dt->SetTransform(Matrix::Translation(0.0, size.height).PreScale(1.0, -1.0));
1821
1822 const gfx::Rect rect{0, 0, float(size.width), float(size.height)};
1823 dt->DrawSurface(surf, rect, rect, DrawSurfaceOptions(),
1824 DrawOptions(1.0f, CompositionOp::OP_SOURCE));
1825
1826 return dt->Snapshot();
1827 }
1828
DidRefresh()1829 void WebGLContext::DidRefresh() {
1830 if (gl) {
1831 gl->FlushIfHeavyGLCallsSinceLastFlush();
1832 }
1833 }
1834
1835 ////////////////////////////////////////////////////////////////////////////////
1836
DrawingBufferSize(const char * const funcName)1837 gfx::IntSize WebGLContext::DrawingBufferSize(const char* const funcName) {
1838 const gfx::IntSize zeros{0, 0};
1839 if (IsContextLost()) return zeros;
1840
1841 if (!EnsureDefaultFB(funcName)) return zeros;
1842
1843 return mDefaultFB->mSize;
1844 }
1845
ValidateAndInitFB(const char * const funcName,const WebGLFramebuffer * const fb)1846 bool WebGLContext::ValidateAndInitFB(const char* const funcName,
1847 const WebGLFramebuffer* const fb) {
1848 if (fb) return fb->ValidateAndInitAttachments(funcName);
1849
1850 if (!EnsureDefaultFB(funcName)) return false;
1851
1852 if (mDefaultFB_IsInvalid) {
1853 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
1854 const GLbitfield bits = LOCAL_GL_COLOR_BUFFER_BIT |
1855 LOCAL_GL_DEPTH_BUFFER_BIT |
1856 LOCAL_GL_STENCIL_BUFFER_BIT;
1857 const bool fakeNoAlpha = !mOptions.alpha;
1858 ForceClearFramebufferWithDefaultValues(bits, fakeNoAlpha);
1859 mDefaultFB_IsInvalid = false;
1860 }
1861 return true;
1862 }
1863
DoBindFB(const WebGLFramebuffer * const fb,const GLenum target) const1864 void WebGLContext::DoBindFB(const WebGLFramebuffer* const fb,
1865 const GLenum target) const {
1866 const GLenum driverFB = fb ? fb->mGLName : mDefaultFB->mFB;
1867 gl->fBindFramebuffer(target, driverFB);
1868 }
1869
BindCurFBForDraw(const char * const funcName)1870 bool WebGLContext::BindCurFBForDraw(const char* const funcName) {
1871 const auto& fb = mBoundDrawFramebuffer;
1872 if (!ValidateAndInitFB(funcName, fb)) return false;
1873
1874 DoBindFB(fb);
1875 return true;
1876 }
1877
BindCurFBForColorRead(const char * const funcName,const webgl::FormatUsageInfo ** const out_format,uint32_t * const out_width,uint32_t * const out_height)1878 bool WebGLContext::BindCurFBForColorRead(
1879 const char* const funcName, const webgl::FormatUsageInfo** const out_format,
1880 uint32_t* const out_width, uint32_t* const out_height) {
1881 const auto& fb = mBoundReadFramebuffer;
1882
1883 if (fb) {
1884 if (!ValidateAndInitFB(funcName, fb)) return false;
1885 if (!fb->ValidateForColorRead(funcName, out_format, out_width, out_height))
1886 return false;
1887
1888 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb->mGLName);
1889 return true;
1890 }
1891
1892 if (!BindDefaultFBForRead(funcName)) return false;
1893
1894 if (mDefaultFB_ReadBuffer == LOCAL_GL_NONE) {
1895 ErrorInvalidOperation(
1896 "%s: Can't read from backbuffer when readBuffer mode is"
1897 " NONE.",
1898 funcName);
1899 return false;
1900 }
1901
1902 auto effFormat = mOptions.alpha ? webgl::EffectiveFormat::RGBA8
1903 : webgl::EffectiveFormat::RGB8;
1904
1905 *out_format = mFormatUsage->GetUsage(effFormat);
1906 MOZ_ASSERT(*out_format);
1907
1908 *out_width = mDefaultFB->mSize.width;
1909 *out_height = mDefaultFB->mSize.height;
1910 return true;
1911 }
1912
BindDefaultFBForRead(const char * const funcName)1913 bool WebGLContext::BindDefaultFBForRead(const char* const funcName) {
1914 if (!ValidateAndInitFB(funcName, nullptr)) return false;
1915
1916 if (!mDefaultFB->mSamples) {
1917 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
1918 return true;
1919 }
1920
1921 if (!mResolvedDefaultFB) {
1922 mResolvedDefaultFB =
1923 MozFramebuffer::Create(gl, mDefaultFB->mSize, 0, false);
1924 if (!mResolvedDefaultFB) {
1925 gfxCriticalNote << funcName << ": Failed to create mResolvedDefaultFB.";
1926 return false;
1927 }
1928 }
1929
1930 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB);
1931 BlitBackbufferToCurDriverFB();
1932
1933 gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mResolvedDefaultFB->mFB);
1934 return true;
1935 }
1936
DoColorMask(const uint8_t bitmask) const1937 void WebGLContext::DoColorMask(const uint8_t bitmask) const {
1938 if (mDriverColorMask != bitmask) {
1939 mDriverColorMask = bitmask;
1940 gl->fColorMask(
1941 bool(mDriverColorMask & (1 << 0)), bool(mDriverColorMask & (1 << 1)),
1942 bool(mDriverColorMask & (1 << 2)), bool(mDriverColorMask & (1 << 3)));
1943 }
1944 }
1945
1946 ////////////////////////////////////////////////////////////////////////////////
1947
ScopedDrawCallWrapper(WebGLContext & webgl)1948 ScopedDrawCallWrapper::ScopedDrawCallWrapper(WebGLContext& webgl)
1949 : mWebGL(webgl) {
1950 uint8_t driverColorMask = mWebGL.mColorWriteMask;
1951 bool driverDepthTest = mWebGL.mDepthTestEnabled;
1952 bool driverStencilTest = mWebGL.mStencilTestEnabled;
1953 const auto& fb = mWebGL.mBoundDrawFramebuffer;
1954 if (!fb) {
1955 if (mWebGL.mDefaultFB_DrawBuffer0 == LOCAL_GL_NONE) {
1956 driverColorMask = 0; // Is this well-optimized enough for depth-first
1957 // rendering?
1958 } else {
1959 driverColorMask &= ~(uint8_t(mWebGL.mNeedsFakeNoAlpha) << 3);
1960 }
1961 driverDepthTest &= !mWebGL.mNeedsFakeNoDepth;
1962 driverStencilTest &= !mWebGL.mNeedsFakeNoStencil;
1963 } else {
1964 if (mWebGL.mNeedsFakeNoStencil_UserFBs &&
1965 fb->DepthAttachment().IsDefined() &&
1966 !fb->StencilAttachment().IsDefined()) {
1967 driverStencilTest = false;
1968 }
1969 }
1970
1971 const auto& gl = mWebGL.gl;
1972 mWebGL.DoColorMask(driverColorMask);
1973 if (mWebGL.mDriverDepthTest != driverDepthTest) {
1974 // "When disabled, the depth comparison and subsequent possible updates to
1975 // the
1976 // depth buffer value are bypassed and the fragment is passed to the next
1977 // operation." [GLES 3.0.5, p177]
1978 mWebGL.mDriverDepthTest = driverDepthTest;
1979 gl->SetEnabled(LOCAL_GL_DEPTH_TEST, mWebGL.mDriverDepthTest);
1980 }
1981 if (mWebGL.mDriverStencilTest != driverStencilTest) {
1982 // "When disabled, the stencil test and associated modifications are not
1983 // made, and
1984 // the fragment is always passed." [GLES 3.0.5, p175]
1985 mWebGL.mDriverStencilTest = driverStencilTest;
1986 gl->SetEnabled(LOCAL_GL_STENCIL_TEST, mWebGL.mDriverStencilTest);
1987 }
1988 }
1989
~ScopedDrawCallWrapper()1990 ScopedDrawCallWrapper::~ScopedDrawCallWrapper() {
1991 if (mWebGL.mBoundDrawFramebuffer) return;
1992
1993 mWebGL.mResolvedDefaultFB = nullptr;
1994
1995 mWebGL.Invalidate();
1996 mWebGL.mShouldPresent = true;
1997 }
1998
1999 ////////////////////////////////////////
2000
IndexedBufferBinding()2001 IndexedBufferBinding::IndexedBufferBinding() : mRangeStart(0), mRangeSize(0) {}
2002
ByteCount() const2003 uint64_t IndexedBufferBinding::ByteCount() const {
2004 if (!mBufferBinding) return 0;
2005
2006 uint64_t bufferSize = mBufferBinding->ByteLength();
2007 if (!mRangeSize) // BindBufferBase
2008 return bufferSize;
2009
2010 if (mRangeStart >= bufferSize) return 0;
2011 bufferSize -= mRangeStart;
2012
2013 return std::min(bufferSize, mRangeSize);
2014 }
2015
2016 ////////////////////////////////////////
2017
ScopedUnpackReset(WebGLContext * webgl)2018 ScopedUnpackReset::ScopedUnpackReset(WebGLContext* webgl)
2019 : ScopedGLWrapper<ScopedUnpackReset>(webgl->gl), mWebGL(webgl) {
2020 // clang-format off
2021 if (mWebGL->mPixelStore_UnpackAlignment != 4) mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
2022
2023 if (mWebGL->IsWebGL2()) {
2024 if (mWebGL->mPixelStore_UnpackRowLength != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH , 0);
2025 if (mWebGL->mPixelStore_UnpackImageHeight != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, 0);
2026 if (mWebGL->mPixelStore_UnpackSkipPixels != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS , 0);
2027 if (mWebGL->mPixelStore_UnpackSkipRows != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS , 0);
2028 if (mWebGL->mPixelStore_UnpackSkipImages != 0) mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES , 0);
2029
2030 if (mWebGL->mBoundPixelUnpackBuffer) mGL->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
2031 }
2032 // clang-format on
2033 }
2034
UnwrapImpl()2035 void ScopedUnpackReset::UnwrapImpl() {
2036 // clang-format off
2037 mGL->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, mWebGL->mPixelStore_UnpackAlignment);
2038
2039 if (mWebGL->IsWebGL2()) {
2040 mGL->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH , mWebGL->mPixelStore_UnpackRowLength );
2041 mGL->fPixelStorei(LOCAL_GL_UNPACK_IMAGE_HEIGHT, mWebGL->mPixelStore_UnpackImageHeight);
2042 mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_PIXELS , mWebGL->mPixelStore_UnpackSkipPixels );
2043 mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_ROWS , mWebGL->mPixelStore_UnpackSkipRows );
2044 mGL->fPixelStorei(LOCAL_GL_UNPACK_SKIP_IMAGES , mWebGL->mPixelStore_UnpackSkipImages );
2045
2046 GLuint pbo = 0;
2047 if (mWebGL->mBoundPixelUnpackBuffer) {
2048 pbo = mWebGL->mBoundPixelUnpackBuffer->mGLName;
2049 }
2050
2051 mGL->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, pbo);
2052 }
2053 // clang-format on
2054 }
2055
2056 ////////////////////
2057
UnwrapImpl()2058 void ScopedFBRebinder::UnwrapImpl() {
2059 const auto fnName = [&](WebGLFramebuffer* fb) {
2060 return fb ? fb->mGLName : 0;
2061 };
2062
2063 if (mWebGL->IsWebGL2()) {
2064 mGL->fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER,
2065 fnName(mWebGL->mBoundDrawFramebuffer));
2066 mGL->fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER,
2067 fnName(mWebGL->mBoundReadFramebuffer));
2068 } else {
2069 MOZ_ASSERT(mWebGL->mBoundDrawFramebuffer == mWebGL->mBoundReadFramebuffer);
2070 mGL->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER,
2071 fnName(mWebGL->mBoundDrawFramebuffer));
2072 }
2073 }
2074
2075 ////////////////////
2076
TargetIfLazy(GLenum target)2077 static GLenum TargetIfLazy(GLenum target) {
2078 switch (target) {
2079 case LOCAL_GL_PIXEL_PACK_BUFFER:
2080 case LOCAL_GL_PIXEL_UNPACK_BUFFER:
2081 return target;
2082
2083 default:
2084 return 0;
2085 }
2086 }
2087
ScopedLazyBind(gl::GLContext * gl,GLenum target,const WebGLBuffer * buf)2088 ScopedLazyBind::ScopedLazyBind(gl::GLContext* gl, GLenum target,
2089 const WebGLBuffer* buf)
2090 : ScopedGLWrapper<ScopedLazyBind>(gl),
2091 mTarget(buf ? TargetIfLazy(target) : 0),
2092 mBuf(buf) {
2093 if (mTarget) {
2094 mGL->fBindBuffer(mTarget, mBuf->mGLName);
2095 }
2096 }
2097
UnwrapImpl()2098 void ScopedLazyBind::UnwrapImpl() {
2099 if (mTarget) {
2100 mGL->fBindBuffer(mTarget, 0);
2101 }
2102 }
2103
2104 ////////////////////////////////////////
2105
Intersect(const int32_t srcSize,const int32_t read0,const int32_t readSize,int32_t * const out_intRead0,int32_t * const out_intWrite0,int32_t * const out_intSize)2106 bool Intersect(const int32_t srcSize, const int32_t read0,
2107 const int32_t readSize, int32_t* const out_intRead0,
2108 int32_t* const out_intWrite0, int32_t* const out_intSize) {
2109 MOZ_ASSERT(srcSize >= 0);
2110 MOZ_ASSERT(readSize >= 0);
2111 const auto read1 = int64_t(read0) + readSize;
2112
2113 int32_t intRead0 = read0; // Clearly doesn't need validation.
2114 int64_t intWrite0 = 0;
2115 int64_t intSize = readSize;
2116
2117 if (read1 <= 0 || read0 >= srcSize) {
2118 // Disjoint ranges.
2119 intSize = 0;
2120 } else {
2121 if (read0 < 0) {
2122 const auto diff = int64_t(0) - read0;
2123 MOZ_ASSERT(diff >= 0);
2124 intRead0 = 0;
2125 intWrite0 = diff;
2126 intSize -= diff;
2127 }
2128 if (read1 > srcSize) {
2129 const auto diff = int64_t(read1) - srcSize;
2130 MOZ_ASSERT(diff >= 0);
2131 intSize -= diff;
2132 }
2133
2134 if (!CheckedInt<int32_t>(intWrite0).isValid() ||
2135 !CheckedInt<int32_t>(intSize).isValid()) {
2136 return false;
2137 }
2138 }
2139
2140 *out_intRead0 = intRead0;
2141 *out_intWrite0 = intWrite0;
2142 *out_intSize = intSize;
2143 return true;
2144 }
2145
2146 // --
2147
AvailGroups(const uint64_t totalAvailItems,const uint64_t firstItemOffset,const uint32_t groupSize,const uint32_t groupStride)2148 uint64_t AvailGroups(const uint64_t totalAvailItems,
2149 const uint64_t firstItemOffset, const uint32_t groupSize,
2150 const uint32_t groupStride) {
2151 MOZ_ASSERT(groupSize && groupStride);
2152 MOZ_ASSERT(groupSize <= groupStride);
2153
2154 if (totalAvailItems <= firstItemOffset) return 0;
2155 const size_t availItems = totalAvailItems - firstItemOffset;
2156
2157 size_t availGroups = availItems / groupStride;
2158 const size_t tailItems = availItems % groupStride;
2159 if (tailItems >= groupSize) {
2160 availGroups += 1;
2161 }
2162 return availGroups;
2163 }
2164
2165 ////////////////////////////////////////////////////////////////////////////////
2166
GetUnpackSize(bool isFunc3D,uint32_t width,uint32_t height,uint32_t depth,uint8_t bytesPerPixel)2167 CheckedUint32 WebGLContext::GetUnpackSize(bool isFunc3D, uint32_t width,
2168 uint32_t height, uint32_t depth,
2169 uint8_t bytesPerPixel) {
2170 if (!width || !height || !depth) return 0;
2171
2172 ////////////////
2173
2174 const auto& maybeRowLength = mPixelStore_UnpackRowLength;
2175 const auto& maybeImageHeight = mPixelStore_UnpackImageHeight;
2176
2177 const auto usedPixelsPerRow =
2178 CheckedUint32(mPixelStore_UnpackSkipPixels) + width;
2179 const auto stridePixelsPerRow =
2180 (maybeRowLength ? CheckedUint32(maybeRowLength) : usedPixelsPerRow);
2181
2182 const auto usedRowsPerImage =
2183 CheckedUint32(mPixelStore_UnpackSkipRows) + height;
2184 const auto strideRowsPerImage =
2185 (maybeImageHeight ? CheckedUint32(maybeImageHeight) : usedRowsPerImage);
2186
2187 const uint32_t skipImages = (isFunc3D ? mPixelStore_UnpackSkipImages : 0);
2188 const CheckedUint32 usedImages = CheckedUint32(skipImages) + depth;
2189
2190 ////////////////
2191
2192 CheckedUint32 strideBytesPerRow = bytesPerPixel * stridePixelsPerRow;
2193 strideBytesPerRow =
2194 RoundUpToMultipleOf(strideBytesPerRow, mPixelStore_UnpackAlignment);
2195
2196 const CheckedUint32 strideBytesPerImage =
2197 strideBytesPerRow * strideRowsPerImage;
2198
2199 ////////////////
2200
2201 CheckedUint32 usedBytesPerRow = bytesPerPixel * usedPixelsPerRow;
2202 // Don't round this to the alignment, since alignment here is really just used
2203 // for establishing stride, particularly in WebGL 1, where you can't set
2204 // ROW_LENGTH.
2205
2206 CheckedUint32 totalBytes = strideBytesPerImage * (usedImages - 1);
2207 totalBytes += strideBytesPerRow * (usedRowsPerImage - 1);
2208 totalBytes += usedBytesPerRow;
2209
2210 return totalBytes;
2211 }
2212
2213 already_AddRefed<layers::SharedSurfaceTextureClient>
GetVRFrame()2214 WebGLContext::GetVRFrame() {
2215 /**
2216 * Swap buffers as though composition has occurred.
2217 * We will then share the resulting front buffer to be submitted to the VR
2218 * compositor.
2219 */
2220 BeginComposition();
2221 EndComposition();
2222
2223 gl::GLScreenBuffer* screen = gl->Screen();
2224 if (!screen) {
2225 return nullptr;
2226 }
2227
2228 RefPtr<SharedSurfaceTextureClient> sharedSurface = screen->Front();
2229 if (!sharedSurface) {
2230 return nullptr;
2231 }
2232
2233 return sharedSurface.forget();
2234 }
2235
2236 ////////////////////////////////////////////////////////////////////////////////
2237
SizeOfViewElem(const dom::ArrayBufferView & view)2238 static inline size_t SizeOfViewElem(const dom::ArrayBufferView& view) {
2239 const auto& elemType = view.Type();
2240 if (elemType == js::Scalar::MaxTypedArrayViewType) // DataViews.
2241 return 1;
2242
2243 return js::Scalar::byteSize(elemType);
2244 }
2245
ValidateArrayBufferView(const char * funcName,const dom::ArrayBufferView & view,GLuint elemOffset,GLuint elemCountOverride,uint8_t ** const out_bytes,size_t * const out_byteLen)2246 bool WebGLContext::ValidateArrayBufferView(const char* funcName,
2247 const dom::ArrayBufferView& view,
2248 GLuint elemOffset,
2249 GLuint elemCountOverride,
2250 uint8_t** const out_bytes,
2251 size_t* const out_byteLen) {
2252 view.ComputeLengthAndData();
2253 uint8_t* const bytes = view.DataAllowShared();
2254 const size_t byteLen = view.LengthAllowShared();
2255
2256 const auto& elemSize = SizeOfViewElem(view);
2257
2258 size_t elemCount = byteLen / elemSize;
2259 if (elemOffset > elemCount) {
2260 ErrorInvalidValue("%s: Invalid offset into ArrayBufferView.", funcName);
2261 return false;
2262 }
2263 elemCount -= elemOffset;
2264
2265 if (elemCountOverride) {
2266 if (elemCountOverride > elemCount) {
2267 ErrorInvalidValue("%s: Invalid sub-length for ArrayBufferView.",
2268 funcName);
2269 return false;
2270 }
2271 elemCount = elemCountOverride;
2272 }
2273
2274 *out_bytes = bytes + (elemOffset * elemSize);
2275 *out_byteLen = elemCount * elemSize;
2276 return true;
2277 }
2278
2279 ////
2280
UpdateMaxDrawBuffers()2281 void WebGLContext::UpdateMaxDrawBuffers() {
2282 mGLMaxColorAttachments =
2283 gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_COLOR_ATTACHMENTS);
2284 mGLMaxDrawBuffers = gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_DRAW_BUFFERS);
2285
2286 // WEBGL_draw_buffers:
2287 // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater
2288 // than or
2289 // equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter."
2290 mGLMaxDrawBuffers = std::min(mGLMaxDrawBuffers, mGLMaxColorAttachments);
2291 }
2292
2293 // --
2294
EnsureAvailabilityRunnable()2295 webgl::AvailabilityRunnable* WebGLContext::EnsureAvailabilityRunnable() {
2296 if (!mAvailabilityRunnable) {
2297 RefPtr<webgl::AvailabilityRunnable> runnable =
2298 new webgl::AvailabilityRunnable(this);
2299
2300 nsIDocument* document = GetOwnerDoc();
2301 if (document) {
2302 document->Dispatch(TaskCategory::Other, runnable.forget());
2303 } else {
2304 NS_DispatchToCurrentThread(runnable.forget());
2305 }
2306 }
2307 return mAvailabilityRunnable;
2308 }
2309
AvailabilityRunnable(WebGLContext * const webgl)2310 webgl::AvailabilityRunnable::AvailabilityRunnable(WebGLContext* const webgl)
2311 : Runnable("webgl::AvailabilityRunnable"), mWebGL(webgl) {
2312 mWebGL->mAvailabilityRunnable = this;
2313 }
2314
~AvailabilityRunnable()2315 webgl::AvailabilityRunnable::~AvailabilityRunnable() {
2316 MOZ_ASSERT(mQueries.empty());
2317 MOZ_ASSERT(mSyncs.empty());
2318 }
2319
Run()2320 nsresult webgl::AvailabilityRunnable::Run() {
2321 for (const auto& cur : mQueries) {
2322 cur->mCanBeAvailable = true;
2323 }
2324 mQueries.clear();
2325
2326 for (const auto& cur : mSyncs) {
2327 cur->mCanBeAvailable = true;
2328 }
2329 mSyncs.clear();
2330
2331 mWebGL->mAvailabilityRunnable = nullptr;
2332 return NS_OK;
2333 }
2334
2335 ////////////////////////////////////////////////////////////////////////////////
2336 // XPCOM goop
2337
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback & callback,const std::vector<IndexedBufferBinding> & field,const char * name,uint32_t flags)2338 void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
2339 const std::vector<IndexedBufferBinding>& field,
2340 const char* name, uint32_t flags) {
2341 for (const auto& cur : field) {
2342 ImplCycleCollectionTraverse(callback, cur.mBufferBinding, name, flags);
2343 }
2344 }
2345
ImplCycleCollectionUnlink(std::vector<IndexedBufferBinding> & field)2346 void ImplCycleCollectionUnlink(std::vector<IndexedBufferBinding>& field) {
2347 field.clear();
2348 }
2349
2350 ////
2351
2352 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext)
2353 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
2354
2355 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(
2356 WebGLContext, mCanvasElement, mOffscreenCanvas, mExtensions,
2357 mBound2DTextures, mBoundCubeMapTextures, mBound3DTextures,
2358 mBound2DArrayTextures, mBoundSamplers, mBoundArrayBuffer,
2359 mBoundCopyReadBuffer, mBoundCopyWriteBuffer, mBoundPixelPackBuffer,
2360 mBoundPixelUnpackBuffer, mBoundTransformFeedback, mBoundUniformBuffer,
2361 mCurrentProgram, mBoundDrawFramebuffer, mBoundReadFramebuffer,
2362 mBoundRenderbuffer, mBoundVertexArray, mDefaultVertexArray,
2363 mQuerySlot_SamplesPassed, mQuerySlot_TFPrimsWritten, mQuerySlot_TimeElapsed)
2364
2365 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLContext)
2366 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
2367 NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
2368 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
2369 // If the exact way we cast to nsISupports here ever changes, fix our
2370 // ToSupports() method.
2371 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports,
2372 nsICanvasRenderingContextInternal)
2373 NS_INTERFACE_MAP_END
2374
2375 } // namespace mozilla
2376