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