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