content/canvas/src/WebGLContext.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     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"
     7 #include "WebGL1Context.h"
     8 #include "WebGLObjectModel.h"
     9 #include "WebGLExtensions.h"
    10 #include "WebGLContextUtils.h"
    11 #include "WebGLBuffer.h"
    12 #include "WebGLVertexAttribData.h"
    13 #include "WebGLMemoryTracker.h"
    14 #include "WebGLFramebuffer.h"
    15 #include "WebGLVertexArray.h"
    16 #include "WebGLQuery.h"
    18 #include "AccessCheck.h"
    19 #include "nsIConsoleService.h"
    20 #include "nsServiceManagerUtils.h"
    21 #include "nsIClassInfoImpl.h"
    22 #include "nsContentUtils.h"
    23 #include "nsIXPConnect.h"
    24 #include "nsError.h"
    25 #include "nsIGfxInfo.h"
    26 #include "nsIWidget.h"
    28 #include "nsIVariant.h"
    30 #include "ImageEncoder.h"
    32 #include "gfxContext.h"
    33 #include "gfxPattern.h"
    34 #include "gfxUtils.h"
    36 #include "CanvasUtils.h"
    37 #include "nsDisplayList.h"
    39 #include "GLContextProvider.h"
    40 #include "GLContext.h"
    41 #include "ScopedGLHelpers.h"
    42 #include "GLReadTexImageHelper.h"
    44 #include "gfxCrashReporterUtils.h"
    46 #include "nsSVGEffects.h"
    48 #include "prenv.h"
    50 #include "mozilla/Preferences.h"
    51 #include "mozilla/Services.h"
    52 #include "mozilla/Telemetry.h"
    54 #include "nsIObserverService.h"
    55 #include "mozilla/Services.h"
    56 #include "mozilla/dom/WebGLRenderingContextBinding.h"
    57 #include "mozilla/dom/BindingUtils.h"
    58 #include "mozilla/dom/ImageData.h"
    59 #include "mozilla/ProcessPriorityManager.h"
    60 #include "mozilla/EnumeratedArrayCycleCollection.h"
    62 #include "Layers.h"
    64 #ifdef MOZ_WIDGET_GONK
    65 #include "mozilla/layers/ShadowLayers.h"
    66 #endif
    68 using namespace mozilla;
    69 using namespace mozilla::dom;
    70 using namespace mozilla::gfx;
    71 using namespace mozilla::gl;
    72 using namespace mozilla::layers;
    74 NS_IMETHODIMP
    75 WebGLMemoryPressureObserver::Observe(nsISupports* aSubject,
    76                                      const char* aTopic,
    77                                      const char16_t* aSomeData)
    78 {
    79     if (strcmp(aTopic, "memory-pressure"))
    80         return NS_OK;
    82     bool wantToLoseContext = true;
    84     if (!mContext->mCanLoseContextInForeground &&
    85         ProcessPriorityManager::CurrentProcessIsForeground())
    86         wantToLoseContext = false;
    87     else if (!nsCRT::strcmp(aSomeData,
    88                             MOZ_UTF16("heap-minimize")))
    89         wantToLoseContext = mContext->mLoseContextOnHeapMinimize;
    91     if (wantToLoseContext)
    92         mContext->ForceLoseContext();
    94     return NS_OK;
    95 }
    98 WebGLContextOptions::WebGLContextOptions()
    99     : alpha(true), depth(true), stencil(false),
   100       premultipliedAlpha(true), antialias(true),
   101       preserveDrawingBuffer(false)
   102 {
   103     // Set default alpha state based on preference.
   104     if (Preferences::GetBool("webgl.default-no-alpha", false))
   105         alpha = false;
   106 }
   108 WebGLContext::WebGLContext()
   109     : gl(nullptr)
   110 {
   111     SetIsDOMBinding();
   113     mGeneration = 0;
   114     mInvalidated = false;
   115     mShouldPresent = true;
   116     mResetLayer = true;
   117     mOptionsFrozen = false;
   119     mActiveTexture = 0;
   120     mPixelStoreFlipY = false;
   121     mPixelStorePremultiplyAlpha = false;
   122     mPixelStoreColorspaceConversion = BROWSER_DEFAULT_WEBGL;
   124     mShaderValidation = true;
   126     mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded;
   128     mVertexAttrib0Vector[0] = 0;
   129     mVertexAttrib0Vector[1] = 0;
   130     mVertexAttrib0Vector[2] = 0;
   131     mVertexAttrib0Vector[3] = 1;
   132     mFakeVertexAttrib0BufferObjectVector[0] = 0;
   133     mFakeVertexAttrib0BufferObjectVector[1] = 0;
   134     mFakeVertexAttrib0BufferObjectVector[2] = 0;
   135     mFakeVertexAttrib0BufferObjectVector[3] = 1;
   136     mFakeVertexAttrib0BufferObjectSize = 0;
   137     mFakeVertexAttrib0BufferObject = 0;
   138     mFakeVertexAttrib0BufferStatus = WebGLVertexAttrib0Status::Default;
   140     mViewportX = 0;
   141     mViewportY = 0;
   142     mViewportWidth = 0;
   143     mViewportHeight = 0;
   145     mScissorTestEnabled = 0;
   146     mDitherEnabled = 1;
   147     mRasterizerDiscardEnabled = 0; // OpenGL ES 3.0 spec p244
   149     // initialize some GL values: we're going to get them from the GL and use them as the sizes of arrays,
   150     // so in case glGetIntegerv leaves them uninitialized because of a GL bug, we would have very weird crashes.
   151     mGLMaxVertexAttribs = 0;
   152     mGLMaxTextureUnits = 0;
   153     mGLMaxTextureSize = 0;
   154     mGLMaxCubeMapTextureSize = 0;
   155     mGLMaxRenderbufferSize = 0;
   156     mGLMaxTextureImageUnits = 0;
   157     mGLMaxVertexTextureImageUnits = 0;
   158     mGLMaxVaryingVectors = 0;
   159     mGLMaxFragmentUniformVectors = 0;
   160     mGLMaxVertexUniformVectors = 0;
   161     mGLMaxColorAttachments = 1;
   162     mGLMaxDrawBuffers = 1;
   163     mGLMaxTransformFeedbackSeparateAttribs = 0;
   165     // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13
   166     mPixelStorePackAlignment = 4;
   167     mPixelStoreUnpackAlignment = 4;
   169     WebGLMemoryTracker::AddWebGLContext(this);
   171     mAllowRestore = true;
   172     mContextLossTimerRunning = false;
   173     mDrawSinceContextLossTimerSet = false;
   174     mContextRestorer = do_CreateInstance("@mozilla.org/timer;1");
   175     mContextStatus = ContextNotLost;
   176     mContextLostErrorSet = false;
   177     mLoseContextOnHeapMinimize = false;
   178     mCanLoseContextInForeground = true;
   180     mAlreadyGeneratedWarnings = 0;
   181     mAlreadyWarnedAboutFakeVertexAttrib0 = false;
   182     mAlreadyWarnedAboutViewportLargerThanDest = false;
   183     mMaxWarnings = Preferences::GetInt("webgl.max-warnings-per-context", 32);
   184     if (mMaxWarnings < -1)
   185     {
   186         GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
   187         mMaxWarnings = 0;
   188     }
   190     mLastUseIndex = 0;
   192     InvalidateBufferFetching();
   194     mBackbufferNeedsClear = true;
   196     mDisableFragHighP = false;
   198     mDrawCallsSinceLastFlush = 0;
   199 }
   201 WebGLContext::~WebGLContext()
   202 {
   203     DestroyResourcesAndContext();
   204     WebGLMemoryTracker::RemoveWebGLContext(this);
   205     TerminateContextLossTimer();
   206     mContextRestorer = nullptr;
   207 }
   209 void
   210 WebGLContext::DestroyResourcesAndContext()
   211 {
   212     if (mMemoryPressureObserver) {
   213         nsCOMPtr<nsIObserverService> observerService
   214             = mozilla::services::GetObserverService();
   215         if (observerService) {
   216             observerService->RemoveObserver(mMemoryPressureObserver,
   217                                             "memory-pressure");
   218         }
   219         mMemoryPressureObserver = nullptr;
   220     }
   222     if (!gl)
   223         return;
   225     gl->MakeCurrent();
   227     mBound2DTextures.Clear();
   228     mBoundCubeMapTextures.Clear();
   229     mBoundArrayBuffer = nullptr;
   230     mBoundTransformFeedbackBuffer = nullptr;
   231     mCurrentProgram = nullptr;
   232     mBoundFramebuffer = nullptr;
   233     mActiveOcclusionQuery = nullptr;
   234     mBoundRenderbuffer = nullptr;
   235     mBoundVertexArray = nullptr;
   236     mDefaultVertexArray = nullptr;
   238     while (!mTextures.isEmpty())
   239         mTextures.getLast()->DeleteOnce();
   240     while (!mVertexArrays.isEmpty())
   241         mVertexArrays.getLast()->DeleteOnce();
   242     while (!mBuffers.isEmpty())
   243         mBuffers.getLast()->DeleteOnce();
   244     while (!mRenderbuffers.isEmpty())
   245         mRenderbuffers.getLast()->DeleteOnce();
   246     while (!mFramebuffers.isEmpty())
   247         mFramebuffers.getLast()->DeleteOnce();
   248     while (!mShaders.isEmpty())
   249         mShaders.getLast()->DeleteOnce();
   250     while (!mPrograms.isEmpty())
   251         mPrograms.getLast()->DeleteOnce();
   252     while (!mQueries.isEmpty())
   253         mQueries.getLast()->DeleteOnce();
   255     mBlackOpaqueTexture2D = nullptr;
   256     mBlackOpaqueTextureCubeMap = nullptr;
   257     mBlackTransparentTexture2D = nullptr;
   258     mBlackTransparentTextureCubeMap = nullptr;
   260     if (mFakeVertexAttrib0BufferObject) {
   261         gl->fDeleteBuffers(1, &mFakeVertexAttrib0BufferObject);
   262     }
   264     // disable all extensions except "WEBGL_lose_context". see bug #927969
   265     // spec: http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
   266     for (size_t i = 0; i < size_t(WebGLExtensionID::Max); ++i) {
   267         WebGLExtensionID extension = WebGLExtensionID(i);
   269         if (!IsExtensionEnabled(extension) || (extension == WebGLExtensionID::WEBGL_lose_context))
   270             continue;
   272         mExtensions[extension]->MarkLost();
   273         mExtensions[extension] = nullptr;
   274     }
   276     // We just got rid of everything, so the context had better
   277     // have been going away.
   278 #ifdef DEBUG
   279     if (gl->DebugMode()) {
   280         printf_stderr("--- WebGL context destroyed: %p\n", gl.get());
   281     }
   282 #endif
   284     gl = nullptr;
   285 }
   287 void
   288 WebGLContext::Invalidate()
   289 {
   290     if (mInvalidated)
   291         return;
   293     if (!mCanvasElement)
   294         return;
   296     nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
   298     mInvalidated = true;
   299     mCanvasElement->InvalidateCanvasContent(nullptr);
   300 }
   302 //
   303 // nsICanvasRenderingContextInternal
   304 //
   306 NS_IMETHODIMP
   307 WebGLContext::SetContextOptions(JSContext* aCx, JS::Handle<JS::Value> aOptions)
   308 {
   309     if (aOptions.isNullOrUndefined() && mOptionsFrozen) {
   310         return NS_OK;
   311     }
   313     WebGLContextAttributes attributes;
   314     NS_ENSURE_TRUE(attributes.Init(aCx, aOptions), NS_ERROR_UNEXPECTED);
   316     WebGLContextOptions newOpts;
   318     newOpts.stencil = attributes.mStencil;
   319     newOpts.depth = attributes.mDepth;
   320     newOpts.premultipliedAlpha = attributes.mPremultipliedAlpha;
   321     newOpts.antialias = attributes.mAntialias;
   322     newOpts.preserveDrawingBuffer = attributes.mPreserveDrawingBuffer;
   323     if (attributes.mAlpha.WasPassed()) {
   324       newOpts.alpha = attributes.mAlpha.Value();
   325     }
   327     // enforce that if stencil is specified, we also give back depth
   328     newOpts.depth |= newOpts.stencil;
   330 #if 0
   331     GenerateWarning("aaHint: %d stencil: %d depth: %d alpha: %d premult: %d preserve: %d\n",
   332                newOpts.antialias ? 1 : 0,
   333                newOpts.stencil ? 1 : 0,
   334                newOpts.depth ? 1 : 0,
   335                newOpts.alpha ? 1 : 0,
   336                newOpts.premultipliedAlpha ? 1 : 0,
   337                newOpts.preserveDrawingBuffer ? 1 : 0);
   338 #endif
   340     if (mOptionsFrozen && newOpts != mOptions) {
   341         // Error if the options are already frozen, and the ones that were asked for
   342         // aren't the same as what they were originally.
   343         return NS_ERROR_FAILURE;
   344     }
   346     mOptions = newOpts;
   347     return NS_OK;
   348 }
   350 #ifdef DEBUG
   351 int32_t
   352 WebGLContext::GetWidth() const
   353 {
   354   return mWidth;
   355 }
   357 int32_t
   358 WebGLContext::GetHeight() const
   359 {
   360   return mHeight;
   361 }
   362 #endif
   364 NS_IMETHODIMP
   365 WebGLContext::SetDimensions(int32_t width, int32_t height)
   366 {
   367     // Early error return cases
   369     if (width < 0 || height < 0) {
   370         GenerateWarning("Canvas size is too large (seems like a negative value wrapped)");
   371         return NS_ERROR_OUT_OF_MEMORY;
   372     }
   374     if (!GetCanvas())
   375         return NS_ERROR_FAILURE;
   377     // Early success return cases
   379     GetCanvas()->InvalidateCanvas();
   381     if (gl && mWidth == width && mHeight == height)
   382         return NS_OK;
   384     // Zero-sized surfaces can cause problems.
   385     if (width == 0) {
   386         width = 1;
   387     }
   388     if (height == 0) {
   389         height = 1;
   390     }
   392     // If we already have a gl context, then we just need to resize it
   393     if (gl) {
   394         MakeContextCurrent();
   396         // If we've already drawn, we should commit the current buffer.
   397         PresentScreenBuffer();
   399         // ResizeOffscreen scraps the current prod buffer before making a new one.
   400         gl->ResizeOffscreen(gfx::IntSize(width, height)); // Doesn't matter if it succeeds (soft-fail)
   401         // It's unlikely that we'll get a proper-sized context if we recreate if we didn't on resize
   403         // everything's good, we're done here
   404         mWidth = gl->OffscreenSize().width;
   405         mHeight = gl->OffscreenSize().height;
   406         mResetLayer = true;
   408         mBackbufferNeedsClear = true;
   410         return NS_OK;
   411     }
   413     // End of early return cases.
   414     // At this point we know that we're not just resizing an existing context,
   415     // we are initializing a new context.
   417     // if we exceeded either the global or the per-principal limit for WebGL contexts,
   418     // lose the oldest-used context now to free resources. Note that we can't do that
   419     // in the WebGLContext constructor as we don't have a canvas element yet there.
   420     // Here is the right place to do so, as we are about to create the OpenGL context
   421     // and that is what can fail if we already have too many.
   422     LoseOldestWebGLContextIfLimitExceeded();
   424     // Get some prefs for some preferred/overriden things
   425     NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
   427 #ifdef XP_WIN
   428     bool preferEGL =
   429         Preferences::GetBool("webgl.prefer-egl", false);
   430     bool preferOpenGL =
   431         Preferences::GetBool("webgl.prefer-native-gl", false);
   432 #endif
   433     bool forceEnabled =
   434         Preferences::GetBool("webgl.force-enabled", false);
   435     bool disabled =
   436         Preferences::GetBool("webgl.disabled", false);
   437     bool prefer16bit =
   438         Preferences::GetBool("webgl.prefer-16bpp", false);
   440     ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
   442     if (disabled)
   443         return NS_ERROR_FAILURE;
   445     // We're going to create an entirely new context.  If our
   446     // generation is not 0 right now (that is, if this isn't the first
   447     // context we're creating), we may have to dispatch a context lost
   448     // event.
   450     // If incrementing the generation would cause overflow,
   451     // don't allow it.  Allowing this would allow us to use
   452     // resource handles created from older context generations.
   453     if (!(mGeneration + 1).isValid())
   454         return NS_ERROR_FAILURE; // exit without changing the value of mGeneration
   456     SurfaceCaps caps;
   458     caps.color = true;
   459     caps.alpha = mOptions.alpha;
   460     caps.depth = mOptions.depth;
   461     caps.stencil = mOptions.stencil;
   463     // we should really have this behind a
   464     // |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
   465     // for now it's just behind a pref for testing/evaluation.
   466     caps.bpp16 = prefer16bit;
   468     caps.preserve = mOptions.preserveDrawingBuffer;
   470 #ifdef MOZ_WIDGET_GONK
   471     nsIWidget *docWidget = nsContentUtils::WidgetForDocument(mCanvasElement->OwnerDoc());
   472     if (docWidget) {
   473         layers::LayerManager *layerManager = docWidget->GetLayerManager();
   474         if (layerManager) {
   475             // XXX we really want "AsSurfaceAllocator" here for generality
   476             layers::ShadowLayerForwarder *forwarder = layerManager->AsShadowForwarder();
   477             if (forwarder) {
   478                 caps.surfaceAllocator = static_cast<layers::ISurfaceAllocator*>(forwarder);
   479             }
   480         }
   481     }
   482 #endif
   484     bool forceMSAA =
   485         Preferences::GetBool("webgl.msaa-force", false);
   487     int32_t status;
   488     nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
   489     if (mOptions.antialias &&
   490         gfxInfo &&
   491         NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_MSAA, &status))) {
   492         if (status == nsIGfxInfo::FEATURE_NO_INFO || forceMSAA) {
   493             caps.antialias = true;
   494         }
   495     }
   497 #ifdef XP_WIN
   498     if (PR_GetEnv("MOZ_WEBGL_PREFER_EGL")) {
   499         preferEGL = true;
   500     }
   501 #endif
   503     // Ask GfxInfo about what we should use
   504     bool useOpenGL = true;
   506 #ifdef XP_WIN
   507     bool useANGLE = true;
   508 #endif
   510     if (gfxInfo && !forceEnabled) {
   511         if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_OPENGL, &status))) {
   512             if (status != nsIGfxInfo::FEATURE_NO_INFO) {
   513                 useOpenGL = false;
   514             }
   515         }
   516 #ifdef XP_WIN
   517         if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_ANGLE, &status))) {
   518             if (status != nsIGfxInfo::FEATURE_NO_INFO) {
   519                 useANGLE = false;
   520             }
   521         }
   522 #endif
   523     }
   525 #ifdef XP_WIN
   526     // allow forcing GL and not EGL/ANGLE
   527     if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL")) {
   528         preferEGL = false;
   529         useANGLE = false;
   530         useOpenGL = true;
   531     }
   532 #endif
   534     gfxIntSize size(width, height);
   536 #ifdef XP_WIN
   537     // if we want EGL, try it now
   538     if (!gl && (preferEGL || useANGLE) && !preferOpenGL) {
   539         gl = gl::GLContextProviderEGL::CreateOffscreen(size, caps);
   540         if (!gl || !InitAndValidateGL()) {
   541             GenerateWarning("Error during ANGLE OpenGL ES initialization");
   542             return NS_ERROR_FAILURE;
   543         }
   544     }
   545 #endif
   547     // try the default provider, whatever that is
   548     if (!gl && useOpenGL) {
   549         gl = gl::GLContextProvider::CreateOffscreen(size, caps);
   550         if (gl && !InitAndValidateGL()) {
   551             GenerateWarning("Error during OpenGL initialization");
   552             return NS_ERROR_FAILURE;
   553         }
   554     }
   556     if (!gl) {
   557         GenerateWarning("Can't get a usable WebGL context");
   558         return NS_ERROR_FAILURE;
   559     }
   561 #ifdef DEBUG
   562     if (gl->DebugMode()) {
   563         printf_stderr("--- WebGL context created: %p\n", gl.get());
   564     }
   565 #endif
   567     mWidth = width;
   568     mHeight = height;
   569     mViewportWidth = width;
   570     mViewportHeight = height;
   571     mResetLayer = true;
   572     mOptionsFrozen = true;
   574     mHasRobustness = gl->HasRobustness();
   576     // increment the generation number
   577     ++mGeneration;
   579 #if 0
   580     if (mGeneration > 0) {
   581         // XXX dispatch context lost event
   582     }
   583 #endif
   585     MakeContextCurrent();
   587     // Make sure that we clear this out, otherwise
   588     // we'll end up displaying random memory
   589     gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
   591     gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f);
   592     gl->fClearDepth(1.0f);
   593     gl->fClearStencil(0);
   595     mBackbufferNeedsClear = true;
   597     // Clear immediately, because we need to present the cleared initial
   598     // buffer.
   599     ClearBackbufferIfNeeded();
   601     mShouldPresent = true;
   603     MOZ_ASSERT(gl->Caps().color == caps.color);
   604     MOZ_ASSERT(gl->Caps().alpha == caps.alpha);
   605     MOZ_ASSERT(gl->Caps().depth == caps.depth || !gl->Caps().depth);
   606     MOZ_ASSERT(gl->Caps().stencil == caps.stencil || !gl->Caps().stencil);
   607     MOZ_ASSERT(gl->Caps().antialias == caps.antialias || !gl->Caps().antialias);
   608     MOZ_ASSERT(gl->Caps().preserve == caps.preserve);
   610     reporter.SetSuccessful();
   611     return NS_OK;
   612 }
   614 void
   615 WebGLContext::ClearBackbufferIfNeeded()
   616 {
   617     if (!mBackbufferNeedsClear)
   618         return;
   620 #ifdef DEBUG
   621     gl->MakeCurrent();
   623     GLuint fb = 0;
   624     gl->GetUIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &fb);
   625     MOZ_ASSERT(fb == 0);
   626 #endif
   628     ClearScreen();
   630     mBackbufferNeedsClear = false;
   631 }
   633 void WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
   634 {
   635 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
   636     // some mobile devices can't have more than 8 GL contexts overall
   637     const size_t kMaxWebGLContextsPerPrincipal = 2;
   638     const size_t kMaxWebGLContexts             = 4;
   639 #else
   640     const size_t kMaxWebGLContextsPerPrincipal = 16;
   641     const size_t kMaxWebGLContexts             = 32;
   642 #endif
   643     MOZ_ASSERT(kMaxWebGLContextsPerPrincipal < kMaxWebGLContexts);
   645     // it's important to update the index on a new context before losing old contexts,
   646     // otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
   647     // when choosing which one to lose first.
   648     UpdateLastUseIndex();
   650     WebGLMemoryTracker::ContextsArrayType &contexts
   651       = WebGLMemoryTracker::Contexts();
   653     // quick exit path, should cover a majority of cases
   654     if (contexts.Length() <= kMaxWebGLContextsPerPrincipal) {
   655         return;
   656     }
   658     // note that here by "context" we mean "non-lost context". See the check for
   659     // IsContextLost() below. Indeed, the point of this function is to maybe lose
   660     // some currently non-lost context.
   662     uint64_t oldestIndex = UINT64_MAX;
   663     uint64_t oldestIndexThisPrincipal = UINT64_MAX;
   664     const WebGLContext *oldestContext = nullptr;
   665     const WebGLContext *oldestContextThisPrincipal = nullptr;
   666     size_t numContexts = 0;
   667     size_t numContextsThisPrincipal = 0;
   669     for(size_t i = 0; i < contexts.Length(); ++i) {
   671         // don't want to lose ourselves.
   672         if (contexts[i] == this)
   673             continue;
   675         if (contexts[i]->IsContextLost())
   676             continue;
   678         if (!contexts[i]->GetCanvas()) {
   679             // Zombie context: the canvas is already destroyed, but something else
   680             // (typically the compositor) is still holding on to the context.
   681             // Killing zombies is a no-brainer.
   682             const_cast<WebGLContext*>(contexts[i])->LoseContext();
   683             continue;
   684         }
   686         numContexts++;
   687         if (contexts[i]->mLastUseIndex < oldestIndex) {
   688             oldestIndex = contexts[i]->mLastUseIndex;
   689             oldestContext = contexts[i];
   690         }
   692         nsIPrincipal *ourPrincipal = GetCanvas()->NodePrincipal();
   693         nsIPrincipal *theirPrincipal = contexts[i]->GetCanvas()->NodePrincipal();
   694         bool samePrincipal;
   695         nsresult rv = ourPrincipal->Equals(theirPrincipal, &samePrincipal);
   696         if (NS_SUCCEEDED(rv) && samePrincipal) {
   697             numContextsThisPrincipal++;
   698             if (contexts[i]->mLastUseIndex < oldestIndexThisPrincipal) {
   699                 oldestIndexThisPrincipal = contexts[i]->mLastUseIndex;
   700                 oldestContextThisPrincipal = contexts[i];
   701             }
   702         }
   703     }
   705     if (numContextsThisPrincipal > kMaxWebGLContextsPerPrincipal) {
   706         GenerateWarning("Exceeded %d live WebGL contexts for this principal, losing the "
   707                         "least recently used one.", kMaxWebGLContextsPerPrincipal);
   708         MOZ_ASSERT(oldestContextThisPrincipal); // if we reach this point, this can't be null
   709         const_cast<WebGLContext*>(oldestContextThisPrincipal)->LoseContext();
   710     } else if (numContexts > kMaxWebGLContexts) {
   711         GenerateWarning("Exceeded %d live WebGL contexts, losing the least recently used one.",
   712                         kMaxWebGLContexts);
   713         MOZ_ASSERT(oldestContext); // if we reach this point, this can't be null
   714         const_cast<WebGLContext*>(oldestContext)->LoseContext();
   715     }
   716 }
   718 void
   719 WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
   720 {
   721     *aImageBuffer = nullptr;
   722     *aFormat = 0;
   724     // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
   725     bool premult;
   726     RefPtr<SourceSurface> snapshot =
   727       GetSurfaceSnapshot(mOptions.premultipliedAlpha ? nullptr : &premult);
   728     if (!snapshot) {
   729         return;
   730     }
   731     MOZ_ASSERT(mOptions.premultipliedAlpha || !premult, "We must get unpremult when we ask for it!");
   733     RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
   735     DataSourceSurface::MappedSurface map;
   736     if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
   737         return;
   738     }
   740     static const fallible_t fallible = fallible_t();
   741     uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
   742     if (!imageBuffer) {
   743         dataSurface->Unmap();
   744         return;
   745     }
   746     memcpy(imageBuffer, map.mData, mWidth * mHeight * 4);
   748     dataSurface->Unmap();
   750     int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
   751     if (!mOptions.premultipliedAlpha) {
   752         // We need to convert to INPUT_FORMAT_RGBA, otherwise
   753         // we are automatically considered premult, and unpremult'd.
   754         // Yes, it is THAT silly.
   755         // Except for different lossy conversions by color,
   756         // we could probably just change the label, and not change the data.
   757         gfxUtils::ConvertBGRAtoRGBA(imageBuffer, mWidth * mHeight * 4);
   758         format = imgIEncoder::INPUT_FORMAT_RGBA;
   759     }
   761     *aImageBuffer = imageBuffer;
   762     *aFormat = format;
   763 }
   765 NS_IMETHODIMP
   766 WebGLContext::GetInputStream(const char* aMimeType,
   767                              const char16_t* aEncoderOptions,
   768                              nsIInputStream **aStream)
   769 {
   770     NS_ASSERTION(gl, "GetInputStream on invalid context?");
   771     if (!gl)
   772         return NS_ERROR_FAILURE;
   774     nsCString enccid("@mozilla.org/image/encoder;2?type=");
   775     enccid += aMimeType;
   776     nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
   777     if (!encoder) {
   778         return NS_ERROR_FAILURE;
   779     }
   781     nsAutoArrayPtr<uint8_t> imageBuffer;
   782     int32_t format = 0;
   783     GetImageBuffer(getter_Transfers(imageBuffer), &format);
   784     if (!imageBuffer) {
   785         return NS_ERROR_FAILURE;
   786     }
   788     return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
   789                                         encoder, aEncoderOptions, aStream);
   790 }
   792 void WebGLContext::UpdateLastUseIndex()
   793 {
   794     static CheckedInt<uint64_t> sIndex = 0;
   796     sIndex++;
   798     // should never happen with 64-bit; trying to handle this would be riskier than
   799     // not handling it as the handler code would never get exercised.
   800     if (!sIndex.isValid()) {
   801         NS_RUNTIMEABORT("Can't believe it's been 2^64 transactions already!");
   802     }
   804     mLastUseIndex = sIndex.value();
   805 }
   807 static uint8_t gWebGLLayerUserData;
   809 namespace mozilla {
   811 class WebGLContextUserData : public LayerUserData {
   812 public:
   813     WebGLContextUserData(HTMLCanvasElement *aContent)
   814         : mContent(aContent)
   815     {}
   817     /* PreTransactionCallback gets called by the Layers code every time the
   818      * WebGL canvas is going to be composited.
   819      */
   820     static void PreTransactionCallback(void* data)
   821     {
   822         WebGLContextUserData* userdata = static_cast<WebGLContextUserData*>(data);
   823         HTMLCanvasElement* canvas = userdata->mContent;
   824         WebGLContext* context = static_cast<WebGLContext*>(canvas->GetContextAtIndex(0));
   826         // Present our screenbuffer, if needed.
   827         context->PresentScreenBuffer();
   828         context->mDrawCallsSinceLastFlush = 0;
   829     }
   831     /** DidTransactionCallback gets called by the Layers code everytime the WebGL canvas gets composite,
   832       * so it really is the right place to put actions that have to be performed upon compositing
   833       */
   834     static void DidTransactionCallback(void* aData)
   835     {
   836         WebGLContextUserData *userdata = static_cast<WebGLContextUserData*>(aData);
   837         HTMLCanvasElement *canvas = userdata->mContent;
   838         WebGLContext *context = static_cast<WebGLContext*>(canvas->GetContextAtIndex(0));
   840         // Mark ourselves as no longer invalidated.
   841         context->MarkContextClean();
   843         context->UpdateLastUseIndex();
   844     }
   846 private:
   847     nsRefPtr<HTMLCanvasElement> mContent;
   848 };
   850 } // end namespace mozilla
   852 already_AddRefed<layers::CanvasLayer>
   853 WebGLContext::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
   854                              CanvasLayer *aOldLayer,
   855                              LayerManager *aManager)
   856 {
   857     if (IsContextLost())
   858         return nullptr;
   860     if (!mResetLayer && aOldLayer &&
   861         aOldLayer->HasUserData(&gWebGLLayerUserData)) {
   862         nsRefPtr<layers::CanvasLayer> ret = aOldLayer;
   863         return ret.forget();
   864     }
   866     nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
   867     if (!canvasLayer) {
   868         NS_WARNING("CreateCanvasLayer returned null!");
   869         return nullptr;
   870     }
   871     WebGLContextUserData *userData = nullptr;
   872     if (aBuilder->IsPaintingToWindow()) {
   873       // Make the layer tell us whenever a transaction finishes (including
   874       // the current transaction), so we can clear our invalidation state and
   875       // start invalidating again. We need to do this for the layer that is
   876       // being painted to a window (there shouldn't be more than one at a time,
   877       // and if there is, flushing the invalidation state more often than
   878       // necessary is harmless).
   880       // The layer will be destroyed when we tear down the presentation
   881       // (at the latest), at which time this userData will be destroyed,
   882       // releasing the reference to the element.
   883       // The userData will receive DidTransactionCallbacks, which flush the
   884       // the invalidation state to indicate that the canvas is up to date.
   885       userData = new WebGLContextUserData(mCanvasElement);
   886       canvasLayer->SetDidTransactionCallback(
   887               WebGLContextUserData::DidTransactionCallback, userData);
   888       canvasLayer->SetPreTransactionCallback(
   889               WebGLContextUserData::PreTransactionCallback, userData);
   890     }
   891     canvasLayer->SetUserData(&gWebGLLayerUserData, userData);
   893     CanvasLayer::Data data;
   894     data.mGLContext = gl;
   895     data.mSize = nsIntSize(mWidth, mHeight);
   896     data.mIsGLAlphaPremult = IsPremultAlpha();
   898     canvasLayer->Initialize(data);
   899     uint32_t flags = gl->Caps().alpha ? 0 : Layer::CONTENT_OPAQUE;
   900     canvasLayer->SetContentFlags(flags);
   901     canvasLayer->Updated();
   903     mResetLayer = false;
   905     return canvasLayer.forget();
   906 }
   908 void
   909 WebGLContext::GetContextAttributes(Nullable<dom::WebGLContextAttributes> &retval)
   910 {
   911     retval.SetNull();
   912     if (IsContextLost())
   913         return;
   915     dom::WebGLContextAttributes& result = retval.SetValue();
   917     const PixelBufferFormat& format = gl->GetPixelFormat();
   919     result.mAlpha.Construct(format.alpha > 0);
   920     result.mDepth = format.depth > 0;
   921     result.mStencil = format.stencil > 0;
   922     result.mAntialias = format.samples > 1;
   923     result.mPremultipliedAlpha = mOptions.premultipliedAlpha;
   924     result.mPreserveDrawingBuffer = mOptions.preserveDrawingBuffer;
   925 }
   927 /* [noscript] DOMString mozGetUnderlyingParamString(in GLenum pname); */
   928 NS_IMETHODIMP
   929 WebGLContext::MozGetUnderlyingParamString(uint32_t pname, nsAString& retval)
   930 {
   931     if (IsContextLost())
   932         return NS_OK;
   934     retval.SetIsVoid(true);
   936     MakeContextCurrent();
   938     switch (pname) {
   939     case LOCAL_GL_VENDOR:
   940     case LOCAL_GL_RENDERER:
   941     case LOCAL_GL_VERSION:
   942     case LOCAL_GL_SHADING_LANGUAGE_VERSION:
   943     case LOCAL_GL_EXTENSIONS: {
   944         const char *s = (const char *) gl->fGetString(pname);
   945         retval.Assign(NS_ConvertASCIItoUTF16(nsDependentCString(s)));
   946     }
   947         break;
   949     default:
   950         return NS_ERROR_INVALID_ARG;
   951     }
   953     return NS_OK;
   954 }
   956 void
   957 WebGLContext::ClearScreen()
   958 {
   959     bool colorAttachmentsMask[WebGLContext::sMaxColorAttachments] = {false};
   961     MakeContextCurrent();
   962     ScopedBindFramebuffer autoFB(gl, 0);
   964     GLbitfield clearMask = LOCAL_GL_COLOR_BUFFER_BIT;
   965     if (mOptions.depth)
   966         clearMask |= LOCAL_GL_DEPTH_BUFFER_BIT;
   967     if (mOptions.stencil)
   968         clearMask |= LOCAL_GL_STENCIL_BUFFER_BIT;
   970     colorAttachmentsMask[0] = true;
   972     ForceClearFramebufferWithDefaultValues(clearMask, colorAttachmentsMask);
   973 }
   975 #ifdef DEBUG
   976 // For NaNs, etc.
   977 static bool IsShadowCorrect(float shadow, float actual) {
   978     if (IsNaN(shadow)) {
   979         // GL is allowed to do anything it wants for NaNs, so if we're shadowing
   980         // a NaN, then whatever `actual` is might be correct.
   981         return true;
   982     }
   984     return shadow == actual;
   985 }
   986 #endif
   988 void
   989 WebGLContext::ForceClearFramebufferWithDefaultValues(GLbitfield mask, const bool colorAttachmentsMask[sMaxColorAttachments])
   990 {
   991     MakeContextCurrent();
   993     bool initializeColorBuffer = 0 != (mask & LOCAL_GL_COLOR_BUFFER_BIT);
   994     bool initializeDepthBuffer = 0 != (mask & LOCAL_GL_DEPTH_BUFFER_BIT);
   995     bool initializeStencilBuffer = 0 != (mask & LOCAL_GL_STENCIL_BUFFER_BIT);
   996     bool drawBuffersIsEnabled = IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers);
   998     GLenum currentDrawBuffers[WebGLContext::sMaxColorAttachments];
  1000     // Fun GL fact: No need to worry about the viewport here, glViewport is just
  1001     // setting up a coordinates transformation, it doesn't affect glClear at all.
  1003 #ifdef DEBUG
  1004     // Scope to hide our variables.
  1006         // Sanity-check that all our state is set properly. Otherwise, when we
  1007         // reset out state to what we *think* it is, we'll get it wrong.
  1009         // Dither shouldn't matter when we're clearing to {0,0,0,0}.
  1010         MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST) == mScissorTestEnabled);
  1012         if (initializeColorBuffer) {
  1013             realGLboolean colorWriteMask[4] = {2, 2, 2, 2};
  1014             GLfloat colorClearValue[4] = {-1.0f, -1.0f, -1.0f, -1.0f};
  1016             gl->fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorWriteMask);
  1017             gl->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, colorClearValue);
  1019             MOZ_ASSERT(colorWriteMask[0] == mColorWriteMask[0] &&
  1020                        colorWriteMask[1] == mColorWriteMask[1] &&
  1021                        colorWriteMask[2] == mColorWriteMask[2] &&
  1022                        colorWriteMask[3] == mColorWriteMask[3]);
  1023             MOZ_ASSERT(IsShadowCorrect(mColorClearValue[0], colorClearValue[0]) &&
  1024                        IsShadowCorrect(mColorClearValue[1], colorClearValue[1]) &&
  1025                        IsShadowCorrect(mColorClearValue[2], colorClearValue[2]) &&
  1026                        IsShadowCorrect(mColorClearValue[3], colorClearValue[3]));
  1029         if (initializeDepthBuffer) {
  1030             realGLboolean depthWriteMask = 2;
  1031             GLfloat depthClearValue = -1.0f;
  1034             gl->fGetBooleanv(LOCAL_GL_DEPTH_WRITEMASK, &depthWriteMask);
  1035             gl->fGetFloatv(LOCAL_GL_DEPTH_CLEAR_VALUE, &depthClearValue);
  1037             MOZ_ASSERT(depthWriteMask == mDepthWriteMask);
  1038             MOZ_ASSERT(IsShadowCorrect(mDepthClearValue, depthClearValue));
  1041         if (initializeStencilBuffer) {
  1042             GLuint stencilWriteMaskFront = 0xdeadbad1;
  1043             GLuint stencilWriteMaskBack  = 0xdeadbad1;
  1044             GLuint stencilClearValue     = 0xdeadbad1;
  1046             gl->GetUIntegerv(LOCAL_GL_STENCIL_WRITEMASK,      &stencilWriteMaskFront);
  1047             gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_WRITEMASK, &stencilWriteMaskBack);
  1048             gl->GetUIntegerv(LOCAL_GL_STENCIL_CLEAR_VALUE,    &stencilClearValue);
  1050             GLuint stencilBits = 0;
  1051             gl->GetUIntegerv(LOCAL_GL_STENCIL_BITS, &stencilBits);
  1052             GLuint stencilMask = (GLuint(1) << stencilBits) - 1;
  1054             MOZ_ASSERT( ( stencilWriteMaskFront & stencilMask) ==
  1055                         (mStencilWriteMaskFront & stencilMask) );
  1056             MOZ_ASSERT( ( stencilWriteMaskBack & stencilMask) ==
  1057                         (mStencilWriteMaskBack & stencilMask) );
  1058             MOZ_ASSERT( ( stencilClearValue & stencilMask) ==
  1059                         (mStencilClearValue & stencilMask) );
  1062 #endif
  1064     // Prepare GL state for clearing.
  1065     gl->fDisable(LOCAL_GL_SCISSOR_TEST);
  1067     if (initializeColorBuffer) {
  1069         if (drawBuffersIsEnabled) {
  1071             GLenum drawBuffersCommand[WebGLContext::sMaxColorAttachments] = { LOCAL_GL_NONE };
  1073             for(int32_t i = 0; i < mGLMaxDrawBuffers; i++) {
  1074                 GLint temp;
  1075                 gl->fGetIntegerv(LOCAL_GL_DRAW_BUFFER0 + i, &temp);
  1076                 currentDrawBuffers[i] = temp;
  1078                 if (colorAttachmentsMask[i]) {
  1079                     drawBuffersCommand[i] = LOCAL_GL_COLOR_ATTACHMENT0 + i;
  1083             gl->fDrawBuffers(mGLMaxDrawBuffers, drawBuffersCommand);
  1086         gl->fColorMask(1, 1, 1, 1);
  1087         gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  1090     if (initializeDepthBuffer) {
  1091         gl->fDepthMask(1);
  1092         gl->fClearDepth(1.0f);
  1095     if (initializeStencilBuffer) {
  1096         // "The clear operation always uses the front stencil write mask
  1097         //  when clearing the stencil buffer."
  1098         gl->fStencilMaskSeparate(LOCAL_GL_FRONT, 0xffffffff);
  1099         gl->fStencilMaskSeparate(LOCAL_GL_BACK,  0xffffffff);
  1100         gl->fClearStencil(0);
  1103     if (mRasterizerDiscardEnabled) {
  1104         gl->fDisable(LOCAL_GL_RASTERIZER_DISCARD);
  1107     // Do the clear!
  1108     gl->fClear(mask);
  1110     // And reset!
  1111     if (mScissorTestEnabled)
  1112         gl->fEnable(LOCAL_GL_SCISSOR_TEST);
  1114     if (mRasterizerDiscardEnabled) {
  1115         gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD);
  1118     // Restore GL state after clearing.
  1119     if (initializeColorBuffer) {
  1120         if (drawBuffersIsEnabled) {
  1121             gl->fDrawBuffers(mGLMaxDrawBuffers, currentDrawBuffers);
  1124         gl->fColorMask(mColorWriteMask[0],
  1125                        mColorWriteMask[1],
  1126                        mColorWriteMask[2],
  1127                        mColorWriteMask[3]);
  1128         gl->fClearColor(mColorClearValue[0],
  1129                         mColorClearValue[1],
  1130                         mColorClearValue[2],
  1131                         mColorClearValue[3]);
  1134     if (initializeDepthBuffer) {
  1135         gl->fDepthMask(mDepthWriteMask);
  1136         gl->fClearDepth(mDepthClearValue);
  1139     if (initializeStencilBuffer) {
  1140         gl->fStencilMaskSeparate(LOCAL_GL_FRONT, mStencilWriteMaskFront);
  1141         gl->fStencilMaskSeparate(LOCAL_GL_BACK,  mStencilWriteMaskBack);
  1142         gl->fClearStencil(mStencilClearValue);
  1146 // For an overview of how WebGL compositing works, see:
  1147 // https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing
  1148 bool
  1149 WebGLContext::PresentScreenBuffer()
  1151     if (IsContextLost()) {
  1152         return false;
  1155     if (!mShouldPresent) {
  1156         return false;
  1159     gl->MakeCurrent();
  1160     MOZ_ASSERT(!mBackbufferNeedsClear);
  1161     if (!gl->PublishFrame()) {
  1162         this->ForceLoseContext();
  1163         return false;
  1166     if (!mOptions.preserveDrawingBuffer) {
  1167         mBackbufferNeedsClear = true;
  1170     mShouldPresent = false;
  1172     return true;
  1175 void
  1176 WebGLContext::DummyFramebufferOperation(const char *info)
  1178     GLenum status = CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
  1179     if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
  1180         ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
  1183 // We use this timer for many things. Here are the things that it is activated for:
  1184 // 1) If a script is using the MOZ_WEBGL_lose_context extension.
  1185 // 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
  1186 //    CONTEXT_LOST_WEBGL error has been triggered.
  1187 // 3) If we are using ANGLE, or anything that supports ARB_robustness, query the
  1188 //    GPU periodically to see if the reset status bit has been set.
  1189 // In all of these situations, we use this timer to send the script context lost
  1190 // and restored events asynchronously. For example, if it triggers a context loss,
  1191 // the webglcontextlost event will be sent to it the next time the robustness timer
  1192 // fires.
  1193 // Note that this timer mechanism is not used unless one of these 3 criteria
  1194 // are met.
  1195 // At a bare minimum, from context lost to context restores, it would take 3
  1196 // full timer iterations: detection, webglcontextlost, webglcontextrestored.
  1197 void
  1198 WebGLContext::RobustnessTimerCallback(nsITimer* timer)
  1200     TerminateContextLossTimer();
  1202     if (!mCanvasElement) {
  1203         // the canvas is gone. That happens when the page was closed before we got
  1204         // this timer event. In this case, there's nothing to do here, just don't crash.
  1205         return;
  1208     // If the context has been lost and we're waiting for it to be restored, do
  1209     // that now.
  1210     if (mContextStatus == ContextLostAwaitingEvent) {
  1211         bool defaultAction;
  1212         nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
  1213                                              static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
  1214                                              NS_LITERAL_STRING("webglcontextlost"),
  1215                                              true,
  1216                                              true,
  1217                                              &defaultAction);
  1219         // If the script didn't handle the event, we don't allow restores.
  1220         if (defaultAction)
  1221             mAllowRestore = false;
  1223         // If the script handled the event and we are allowing restores, then
  1224         // mark it to be restored. Otherwise, leave it as context lost
  1225         // (unusable).
  1226         if (!defaultAction && mAllowRestore) {
  1227             ForceRestoreContext();
  1228             // Restart the timer so that it will be restored on the next
  1229             // callback.
  1230             SetupContextLossTimer();
  1231         } else {
  1232             mContextStatus = ContextLost;
  1234     } else if (mContextStatus == ContextLostAwaitingRestore) {
  1235         // Try to restore the context. If it fails, try again later.
  1236         if (NS_FAILED(SetDimensions(mWidth, mHeight))) {
  1237             SetupContextLossTimer();
  1238             return;
  1240         mContextStatus = ContextNotLost;
  1241         nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
  1242                                              static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
  1243                                              NS_LITERAL_STRING("webglcontextrestored"),
  1244                                              true,
  1245                                              true);
  1246         // Set all flags back to the state they were in before the context was
  1247         // lost.
  1248         mEmitContextLostErrorOnce = true;
  1249         mAllowRestore = true;
  1252     MaybeRestoreContext();
  1253     return;
  1256 void
  1257 WebGLContext::MaybeRestoreContext()
  1259     // Don't try to handle it if we already know it's busted.
  1260     if (mContextStatus != ContextNotLost || gl == nullptr)
  1261         return;
  1263     bool isEGL = gl->GetContextType() == gl::GLContextType::EGL,
  1264          isANGLE = gl->IsANGLE();
  1266     GLContext::ContextResetARB resetStatus = GLContext::CONTEXT_NO_ERROR;
  1267     if (mHasRobustness) {
  1268         gl->MakeCurrent();
  1269         resetStatus = (GLContext::ContextResetARB) gl->fGetGraphicsResetStatus();
  1270     } else if (isEGL) {
  1271         // Simulate a ARB_robustness guilty context loss for when we
  1272         // get an EGL_CONTEXT_LOST error. It may not actually be guilty,
  1273         // but we can't make any distinction, so we must assume the worst
  1274         // case.
  1275         if (!gl->MakeCurrent(true) && gl->IsContextLost()) {
  1276             resetStatus = GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB;
  1280     if (resetStatus != GLContext::CONTEXT_NO_ERROR) {
  1281         // It's already lost, but clean up after it and signal to JS that it is
  1282         // lost.
  1283         ForceLoseContext();
  1286     switch (resetStatus) {
  1287         case GLContext::CONTEXT_NO_ERROR:
  1288             // If there has been activity since the timer was set, it's possible
  1289             // that we did or are going to miss something, so clear this flag and
  1290             // run it again some time later.
  1291             if (mDrawSinceContextLossTimerSet)
  1292                 SetupContextLossTimer();
  1293             break;
  1294         case GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB:
  1295             NS_WARNING("WebGL content on the page caused the graphics card to reset; not restoring the context");
  1296             mAllowRestore = false;
  1297             break;
  1298         case GLContext::CONTEXT_INNOCENT_CONTEXT_RESET_ARB:
  1299             break;
  1300         case GLContext::CONTEXT_UNKNOWN_CONTEXT_RESET_ARB:
  1301             NS_WARNING("WebGL content on the page might have caused the graphics card to reset");
  1302             if (isEGL && isANGLE) {
  1303                 // If we're using ANGLE, we ONLY get back UNKNOWN context resets, including for guilty contexts.
  1304                 // This means that we can't restore it or risk restoring a guilty context. Should this ever change,
  1305                 // we can get rid of the whole IsANGLE() junk from GLContext.h since, as of writing, this is the
  1306                 // only use for it. See ANGLE issue 261.
  1307                 mAllowRestore = false;
  1309             break;
  1313 void
  1314 WebGLContext::ForceLoseContext()
  1316     if (mContextStatus == ContextLostAwaitingEvent)
  1317         return;
  1319     mContextStatus = ContextLostAwaitingEvent;
  1320     // Queue up a task to restore the event.
  1321     SetupContextLossTimer();
  1322     DestroyResourcesAndContext();
  1325 void
  1326 WebGLContext::ForceRestoreContext()
  1328     mContextStatus = ContextLostAwaitingRestore;
  1331 void
  1332 WebGLContext::MakeContextCurrent() const { gl->MakeCurrent(); }
  1334 mozilla::TemporaryRef<mozilla::gfx::SourceSurface>
  1335 WebGLContext::GetSurfaceSnapshot(bool* aPremultAlpha)
  1337     if (!gl)
  1338         return nullptr;
  1340     nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
  1341                                                          gfxImageFormat::ARGB32,
  1342                                                          mWidth * 4, 0, false);
  1343     if (surf->CairoStatus() != 0) {
  1344         return nullptr;
  1347     gl->MakeCurrent();
  1349         ScopedBindFramebuffer autoFB(gl, 0);
  1350         ClearBackbufferIfNeeded();
  1351         ReadPixelsIntoImageSurface(gl, surf);
  1354     if (aPremultAlpha) {
  1355         *aPremultAlpha = true;
  1357     bool srcPremultAlpha = mOptions.premultipliedAlpha;
  1358     if (!srcPremultAlpha) {
  1359         if (aPremultAlpha) {
  1360             *aPremultAlpha = false;
  1361         } else {
  1362             gfxUtils::PremultiplyImageSurface(surf);
  1363             surf->MarkDirty();
  1367     RefPtr<DrawTarget> dt =
  1368         Factory::CreateDrawTarget(BackendType::CAIRO,
  1369                                   IntSize(mWidth, mHeight),
  1370                                   SurfaceFormat::B8G8R8A8);
  1372     if (!dt) {
  1373         return nullptr;
  1376     RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surf);
  1378     Matrix m;
  1379     m.Translate(0.0, mHeight);
  1380     m.Scale(1.0, -1.0);
  1381     dt->SetTransform(m);
  1383     dt->DrawSurface(source,
  1384                     Rect(0, 0, mWidth, mHeight),
  1385                     Rect(0, 0, mWidth, mHeight),
  1386                     DrawSurfaceOptions(),
  1387                     DrawOptions(1.0f, CompositionOp::OP_SOURCE));
  1389     return dt->Snapshot();
  1392 //
  1393 // XPCOM goop
  1394 //
  1396 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext)
  1397 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
  1399 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_13(WebGLContext,
  1400   mCanvasElement,
  1401   mExtensions,
  1402   mBound2DTextures,
  1403   mBoundCubeMapTextures,
  1404   mBoundArrayBuffer,
  1405   mBoundTransformFeedbackBuffer,
  1406   mCurrentProgram,
  1407   mBoundFramebuffer,
  1408   mBoundRenderbuffer,
  1409   mBoundVertexArray,
  1410   mDefaultVertexArray,
  1411   mActiveOcclusionQuery,
  1412   mActiveTransformFeedbackQuery)
  1414 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLContext)
  1415   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  1416   NS_INTERFACE_MAP_ENTRY(nsIDOMWebGLRenderingContext)
  1417   NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
  1418   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
  1419   // If the exact way we cast to nsISupports here ever changes, fix our
  1420   // ToSupports() method.
  1421   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMWebGLRenderingContext)
  1422 NS_INTERFACE_MAP_END

mercurial