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/. */
6 #include "WebGLContext.h"
8 #include "WebGLContextLossHandler.h"
9 #include "WebGL1Context.h"
10 #include "WebGLObjectModel.h"
11 #include "WebGLExtensions.h"
12 #include "WebGLContextUtils.h"
13 #include "WebGLBuffer.h"
14 #include "WebGLVertexAttribData.h"
15 #include "WebGLMemoryTracker.h"
16 #include "WebGLFramebuffer.h"
17 #include "WebGLVertexArray.h"
18 #include "WebGLQuery.h"
20 #include "GLBlitHelper.h"
21 #include "AccessCheck.h"
22 #include "nsIConsoleService.h"
23 #include "nsServiceManagerUtils.h"
24 #include "nsIClassInfoImpl.h"
25 #include "nsContentUtils.h"
26 #include "nsIXPConnect.h"
28 #include "nsIGfxInfo.h"
29 #include "nsIWidget.h"
31 #include "nsIVariant.h"
33 #include "ImageEncoder.h"
34 #include "ImageContainer.h"
36 #include "gfxContext.h"
37 #include "gfxPattern.h"
41 #include "CanvasUtils.h"
42 #include "nsDisplayList.h"
44 #include "GLContextProvider.h"
45 #include "GLContext.h"
46 #include "ScopedGLHelpers.h"
47 #include "GLReadTexImageHelper.h"
49 #include "gfxCrashReporterUtils.h"
51 #include "nsSVGEffects.h"
55 #include "mozilla/Preferences.h"
56 #include "mozilla/Services.h"
57 #include "mozilla/Telemetry.h"
59 #include "nsIObserverService.h"
60 #include "nsIDOMEvent.h"
61 #include "mozilla/Services.h"
62 #include "mozilla/dom/WebGLRenderingContextBinding.h"
63 #include "mozilla/dom/BindingUtils.h"
64 #include "mozilla/dom/HTMLVideoElement.h"
65 #include "mozilla/dom/ImageData.h"
66 #include "mozilla/ProcessPriorityManager.h"
67 #include "mozilla/EnumeratedArrayCycleCollection.h"
71 #ifdef MOZ_WIDGET_GONK
72 #include "mozilla/layers/ShadowLayers.h"
77 using namespace mozilla
;
78 using namespace mozilla::dom
;
79 using namespace mozilla::gfx
;
80 using namespace mozilla::gl
;
81 using namespace mozilla::layers
;
83 WebGLObserver::WebGLObserver(WebGLContext
* aContext
)
88 WebGLObserver::~WebGLObserver()
93 WebGLObserver::Destroy()
95 UnregisterMemoryPressureEvent();
96 UnregisterVisibilityChangeEvent();
101 WebGLObserver::RegisterVisibilityChangeEvent()
107 HTMLCanvasElement
* canvasElement
= mContext
->GetCanvas();
109 MOZ_ASSERT(canvasElement
);
112 nsIDocument
* document
= canvasElement
->OwnerDoc();
114 document
->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
122 WebGLObserver::UnregisterVisibilityChangeEvent()
128 HTMLCanvasElement
* canvasElement
= mContext
->GetCanvas();
131 nsIDocument
* document
= canvasElement
->OwnerDoc();
133 document
->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
140 WebGLObserver::RegisterMemoryPressureEvent()
146 nsCOMPtr
<nsIObserverService
> observerService
=
147 mozilla::services::GetObserverService();
149 MOZ_ASSERT(observerService
);
151 if (observerService
) {
152 observerService
->AddObserver(this, "memory-pressure", false);
157 WebGLObserver::UnregisterMemoryPressureEvent()
163 nsCOMPtr
<nsIObserverService
> observerService
=
164 mozilla::services::GetObserverService();
166 // Do not assert on observerService here. This might be triggered by
167 // the cycle collector at a late enough time, that XPCOM services are
168 // no longer available. See bug 1029504.
169 if (observerService
) {
170 observerService
->RemoveObserver(this, "memory-pressure");
175 WebGLObserver::Observe(nsISupports
* aSubject
,
177 const char16_t
* aSomeData
)
179 if (!mContext
|| strcmp(aTopic
, "memory-pressure")) {
183 bool wantToLoseContext
= mContext
->mLoseContextOnMemoryPressure
;
185 if (!mContext
->mCanLoseContextInForeground
&&
186 ProcessPriorityManager::CurrentProcessIsForeground())
188 wantToLoseContext
= false;
191 if (wantToLoseContext
) {
192 mContext
->ForceLoseContext();
199 WebGLObserver::HandleEvent(nsIDOMEvent
* aEvent
)
202 aEvent
->GetType(type
);
203 if (!mContext
|| !type
.EqualsLiteral("visibilitychange")) {
207 HTMLCanvasElement
* canvasElement
= mContext
->GetCanvas();
209 MOZ_ASSERT(canvasElement
);
211 if (canvasElement
&& !canvasElement
->OwnerDoc()->Hidden()) {
212 mContext
->ForceRestoreContext();
218 WebGLContextOptions::WebGLContextOptions()
219 : alpha(true), depth(true), stencil(false),
220 premultipliedAlpha(true), antialias(true),
221 preserveDrawingBuffer(false)
223 // Set default alpha state based on preference.
224 if (Preferences::GetBool("webgl.default-no-alpha", false))
228 WebGLContext::WebGLContext()
234 mInvalidated
= false;
235 mShouldPresent
= true;
237 mOptionsFrozen
= false;
240 mPixelStoreFlipY
= false;
241 mPixelStorePremultiplyAlpha
= false;
242 mPixelStoreColorspaceConversion
= BROWSER_DEFAULT_WEBGL
;
244 mShaderValidation
= true;
246 mFakeBlackStatus
= WebGLContextFakeBlackStatus::NotNeeded
;
248 mVertexAttrib0Vector
[0] = 0;
249 mVertexAttrib0Vector
[1] = 0;
250 mVertexAttrib0Vector
[2] = 0;
251 mVertexAttrib0Vector
[3] = 1;
252 mFakeVertexAttrib0BufferObjectVector
[0] = 0;
253 mFakeVertexAttrib0BufferObjectVector
[1] = 0;
254 mFakeVertexAttrib0BufferObjectVector
[2] = 0;
255 mFakeVertexAttrib0BufferObjectVector
[3] = 1;
256 mFakeVertexAttrib0BufferObjectSize
= 0;
257 mFakeVertexAttrib0BufferObject
= 0;
258 mFakeVertexAttrib0BufferStatus
= WebGLVertexAttrib0Status::Default
;
265 mScissorTestEnabled
= 0;
267 mRasterizerDiscardEnabled
= 0; // OpenGL ES 3.0 spec p244
269 // initialize some GL values: we're going to get them from the GL and use them as the sizes of arrays,
270 // so in case glGetIntegerv leaves them uninitialized because of a GL bug, we would have very weird crashes.
271 mGLMaxVertexAttribs
= 0;
272 mGLMaxTextureUnits
= 0;
273 mGLMaxTextureSize
= 0;
274 mGLMaxCubeMapTextureSize
= 0;
275 mGLMaxRenderbufferSize
= 0;
276 mGLMaxTextureImageUnits
= 0;
277 mGLMaxVertexTextureImageUnits
= 0;
278 mGLMaxVaryingVectors
= 0;
279 mGLMaxFragmentUniformVectors
= 0;
280 mGLMaxVertexUniformVectors
= 0;
281 mGLMaxColorAttachments
= 1;
282 mGLMaxDrawBuffers
= 1;
283 mGLMaxTransformFeedbackSeparateAttribs
= 0;
285 // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13
286 mPixelStorePackAlignment
= 4;
287 mPixelStoreUnpackAlignment
= 4;
289 WebGLMemoryTracker::AddWebGLContext(this);
291 mAllowContextRestore
= true;
292 mLastLossWasSimulated
= false;
293 mContextLossHandler
= new WebGLContextLossHandler(this);
294 mContextStatus
= ContextNotLost
;
295 mLoseContextOnMemoryPressure
= false;
296 mCanLoseContextInForeground
= true;
297 mRestoreWhenVisible
= false;
299 mAlreadyGeneratedWarnings
= 0;
300 mAlreadyWarnedAboutFakeVertexAttrib0
= false;
301 mAlreadyWarnedAboutViewportLargerThanDest
= false;
302 mMaxWarnings
= Preferences::GetInt("webgl.max-warnings-per-context", 32);
303 if (mMaxWarnings
< -1)
305 GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
309 mContextObserver
= new WebGLObserver(this);
310 MOZ_RELEASE_ASSERT(mContextObserver
, "Can't alloc WebGLContextObserver");
314 InvalidateBufferFetching();
316 mBackbufferNeedsClear
= true;
318 mDisableFragHighP
= false;
320 mDrawCallsSinceLastFlush
= 0;
323 WebGLContext::~WebGLContext()
325 mContextObserver
->Destroy();
327 DestroyResourcesAndContext();
328 WebGLMemoryTracker::RemoveWebGLContext(this);
330 mContextLossHandler
->DisableTimer();
331 mContextLossHandler
= nullptr;
335 WebGLContext::DestroyResourcesAndContext()
337 mContextObserver
->UnregisterMemoryPressureEvent();
344 mBound2DTextures
.Clear();
345 mBoundCubeMapTextures
.Clear();
346 mBoundArrayBuffer
= nullptr;
347 mBoundTransformFeedbackBuffer
= nullptr;
348 mCurrentProgram
= nullptr;
349 mBoundFramebuffer
= nullptr;
350 mActiveOcclusionQuery
= nullptr;
351 mBoundRenderbuffer
= nullptr;
352 mBoundVertexArray
= nullptr;
353 mDefaultVertexArray
= nullptr;
355 while (!mTextures
.isEmpty())
356 mTextures
.getLast()->DeleteOnce();
357 while (!mVertexArrays
.isEmpty())
358 mVertexArrays
.getLast()->DeleteOnce();
359 while (!mBuffers
.isEmpty())
360 mBuffers
.getLast()->DeleteOnce();
361 while (!mRenderbuffers
.isEmpty())
362 mRenderbuffers
.getLast()->DeleteOnce();
363 while (!mFramebuffers
.isEmpty())
364 mFramebuffers
.getLast()->DeleteOnce();
365 while (!mShaders
.isEmpty())
366 mShaders
.getLast()->DeleteOnce();
367 while (!mPrograms
.isEmpty())
368 mPrograms
.getLast()->DeleteOnce();
369 while (!mQueries
.isEmpty())
370 mQueries
.getLast()->DeleteOnce();
372 mBlackOpaqueTexture2D
= nullptr;
373 mBlackOpaqueTextureCubeMap
= nullptr;
374 mBlackTransparentTexture2D
= nullptr;
375 mBlackTransparentTextureCubeMap
= nullptr;
377 if (mFakeVertexAttrib0BufferObject
) {
378 gl
->fDeleteBuffers(1, &mFakeVertexAttrib0BufferObject
);
381 // disable all extensions except "WEBGL_lose_context". see bug #927969
382 // spec: http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
383 for (size_t i
= 0; i
< size_t(WebGLExtensionID::Max
); ++i
) {
384 WebGLExtensionID extension
= WebGLExtensionID(i
);
386 if (!IsExtensionEnabled(extension
) || (extension
== WebGLExtensionID::WEBGL_lose_context
))
389 mExtensions
[extension
]->MarkLost();
390 mExtensions
[extension
] = nullptr;
393 // We just got rid of everything, so the context had better
394 // have been going away.
396 if (gl
->DebugMode()) {
397 printf_stderr("--- WebGL context destroyed: %p\n", gl
.get());
405 WebGLContext::Invalidate()
413 nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement
);
416 mCanvasElement
->InvalidateCanvasContent(nullptr);
420 // nsICanvasRenderingContextInternal
424 WebGLContext::SetContextOptions(JSContext
* aCx
, JS::Handle
<JS::Value
> aOptions
)
426 if (aOptions
.isNullOrUndefined() && mOptionsFrozen
) {
430 WebGLContextAttributes attributes
;
431 NS_ENSURE_TRUE(attributes
.Init(aCx
, aOptions
), NS_ERROR_UNEXPECTED
);
433 WebGLContextOptions newOpts
;
435 newOpts
.stencil
= attributes
.mStencil
;
436 newOpts
.depth
= attributes
.mDepth
;
437 newOpts
.premultipliedAlpha
= attributes
.mPremultipliedAlpha
;
438 newOpts
.antialias
= attributes
.mAntialias
;
439 newOpts
.preserveDrawingBuffer
= attributes
.mPreserveDrawingBuffer
;
441 if (attributes
.mAlpha
.WasPassed()) {
442 newOpts
.alpha
= attributes
.mAlpha
.Value();
445 // Don't do antialiasing if we've disabled MSAA.
446 if (!gfxPrefs::MSAALevel()) {
447 newOpts
.antialias
= false;
451 GenerateWarning("aaHint: %d stencil: %d depth: %d alpha: %d premult: %d preserve: %d\n",
452 newOpts
.antialias
? 1 : 0,
453 newOpts
.stencil
? 1 : 0,
454 newOpts
.depth
? 1 : 0,
455 newOpts
.alpha
? 1 : 0,
456 newOpts
.premultipliedAlpha
? 1 : 0,
457 newOpts
.preserveDrawingBuffer
? 1 : 0);
460 if (mOptionsFrozen
&& newOpts
!= mOptions
) {
461 // Error if the options are already frozen, and the ones that were asked for
462 // aren't the same as what they were originally.
463 return NS_ERROR_FAILURE
;
472 WebGLContext::GetWidth() const
478 WebGLContext::GetHeight() const
484 /* So there are a number of points of failure here. We might fail based
485 * on EGL vs. WGL, or we might fail to alloc a too-large size, or we
486 * might not be able to create a context with a certain combo of context
489 * We don't want to test the complete fallback matrix. (for now, at
490 * least) Instead, attempt creation in this order:
491 * 1. By platform API. (e.g. EGL vs. WGL)
492 * 2. By context creation attribs.
495 * That is, try to create headless contexts based on the platform API.
496 * Next, create dummy-sized backbuffers for the contexts with the right
497 * caps. Finally, resize the backbuffer to an acceptable size given the
502 IsFeatureInBlacklist(const nsCOMPtr
<nsIGfxInfo
>& gfxInfo
, int32_t feature
)
505 if (!NS_SUCCEEDED(gfxInfo
->GetFeatureStatus(feature
, &status
)))
508 return status
!= nsIGfxInfo::FEATURE_STATUS_OK
;
511 static already_AddRefed
<GLContext
>
512 CreateHeadlessNativeGL(bool forceEnabled
,
513 const nsCOMPtr
<nsIGfxInfo
>& gfxInfo
,
517 IsFeatureInBlacklist(gfxInfo
, nsIGfxInfo::FEATURE_WEBGL_OPENGL
))
519 webgl
->GenerateWarning("Refused to create native OpenGL context"
520 " because of blacklisting.");
524 nsRefPtr
<GLContext
> gl
= gl::GLContextProvider::CreateHeadless();
526 webgl
->GenerateWarning("Error during native OpenGL init.");
529 MOZ_ASSERT(!gl
->IsANGLE());
534 // Note that we have a separate call for ANGLE and EGL, even though
535 // right now, we get ANGLE implicitly by using EGL on Windows.
536 // Eventually, we want to be able to pick ANGLE-EGL or native EGL.
537 static already_AddRefed
<GLContext
>
538 CreateHeadlessANGLE(bool forceEnabled
,
539 const nsCOMPtr
<nsIGfxInfo
>& gfxInfo
,
542 nsRefPtr
<GLContext
> gl
;
546 IsFeatureInBlacklist(gfxInfo
, nsIGfxInfo::FEATURE_WEBGL_ANGLE
))
548 webgl
->GenerateWarning("Refused to create ANGLE OpenGL context"
549 " because of blacklisting.");
553 gl
= gl::GLContextProviderEGL::CreateHeadless();
555 webgl
->GenerateWarning("Error during ANGLE OpenGL init.");
558 MOZ_ASSERT(gl
->IsANGLE());
564 static already_AddRefed
<GLContext
>
565 CreateHeadlessEGL(bool forceEnabled
,
566 const nsCOMPtr
<nsIGfxInfo
>& gfxInfo
,
569 nsRefPtr
<GLContext
> gl
;
572 gl
= gl::GLContextProviderEGL::CreateHeadless();
574 webgl
->GenerateWarning("Error during EGL OpenGL init.");
577 MOZ_ASSERT(!gl
->IsANGLE());
584 static already_AddRefed
<GLContext
>
585 CreateHeadlessGL(bool forceEnabled
,
586 const nsCOMPtr
<nsIGfxInfo
>& gfxInfo
,
589 bool preferEGL
= PR_GetEnv("MOZ_WEBGL_PREFER_EGL");
590 bool disableANGLE
= Preferences::GetBool("webgl.disable-angle", false);
592 if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL")) {
596 nsRefPtr
<GLContext
> gl
;
599 gl
= CreateHeadlessEGL(forceEnabled
, gfxInfo
, webgl
);
601 if (!gl
&& !disableANGLE
)
602 gl
= CreateHeadlessANGLE(forceEnabled
, gfxInfo
, webgl
);
605 gl
= CreateHeadlessNativeGL(forceEnabled
, gfxInfo
, webgl
);
610 // Try to create a dummy offscreen with the given caps.
612 CreateOffscreenWithCaps(GLContext
* gl
, const SurfaceCaps
& caps
)
614 gfx::IntSize
dummySize(16, 16);
615 return gl
->InitOffscreen(dummySize
, caps
);
619 PopulateCapFallbackQueue(const SurfaceCaps
& baseCaps
,
620 std::queue
<SurfaceCaps
>* fallbackCaps
)
622 fallbackCaps
->push(baseCaps
);
624 // Dropping antialias drops our quality, but not our correctness.
625 // The user basically doesn't have to handle if this fails, they
626 // just get reduced quality.
627 if (baseCaps
.antialias
) {
628 SurfaceCaps
nextCaps(baseCaps
);
629 nextCaps
.antialias
= false;
630 PopulateCapFallbackQueue(nextCaps
, fallbackCaps
);
633 // If we have to drop one of depth or stencil, we'd prefer to keep
634 // depth. However, the client app will need to handle if this
636 if (baseCaps
.stencil
) {
637 SurfaceCaps
nextCaps(baseCaps
);
638 nextCaps
.stencil
= false;
639 PopulateCapFallbackQueue(nextCaps
, fallbackCaps
);
642 if (baseCaps
.depth
) {
643 SurfaceCaps
nextCaps(baseCaps
);
644 nextCaps
.depth
= false;
645 PopulateCapFallbackQueue(nextCaps
, fallbackCaps
);
650 CreateOffscreen(GLContext
* gl
,
651 const WebGLContextOptions
& options
,
652 const nsCOMPtr
<nsIGfxInfo
>& gfxInfo
,
654 layers::ISurfaceAllocator
* surfAllocator
)
656 SurfaceCaps baseCaps
;
658 baseCaps
.color
= true;
659 baseCaps
.alpha
= options
.alpha
;
660 baseCaps
.antialias
= options
.antialias
;
661 baseCaps
.depth
= options
.depth
;
662 baseCaps
.preserve
= options
.preserveDrawingBuffer
;
663 baseCaps
.stencil
= options
.stencil
;
665 // we should really have this behind a
666 // |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
667 // for now it's just behind a pref for testing/evaluation.
668 baseCaps
.bpp16
= Preferences::GetBool("webgl.prefer-16bpp", false);
670 #ifdef MOZ_WIDGET_GONK
671 baseCaps
.surfaceAllocator
= surfAllocator
;
674 // Done with baseCaps construction.
676 bool forceAllowAA
= Preferences::GetBool("webgl.msaa-force", false);
678 IsFeatureInBlacklist(gfxInfo
, nsIGfxInfo::FEATURE_WEBGL_MSAA
))
680 webgl
->GenerateWarning("Disallowing antialiased backbuffers due"
681 " to blacklisting.");
682 baseCaps
.antialias
= false;
685 std::queue
<SurfaceCaps
> fallbackCaps
;
686 PopulateCapFallbackQueue(baseCaps
, &fallbackCaps
);
688 bool created
= false;
689 while (!fallbackCaps
.empty()) {
690 SurfaceCaps
& caps
= fallbackCaps
.front();
692 created
= CreateOffscreenWithCaps(gl
, caps
);
703 WebGLContext::CreateOffscreenGL(bool forceEnabled
)
705 nsCOMPtr
<nsIGfxInfo
> gfxInfo
= do_GetService("@mozilla.org/gfx/info;1");
707 layers::ISurfaceAllocator
* surfAllocator
= nullptr;
708 #ifdef MOZ_WIDGET_GONK
709 nsIWidget
* docWidget
= nsContentUtils::WidgetForDocument(mCanvasElement
->OwnerDoc());
711 layers::LayerManager
* layerManager
= docWidget
->GetLayerManager();
713 // XXX we really want "AsSurfaceAllocator" here for generality
714 layers::ShadowLayerForwarder
* forwarder
= layerManager
->AsShadowForwarder();
716 surfAllocator
= static_cast<layers::ISurfaceAllocator
*>(forwarder
);
722 gl
= CreateHeadlessGL(forceEnabled
, gfxInfo
, this);
728 if (!CreateOffscreen(gl
, mOptions
, gfxInfo
, this, surfAllocator
))
731 if (!InitAndValidateGL())
741 // Fallback for resizes:
743 WebGLContext::ResizeBackbuffer(uint32_t requestedWidth
, uint32_t requestedHeight
)
745 uint32_t width
= requestedWidth
;
746 uint32_t height
= requestedHeight
;
748 bool resized
= false;
749 while (width
|| height
) {
750 width
= width
? width
: 1;
751 height
= height
? height
: 1;
753 gfx::IntSize
curSize(width
, height
);
754 if (gl
->ResizeOffscreen(curSize
)) {
766 mWidth
= gl
->OffscreenSize().width
;
767 mHeight
= gl
->OffscreenSize().height
;
768 MOZ_ASSERT((uint32_t)mWidth
== width
);
769 MOZ_ASSERT((uint32_t)mHeight
== height
);
771 if (width
!= requestedWidth
||
772 height
!= requestedHeight
)
774 GenerateWarning("Requested size %dx%d was too large, but resize"
775 " to %dx%d succeeded.",
776 requestedWidth
, requestedHeight
,
784 WebGLContext::SetDimensions(int32_t sWidth
, int32_t sHeight
)
786 // Early error return cases
788 return NS_ERROR_FAILURE
;
790 if (sWidth
< 0 || sHeight
< 0) {
791 GenerateWarning("Canvas size is too large (seems like a negative value wrapped)");
792 return NS_ERROR_OUT_OF_MEMORY
;
795 uint32_t width
= sWidth
;
796 uint32_t height
= sHeight
;
798 // Early success return cases
799 GetCanvas()->InvalidateCanvas();
801 // Zero-sized surfaces can cause problems.
809 // If we already have a gl context, then we just need to resize it
811 if ((uint32_t)mWidth
== width
&&
812 (uint32_t)mHeight
== height
)
820 MakeContextCurrent();
822 // If we've already drawn, we should commit the current buffer.
823 PresentScreenBuffer();
825 // ResizeOffscreen scraps the current prod buffer before making a new one.
826 if (!ResizeBackbuffer(width
, height
)) {
827 GenerateWarning("WebGL context failed to resize.");
832 // everything's good, we're done here
834 mBackbufferNeedsClear
= true;
839 // End of early return cases.
840 // At this point we know that we're not just resizing an existing context,
841 // we are initializing a new context.
843 // if we exceeded either the global or the per-principal limit for WebGL contexts,
844 // lose the oldest-used context now to free resources. Note that we can't do that
845 // in the WebGLContext constructor as we don't have a canvas element yet there.
846 // Here is the right place to do so, as we are about to create the OpenGL context
847 // and that is what can fail if we already have too many.
848 LoseOldestWebGLContextIfLimitExceeded();
850 // We're going to create an entirely new context. If our
851 // generation is not 0 right now (that is, if this isn't the first
852 // context we're creating), we may have to dispatch a context lost
855 // If incrementing the generation would cause overflow,
856 // don't allow it. Allowing this would allow us to use
857 // resource handles created from older context generations.
858 if (!(mGeneration
+ 1).isValid()) {
859 GenerateWarning("Too many WebGL contexts created this run.");
860 return NS_ERROR_FAILURE
; // exit without changing the value of mGeneration
863 // Get some prefs for some preferred/overriden things
864 NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE
);
866 bool disabled
= Preferences::GetBool("webgl.disabled", false);
868 GenerateWarning("WebGL creation is disabled, and so disallowed here.");
869 return NS_ERROR_FAILURE
;
872 // Alright, now let's start trying.
873 bool forceEnabled
= Preferences::GetBool("webgl.force-enabled", false);
874 ScopedGfxFeatureReporter
reporter("WebGL", forceEnabled
);
876 if (!CreateOffscreenGL(forceEnabled
)) {
877 GenerateWarning("WebGL creation failed.");
878 return NS_ERROR_FAILURE
;
882 if (!ResizeBackbuffer(width
, height
)) {
883 GenerateWarning("Initializing WebGL backbuffer failed.");
884 return NS_ERROR_FAILURE
;
888 if (gl
->DebugMode()) {
889 printf_stderr("--- WebGL context created: %p\n", gl
.get());
894 mOptionsFrozen
= true;
896 // increment the generation number
899 MakeContextCurrent();
901 gl
->fViewport(0, 0, mWidth
, mHeight
);
902 mViewportWidth
= mWidth
;
903 mViewportHeight
= mHeight
;
905 // Make sure that we clear this out, otherwise
906 // we'll end up displaying random memory
907 gl
->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER
, 0);
909 AssertCachedBindings();
912 // Clear immediately, because we need to present the cleared initial
914 mBackbufferNeedsClear
= true;
915 ClearBackbufferIfNeeded();
917 mShouldPresent
= true;
919 MOZ_ASSERT(gl
->Caps().color
);
920 MOZ_ASSERT(gl
->Caps().alpha
== mOptions
.alpha
);
921 MOZ_ASSERT(gl
->Caps().depth
== mOptions
.depth
|| !gl
->Caps().depth
);
922 MOZ_ASSERT(gl
->Caps().stencil
== mOptions
.stencil
|| !gl
->Caps().stencil
);
923 MOZ_ASSERT(gl
->Caps().antialias
== mOptions
.antialias
|| !gl
->Caps().antialias
);
924 MOZ_ASSERT(gl
->Caps().preserve
== mOptions
.preserveDrawingBuffer
);
926 AssertCachedBindings();
929 reporter
.SetSuccessful();
934 WebGLContext::ClearBackbufferIfNeeded()
936 if (!mBackbufferNeedsClear
)
943 gl
->GetUIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING
, &fb
);
949 mBackbufferNeedsClear
= false;
952 void WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
954 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
955 // some mobile devices can't have more than 8 GL contexts overall
956 const size_t kMaxWebGLContextsPerPrincipal
= 2;
957 const size_t kMaxWebGLContexts
= 4;
959 const size_t kMaxWebGLContextsPerPrincipal
= 16;
960 const size_t kMaxWebGLContexts
= 32;
962 MOZ_ASSERT(kMaxWebGLContextsPerPrincipal
< kMaxWebGLContexts
);
964 // it's important to update the index on a new context before losing old contexts,
965 // otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
966 // when choosing which one to lose first.
967 UpdateLastUseIndex();
969 WebGLMemoryTracker::ContextsArrayType
&contexts
970 = WebGLMemoryTracker::Contexts();
972 // quick exit path, should cover a majority of cases
973 if (contexts
.Length() <= kMaxWebGLContextsPerPrincipal
) {
977 // note that here by "context" we mean "non-lost context". See the check for
978 // IsContextLost() below. Indeed, the point of this function is to maybe lose
979 // some currently non-lost context.
981 uint64_t oldestIndex
= UINT64_MAX
;
982 uint64_t oldestIndexThisPrincipal
= UINT64_MAX
;
983 const WebGLContext
*oldestContext
= nullptr;
984 const WebGLContext
*oldestContextThisPrincipal
= nullptr;
985 size_t numContexts
= 0;
986 size_t numContextsThisPrincipal
= 0;
988 for(size_t i
= 0; i
< contexts
.Length(); ++i
) {
990 // don't want to lose ourselves.
991 if (contexts
[i
] == this)
994 if (contexts
[i
]->IsContextLost())
997 if (!contexts
[i
]->GetCanvas()) {
998 // Zombie context: the canvas is already destroyed, but something else
999 // (typically the compositor) is still holding on to the context.
1000 // Killing zombies is a no-brainer.
1001 const_cast<WebGLContext
*>(contexts
[i
])->LoseContext();
1006 if (contexts
[i
]->mLastUseIndex
< oldestIndex
) {
1007 oldestIndex
= contexts
[i
]->mLastUseIndex
;
1008 oldestContext
= contexts
[i
];
1011 nsIPrincipal
*ourPrincipal
= GetCanvas()->NodePrincipal();
1012 nsIPrincipal
*theirPrincipal
= contexts
[i
]->GetCanvas()->NodePrincipal();
1014 nsresult rv
= ourPrincipal
->Equals(theirPrincipal
, &samePrincipal
);
1015 if (NS_SUCCEEDED(rv
) && samePrincipal
) {
1016 numContextsThisPrincipal
++;
1017 if (contexts
[i
]->mLastUseIndex
< oldestIndexThisPrincipal
) {
1018 oldestIndexThisPrincipal
= contexts
[i
]->mLastUseIndex
;
1019 oldestContextThisPrincipal
= contexts
[i
];
1024 if (numContextsThisPrincipal
> kMaxWebGLContextsPerPrincipal
) {
1025 GenerateWarning("Exceeded %d live WebGL contexts for this principal, losing the "
1026 "least recently used one.", kMaxWebGLContextsPerPrincipal
);
1027 MOZ_ASSERT(oldestContextThisPrincipal
); // if we reach this point, this can't be null
1028 const_cast<WebGLContext
*>(oldestContextThisPrincipal
)->LoseContext();
1029 } else if (numContexts
> kMaxWebGLContexts
) {
1030 GenerateWarning("Exceeded %d live WebGL contexts, losing the least recently used one.",
1032 MOZ_ASSERT(oldestContext
); // if we reach this point, this can't be null
1033 const_cast<WebGLContext
*>(oldestContext
)->LoseContext();
1038 WebGLContext::GetImageBuffer(uint8_t** aImageBuffer
, int32_t* aFormat
)
1040 *aImageBuffer
= nullptr;
1043 // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
1045 RefPtr
<SourceSurface
> snapshot
=
1046 GetSurfaceSnapshot(mOptions
.premultipliedAlpha
? nullptr : &premult
);
1050 MOZ_ASSERT(mOptions
.premultipliedAlpha
|| !premult
, "We must get unpremult when we ask for it!");
1052 RefPtr
<DataSourceSurface
> dataSurface
= snapshot
->GetDataSurface();
1054 DataSourceSurface::MappedSurface map
;
1055 if (!dataSurface
->Map(DataSourceSurface::MapType::READ
, &map
)) {
1059 static const fallible_t fallible
= fallible_t();
1060 uint8_t* imageBuffer
= new (fallible
) uint8_t[mWidth
* mHeight
* 4];
1062 dataSurface
->Unmap();
1065 memcpy(imageBuffer
, map
.mData
, mWidth
* mHeight
* 4);
1067 dataSurface
->Unmap();
1069 int32_t format
= imgIEncoder::INPUT_FORMAT_HOSTARGB
;
1070 if (!mOptions
.premultipliedAlpha
) {
1071 // We need to convert to INPUT_FORMAT_RGBA, otherwise
1072 // we are automatically considered premult, and unpremult'd.
1073 // Yes, it is THAT silly.
1074 // Except for different lossy conversions by color,
1075 // we could probably just change the label, and not change the data.
1076 gfxUtils::ConvertBGRAtoRGBA(imageBuffer
, mWidth
* mHeight
* 4);
1077 format
= imgIEncoder::INPUT_FORMAT_RGBA
;
1080 *aImageBuffer
= imageBuffer
;
1085 WebGLContext::GetInputStream(const char* aMimeType
,
1086 const char16_t
* aEncoderOptions
,
1087 nsIInputStream
**aStream
)
1089 NS_ASSERTION(gl
, "GetInputStream on invalid context?");
1091 return NS_ERROR_FAILURE
;
1093 nsCString
enccid("@mozilla.org/image/encoder;2?type=");
1094 enccid
+= aMimeType
;
1095 nsCOMPtr
<imgIEncoder
> encoder
= do_CreateInstance(enccid
.get());
1097 return NS_ERROR_FAILURE
;
1100 nsAutoArrayPtr
<uint8_t> imageBuffer
;
1102 GetImageBuffer(getter_Transfers(imageBuffer
), &format
);
1104 return NS_ERROR_FAILURE
;
1107 return ImageEncoder::GetInputStream(mWidth
, mHeight
, imageBuffer
, format
,
1108 encoder
, aEncoderOptions
, aStream
);
1111 void WebGLContext::UpdateLastUseIndex()
1113 static CheckedInt
<uint64_t> sIndex
= 0;
1117 // should never happen with 64-bit; trying to handle this would be riskier than
1118 // not handling it as the handler code would never get exercised.
1119 if (!sIndex
.isValid()) {
1120 NS_RUNTIMEABORT("Can't believe it's been 2^64 transactions already!");
1123 mLastUseIndex
= sIndex
.value();
1126 static uint8_t gWebGLLayerUserData
;
1130 class WebGLContextUserData
: public LayerUserData
{
1132 WebGLContextUserData(HTMLCanvasElement
*aContent
)
1133 : mContent(aContent
)
1136 /* PreTransactionCallback gets called by the Layers code every time the
1137 * WebGL canvas is going to be composited.
1139 static void PreTransactionCallback(void* data
)
1141 WebGLContextUserData
* userdata
= static_cast<WebGLContextUserData
*>(data
);
1142 HTMLCanvasElement
* canvas
= userdata
->mContent
;
1143 WebGLContext
* context
= static_cast<WebGLContext
*>(canvas
->GetContextAtIndex(0));
1145 // Present our screenbuffer, if needed.
1146 context
->PresentScreenBuffer();
1147 context
->mDrawCallsSinceLastFlush
= 0;
1150 /** DidTransactionCallback gets called by the Layers code everytime the WebGL canvas gets composite,
1151 * so it really is the right place to put actions that have to be performed upon compositing
1153 static void DidTransactionCallback(void* aData
)
1155 WebGLContextUserData
*userdata
= static_cast<WebGLContextUserData
*>(aData
);
1156 HTMLCanvasElement
*canvas
= userdata
->mContent
;
1157 WebGLContext
*context
= static_cast<WebGLContext
*>(canvas
->GetContextAtIndex(0));
1159 // Mark ourselves as no longer invalidated.
1160 context
->MarkContextClean();
1162 context
->UpdateLastUseIndex();
1166 nsRefPtr
<HTMLCanvasElement
> mContent
;
1169 } // end namespace mozilla
1171 already_AddRefed
<layers::CanvasLayer
>
1172 WebGLContext::GetCanvasLayer(nsDisplayListBuilder
* aBuilder
,
1173 CanvasLayer
*aOldLayer
,
1174 LayerManager
*aManager
)
1176 if (IsContextLost())
1179 if (!mResetLayer
&& aOldLayer
&&
1180 aOldLayer
->HasUserData(&gWebGLLayerUserData
)) {
1181 nsRefPtr
<layers::CanvasLayer
> ret
= aOldLayer
;
1182 return ret
.forget();
1185 nsRefPtr
<CanvasLayer
> canvasLayer
= aManager
->CreateCanvasLayer();
1187 NS_WARNING("CreateCanvasLayer returned null!");
1190 WebGLContextUserData
*userData
= nullptr;
1191 if (aBuilder
->IsPaintingToWindow()) {
1192 // Make the layer tell us whenever a transaction finishes (including
1193 // the current transaction), so we can clear our invalidation state and
1194 // start invalidating again. We need to do this for the layer that is
1195 // being painted to a window (there shouldn't be more than one at a time,
1196 // and if there is, flushing the invalidation state more often than
1197 // necessary is harmless).
1199 // The layer will be destroyed when we tear down the presentation
1200 // (at the latest), at which time this userData will be destroyed,
1201 // releasing the reference to the element.
1202 // The userData will receive DidTransactionCallbacks, which flush the
1203 // the invalidation state to indicate that the canvas is up to date.
1204 userData
= new WebGLContextUserData(mCanvasElement
);
1205 canvasLayer
->SetDidTransactionCallback(
1206 WebGLContextUserData::DidTransactionCallback
, userData
);
1207 canvasLayer
->SetPreTransactionCallback(
1208 WebGLContextUserData::PreTransactionCallback
, userData
);
1210 canvasLayer
->SetUserData(&gWebGLLayerUserData
, userData
);
1212 CanvasLayer::Data data
;
1213 data
.mGLContext
= gl
;
1214 data
.mSize
= nsIntSize(mWidth
, mHeight
);
1215 data
.mHasAlpha
= gl
->Caps().alpha
;
1216 data
.mIsGLAlphaPremult
= IsPremultAlpha() || !data
.mHasAlpha
;
1218 canvasLayer
->Initialize(data
);
1219 uint32_t flags
= gl
->Caps().alpha
? 0 : Layer::CONTENT_OPAQUE
;
1220 canvasLayer
->SetContentFlags(flags
);
1221 canvasLayer
->Updated();
1223 mResetLayer
= false;
1225 return canvasLayer
.forget();
1229 WebGLContext::GetContextAttributes(Nullable
<dom::WebGLContextAttributes
> &retval
)
1232 if (IsContextLost())
1235 dom::WebGLContextAttributes
& result
= retval
.SetValue();
1237 const PixelBufferFormat
& format
= gl
->GetPixelFormat();
1239 result
.mAlpha
.Construct(format
.alpha
> 0);
1240 result
.mDepth
= format
.depth
> 0;
1241 result
.mStencil
= format
.stencil
> 0;
1242 result
.mAntialias
= format
.samples
> 1;
1243 result
.mPremultipliedAlpha
= mOptions
.premultipliedAlpha
;
1244 result
.mPreserveDrawingBuffer
= mOptions
.preserveDrawingBuffer
;
1247 /* [noscript] DOMString mozGetUnderlyingParamString(in GLenum pname); */
1249 WebGLContext::MozGetUnderlyingParamString(uint32_t pname
, nsAString
& retval
)
1251 if (IsContextLost())
1254 retval
.SetIsVoid(true);
1256 MakeContextCurrent();
1259 case LOCAL_GL_VENDOR
:
1260 case LOCAL_GL_RENDERER
:
1261 case LOCAL_GL_VERSION
:
1262 case LOCAL_GL_SHADING_LANGUAGE_VERSION
:
1263 case LOCAL_GL_EXTENSIONS
: {
1264 const char *s
= (const char *) gl
->fGetString(pname
);
1265 retval
.Assign(NS_ConvertASCIItoUTF16(nsDependentCString(s
)));
1270 return NS_ERROR_INVALID_ARG
;
1277 WebGLContext::ClearScreen()
1279 bool colorAttachmentsMask
[WebGLContext::kMaxColorAttachments
] = {false};
1281 MakeContextCurrent();
1282 ScopedBindFramebuffer
autoFB(gl
, 0);
1284 GLbitfield clearMask
= LOCAL_GL_COLOR_BUFFER_BIT
;
1286 clearMask
|= LOCAL_GL_DEPTH_BUFFER_BIT
;
1287 if (mOptions
.stencil
)
1288 clearMask
|= LOCAL_GL_STENCIL_BUFFER_BIT
;
1290 colorAttachmentsMask
[0] = true;
1292 ForceClearFramebufferWithDefaultValues(clearMask
, colorAttachmentsMask
);
1296 WebGLContext::ForceClearFramebufferWithDefaultValues(GLbitfield mask
, const bool colorAttachmentsMask
[kMaxColorAttachments
])
1298 MakeContextCurrent();
1300 bool initializeColorBuffer
= 0 != (mask
& LOCAL_GL_COLOR_BUFFER_BIT
);
1301 bool initializeDepthBuffer
= 0 != (mask
& LOCAL_GL_DEPTH_BUFFER_BIT
);
1302 bool initializeStencilBuffer
= 0 != (mask
& LOCAL_GL_STENCIL_BUFFER_BIT
);
1303 bool drawBuffersIsEnabled
= IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers
);
1304 bool shouldOverrideDrawBuffers
= false;
1306 GLenum currentDrawBuffers
[WebGLContext::kMaxColorAttachments
];
1308 // Fun GL fact: No need to worry about the viewport here, glViewport is just
1309 // setting up a coordinates transformation, it doesn't affect glClear at all.
1310 AssertCachedState(); // Can't check cached bindings, as we could
1311 // have a different FB bound temporarily.
1313 // Prepare GL state for clearing.
1314 gl
->fDisable(LOCAL_GL_SCISSOR_TEST
);
1316 if (initializeColorBuffer
) {
1318 if (drawBuffersIsEnabled
) {
1320 GLenum drawBuffersCommand
[WebGLContext::kMaxColorAttachments
] = { LOCAL_GL_NONE
};
1322 for(int32_t i
= 0; i
< mGLMaxDrawBuffers
; i
++) {
1324 gl
->fGetIntegerv(LOCAL_GL_DRAW_BUFFER0
+ i
, &temp
);
1325 currentDrawBuffers
[i
] = temp
;
1327 if (colorAttachmentsMask
[i
]) {
1328 drawBuffersCommand
[i
] = LOCAL_GL_COLOR_ATTACHMENT0
+ i
;
1330 if (currentDrawBuffers
[i
] != drawBuffersCommand
[i
])
1331 shouldOverrideDrawBuffers
= true;
1333 // calling draw buffers can cause resolves on adreno drivers so
1334 // we try to avoid calling it
1335 if (shouldOverrideDrawBuffers
)
1336 gl
->fDrawBuffers(mGLMaxDrawBuffers
, drawBuffersCommand
);
1339 gl
->fColorMask(1, 1, 1, 1);
1340 gl
->fClearColor(0.0f
, 0.0f
, 0.0f
, 0.0f
);
1343 if (initializeDepthBuffer
) {
1345 gl
->fClearDepth(1.0f
);
1348 if (initializeStencilBuffer
) {
1349 // "The clear operation always uses the front stencil write mask
1350 // when clearing the stencil buffer."
1351 gl
->fStencilMaskSeparate(LOCAL_GL_FRONT
, 0xffffffff);
1352 gl
->fStencilMaskSeparate(LOCAL_GL_BACK
, 0xffffffff);
1353 gl
->fClearStencil(0);
1356 if (mRasterizerDiscardEnabled
) {
1357 gl
->fDisable(LOCAL_GL_RASTERIZER_DISCARD
);
1364 if (mScissorTestEnabled
)
1365 gl
->fEnable(LOCAL_GL_SCISSOR_TEST
);
1367 if (mRasterizerDiscardEnabled
) {
1368 gl
->fEnable(LOCAL_GL_RASTERIZER_DISCARD
);
1371 // Restore GL state after clearing.
1372 if (initializeColorBuffer
) {
1373 if (shouldOverrideDrawBuffers
) {
1374 gl
->fDrawBuffers(mGLMaxDrawBuffers
, currentDrawBuffers
);
1377 gl
->fColorMask(mColorWriteMask
[0],
1380 mColorWriteMask
[3]);
1381 gl
->fClearColor(mColorClearValue
[0],
1382 mColorClearValue
[1],
1383 mColorClearValue
[2],
1384 mColorClearValue
[3]);
1387 if (initializeDepthBuffer
) {
1388 gl
->fDepthMask(mDepthWriteMask
);
1389 gl
->fClearDepth(mDepthClearValue
);
1392 if (initializeStencilBuffer
) {
1393 gl
->fStencilMaskSeparate(LOCAL_GL_FRONT
, mStencilWriteMaskFront
);
1394 gl
->fStencilMaskSeparate(LOCAL_GL_BACK
, mStencilWriteMaskBack
);
1395 gl
->fClearStencil(mStencilClearValue
);
1399 // For an overview of how WebGL compositing works, see:
1400 // https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing
1402 WebGLContext::PresentScreenBuffer()
1404 if (IsContextLost()) {
1408 if (!mShouldPresent
) {
1413 MOZ_ASSERT(!mBackbufferNeedsClear
);
1414 if (!gl
->PublishFrame()) {
1419 if (!mOptions
.preserveDrawingBuffer
) {
1420 mBackbufferNeedsClear
= true;
1423 mShouldPresent
= false;
1429 WebGLContext::DummyFramebufferOperation(const char *info
)
1431 GLenum status
= CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER
);
1432 if (status
!= LOCAL_GL_FRAMEBUFFER_COMPLETE
)
1433 ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info
);
1437 CheckContextLost(GLContext
* gl
, bool* out_isGuilty
)
1440 MOZ_ASSERT(out_isGuilty
);
1442 bool isEGL
= gl
->GetContextType() == gl::GLContextType::EGL
;
1444 GLenum resetStatus
= LOCAL_GL_NO_ERROR
;
1445 if (gl
->HasRobustness()) {
1447 resetStatus
= gl
->fGetGraphicsResetStatus();
1449 // Simulate a ARB_robustness guilty context loss for when we
1450 // get an EGL_CONTEXT_LOST error. It may not actually be guilty,
1451 // but we can't make any distinction.
1452 if (!gl
->MakeCurrent(true) && gl
->IsContextLost()) {
1453 resetStatus
= LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB
;
1457 if (resetStatus
== LOCAL_GL_NO_ERROR
) {
1458 *out_isGuilty
= false;
1462 // Assume guilty unless we find otherwise!
1463 bool isGuilty
= true;
1464 switch (resetStatus
) {
1465 case LOCAL_GL_INNOCENT_CONTEXT_RESET_ARB
:
1466 // Either nothing wrong, or not our fault.
1469 case LOCAL_GL_GUILTY_CONTEXT_RESET_ARB
:
1470 NS_WARNING("WebGL content on the page definitely caused the graphics"
1473 case LOCAL_GL_UNKNOWN_CONTEXT_RESET_ARB
:
1474 NS_WARNING("WebGL content on the page might have caused the graphics"
1476 // If we can't tell, assume guilty.
1479 MOZ_ASSERT(false, "Unreachable.");
1480 // If we do get here, let's pretend to be guilty as an escape plan.
1485 NS_WARNING("WebGL context on this page is considered guilty, and will"
1486 " not be restored.");
1489 *out_isGuilty
= isGuilty
;
1494 WebGLContext::TryToRestoreContext()
1496 if (NS_FAILED(SetDimensions(mWidth
, mHeight
)))
1503 WebGLContext::RunContextLossTimer()
1505 mContextLossHandler
->RunTimer();
1508 class UpdateContextLossStatusTask
: public nsRunnable
1510 nsRefPtr
<WebGLContext
> mContext
;
1513 UpdateContextLossStatusTask(WebGLContext
* context
)
1519 mContext
->UpdateContextLossStatus();
1526 WebGLContext::EnqueueUpdateContextLossStatus()
1528 nsCOMPtr
<nsIRunnable
> task
= new UpdateContextLossStatusTask(this);
1529 NS_DispatchToCurrentThread(task
);
1532 // We use this timer for many things. Here are the things that it is activated for:
1533 // 1) If a script is using the MOZ_WEBGL_lose_context extension.
1534 // 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
1535 // CONTEXT_LOST_WEBGL error has been triggered.
1536 // 3) If we are using ANGLE, or anything that supports ARB_robustness, query the
1537 // GPU periodically to see if the reset status bit has been set.
1538 // In all of these situations, we use this timer to send the script context lost
1539 // and restored events asynchronously. For example, if it triggers a context loss,
1540 // the webglcontextlost event will be sent to it the next time the robustness timer
1542 // Note that this timer mechanism is not used unless one of these 3 criteria
1544 // At a bare minimum, from context lost to context restores, it would take 3
1545 // full timer iterations: detection, webglcontextlost, webglcontextrestored.
1547 WebGLContext::UpdateContextLossStatus()
1549 if (!mCanvasElement
) {
1550 // the canvas is gone. That happens when the page was closed before we got
1551 // this timer event. In this case, there's nothing to do here, just don't crash.
1554 if (mContextStatus
== ContextNotLost
) {
1555 // We don't know that we're lost, but we might be, so we need to
1556 // check. If we're guilty, don't allow restores, though.
1558 bool isGuilty
= true;
1559 MOZ_ASSERT(gl
); // Shouldn't be missing gl if we're NotLost.
1560 bool isContextLost
= CheckContextLost(gl
, &isGuilty
);
1562 if (isContextLost
) {
1564 mAllowContextRestore
= false;
1572 if (mContextStatus
== ContextLostAwaitingEvent
) {
1573 // The context has been lost and we haven't yet triggered the
1574 // callback, so do that now.
1576 bool useDefaultHandler
;
1577 nsContentUtils::DispatchTrustedEvent(mCanvasElement
->OwnerDoc(),
1578 static_cast<nsIDOMHTMLCanvasElement
*>(mCanvasElement
),
1579 NS_LITERAL_STRING("webglcontextlost"),
1582 &useDefaultHandler
);
1583 // We sent the callback, so we're just 'regular lost' now.
1584 mContextStatus
= ContextLost
;
1585 // If we're told to use the default handler, it means the script
1586 // didn't bother to handle the event. In this case, we shouldn't
1587 // auto-restore the context.
1588 if (useDefaultHandler
)
1589 mAllowContextRestore
= false;
1594 if (mContextStatus
== ContextLost
) {
1595 // Context is lost, and we've already sent the callback. We
1596 // should try to restore the context if we're both allowed to,
1599 // Are we allowed to restore the context?
1600 if (!mAllowContextRestore
)
1603 // If we're only simulated-lost, we shouldn't auto-restore, and
1604 // instead we should wait for restoreContext() to be called.
1605 if (mLastLossWasSimulated
)
1608 // Restore when the app is visible
1609 if (mRestoreWhenVisible
)
1612 ForceRestoreContext();
1616 if (mContextStatus
== ContextLostAwaitingRestore
) {
1617 // Context is lost, but we should try to restore it.
1619 if (!mAllowContextRestore
) {
1620 // We might decide this after thinking we'd be OK restoring
1621 // the context, so downgrade.
1622 mContextStatus
= ContextLost
;
1626 if (!TryToRestoreContext()) {
1627 // Failed to restore. Try again later.
1628 mContextLossHandler
->RunTimer();
1633 mContextStatus
= ContextNotLost
;
1634 nsContentUtils::DispatchTrustedEvent(mCanvasElement
->OwnerDoc(),
1635 static_cast<nsIDOMHTMLCanvasElement
*>(mCanvasElement
),
1636 NS_LITERAL_STRING("webglcontextrestored"),
1639 mEmitContextLostErrorOnce
= true;
1645 WebGLContext::ForceLoseContext(bool simulateLosing
)
1647 printf_stderr("WebGL(%p)::ForceLoseContext\n", this);
1648 MOZ_ASSERT(!IsContextLost());
1649 mContextStatus
= ContextLostAwaitingEvent
;
1650 mContextLostErrorSet
= false;
1653 DestroyResourcesAndContext();
1654 mLastLossWasSimulated
= simulateLosing
;
1656 // Register visibility change observer to defer the context restoring.
1657 // Restore the context when the app is visible.
1658 if (mRestoreWhenVisible
&& !mLastLossWasSimulated
) {
1659 mContextObserver
->RegisterVisibilityChangeEvent();
1662 // Queue up a task, since we know the status changed.
1663 EnqueueUpdateContextLossStatus();
1667 WebGLContext::ForceRestoreContext()
1669 printf_stderr("WebGL(%p)::ForceRestoreContext\n", this);
1670 mContextStatus
= ContextLostAwaitingRestore
;
1671 mAllowContextRestore
= true; // Hey, you did say 'force'.
1673 mContextObserver
->UnregisterVisibilityChangeEvent();
1675 // Queue up a task, since we know the status changed.
1676 EnqueueUpdateContextLossStatus();
1680 WebGLContext::MakeContextCurrent() const { gl
->MakeCurrent(); }
1682 mozilla::TemporaryRef
<mozilla::gfx::SourceSurface
>
1683 WebGLContext::GetSurfaceSnapshot(bool* aPremultAlpha
)
1688 bool hasAlpha
= mOptions
.alpha
;
1689 SurfaceFormat surfFormat
= hasAlpha
? SurfaceFormat::B8G8R8A8
1690 : SurfaceFormat::B8G8R8X8
;
1691 RefPtr
<DataSourceSurface
> surf
;
1692 surf
= Factory::CreateDataSourceSurfaceWithStride(IntSize(mWidth
, mHeight
),
1695 if (NS_WARN_IF(!surf
)) {
1701 ScopedBindFramebuffer
autoFB(gl
, 0);
1702 ClearBackbufferIfNeeded();
1703 ReadPixelsIntoDataSurface(gl
, surf
);
1706 if (aPremultAlpha
) {
1707 *aPremultAlpha
= true;
1709 bool srcPremultAlpha
= mOptions
.premultipliedAlpha
;
1710 if (!srcPremultAlpha
) {
1711 if (aPremultAlpha
) {
1712 *aPremultAlpha
= false;
1714 gfxUtils::PremultiplyDataSurface(surf
, surf
);
1718 RefPtr
<DrawTarget
> dt
=
1719 Factory::CreateDrawTarget(BackendType::CAIRO
,
1720 IntSize(mWidth
, mHeight
),
1721 SurfaceFormat::B8G8R8A8
);
1728 m
.Translate(0.0, mHeight
);
1730 dt
->SetTransform(m
);
1732 dt
->DrawSurface(surf
,
1733 Rect(0, 0, mWidth
, mHeight
),
1734 Rect(0, 0, mWidth
, mHeight
),
1735 DrawSurfaceOptions(),
1736 DrawOptions(1.0f
, CompositionOp::OP_SOURCE
));
1738 return dt
->Snapshot();
1741 bool WebGLContext::TexImageFromVideoElement(GLenum target
, GLint level
,
1742 GLenum internalformat
, GLenum format
, GLenum type
,
1743 mozilla::dom::Element
& elt
)
1745 HTMLVideoElement
* video
= HTMLVideoElement::FromContentOrNull(&elt
);
1750 uint16_t readyState
;
1751 if (NS_SUCCEEDED(video
->GetReadyState(&readyState
)) &&
1752 readyState
< nsIDOMHTMLMediaElement::HAVE_CURRENT_DATA
)
1754 //No frame inside, just return
1758 // If it doesn't have a principal, just bail
1759 nsCOMPtr
<nsIPrincipal
> principal
= video
->GetCurrentPrincipal();
1764 mozilla::layers::ImageContainer
* container
= video
->GetImageContainer();
1769 if (video
->GetCORSMode() == CORS_NONE
) {
1771 nsresult rv
= mCanvasElement
->NodePrincipal()->Subsumes(principal
, &subsumes
);
1772 if (NS_FAILED(rv
) || !subsumes
) {
1773 GenerateWarning("It is forbidden to load a WebGL texture from a cross-domain element that has not been validated with CORS. "
1774 "See https://developer.mozilla.org/en/WebGL/Cross-Domain_Textures");
1780 nsRefPtr
<mozilla::layers::Image
> srcImage
= container
->LockCurrentImage();
1781 WebGLTexture
* tex
= activeBoundTextureForTarget(target
);
1783 const WebGLTexture::ImageInfo
& info
= tex
->ImageInfoAt(target
, 0);
1784 bool dimensionsMatch
= info
.Width() == srcImage
->GetSize().width
&&
1785 info
.Height() == srcImage
->GetSize().height
;
1786 if (!dimensionsMatch
) {
1787 // we need to allocation
1788 gl
->fTexImage2D(target
, level
, internalformat
, srcImage
->GetSize().width
, srcImage
->GetSize().height
, 0, format
, type
, nullptr);
1790 bool ok
= gl
->BlitHelper()->BlitImageToTexture(srcImage
.get(), srcImage
->GetSize(), tex
->GLName(), target
, mPixelStoreFlipY
);
1792 tex
->SetImageInfo(target
, level
, srcImage
->GetSize().width
, srcImage
->GetSize().height
, format
, type
, WebGLImageDataStatus::InitializedImageData
);
1796 container
->UnlockCurrentImage();
1804 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext
)
1805 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext
)
1807 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLContext
,
1811 mBoundCubeMapTextures
,
1813 mBoundTransformFeedbackBuffer
,
1818 mDefaultVertexArray
,
1819 mActiveOcclusionQuery
,
1820 mActiveTransformFeedbackQuery
)
1822 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLContext
)
1823 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1824 NS_INTERFACE_MAP_ENTRY(nsIDOMWebGLRenderingContext
)
1825 NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal
)
1826 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
1827 // If the exact way we cast to nsISupports here ever changes, fix our
1828 // ToSupports() method.
1829 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDOMWebGLRenderingContext
)
1830 NS_INTERFACE_MAP_END