content/canvas/src/WebGLContext.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/canvas/src/WebGLContext.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1422 @@
     1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +#include "WebGLContext.h"
    1.10 +#include "WebGL1Context.h"
    1.11 +#include "WebGLObjectModel.h"
    1.12 +#include "WebGLExtensions.h"
    1.13 +#include "WebGLContextUtils.h"
    1.14 +#include "WebGLBuffer.h"
    1.15 +#include "WebGLVertexAttribData.h"
    1.16 +#include "WebGLMemoryTracker.h"
    1.17 +#include "WebGLFramebuffer.h"
    1.18 +#include "WebGLVertexArray.h"
    1.19 +#include "WebGLQuery.h"
    1.20 +
    1.21 +#include "AccessCheck.h"
    1.22 +#include "nsIConsoleService.h"
    1.23 +#include "nsServiceManagerUtils.h"
    1.24 +#include "nsIClassInfoImpl.h"
    1.25 +#include "nsContentUtils.h"
    1.26 +#include "nsIXPConnect.h"
    1.27 +#include "nsError.h"
    1.28 +#include "nsIGfxInfo.h"
    1.29 +#include "nsIWidget.h"
    1.30 +
    1.31 +#include "nsIVariant.h"
    1.32 +
    1.33 +#include "ImageEncoder.h"
    1.34 +
    1.35 +#include "gfxContext.h"
    1.36 +#include "gfxPattern.h"
    1.37 +#include "gfxUtils.h"
    1.38 +
    1.39 +#include "CanvasUtils.h"
    1.40 +#include "nsDisplayList.h"
    1.41 +
    1.42 +#include "GLContextProvider.h"
    1.43 +#include "GLContext.h"
    1.44 +#include "ScopedGLHelpers.h"
    1.45 +#include "GLReadTexImageHelper.h"
    1.46 +
    1.47 +#include "gfxCrashReporterUtils.h"
    1.48 +
    1.49 +#include "nsSVGEffects.h"
    1.50 +
    1.51 +#include "prenv.h"
    1.52 +
    1.53 +#include "mozilla/Preferences.h"
    1.54 +#include "mozilla/Services.h"
    1.55 +#include "mozilla/Telemetry.h"
    1.56 +
    1.57 +#include "nsIObserverService.h"
    1.58 +#include "mozilla/Services.h"
    1.59 +#include "mozilla/dom/WebGLRenderingContextBinding.h"
    1.60 +#include "mozilla/dom/BindingUtils.h"
    1.61 +#include "mozilla/dom/ImageData.h"
    1.62 +#include "mozilla/ProcessPriorityManager.h"
    1.63 +#include "mozilla/EnumeratedArrayCycleCollection.h"
    1.64 +
    1.65 +#include "Layers.h"
    1.66 +
    1.67 +#ifdef MOZ_WIDGET_GONK
    1.68 +#include "mozilla/layers/ShadowLayers.h"
    1.69 +#endif
    1.70 +
    1.71 +using namespace mozilla;
    1.72 +using namespace mozilla::dom;
    1.73 +using namespace mozilla::gfx;
    1.74 +using namespace mozilla::gl;
    1.75 +using namespace mozilla::layers;
    1.76 +
    1.77 +NS_IMETHODIMP
    1.78 +WebGLMemoryPressureObserver::Observe(nsISupports* aSubject,
    1.79 +                                     const char* aTopic,
    1.80 +                                     const char16_t* aSomeData)
    1.81 +{
    1.82 +    if (strcmp(aTopic, "memory-pressure"))
    1.83 +        return NS_OK;
    1.84 +
    1.85 +    bool wantToLoseContext = true;
    1.86 +
    1.87 +    if (!mContext->mCanLoseContextInForeground &&
    1.88 +        ProcessPriorityManager::CurrentProcessIsForeground())
    1.89 +        wantToLoseContext = false;
    1.90 +    else if (!nsCRT::strcmp(aSomeData,
    1.91 +                            MOZ_UTF16("heap-minimize")))
    1.92 +        wantToLoseContext = mContext->mLoseContextOnHeapMinimize;
    1.93 +
    1.94 +    if (wantToLoseContext)
    1.95 +        mContext->ForceLoseContext();
    1.96 +
    1.97 +    return NS_OK;
    1.98 +}
    1.99 +
   1.100 +
   1.101 +WebGLContextOptions::WebGLContextOptions()
   1.102 +    : alpha(true), depth(true), stencil(false),
   1.103 +      premultipliedAlpha(true), antialias(true),
   1.104 +      preserveDrawingBuffer(false)
   1.105 +{
   1.106 +    // Set default alpha state based on preference.
   1.107 +    if (Preferences::GetBool("webgl.default-no-alpha", false))
   1.108 +        alpha = false;
   1.109 +}
   1.110 +
   1.111 +WebGLContext::WebGLContext()
   1.112 +    : gl(nullptr)
   1.113 +{
   1.114 +    SetIsDOMBinding();
   1.115 +
   1.116 +    mGeneration = 0;
   1.117 +    mInvalidated = false;
   1.118 +    mShouldPresent = true;
   1.119 +    mResetLayer = true;
   1.120 +    mOptionsFrozen = false;
   1.121 +
   1.122 +    mActiveTexture = 0;
   1.123 +    mPixelStoreFlipY = false;
   1.124 +    mPixelStorePremultiplyAlpha = false;
   1.125 +    mPixelStoreColorspaceConversion = BROWSER_DEFAULT_WEBGL;
   1.126 +
   1.127 +    mShaderValidation = true;
   1.128 +
   1.129 +    mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded;
   1.130 +
   1.131 +    mVertexAttrib0Vector[0] = 0;
   1.132 +    mVertexAttrib0Vector[1] = 0;
   1.133 +    mVertexAttrib0Vector[2] = 0;
   1.134 +    mVertexAttrib0Vector[3] = 1;
   1.135 +    mFakeVertexAttrib0BufferObjectVector[0] = 0;
   1.136 +    mFakeVertexAttrib0BufferObjectVector[1] = 0;
   1.137 +    mFakeVertexAttrib0BufferObjectVector[2] = 0;
   1.138 +    mFakeVertexAttrib0BufferObjectVector[3] = 1;
   1.139 +    mFakeVertexAttrib0BufferObjectSize = 0;
   1.140 +    mFakeVertexAttrib0BufferObject = 0;
   1.141 +    mFakeVertexAttrib0BufferStatus = WebGLVertexAttrib0Status::Default;
   1.142 +
   1.143 +    mViewportX = 0;
   1.144 +    mViewportY = 0;
   1.145 +    mViewportWidth = 0;
   1.146 +    mViewportHeight = 0;
   1.147 +
   1.148 +    mScissorTestEnabled = 0;
   1.149 +    mDitherEnabled = 1;
   1.150 +    mRasterizerDiscardEnabled = 0; // OpenGL ES 3.0 spec p244
   1.151 +
   1.152 +    // initialize some GL values: we're going to get them from the GL and use them as the sizes of arrays,
   1.153 +    // so in case glGetIntegerv leaves them uninitialized because of a GL bug, we would have very weird crashes.
   1.154 +    mGLMaxVertexAttribs = 0;
   1.155 +    mGLMaxTextureUnits = 0;
   1.156 +    mGLMaxTextureSize = 0;
   1.157 +    mGLMaxCubeMapTextureSize = 0;
   1.158 +    mGLMaxRenderbufferSize = 0;
   1.159 +    mGLMaxTextureImageUnits = 0;
   1.160 +    mGLMaxVertexTextureImageUnits = 0;
   1.161 +    mGLMaxVaryingVectors = 0;
   1.162 +    mGLMaxFragmentUniformVectors = 0;
   1.163 +    mGLMaxVertexUniformVectors = 0;
   1.164 +    mGLMaxColorAttachments = 1;
   1.165 +    mGLMaxDrawBuffers = 1;
   1.166 +    mGLMaxTransformFeedbackSeparateAttribs = 0;
   1.167 +
   1.168 +    // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13
   1.169 +    mPixelStorePackAlignment = 4;
   1.170 +    mPixelStoreUnpackAlignment = 4;
   1.171 +
   1.172 +    WebGLMemoryTracker::AddWebGLContext(this);
   1.173 +
   1.174 +    mAllowRestore = true;
   1.175 +    mContextLossTimerRunning = false;
   1.176 +    mDrawSinceContextLossTimerSet = false;
   1.177 +    mContextRestorer = do_CreateInstance("@mozilla.org/timer;1");
   1.178 +    mContextStatus = ContextNotLost;
   1.179 +    mContextLostErrorSet = false;
   1.180 +    mLoseContextOnHeapMinimize = false;
   1.181 +    mCanLoseContextInForeground = true;
   1.182 +
   1.183 +    mAlreadyGeneratedWarnings = 0;
   1.184 +    mAlreadyWarnedAboutFakeVertexAttrib0 = false;
   1.185 +    mAlreadyWarnedAboutViewportLargerThanDest = false;
   1.186 +    mMaxWarnings = Preferences::GetInt("webgl.max-warnings-per-context", 32);
   1.187 +    if (mMaxWarnings < -1)
   1.188 +    {
   1.189 +        GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
   1.190 +        mMaxWarnings = 0;
   1.191 +    }
   1.192 +
   1.193 +    mLastUseIndex = 0;
   1.194 +
   1.195 +    InvalidateBufferFetching();
   1.196 +
   1.197 +    mBackbufferNeedsClear = true;
   1.198 +
   1.199 +    mDisableFragHighP = false;
   1.200 +
   1.201 +    mDrawCallsSinceLastFlush = 0;
   1.202 +}
   1.203 +
   1.204 +WebGLContext::~WebGLContext()
   1.205 +{
   1.206 +    DestroyResourcesAndContext();
   1.207 +    WebGLMemoryTracker::RemoveWebGLContext(this);
   1.208 +    TerminateContextLossTimer();
   1.209 +    mContextRestorer = nullptr;
   1.210 +}
   1.211 +
   1.212 +void
   1.213 +WebGLContext::DestroyResourcesAndContext()
   1.214 +{
   1.215 +    if (mMemoryPressureObserver) {
   1.216 +        nsCOMPtr<nsIObserverService> observerService
   1.217 +            = mozilla::services::GetObserverService();
   1.218 +        if (observerService) {
   1.219 +            observerService->RemoveObserver(mMemoryPressureObserver,
   1.220 +                                            "memory-pressure");
   1.221 +        }
   1.222 +        mMemoryPressureObserver = nullptr;
   1.223 +    }
   1.224 +
   1.225 +    if (!gl)
   1.226 +        return;
   1.227 +
   1.228 +    gl->MakeCurrent();
   1.229 +
   1.230 +    mBound2DTextures.Clear();
   1.231 +    mBoundCubeMapTextures.Clear();
   1.232 +    mBoundArrayBuffer = nullptr;
   1.233 +    mBoundTransformFeedbackBuffer = nullptr;
   1.234 +    mCurrentProgram = nullptr;
   1.235 +    mBoundFramebuffer = nullptr;
   1.236 +    mActiveOcclusionQuery = nullptr;
   1.237 +    mBoundRenderbuffer = nullptr;
   1.238 +    mBoundVertexArray = nullptr;
   1.239 +    mDefaultVertexArray = nullptr;
   1.240 +
   1.241 +    while (!mTextures.isEmpty())
   1.242 +        mTextures.getLast()->DeleteOnce();
   1.243 +    while (!mVertexArrays.isEmpty())
   1.244 +        mVertexArrays.getLast()->DeleteOnce();
   1.245 +    while (!mBuffers.isEmpty())
   1.246 +        mBuffers.getLast()->DeleteOnce();
   1.247 +    while (!mRenderbuffers.isEmpty())
   1.248 +        mRenderbuffers.getLast()->DeleteOnce();
   1.249 +    while (!mFramebuffers.isEmpty())
   1.250 +        mFramebuffers.getLast()->DeleteOnce();
   1.251 +    while (!mShaders.isEmpty())
   1.252 +        mShaders.getLast()->DeleteOnce();
   1.253 +    while (!mPrograms.isEmpty())
   1.254 +        mPrograms.getLast()->DeleteOnce();
   1.255 +    while (!mQueries.isEmpty())
   1.256 +        mQueries.getLast()->DeleteOnce();
   1.257 +
   1.258 +    mBlackOpaqueTexture2D = nullptr;
   1.259 +    mBlackOpaqueTextureCubeMap = nullptr;
   1.260 +    mBlackTransparentTexture2D = nullptr;
   1.261 +    mBlackTransparentTextureCubeMap = nullptr;
   1.262 +
   1.263 +    if (mFakeVertexAttrib0BufferObject) {
   1.264 +        gl->fDeleteBuffers(1, &mFakeVertexAttrib0BufferObject);
   1.265 +    }
   1.266 +
   1.267 +    // disable all extensions except "WEBGL_lose_context". see bug #927969
   1.268 +    // spec: http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.15.2
   1.269 +    for (size_t i = 0; i < size_t(WebGLExtensionID::Max); ++i) {
   1.270 +        WebGLExtensionID extension = WebGLExtensionID(i);
   1.271 +
   1.272 +        if (!IsExtensionEnabled(extension) || (extension == WebGLExtensionID::WEBGL_lose_context))
   1.273 +            continue;
   1.274 +
   1.275 +        mExtensions[extension]->MarkLost();
   1.276 +        mExtensions[extension] = nullptr;
   1.277 +    }
   1.278 +
   1.279 +    // We just got rid of everything, so the context had better
   1.280 +    // have been going away.
   1.281 +#ifdef DEBUG
   1.282 +    if (gl->DebugMode()) {
   1.283 +        printf_stderr("--- WebGL context destroyed: %p\n", gl.get());
   1.284 +    }
   1.285 +#endif
   1.286 +
   1.287 +    gl = nullptr;
   1.288 +}
   1.289 +
   1.290 +void
   1.291 +WebGLContext::Invalidate()
   1.292 +{
   1.293 +    if (mInvalidated)
   1.294 +        return;
   1.295 +
   1.296 +    if (!mCanvasElement)
   1.297 +        return;
   1.298 +
   1.299 +    nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
   1.300 +
   1.301 +    mInvalidated = true;
   1.302 +    mCanvasElement->InvalidateCanvasContent(nullptr);
   1.303 +}
   1.304 +
   1.305 +//
   1.306 +// nsICanvasRenderingContextInternal
   1.307 +//
   1.308 +
   1.309 +NS_IMETHODIMP
   1.310 +WebGLContext::SetContextOptions(JSContext* aCx, JS::Handle<JS::Value> aOptions)
   1.311 +{
   1.312 +    if (aOptions.isNullOrUndefined() && mOptionsFrozen) {
   1.313 +        return NS_OK;
   1.314 +    }
   1.315 +
   1.316 +    WebGLContextAttributes attributes;
   1.317 +    NS_ENSURE_TRUE(attributes.Init(aCx, aOptions), NS_ERROR_UNEXPECTED);
   1.318 +
   1.319 +    WebGLContextOptions newOpts;
   1.320 +
   1.321 +    newOpts.stencil = attributes.mStencil;
   1.322 +    newOpts.depth = attributes.mDepth;
   1.323 +    newOpts.premultipliedAlpha = attributes.mPremultipliedAlpha;
   1.324 +    newOpts.antialias = attributes.mAntialias;
   1.325 +    newOpts.preserveDrawingBuffer = attributes.mPreserveDrawingBuffer;
   1.326 +    if (attributes.mAlpha.WasPassed()) {
   1.327 +      newOpts.alpha = attributes.mAlpha.Value();
   1.328 +    }
   1.329 +
   1.330 +    // enforce that if stencil is specified, we also give back depth
   1.331 +    newOpts.depth |= newOpts.stencil;
   1.332 +
   1.333 +#if 0
   1.334 +    GenerateWarning("aaHint: %d stencil: %d depth: %d alpha: %d premult: %d preserve: %d\n",
   1.335 +               newOpts.antialias ? 1 : 0,
   1.336 +               newOpts.stencil ? 1 : 0,
   1.337 +               newOpts.depth ? 1 : 0,
   1.338 +               newOpts.alpha ? 1 : 0,
   1.339 +               newOpts.premultipliedAlpha ? 1 : 0,
   1.340 +               newOpts.preserveDrawingBuffer ? 1 : 0);
   1.341 +#endif
   1.342 +
   1.343 +    if (mOptionsFrozen && newOpts != mOptions) {
   1.344 +        // Error if the options are already frozen, and the ones that were asked for
   1.345 +        // aren't the same as what they were originally.
   1.346 +        return NS_ERROR_FAILURE;
   1.347 +    }
   1.348 +
   1.349 +    mOptions = newOpts;
   1.350 +    return NS_OK;
   1.351 +}
   1.352 +
   1.353 +#ifdef DEBUG
   1.354 +int32_t
   1.355 +WebGLContext::GetWidth() const
   1.356 +{
   1.357 +  return mWidth;
   1.358 +}
   1.359 +
   1.360 +int32_t
   1.361 +WebGLContext::GetHeight() const
   1.362 +{
   1.363 +  return mHeight;
   1.364 +}
   1.365 +#endif
   1.366 +
   1.367 +NS_IMETHODIMP
   1.368 +WebGLContext::SetDimensions(int32_t width, int32_t height)
   1.369 +{
   1.370 +    // Early error return cases
   1.371 +
   1.372 +    if (width < 0 || height < 0) {
   1.373 +        GenerateWarning("Canvas size is too large (seems like a negative value wrapped)");
   1.374 +        return NS_ERROR_OUT_OF_MEMORY;
   1.375 +    }
   1.376 +
   1.377 +    if (!GetCanvas())
   1.378 +        return NS_ERROR_FAILURE;
   1.379 +
   1.380 +    // Early success return cases
   1.381 +
   1.382 +    GetCanvas()->InvalidateCanvas();
   1.383 +
   1.384 +    if (gl && mWidth == width && mHeight == height)
   1.385 +        return NS_OK;
   1.386 +
   1.387 +    // Zero-sized surfaces can cause problems.
   1.388 +    if (width == 0) {
   1.389 +        width = 1;
   1.390 +    }
   1.391 +    if (height == 0) {
   1.392 +        height = 1;
   1.393 +    }
   1.394 +
   1.395 +    // If we already have a gl context, then we just need to resize it
   1.396 +    if (gl) {
   1.397 +        MakeContextCurrent();
   1.398 +
   1.399 +        // If we've already drawn, we should commit the current buffer.
   1.400 +        PresentScreenBuffer();
   1.401 +
   1.402 +        // ResizeOffscreen scraps the current prod buffer before making a new one.
   1.403 +        gl->ResizeOffscreen(gfx::IntSize(width, height)); // Doesn't matter if it succeeds (soft-fail)
   1.404 +        // It's unlikely that we'll get a proper-sized context if we recreate if we didn't on resize
   1.405 +
   1.406 +        // everything's good, we're done here
   1.407 +        mWidth = gl->OffscreenSize().width;
   1.408 +        mHeight = gl->OffscreenSize().height;
   1.409 +        mResetLayer = true;
   1.410 +
   1.411 +        mBackbufferNeedsClear = true;
   1.412 +
   1.413 +        return NS_OK;
   1.414 +    }
   1.415 +
   1.416 +    // End of early return cases.
   1.417 +    // At this point we know that we're not just resizing an existing context,
   1.418 +    // we are initializing a new context.
   1.419 +
   1.420 +    // if we exceeded either the global or the per-principal limit for WebGL contexts,
   1.421 +    // lose the oldest-used context now to free resources. Note that we can't do that
   1.422 +    // in the WebGLContext constructor as we don't have a canvas element yet there.
   1.423 +    // Here is the right place to do so, as we are about to create the OpenGL context
   1.424 +    // and that is what can fail if we already have too many.
   1.425 +    LoseOldestWebGLContextIfLimitExceeded();
   1.426 +
   1.427 +    // Get some prefs for some preferred/overriden things
   1.428 +    NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
   1.429 +
   1.430 +#ifdef XP_WIN
   1.431 +    bool preferEGL =
   1.432 +        Preferences::GetBool("webgl.prefer-egl", false);
   1.433 +    bool preferOpenGL =
   1.434 +        Preferences::GetBool("webgl.prefer-native-gl", false);
   1.435 +#endif
   1.436 +    bool forceEnabled =
   1.437 +        Preferences::GetBool("webgl.force-enabled", false);
   1.438 +    bool disabled =
   1.439 +        Preferences::GetBool("webgl.disabled", false);
   1.440 +    bool prefer16bit =
   1.441 +        Preferences::GetBool("webgl.prefer-16bpp", false);
   1.442 +
   1.443 +    ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
   1.444 +
   1.445 +    if (disabled)
   1.446 +        return NS_ERROR_FAILURE;
   1.447 +
   1.448 +    // We're going to create an entirely new context.  If our
   1.449 +    // generation is not 0 right now (that is, if this isn't the first
   1.450 +    // context we're creating), we may have to dispatch a context lost
   1.451 +    // event.
   1.452 +
   1.453 +    // If incrementing the generation would cause overflow,
   1.454 +    // don't allow it.  Allowing this would allow us to use
   1.455 +    // resource handles created from older context generations.
   1.456 +    if (!(mGeneration + 1).isValid())
   1.457 +        return NS_ERROR_FAILURE; // exit without changing the value of mGeneration
   1.458 +
   1.459 +    SurfaceCaps caps;
   1.460 +
   1.461 +    caps.color = true;
   1.462 +    caps.alpha = mOptions.alpha;
   1.463 +    caps.depth = mOptions.depth;
   1.464 +    caps.stencil = mOptions.stencil;
   1.465 +
   1.466 +    // we should really have this behind a
   1.467 +    // |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
   1.468 +    // for now it's just behind a pref for testing/evaluation.
   1.469 +    caps.bpp16 = prefer16bit;
   1.470 +
   1.471 +    caps.preserve = mOptions.preserveDrawingBuffer;
   1.472 +
   1.473 +#ifdef MOZ_WIDGET_GONK
   1.474 +    nsIWidget *docWidget = nsContentUtils::WidgetForDocument(mCanvasElement->OwnerDoc());
   1.475 +    if (docWidget) {
   1.476 +        layers::LayerManager *layerManager = docWidget->GetLayerManager();
   1.477 +        if (layerManager) {
   1.478 +            // XXX we really want "AsSurfaceAllocator" here for generality
   1.479 +            layers::ShadowLayerForwarder *forwarder = layerManager->AsShadowForwarder();
   1.480 +            if (forwarder) {
   1.481 +                caps.surfaceAllocator = static_cast<layers::ISurfaceAllocator*>(forwarder);
   1.482 +            }
   1.483 +        }
   1.484 +    }
   1.485 +#endif
   1.486 +
   1.487 +    bool forceMSAA =
   1.488 +        Preferences::GetBool("webgl.msaa-force", false);
   1.489 +
   1.490 +    int32_t status;
   1.491 +    nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
   1.492 +    if (mOptions.antialias &&
   1.493 +        gfxInfo &&
   1.494 +        NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_MSAA, &status))) {
   1.495 +        if (status == nsIGfxInfo::FEATURE_NO_INFO || forceMSAA) {
   1.496 +            caps.antialias = true;
   1.497 +        }
   1.498 +    }
   1.499 +
   1.500 +#ifdef XP_WIN
   1.501 +    if (PR_GetEnv("MOZ_WEBGL_PREFER_EGL")) {
   1.502 +        preferEGL = true;
   1.503 +    }
   1.504 +#endif
   1.505 +
   1.506 +    // Ask GfxInfo about what we should use
   1.507 +    bool useOpenGL = true;
   1.508 +
   1.509 +#ifdef XP_WIN
   1.510 +    bool useANGLE = true;
   1.511 +#endif
   1.512 +
   1.513 +    if (gfxInfo && !forceEnabled) {
   1.514 +        if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_OPENGL, &status))) {
   1.515 +            if (status != nsIGfxInfo::FEATURE_NO_INFO) {
   1.516 +                useOpenGL = false;
   1.517 +            }
   1.518 +        }
   1.519 +#ifdef XP_WIN
   1.520 +        if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_ANGLE, &status))) {
   1.521 +            if (status != nsIGfxInfo::FEATURE_NO_INFO) {
   1.522 +                useANGLE = false;
   1.523 +            }
   1.524 +        }
   1.525 +#endif
   1.526 +    }
   1.527 +
   1.528 +#ifdef XP_WIN
   1.529 +    // allow forcing GL and not EGL/ANGLE
   1.530 +    if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL")) {
   1.531 +        preferEGL = false;
   1.532 +        useANGLE = false;
   1.533 +        useOpenGL = true;
   1.534 +    }
   1.535 +#endif
   1.536 +
   1.537 +    gfxIntSize size(width, height);
   1.538 +
   1.539 +#ifdef XP_WIN
   1.540 +    // if we want EGL, try it now
   1.541 +    if (!gl && (preferEGL || useANGLE) && !preferOpenGL) {
   1.542 +        gl = gl::GLContextProviderEGL::CreateOffscreen(size, caps);
   1.543 +        if (!gl || !InitAndValidateGL()) {
   1.544 +            GenerateWarning("Error during ANGLE OpenGL ES initialization");
   1.545 +            return NS_ERROR_FAILURE;
   1.546 +        }
   1.547 +    }
   1.548 +#endif
   1.549 +
   1.550 +    // try the default provider, whatever that is
   1.551 +    if (!gl && useOpenGL) {
   1.552 +        gl = gl::GLContextProvider::CreateOffscreen(size, caps);
   1.553 +        if (gl && !InitAndValidateGL()) {
   1.554 +            GenerateWarning("Error during OpenGL initialization");
   1.555 +            return NS_ERROR_FAILURE;
   1.556 +        }
   1.557 +    }
   1.558 +
   1.559 +    if (!gl) {
   1.560 +        GenerateWarning("Can't get a usable WebGL context");
   1.561 +        return NS_ERROR_FAILURE;
   1.562 +    }
   1.563 +
   1.564 +#ifdef DEBUG
   1.565 +    if (gl->DebugMode()) {
   1.566 +        printf_stderr("--- WebGL context created: %p\n", gl.get());
   1.567 +    }
   1.568 +#endif
   1.569 +
   1.570 +    mWidth = width;
   1.571 +    mHeight = height;
   1.572 +    mViewportWidth = width;
   1.573 +    mViewportHeight = height;
   1.574 +    mResetLayer = true;
   1.575 +    mOptionsFrozen = true;
   1.576 +
   1.577 +    mHasRobustness = gl->HasRobustness();
   1.578 +
   1.579 +    // increment the generation number
   1.580 +    ++mGeneration;
   1.581 +
   1.582 +#if 0
   1.583 +    if (mGeneration > 0) {
   1.584 +        // XXX dispatch context lost event
   1.585 +    }
   1.586 +#endif
   1.587 +
   1.588 +    MakeContextCurrent();
   1.589 +
   1.590 +    // Make sure that we clear this out, otherwise
   1.591 +    // we'll end up displaying random memory
   1.592 +    gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
   1.593 +
   1.594 +    gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f);
   1.595 +    gl->fClearDepth(1.0f);
   1.596 +    gl->fClearStencil(0);
   1.597 +
   1.598 +    mBackbufferNeedsClear = true;
   1.599 +
   1.600 +    // Clear immediately, because we need to present the cleared initial
   1.601 +    // buffer.
   1.602 +    ClearBackbufferIfNeeded();
   1.603 +
   1.604 +    mShouldPresent = true;
   1.605 +
   1.606 +    MOZ_ASSERT(gl->Caps().color == caps.color);
   1.607 +    MOZ_ASSERT(gl->Caps().alpha == caps.alpha);
   1.608 +    MOZ_ASSERT(gl->Caps().depth == caps.depth || !gl->Caps().depth);
   1.609 +    MOZ_ASSERT(gl->Caps().stencil == caps.stencil || !gl->Caps().stencil);
   1.610 +    MOZ_ASSERT(gl->Caps().antialias == caps.antialias || !gl->Caps().antialias);
   1.611 +    MOZ_ASSERT(gl->Caps().preserve == caps.preserve);
   1.612 +
   1.613 +    reporter.SetSuccessful();
   1.614 +    return NS_OK;
   1.615 +}
   1.616 +
   1.617 +void
   1.618 +WebGLContext::ClearBackbufferIfNeeded()
   1.619 +{
   1.620 +    if (!mBackbufferNeedsClear)
   1.621 +        return;
   1.622 +
   1.623 +#ifdef DEBUG
   1.624 +    gl->MakeCurrent();
   1.625 +
   1.626 +    GLuint fb = 0;
   1.627 +    gl->GetUIntegerv(LOCAL_GL_FRAMEBUFFER_BINDING, &fb);
   1.628 +    MOZ_ASSERT(fb == 0);
   1.629 +#endif
   1.630 +
   1.631 +    ClearScreen();
   1.632 +
   1.633 +    mBackbufferNeedsClear = false;
   1.634 +}
   1.635 +
   1.636 +void WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
   1.637 +{
   1.638 +#ifdef MOZ_GFX_OPTIMIZE_MOBILE
   1.639 +    // some mobile devices can't have more than 8 GL contexts overall
   1.640 +    const size_t kMaxWebGLContextsPerPrincipal = 2;
   1.641 +    const size_t kMaxWebGLContexts             = 4;
   1.642 +#else
   1.643 +    const size_t kMaxWebGLContextsPerPrincipal = 16;
   1.644 +    const size_t kMaxWebGLContexts             = 32;
   1.645 +#endif
   1.646 +    MOZ_ASSERT(kMaxWebGLContextsPerPrincipal < kMaxWebGLContexts);
   1.647 +
   1.648 +    // it's important to update the index on a new context before losing old contexts,
   1.649 +    // otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
   1.650 +    // when choosing which one to lose first.
   1.651 +    UpdateLastUseIndex();
   1.652 +
   1.653 +    WebGLMemoryTracker::ContextsArrayType &contexts
   1.654 +      = WebGLMemoryTracker::Contexts();
   1.655 +
   1.656 +    // quick exit path, should cover a majority of cases
   1.657 +    if (contexts.Length() <= kMaxWebGLContextsPerPrincipal) {
   1.658 +        return;
   1.659 +    }
   1.660 +
   1.661 +    // note that here by "context" we mean "non-lost context". See the check for
   1.662 +    // IsContextLost() below. Indeed, the point of this function is to maybe lose
   1.663 +    // some currently non-lost context.
   1.664 +
   1.665 +    uint64_t oldestIndex = UINT64_MAX;
   1.666 +    uint64_t oldestIndexThisPrincipal = UINT64_MAX;
   1.667 +    const WebGLContext *oldestContext = nullptr;
   1.668 +    const WebGLContext *oldestContextThisPrincipal = nullptr;
   1.669 +    size_t numContexts = 0;
   1.670 +    size_t numContextsThisPrincipal = 0;
   1.671 +
   1.672 +    for(size_t i = 0; i < contexts.Length(); ++i) {
   1.673 +
   1.674 +        // don't want to lose ourselves.
   1.675 +        if (contexts[i] == this)
   1.676 +            continue;
   1.677 +
   1.678 +        if (contexts[i]->IsContextLost())
   1.679 +            continue;
   1.680 +
   1.681 +        if (!contexts[i]->GetCanvas()) {
   1.682 +            // Zombie context: the canvas is already destroyed, but something else
   1.683 +            // (typically the compositor) is still holding on to the context.
   1.684 +            // Killing zombies is a no-brainer.
   1.685 +            const_cast<WebGLContext*>(contexts[i])->LoseContext();
   1.686 +            continue;
   1.687 +        }
   1.688 +
   1.689 +        numContexts++;
   1.690 +        if (contexts[i]->mLastUseIndex < oldestIndex) {
   1.691 +            oldestIndex = contexts[i]->mLastUseIndex;
   1.692 +            oldestContext = contexts[i];
   1.693 +        }
   1.694 +
   1.695 +        nsIPrincipal *ourPrincipal = GetCanvas()->NodePrincipal();
   1.696 +        nsIPrincipal *theirPrincipal = contexts[i]->GetCanvas()->NodePrincipal();
   1.697 +        bool samePrincipal;
   1.698 +        nsresult rv = ourPrincipal->Equals(theirPrincipal, &samePrincipal);
   1.699 +        if (NS_SUCCEEDED(rv) && samePrincipal) {
   1.700 +            numContextsThisPrincipal++;
   1.701 +            if (contexts[i]->mLastUseIndex < oldestIndexThisPrincipal) {
   1.702 +                oldestIndexThisPrincipal = contexts[i]->mLastUseIndex;
   1.703 +                oldestContextThisPrincipal = contexts[i];
   1.704 +            }
   1.705 +        }
   1.706 +    }
   1.707 +
   1.708 +    if (numContextsThisPrincipal > kMaxWebGLContextsPerPrincipal) {
   1.709 +        GenerateWarning("Exceeded %d live WebGL contexts for this principal, losing the "
   1.710 +                        "least recently used one.", kMaxWebGLContextsPerPrincipal);
   1.711 +        MOZ_ASSERT(oldestContextThisPrincipal); // if we reach this point, this can't be null
   1.712 +        const_cast<WebGLContext*>(oldestContextThisPrincipal)->LoseContext();
   1.713 +    } else if (numContexts > kMaxWebGLContexts) {
   1.714 +        GenerateWarning("Exceeded %d live WebGL contexts, losing the least recently used one.",
   1.715 +                        kMaxWebGLContexts);
   1.716 +        MOZ_ASSERT(oldestContext); // if we reach this point, this can't be null
   1.717 +        const_cast<WebGLContext*>(oldestContext)->LoseContext();
   1.718 +    }
   1.719 +}
   1.720 +
   1.721 +void
   1.722 +WebGLContext::GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat)
   1.723 +{
   1.724 +    *aImageBuffer = nullptr;
   1.725 +    *aFormat = 0;
   1.726 +
   1.727 +    // Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
   1.728 +    bool premult;
   1.729 +    RefPtr<SourceSurface> snapshot =
   1.730 +      GetSurfaceSnapshot(mOptions.premultipliedAlpha ? nullptr : &premult);
   1.731 +    if (!snapshot) {
   1.732 +        return;
   1.733 +    }
   1.734 +    MOZ_ASSERT(mOptions.premultipliedAlpha || !premult, "We must get unpremult when we ask for it!");
   1.735 +
   1.736 +    RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
   1.737 +
   1.738 +    DataSourceSurface::MappedSurface map;
   1.739 +    if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map)) {
   1.740 +        return;
   1.741 +    }
   1.742 +
   1.743 +    static const fallible_t fallible = fallible_t();
   1.744 +    uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
   1.745 +    if (!imageBuffer) {
   1.746 +        dataSurface->Unmap();
   1.747 +        return;
   1.748 +    }
   1.749 +    memcpy(imageBuffer, map.mData, mWidth * mHeight * 4);
   1.750 +
   1.751 +    dataSurface->Unmap();
   1.752 +
   1.753 +    int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
   1.754 +    if (!mOptions.premultipliedAlpha) {
   1.755 +        // We need to convert to INPUT_FORMAT_RGBA, otherwise
   1.756 +        // we are automatically considered premult, and unpremult'd.
   1.757 +        // Yes, it is THAT silly.
   1.758 +        // Except for different lossy conversions by color,
   1.759 +        // we could probably just change the label, and not change the data.
   1.760 +        gfxUtils::ConvertBGRAtoRGBA(imageBuffer, mWidth * mHeight * 4);
   1.761 +        format = imgIEncoder::INPUT_FORMAT_RGBA;
   1.762 +    }
   1.763 +
   1.764 +    *aImageBuffer = imageBuffer;
   1.765 +    *aFormat = format;
   1.766 +}
   1.767 +
   1.768 +NS_IMETHODIMP
   1.769 +WebGLContext::GetInputStream(const char* aMimeType,
   1.770 +                             const char16_t* aEncoderOptions,
   1.771 +                             nsIInputStream **aStream)
   1.772 +{
   1.773 +    NS_ASSERTION(gl, "GetInputStream on invalid context?");
   1.774 +    if (!gl)
   1.775 +        return NS_ERROR_FAILURE;
   1.776 +
   1.777 +    nsCString enccid("@mozilla.org/image/encoder;2?type=");
   1.778 +    enccid += aMimeType;
   1.779 +    nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
   1.780 +    if (!encoder) {
   1.781 +        return NS_ERROR_FAILURE;
   1.782 +    }
   1.783 +
   1.784 +    nsAutoArrayPtr<uint8_t> imageBuffer;
   1.785 +    int32_t format = 0;
   1.786 +    GetImageBuffer(getter_Transfers(imageBuffer), &format);
   1.787 +    if (!imageBuffer) {
   1.788 +        return NS_ERROR_FAILURE;
   1.789 +    }
   1.790 +
   1.791 +    return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
   1.792 +                                        encoder, aEncoderOptions, aStream);
   1.793 +}
   1.794 +
   1.795 +void WebGLContext::UpdateLastUseIndex()
   1.796 +{
   1.797 +    static CheckedInt<uint64_t> sIndex = 0;
   1.798 +
   1.799 +    sIndex++;
   1.800 +
   1.801 +    // should never happen with 64-bit; trying to handle this would be riskier than
   1.802 +    // not handling it as the handler code would never get exercised.
   1.803 +    if (!sIndex.isValid()) {
   1.804 +        NS_RUNTIMEABORT("Can't believe it's been 2^64 transactions already!");
   1.805 +    }
   1.806 +
   1.807 +    mLastUseIndex = sIndex.value();
   1.808 +}
   1.809 +
   1.810 +static uint8_t gWebGLLayerUserData;
   1.811 +
   1.812 +namespace mozilla {
   1.813 +
   1.814 +class WebGLContextUserData : public LayerUserData {
   1.815 +public:
   1.816 +    WebGLContextUserData(HTMLCanvasElement *aContent)
   1.817 +        : mContent(aContent)
   1.818 +    {}
   1.819 +
   1.820 +    /* PreTransactionCallback gets called by the Layers code every time the
   1.821 +     * WebGL canvas is going to be composited.
   1.822 +     */
   1.823 +    static void PreTransactionCallback(void* data)
   1.824 +    {
   1.825 +        WebGLContextUserData* userdata = static_cast<WebGLContextUserData*>(data);
   1.826 +        HTMLCanvasElement* canvas = userdata->mContent;
   1.827 +        WebGLContext* context = static_cast<WebGLContext*>(canvas->GetContextAtIndex(0));
   1.828 +
   1.829 +        // Present our screenbuffer, if needed.
   1.830 +        context->PresentScreenBuffer();
   1.831 +        context->mDrawCallsSinceLastFlush = 0;
   1.832 +    }
   1.833 +
   1.834 +    /** DidTransactionCallback gets called by the Layers code everytime the WebGL canvas gets composite,
   1.835 +      * so it really is the right place to put actions that have to be performed upon compositing
   1.836 +      */
   1.837 +    static void DidTransactionCallback(void* aData)
   1.838 +    {
   1.839 +        WebGLContextUserData *userdata = static_cast<WebGLContextUserData*>(aData);
   1.840 +        HTMLCanvasElement *canvas = userdata->mContent;
   1.841 +        WebGLContext *context = static_cast<WebGLContext*>(canvas->GetContextAtIndex(0));
   1.842 +
   1.843 +        // Mark ourselves as no longer invalidated.
   1.844 +        context->MarkContextClean();
   1.845 +
   1.846 +        context->UpdateLastUseIndex();
   1.847 +    }
   1.848 +
   1.849 +private:
   1.850 +    nsRefPtr<HTMLCanvasElement> mContent;
   1.851 +};
   1.852 +
   1.853 +} // end namespace mozilla
   1.854 +
   1.855 +already_AddRefed<layers::CanvasLayer>
   1.856 +WebGLContext::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
   1.857 +                             CanvasLayer *aOldLayer,
   1.858 +                             LayerManager *aManager)
   1.859 +{
   1.860 +    if (IsContextLost())
   1.861 +        return nullptr;
   1.862 +
   1.863 +    if (!mResetLayer && aOldLayer &&
   1.864 +        aOldLayer->HasUserData(&gWebGLLayerUserData)) {
   1.865 +        nsRefPtr<layers::CanvasLayer> ret = aOldLayer;
   1.866 +        return ret.forget();
   1.867 +    }
   1.868 +
   1.869 +    nsRefPtr<CanvasLayer> canvasLayer = aManager->CreateCanvasLayer();
   1.870 +    if (!canvasLayer) {
   1.871 +        NS_WARNING("CreateCanvasLayer returned null!");
   1.872 +        return nullptr;
   1.873 +    }
   1.874 +    WebGLContextUserData *userData = nullptr;
   1.875 +    if (aBuilder->IsPaintingToWindow()) {
   1.876 +      // Make the layer tell us whenever a transaction finishes (including
   1.877 +      // the current transaction), so we can clear our invalidation state and
   1.878 +      // start invalidating again. We need to do this for the layer that is
   1.879 +      // being painted to a window (there shouldn't be more than one at a time,
   1.880 +      // and if there is, flushing the invalidation state more often than
   1.881 +      // necessary is harmless).
   1.882 +
   1.883 +      // The layer will be destroyed when we tear down the presentation
   1.884 +      // (at the latest), at which time this userData will be destroyed,
   1.885 +      // releasing the reference to the element.
   1.886 +      // The userData will receive DidTransactionCallbacks, which flush the
   1.887 +      // the invalidation state to indicate that the canvas is up to date.
   1.888 +      userData = new WebGLContextUserData(mCanvasElement);
   1.889 +      canvasLayer->SetDidTransactionCallback(
   1.890 +              WebGLContextUserData::DidTransactionCallback, userData);
   1.891 +      canvasLayer->SetPreTransactionCallback(
   1.892 +              WebGLContextUserData::PreTransactionCallback, userData);
   1.893 +    }
   1.894 +    canvasLayer->SetUserData(&gWebGLLayerUserData, userData);
   1.895 +
   1.896 +    CanvasLayer::Data data;
   1.897 +    data.mGLContext = gl;
   1.898 +    data.mSize = nsIntSize(mWidth, mHeight);
   1.899 +    data.mIsGLAlphaPremult = IsPremultAlpha();
   1.900 +
   1.901 +    canvasLayer->Initialize(data);
   1.902 +    uint32_t flags = gl->Caps().alpha ? 0 : Layer::CONTENT_OPAQUE;
   1.903 +    canvasLayer->SetContentFlags(flags);
   1.904 +    canvasLayer->Updated();
   1.905 +
   1.906 +    mResetLayer = false;
   1.907 +
   1.908 +    return canvasLayer.forget();
   1.909 +}
   1.910 +
   1.911 +void
   1.912 +WebGLContext::GetContextAttributes(Nullable<dom::WebGLContextAttributes> &retval)
   1.913 +{
   1.914 +    retval.SetNull();
   1.915 +    if (IsContextLost())
   1.916 +        return;
   1.917 +
   1.918 +    dom::WebGLContextAttributes& result = retval.SetValue();
   1.919 +
   1.920 +    const PixelBufferFormat& format = gl->GetPixelFormat();
   1.921 +
   1.922 +    result.mAlpha.Construct(format.alpha > 0);
   1.923 +    result.mDepth = format.depth > 0;
   1.924 +    result.mStencil = format.stencil > 0;
   1.925 +    result.mAntialias = format.samples > 1;
   1.926 +    result.mPremultipliedAlpha = mOptions.premultipliedAlpha;
   1.927 +    result.mPreserveDrawingBuffer = mOptions.preserveDrawingBuffer;
   1.928 +}
   1.929 +
   1.930 +/* [noscript] DOMString mozGetUnderlyingParamString(in GLenum pname); */
   1.931 +NS_IMETHODIMP
   1.932 +WebGLContext::MozGetUnderlyingParamString(uint32_t pname, nsAString& retval)
   1.933 +{
   1.934 +    if (IsContextLost())
   1.935 +        return NS_OK;
   1.936 +
   1.937 +    retval.SetIsVoid(true);
   1.938 +
   1.939 +    MakeContextCurrent();
   1.940 +
   1.941 +    switch (pname) {
   1.942 +    case LOCAL_GL_VENDOR:
   1.943 +    case LOCAL_GL_RENDERER:
   1.944 +    case LOCAL_GL_VERSION:
   1.945 +    case LOCAL_GL_SHADING_LANGUAGE_VERSION:
   1.946 +    case LOCAL_GL_EXTENSIONS: {
   1.947 +        const char *s = (const char *) gl->fGetString(pname);
   1.948 +        retval.Assign(NS_ConvertASCIItoUTF16(nsDependentCString(s)));
   1.949 +    }
   1.950 +        break;
   1.951 +
   1.952 +    default:
   1.953 +        return NS_ERROR_INVALID_ARG;
   1.954 +    }
   1.955 +
   1.956 +    return NS_OK;
   1.957 +}
   1.958 +
   1.959 +void
   1.960 +WebGLContext::ClearScreen()
   1.961 +{
   1.962 +    bool colorAttachmentsMask[WebGLContext::sMaxColorAttachments] = {false};
   1.963 +
   1.964 +    MakeContextCurrent();
   1.965 +    ScopedBindFramebuffer autoFB(gl, 0);
   1.966 +
   1.967 +    GLbitfield clearMask = LOCAL_GL_COLOR_BUFFER_BIT;
   1.968 +    if (mOptions.depth)
   1.969 +        clearMask |= LOCAL_GL_DEPTH_BUFFER_BIT;
   1.970 +    if (mOptions.stencil)
   1.971 +        clearMask |= LOCAL_GL_STENCIL_BUFFER_BIT;
   1.972 +
   1.973 +    colorAttachmentsMask[0] = true;
   1.974 +
   1.975 +    ForceClearFramebufferWithDefaultValues(clearMask, colorAttachmentsMask);
   1.976 +}
   1.977 +
   1.978 +#ifdef DEBUG
   1.979 +// For NaNs, etc.
   1.980 +static bool IsShadowCorrect(float shadow, float actual) {
   1.981 +    if (IsNaN(shadow)) {
   1.982 +        // GL is allowed to do anything it wants for NaNs, so if we're shadowing
   1.983 +        // a NaN, then whatever `actual` is might be correct.
   1.984 +        return true;
   1.985 +    }
   1.986 +
   1.987 +    return shadow == actual;
   1.988 +}
   1.989 +#endif
   1.990 +
   1.991 +void
   1.992 +WebGLContext::ForceClearFramebufferWithDefaultValues(GLbitfield mask, const bool colorAttachmentsMask[sMaxColorAttachments])
   1.993 +{
   1.994 +    MakeContextCurrent();
   1.995 +
   1.996 +    bool initializeColorBuffer = 0 != (mask & LOCAL_GL_COLOR_BUFFER_BIT);
   1.997 +    bool initializeDepthBuffer = 0 != (mask & LOCAL_GL_DEPTH_BUFFER_BIT);
   1.998 +    bool initializeStencilBuffer = 0 != (mask & LOCAL_GL_STENCIL_BUFFER_BIT);
   1.999 +    bool drawBuffersIsEnabled = IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers);
  1.1000 +
  1.1001 +    GLenum currentDrawBuffers[WebGLContext::sMaxColorAttachments];
  1.1002 +
  1.1003 +    // Fun GL fact: No need to worry about the viewport here, glViewport is just
  1.1004 +    // setting up a coordinates transformation, it doesn't affect glClear at all.
  1.1005 +
  1.1006 +#ifdef DEBUG
  1.1007 +    // Scope to hide our variables.
  1.1008 +    {
  1.1009 +        // Sanity-check that all our state is set properly. Otherwise, when we
  1.1010 +        // reset out state to what we *think* it is, we'll get it wrong.
  1.1011 +
  1.1012 +        // Dither shouldn't matter when we're clearing to {0,0,0,0}.
  1.1013 +        MOZ_ASSERT(gl->fIsEnabled(LOCAL_GL_SCISSOR_TEST) == mScissorTestEnabled);
  1.1014 +
  1.1015 +        if (initializeColorBuffer) {
  1.1016 +            realGLboolean colorWriteMask[4] = {2, 2, 2, 2};
  1.1017 +            GLfloat colorClearValue[4] = {-1.0f, -1.0f, -1.0f, -1.0f};
  1.1018 +
  1.1019 +            gl->fGetBooleanv(LOCAL_GL_COLOR_WRITEMASK, colorWriteMask);
  1.1020 +            gl->fGetFloatv(LOCAL_GL_COLOR_CLEAR_VALUE, colorClearValue);
  1.1021 +
  1.1022 +            MOZ_ASSERT(colorWriteMask[0] == mColorWriteMask[0] &&
  1.1023 +                       colorWriteMask[1] == mColorWriteMask[1] &&
  1.1024 +                       colorWriteMask[2] == mColorWriteMask[2] &&
  1.1025 +                       colorWriteMask[3] == mColorWriteMask[3]);
  1.1026 +            MOZ_ASSERT(IsShadowCorrect(mColorClearValue[0], colorClearValue[0]) &&
  1.1027 +                       IsShadowCorrect(mColorClearValue[1], colorClearValue[1]) &&
  1.1028 +                       IsShadowCorrect(mColorClearValue[2], colorClearValue[2]) &&
  1.1029 +                       IsShadowCorrect(mColorClearValue[3], colorClearValue[3]));
  1.1030 +        }
  1.1031 +
  1.1032 +        if (initializeDepthBuffer) {
  1.1033 +            realGLboolean depthWriteMask = 2;
  1.1034 +            GLfloat depthClearValue = -1.0f;
  1.1035 +
  1.1036 +
  1.1037 +            gl->fGetBooleanv(LOCAL_GL_DEPTH_WRITEMASK, &depthWriteMask);
  1.1038 +            gl->fGetFloatv(LOCAL_GL_DEPTH_CLEAR_VALUE, &depthClearValue);
  1.1039 +
  1.1040 +            MOZ_ASSERT(depthWriteMask == mDepthWriteMask);
  1.1041 +            MOZ_ASSERT(IsShadowCorrect(mDepthClearValue, depthClearValue));
  1.1042 +        }
  1.1043 +
  1.1044 +        if (initializeStencilBuffer) {
  1.1045 +            GLuint stencilWriteMaskFront = 0xdeadbad1;
  1.1046 +            GLuint stencilWriteMaskBack  = 0xdeadbad1;
  1.1047 +            GLuint stencilClearValue     = 0xdeadbad1;
  1.1048 +
  1.1049 +            gl->GetUIntegerv(LOCAL_GL_STENCIL_WRITEMASK,      &stencilWriteMaskFront);
  1.1050 +            gl->GetUIntegerv(LOCAL_GL_STENCIL_BACK_WRITEMASK, &stencilWriteMaskBack);
  1.1051 +            gl->GetUIntegerv(LOCAL_GL_STENCIL_CLEAR_VALUE,    &stencilClearValue);
  1.1052 +
  1.1053 +            GLuint stencilBits = 0;
  1.1054 +            gl->GetUIntegerv(LOCAL_GL_STENCIL_BITS, &stencilBits);
  1.1055 +            GLuint stencilMask = (GLuint(1) << stencilBits) - 1;
  1.1056 +
  1.1057 +            MOZ_ASSERT( ( stencilWriteMaskFront & stencilMask) ==
  1.1058 +                        (mStencilWriteMaskFront & stencilMask) );
  1.1059 +            MOZ_ASSERT( ( stencilWriteMaskBack & stencilMask) ==
  1.1060 +                        (mStencilWriteMaskBack & stencilMask) );
  1.1061 +            MOZ_ASSERT( ( stencilClearValue & stencilMask) ==
  1.1062 +                        (mStencilClearValue & stencilMask) );
  1.1063 +        }
  1.1064 +    }
  1.1065 +#endif
  1.1066 +
  1.1067 +    // Prepare GL state for clearing.
  1.1068 +    gl->fDisable(LOCAL_GL_SCISSOR_TEST);
  1.1069 +
  1.1070 +    if (initializeColorBuffer) {
  1.1071 +
  1.1072 +        if (drawBuffersIsEnabled) {
  1.1073 +
  1.1074 +            GLenum drawBuffersCommand[WebGLContext::sMaxColorAttachments] = { LOCAL_GL_NONE };
  1.1075 +
  1.1076 +            for(int32_t i = 0; i < mGLMaxDrawBuffers; i++) {
  1.1077 +                GLint temp;
  1.1078 +                gl->fGetIntegerv(LOCAL_GL_DRAW_BUFFER0 + i, &temp);
  1.1079 +                currentDrawBuffers[i] = temp;
  1.1080 +
  1.1081 +                if (colorAttachmentsMask[i]) {
  1.1082 +                    drawBuffersCommand[i] = LOCAL_GL_COLOR_ATTACHMENT0 + i;
  1.1083 +                }
  1.1084 +            }
  1.1085 +
  1.1086 +            gl->fDrawBuffers(mGLMaxDrawBuffers, drawBuffersCommand);
  1.1087 +        }
  1.1088 +
  1.1089 +        gl->fColorMask(1, 1, 1, 1);
  1.1090 +        gl->fClearColor(0.0f, 0.0f, 0.0f, 0.0f);
  1.1091 +    }
  1.1092 +
  1.1093 +    if (initializeDepthBuffer) {
  1.1094 +        gl->fDepthMask(1);
  1.1095 +        gl->fClearDepth(1.0f);
  1.1096 +    }
  1.1097 +
  1.1098 +    if (initializeStencilBuffer) {
  1.1099 +        // "The clear operation always uses the front stencil write mask
  1.1100 +        //  when clearing the stencil buffer."
  1.1101 +        gl->fStencilMaskSeparate(LOCAL_GL_FRONT, 0xffffffff);
  1.1102 +        gl->fStencilMaskSeparate(LOCAL_GL_BACK,  0xffffffff);
  1.1103 +        gl->fClearStencil(0);
  1.1104 +    }
  1.1105 +
  1.1106 +    if (mRasterizerDiscardEnabled) {
  1.1107 +        gl->fDisable(LOCAL_GL_RASTERIZER_DISCARD);
  1.1108 +    }
  1.1109 +
  1.1110 +    // Do the clear!
  1.1111 +    gl->fClear(mask);
  1.1112 +
  1.1113 +    // And reset!
  1.1114 +    if (mScissorTestEnabled)
  1.1115 +        gl->fEnable(LOCAL_GL_SCISSOR_TEST);
  1.1116 +
  1.1117 +    if (mRasterizerDiscardEnabled) {
  1.1118 +        gl->fEnable(LOCAL_GL_RASTERIZER_DISCARD);
  1.1119 +    }
  1.1120 +
  1.1121 +    // Restore GL state after clearing.
  1.1122 +    if (initializeColorBuffer) {
  1.1123 +        if (drawBuffersIsEnabled) {
  1.1124 +            gl->fDrawBuffers(mGLMaxDrawBuffers, currentDrawBuffers);
  1.1125 +        }
  1.1126 +
  1.1127 +        gl->fColorMask(mColorWriteMask[0],
  1.1128 +                       mColorWriteMask[1],
  1.1129 +                       mColorWriteMask[2],
  1.1130 +                       mColorWriteMask[3]);
  1.1131 +        gl->fClearColor(mColorClearValue[0],
  1.1132 +                        mColorClearValue[1],
  1.1133 +                        mColorClearValue[2],
  1.1134 +                        mColorClearValue[3]);
  1.1135 +    }
  1.1136 +
  1.1137 +    if (initializeDepthBuffer) {
  1.1138 +        gl->fDepthMask(mDepthWriteMask);
  1.1139 +        gl->fClearDepth(mDepthClearValue);
  1.1140 +    }
  1.1141 +
  1.1142 +    if (initializeStencilBuffer) {
  1.1143 +        gl->fStencilMaskSeparate(LOCAL_GL_FRONT, mStencilWriteMaskFront);
  1.1144 +        gl->fStencilMaskSeparate(LOCAL_GL_BACK,  mStencilWriteMaskBack);
  1.1145 +        gl->fClearStencil(mStencilClearValue);
  1.1146 +    }
  1.1147 +}
  1.1148 +
  1.1149 +// For an overview of how WebGL compositing works, see:
  1.1150 +// https://wiki.mozilla.org/Platform/GFX/WebGL/Compositing
  1.1151 +bool
  1.1152 +WebGLContext::PresentScreenBuffer()
  1.1153 +{
  1.1154 +    if (IsContextLost()) {
  1.1155 +        return false;
  1.1156 +    }
  1.1157 +
  1.1158 +    if (!mShouldPresent) {
  1.1159 +        return false;
  1.1160 +    }
  1.1161 +
  1.1162 +    gl->MakeCurrent();
  1.1163 +    MOZ_ASSERT(!mBackbufferNeedsClear);
  1.1164 +    if (!gl->PublishFrame()) {
  1.1165 +        this->ForceLoseContext();
  1.1166 +        return false;
  1.1167 +    }
  1.1168 +
  1.1169 +    if (!mOptions.preserveDrawingBuffer) {
  1.1170 +        mBackbufferNeedsClear = true;
  1.1171 +    }
  1.1172 +
  1.1173 +    mShouldPresent = false;
  1.1174 +
  1.1175 +    return true;
  1.1176 +}
  1.1177 +
  1.1178 +void
  1.1179 +WebGLContext::DummyFramebufferOperation(const char *info)
  1.1180 +{
  1.1181 +    GLenum status = CheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
  1.1182 +    if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE)
  1.1183 +        ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", info);
  1.1184 +}
  1.1185 +
  1.1186 +// We use this timer for many things. Here are the things that it is activated for:
  1.1187 +// 1) If a script is using the MOZ_WEBGL_lose_context extension.
  1.1188 +// 2) If we are using EGL and _NOT ANGLE_, we query periodically to see if the
  1.1189 +//    CONTEXT_LOST_WEBGL error has been triggered.
  1.1190 +// 3) If we are using ANGLE, or anything that supports ARB_robustness, query the
  1.1191 +//    GPU periodically to see if the reset status bit has been set.
  1.1192 +// In all of these situations, we use this timer to send the script context lost
  1.1193 +// and restored events asynchronously. For example, if it triggers a context loss,
  1.1194 +// the webglcontextlost event will be sent to it the next time the robustness timer
  1.1195 +// fires.
  1.1196 +// Note that this timer mechanism is not used unless one of these 3 criteria
  1.1197 +// are met.
  1.1198 +// At a bare minimum, from context lost to context restores, it would take 3
  1.1199 +// full timer iterations: detection, webglcontextlost, webglcontextrestored.
  1.1200 +void
  1.1201 +WebGLContext::RobustnessTimerCallback(nsITimer* timer)
  1.1202 +{
  1.1203 +    TerminateContextLossTimer();
  1.1204 +
  1.1205 +    if (!mCanvasElement) {
  1.1206 +        // the canvas is gone. That happens when the page was closed before we got
  1.1207 +        // this timer event. In this case, there's nothing to do here, just don't crash.
  1.1208 +        return;
  1.1209 +    }
  1.1210 +
  1.1211 +    // If the context has been lost and we're waiting for it to be restored, do
  1.1212 +    // that now.
  1.1213 +    if (mContextStatus == ContextLostAwaitingEvent) {
  1.1214 +        bool defaultAction;
  1.1215 +        nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
  1.1216 +                                             static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
  1.1217 +                                             NS_LITERAL_STRING("webglcontextlost"),
  1.1218 +                                             true,
  1.1219 +                                             true,
  1.1220 +                                             &defaultAction);
  1.1221 +
  1.1222 +        // If the script didn't handle the event, we don't allow restores.
  1.1223 +        if (defaultAction)
  1.1224 +            mAllowRestore = false;
  1.1225 +
  1.1226 +        // If the script handled the event and we are allowing restores, then
  1.1227 +        // mark it to be restored. Otherwise, leave it as context lost
  1.1228 +        // (unusable).
  1.1229 +        if (!defaultAction && mAllowRestore) {
  1.1230 +            ForceRestoreContext();
  1.1231 +            // Restart the timer so that it will be restored on the next
  1.1232 +            // callback.
  1.1233 +            SetupContextLossTimer();
  1.1234 +        } else {
  1.1235 +            mContextStatus = ContextLost;
  1.1236 +        }
  1.1237 +    } else if (mContextStatus == ContextLostAwaitingRestore) {
  1.1238 +        // Try to restore the context. If it fails, try again later.
  1.1239 +        if (NS_FAILED(SetDimensions(mWidth, mHeight))) {
  1.1240 +            SetupContextLossTimer();
  1.1241 +            return;
  1.1242 +        }
  1.1243 +        mContextStatus = ContextNotLost;
  1.1244 +        nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
  1.1245 +                                             static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
  1.1246 +                                             NS_LITERAL_STRING("webglcontextrestored"),
  1.1247 +                                             true,
  1.1248 +                                             true);
  1.1249 +        // Set all flags back to the state they were in before the context was
  1.1250 +        // lost.
  1.1251 +        mEmitContextLostErrorOnce = true;
  1.1252 +        mAllowRestore = true;
  1.1253 +    }
  1.1254 +
  1.1255 +    MaybeRestoreContext();
  1.1256 +    return;
  1.1257 +}
  1.1258 +
  1.1259 +void
  1.1260 +WebGLContext::MaybeRestoreContext()
  1.1261 +{
  1.1262 +    // Don't try to handle it if we already know it's busted.
  1.1263 +    if (mContextStatus != ContextNotLost || gl == nullptr)
  1.1264 +        return;
  1.1265 +
  1.1266 +    bool isEGL = gl->GetContextType() == gl::GLContextType::EGL,
  1.1267 +         isANGLE = gl->IsANGLE();
  1.1268 +
  1.1269 +    GLContext::ContextResetARB resetStatus = GLContext::CONTEXT_NO_ERROR;
  1.1270 +    if (mHasRobustness) {
  1.1271 +        gl->MakeCurrent();
  1.1272 +        resetStatus = (GLContext::ContextResetARB) gl->fGetGraphicsResetStatus();
  1.1273 +    } else if (isEGL) {
  1.1274 +        // Simulate a ARB_robustness guilty context loss for when we
  1.1275 +        // get an EGL_CONTEXT_LOST error. It may not actually be guilty,
  1.1276 +        // but we can't make any distinction, so we must assume the worst
  1.1277 +        // case.
  1.1278 +        if (!gl->MakeCurrent(true) && gl->IsContextLost()) {
  1.1279 +            resetStatus = GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB;
  1.1280 +        }
  1.1281 +    }
  1.1282 +
  1.1283 +    if (resetStatus != GLContext::CONTEXT_NO_ERROR) {
  1.1284 +        // It's already lost, but clean up after it and signal to JS that it is
  1.1285 +        // lost.
  1.1286 +        ForceLoseContext();
  1.1287 +    }
  1.1288 +
  1.1289 +    switch (resetStatus) {
  1.1290 +        case GLContext::CONTEXT_NO_ERROR:
  1.1291 +            // If there has been activity since the timer was set, it's possible
  1.1292 +            // that we did or are going to miss something, so clear this flag and
  1.1293 +            // run it again some time later.
  1.1294 +            if (mDrawSinceContextLossTimerSet)
  1.1295 +                SetupContextLossTimer();
  1.1296 +            break;
  1.1297 +        case GLContext::CONTEXT_GUILTY_CONTEXT_RESET_ARB:
  1.1298 +            NS_WARNING("WebGL content on the page caused the graphics card to reset; not restoring the context");
  1.1299 +            mAllowRestore = false;
  1.1300 +            break;
  1.1301 +        case GLContext::CONTEXT_INNOCENT_CONTEXT_RESET_ARB:
  1.1302 +            break;
  1.1303 +        case GLContext::CONTEXT_UNKNOWN_CONTEXT_RESET_ARB:
  1.1304 +            NS_WARNING("WebGL content on the page might have caused the graphics card to reset");
  1.1305 +            if (isEGL && isANGLE) {
  1.1306 +                // If we're using ANGLE, we ONLY get back UNKNOWN context resets, including for guilty contexts.
  1.1307 +                // This means that we can't restore it or risk restoring a guilty context. Should this ever change,
  1.1308 +                // we can get rid of the whole IsANGLE() junk from GLContext.h since, as of writing, this is the
  1.1309 +                // only use for it. See ANGLE issue 261.
  1.1310 +                mAllowRestore = false;
  1.1311 +            }
  1.1312 +            break;
  1.1313 +    }
  1.1314 +}
  1.1315 +
  1.1316 +void
  1.1317 +WebGLContext::ForceLoseContext()
  1.1318 +{
  1.1319 +    if (mContextStatus == ContextLostAwaitingEvent)
  1.1320 +        return;
  1.1321 +
  1.1322 +    mContextStatus = ContextLostAwaitingEvent;
  1.1323 +    // Queue up a task to restore the event.
  1.1324 +    SetupContextLossTimer();
  1.1325 +    DestroyResourcesAndContext();
  1.1326 +}
  1.1327 +
  1.1328 +void
  1.1329 +WebGLContext::ForceRestoreContext()
  1.1330 +{
  1.1331 +    mContextStatus = ContextLostAwaitingRestore;
  1.1332 +}
  1.1333 +
  1.1334 +void
  1.1335 +WebGLContext::MakeContextCurrent() const { gl->MakeCurrent(); }
  1.1336 +
  1.1337 +mozilla::TemporaryRef<mozilla::gfx::SourceSurface>
  1.1338 +WebGLContext::GetSurfaceSnapshot(bool* aPremultAlpha)
  1.1339 +{
  1.1340 +    if (!gl)
  1.1341 +        return nullptr;
  1.1342 +
  1.1343 +    nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
  1.1344 +                                                         gfxImageFormat::ARGB32,
  1.1345 +                                                         mWidth * 4, 0, false);
  1.1346 +    if (surf->CairoStatus() != 0) {
  1.1347 +        return nullptr;
  1.1348 +    }
  1.1349 +
  1.1350 +    gl->MakeCurrent();
  1.1351 +    {
  1.1352 +        ScopedBindFramebuffer autoFB(gl, 0);
  1.1353 +        ClearBackbufferIfNeeded();
  1.1354 +        ReadPixelsIntoImageSurface(gl, surf);
  1.1355 +    }
  1.1356 +
  1.1357 +    if (aPremultAlpha) {
  1.1358 +        *aPremultAlpha = true;
  1.1359 +    }
  1.1360 +    bool srcPremultAlpha = mOptions.premultipliedAlpha;
  1.1361 +    if (!srcPremultAlpha) {
  1.1362 +        if (aPremultAlpha) {
  1.1363 +            *aPremultAlpha = false;
  1.1364 +        } else {
  1.1365 +            gfxUtils::PremultiplyImageSurface(surf);
  1.1366 +            surf->MarkDirty();
  1.1367 +        }
  1.1368 +    }
  1.1369 +
  1.1370 +    RefPtr<DrawTarget> dt =
  1.1371 +        Factory::CreateDrawTarget(BackendType::CAIRO,
  1.1372 +                                  IntSize(mWidth, mHeight),
  1.1373 +                                  SurfaceFormat::B8G8R8A8);
  1.1374 +
  1.1375 +    if (!dt) {
  1.1376 +        return nullptr;
  1.1377 +    }
  1.1378 +
  1.1379 +    RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surf);
  1.1380 +
  1.1381 +    Matrix m;
  1.1382 +    m.Translate(0.0, mHeight);
  1.1383 +    m.Scale(1.0, -1.0);
  1.1384 +    dt->SetTransform(m);
  1.1385 +
  1.1386 +    dt->DrawSurface(source,
  1.1387 +                    Rect(0, 0, mWidth, mHeight),
  1.1388 +                    Rect(0, 0, mWidth, mHeight),
  1.1389 +                    DrawSurfaceOptions(),
  1.1390 +                    DrawOptions(1.0f, CompositionOp::OP_SOURCE));
  1.1391 +
  1.1392 +    return dt->Snapshot();
  1.1393 +}
  1.1394 +
  1.1395 +//
  1.1396 +// XPCOM goop
  1.1397 +//
  1.1398 +
  1.1399 +NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext)
  1.1400 +NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
  1.1401 +
  1.1402 +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_13(WebGLContext,
  1.1403 +  mCanvasElement,
  1.1404 +  mExtensions,
  1.1405 +  mBound2DTextures,
  1.1406 +  mBoundCubeMapTextures,
  1.1407 +  mBoundArrayBuffer,
  1.1408 +  mBoundTransformFeedbackBuffer,
  1.1409 +  mCurrentProgram,
  1.1410 +  mBoundFramebuffer,
  1.1411 +  mBoundRenderbuffer,
  1.1412 +  mBoundVertexArray,
  1.1413 +  mDefaultVertexArray,
  1.1414 +  mActiveOcclusionQuery,
  1.1415 +  mActiveTransformFeedbackQuery)
  1.1416 +
  1.1417 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLContext)
  1.1418 +  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  1.1419 +  NS_INTERFACE_MAP_ENTRY(nsIDOMWebGLRenderingContext)
  1.1420 +  NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
  1.1421 +  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
  1.1422 +  // If the exact way we cast to nsISupports here ever changes, fix our
  1.1423 +  // ToSupports() method.
  1.1424 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMWebGLRenderingContext)
  1.1425 +NS_INTERFACE_MAP_END

mercurial