Bug 1427668 - Lose context if EnsureDefaultFB fails. - r=daoshengmu
authorJeff Gilbert <jgilbert@mozilla.com>
Thu, 21 Dec 2017 17:14:54 -0800
changeset 449843 25e41ca9c92ad18ab9760f8bb130df171a80f03d
parent 449842 e31cefdad58256dfe46d32f6555aadfbcc01df84
child 449844 e1edf80827aa769a8d179fe0cf0e3a411a38c392
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdaoshengmu
bugs1427668
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1427668 - Lose context if EnsureDefaultFB fails. - r=daoshengmu MozReview-Commit-ID: 8OqXYXpUv4I
dom/canvas/CanvasRenderingContext2D.cpp
dom/canvas/CanvasRenderingContext2D.h
dom/canvas/ImageBitmapRenderingContext.cpp
dom/canvas/ImageBitmapRenderingContext.h
dom/canvas/WebGL2ContextFramebuffers.cpp
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextState.cpp
dom/canvas/nsICanvasRenderingContextInternal.h
gfx/gl/MozFramebuffer.cpp
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1942,28 +1942,16 @@ CanvasRenderingContext2D::TryBasicTarget
   if (!aOutDT) {
     return false;
   }
 
   aOutProvider = new PersistentBufferProviderBasic(aOutDT);
   return true;
 }
 
-int32_t
-CanvasRenderingContext2D::GetWidth() const
-{
-  return mWidth;
-}
-
-int32_t
-CanvasRenderingContext2D::GetHeight() const
-{
-  return mHeight;
-}
-
 NS_IMETHODIMP
 CanvasRenderingContext2D::SetDimensions(int32_t aWidth, int32_t aHeight)
 {
   ClearTarget();
 
   // Zero sized surfaces can cause problems.
   mZero = false;
   if (aHeight == 0) {
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -421,19 +421,19 @@ public:
 
   bool SwitchRenderingMode(RenderingMode aRenderingMode);
 
   // Eventually this should be deprecated. Keeping for now to keep the binding functional.
   void Demote();
 
   nsresult Redraw();
 
-  virtual int32_t GetWidth() const override;
-  virtual int32_t GetHeight() const override;
   gfx::IntSize GetSize() const { return gfx::IntSize(mWidth, mHeight); }
+  virtual int32_t GetWidth() override { return GetSize().width; }
+  virtual int32_t GetHeight() override { return GetSize().height; }
 
   // nsICanvasRenderingContextInternal
   /**
     * Gets the pres shell from either the canvas element or the doc shell
     */
   virtual nsIPresShell *GetPresShell() override {
     if (mCanvasElement) {
       return mCanvasElement->OwnerDoc()->GetShell();
--- a/dom/canvas/ImageBitmapRenderingContext.cpp
+++ b/dom/canvas/ImageBitmapRenderingContext.cpp
@@ -65,28 +65,16 @@ ImageBitmapRenderingContext::TransferFro
 
   if (!mImage) {
     return;
   }
 
   Redraw(gfxRect(0, 0, mWidth, mHeight));
 }
 
-int32_t
-ImageBitmapRenderingContext::GetWidth() const
-{
-  return mWidth;
-}
-
-int32_t
-ImageBitmapRenderingContext::GetHeight() const
-{
-  return mHeight;
-}
-
 NS_IMETHODIMP
 ImageBitmapRenderingContext::SetDimensions(int32_t aWidth, int32_t aHeight)
 {
   mWidth = aWidth;
   mHeight = aHeight;
   return NS_OK;
 }
 
--- a/dom/canvas/ImageBitmapRenderingContext.h
+++ b/dom/canvas/ImageBitmapRenderingContext.h
@@ -45,18 +45,18 @@ public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ImageBitmapRenderingContext)
 
   void TransferImageBitmap(ImageBitmap& aImageBitmap);
   void TransferFromImageBitmap(ImageBitmap& aImageBitmap);
 
   // nsICanvasRenderingContextInternal
-  virtual int32_t GetWidth() const override;
-  virtual int32_t GetHeight() const override;
+  virtual int32_t GetWidth() override { return mWidth; }
+  virtual int32_t GetHeight() override { return mHeight; }
 
   NS_IMETHOD SetDimensions(int32_t aWidth, int32_t aHeight) override;
 
   NS_IMETHOD InitializeWithDrawTarget(nsIDocShell* aDocShell,
                                       NotNull<gfx::DrawTarget*> aTarget) override;
 
   virtual mozilla::UniquePtr<uint8_t[]> GetImageBuffer(int32_t* aFormat) override;
   NS_IMETHOD GetInputStream(const char* aMimeType,
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -174,17 +174,17 @@ WebGLContext::ValidateInvalidateFramebuf
         MOZ_CRASH("GFX: Bad target.");
     }
 
     if (fb) {
         const auto fbStatus = fb->CheckFramebufferStatus(funcName);
         if (fbStatus != LOCAL_GL_FRAMEBUFFER_COMPLETE)
             return false; // Not an error, but don't run forward to driver either.
     } else {
-        if (!EnsureDefaultFB())
+        if (!EnsureDefaultFB(funcName))
             return false;
     }
     DoBindFB(fb, target);
 
     *out_glNumAttachments = attachments.Length();
     *out_glAttachments = attachments.Elements();
 
     if (fb) {
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -735,17 +735,17 @@ WebGLContext::CreateAndInitGL(bool force
     out_failReasons->push_back(FailureReason("FEATURE_FAILURE_WEBGL_EXHAUSTED_DRIVERS",
                                              "Exhausted GL driver options."));
     return false;
 }
 
 // Fallback for resizes:
 
 bool
-WebGLContext::EnsureDefaultFB() const
+WebGLContext::EnsureDefaultFB(const char* const funcName)
 {
     if (mDefaultFB) {
         MOZ_ASSERT(mDefaultFB->mSize == mRequestedSize);
         return true;
     }
 
     const bool depthStencil = mOptions.depth || mOptions.stencil;
     auto attemptSize = mRequestedSize;
@@ -771,27 +771,31 @@ WebGLContext::EnsureDefaultFB() const
 
         if (mDefaultFB)
             break;
 
         attemptSize.width /= 2;
         attemptSize.height /= 2;
     }
 
-    if (!mDefaultFB)
+    if (!mDefaultFB) {
+        GenerateWarning("%s: Backbuffer resize failed. Losing context.", funcName);
+        ForceLoseContext();
         return false;
+    }
 
     mDefaultFB_IsInvalid = true;
 
     if (mDefaultFB->mSize != mRequestedSize) {
         GenerateWarning("Requested size %dx%d was too large, but resize"
                           " to %dx%d succeeded.",
                         mRequestedSize.width, mRequestedSize.height,
                         mDefaultFB->mSize.width, mDefaultFB->mSize.height);
     }
+    mRequestedSize = mDefaultFB->mSize;
     return true;
 }
 
 void
 WebGLContext::ThrowEvent_WebGLContextCreationError(const nsACString& text)
 {
     RefPtr<EventTarget> target = mCanvasElement;
     if (!target && mOffscreenCanvas) {
@@ -964,17 +968,16 @@ WebGLContext::SetDimensions(int32_t sign
             text.AppendASCII("\n* ");
             text.Append(cur.info);
         }
         failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_REASON");
         ThrowEvent_WebGLContextCreationError(text);
         return NS_ERROR_FAILURE;
     }
     MOZ_ASSERT(gl);
-    MOZ_ASSERT_IF(mOptions.alpha, gl->Caps().alpha);
 
     if (mOptions.failIfMajorPerformanceCaveat) {
         if (gl->IsWARP()) {
             DestroyResourcesAndContext();
             MOZ_ASSERT(!gl);
 
             failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_PERF_WARP");
             const nsLiteralCString text("failIfMajorPerformanceCaveat: Driver is not"
@@ -995,17 +998,19 @@ WebGLContext::SetDimensions(int32_t sign
             ThrowEvent_WebGLContextCreationError(text);
             return NS_ERROR_FAILURE;
         }
 #endif
     }
 
     MOZ_ASSERT(!mDefaultFB);
     mRequestedSize = {width, height};
-    if (!EnsureDefaultFB()) {
+    if (!EnsureDefaultFB("context initialization")) {
+        MOZ_ASSERT(!gl);
+
         failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WEBGL_BACKBUFFER");
         const nsLiteralCString text("Initializing WebGL backbuffer failed.");
         ThrowEvent_WebGLContextCreationError(text);
         return NS_ERROR_FAILURE;
     }
 
     if (GLContext::ShouldSpew()) {
         printf_stderr("--- WebGL context created: %p\n", gl.get());
@@ -1315,17 +1320,17 @@ WebGLContext::InitializeCanvasRenderer(n
         // the invalidation state to indicate that the canvas is up to date.
         data.mPreTransCallback = WebGLContextUserData::PreTransactionCallback;
         data.mPreTransCallbackData = this;
         data.mDidTransCallback = WebGLContextUserData::DidTransactionCallback;
         data.mDidTransCallbackData = this;
     }
 
     data.mGLContext = gl;
-    data.mSize = DrawingBufferSize();
+    data.mSize = DrawingBufferSize("InitializeCanvasRenderer");
     data.mHasAlpha = mOptions.alpha;
     data.mIsGLAlphaPremult = IsPremultAlpha() || !data.mHasAlpha;
 
     aRenderer->Initialize(data);
     aRenderer->SetDirty();
     return true;
 }
 
@@ -1956,40 +1961,37 @@ WebGLContext::DidRefresh()
     if (gl) {
         gl->FlushIfHeavyGLCallsSinceLastFlush();
     }
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 gfx::IntSize
-WebGLContext::DrawingBufferSize() const
+WebGLContext::DrawingBufferSize(const char* const funcName)
 {
     const gfx::IntSize zeros{0, 0};
     if (IsContextLost())
         return zeros;
 
-    if (!EnsureDefaultFB())
+    if (!EnsureDefaultFB(funcName))
         return zeros;
 
     return mDefaultFB->mSize;
 }
 
 bool
 WebGLContext::ValidateAndInitFB(const char* const funcName,
                                 const WebGLFramebuffer* const fb)
 {
     if (fb)
         return fb->ValidateAndInitAttachments(funcName);
 
-    if (!EnsureDefaultFB()) {
-        GenerateWarning("%s: Lazy resize failed. Losing context.", funcName);
-        ForceLoseContext();
+    if (!EnsureDefaultFB(funcName))
         return false;
-    }
 
     if (mDefaultFB_IsInvalid) {
         gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, mDefaultFB->mFB);
         const GLbitfield bits = LOCAL_GL_COLOR_BUFFER_BIT |
                                 LOCAL_GL_DEPTH_BUFFER_BIT |
                                 LOCAL_GL_STENCIL_BUFFER_BIT;
         const bool fakeNoAlpha = !mOptions.alpha;
         ForceClearFramebufferWithDefaultValues(bits, fakeNoAlpha);
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -337,18 +337,18 @@ public:
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override = 0;
 
     NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT
 
     virtual void OnVisibilityChange() override;
     virtual void OnMemoryPressure() override;
 
     // nsICanvasRenderingContextInternal
-    virtual int32_t GetWidth() const override { return DrawingBufferWidth(); }
-    virtual int32_t GetHeight() const override { return DrawingBufferHeight(); }
+    virtual int32_t GetWidth() override { return DrawingBufferWidth("get width"); }
+    virtual int32_t GetHeight() override { return DrawingBufferHeight("get height"); }
 
     NS_IMETHOD SetDimensions(int32_t width, int32_t height) override;
     NS_IMETHOD InitializeWithDrawTarget(nsIDocShell*,
                                         NotNull<gfx::DrawTarget*>) override
     {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
 
@@ -496,20 +496,24 @@ public:
 
     dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
     nsIDocument* GetOwnerDoc() const;
 
     // WebIDL WebGLRenderingContext API
     void Commit();
     void GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval);
 private:
-    gfx::IntSize DrawingBufferSize() const;
+    gfx::IntSize DrawingBufferSize(const char* funcName);
 public:
-    GLsizei DrawingBufferWidth() const { return DrawingBufferSize().width; }
-    GLsizei DrawingBufferHeight() const { return DrawingBufferSize().height; }
+    GLsizei DrawingBufferWidth(const char* const funcName = "drawingBufferWidth") {
+        return DrawingBufferSize(funcName).width;
+    }
+    GLsizei DrawingBufferHeight(const char* const funcName = "drawingBufferHeight") {
+        return DrawingBufferSize(funcName).height;
+    }
 
     layers::LayersBackend GetCompositorBackendType() const;
 
     void
     GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval);
 
     bool IsContextLost() const { return mContextStatus != ContextNotLost; }
     void GetSupportedExtensions(dom::Nullable< nsTArray<nsString> >& retval,
@@ -1985,24 +1989,24 @@ protected:
 
     const bool mAllowFBInvalidation;
 
     bool Has64BitTimestamps() const;
 
     // --
 
     const uint8_t mMsaaSamples;
-    gfx::IntSize mRequestedSize;
+    mutable gfx::IntSize mRequestedSize;
     mutable UniquePtr<gl::MozFramebuffer> mDefaultFB;
     mutable bool mDefaultFB_IsInvalid;
     mutable UniquePtr<gl::MozFramebuffer> mResolvedDefaultFB;
 
     // --
 
-    bool EnsureDefaultFB() const;
+    bool EnsureDefaultFB(const char* funcName);
     bool ValidateAndInitFB(const char* funcName, const WebGLFramebuffer* fb);
     void DoBindFB(const WebGLFramebuffer* fb, GLenum target = LOCAL_GL_FRAMEBUFFER) const;
 
     bool BindCurFBForDraw(const char* funcName);
     bool BindCurFBForColorRead(const char* funcName,
                                const webgl::FormatUsageInfo** out_format,
                                uint32_t* out_width, uint32_t* out_height);
     void DoColorMask(uint8_t bitmask) const;
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -357,17 +357,17 @@ WebGLContext::GetParameter(JSContext* cx
             return JS::Int32Value(refValue & stencilMask);
         }
 
         case LOCAL_GL_SAMPLE_BUFFERS:
         case LOCAL_GL_SAMPLES: {
             const auto& fb = mBoundDrawFramebuffer;
             auto samples = [&]() -> Maybe<uint32_t> {
                 if (!fb) {
-                    if (!EnsureDefaultFB())
+                    if (!EnsureDefaultFB(funcName))
                         return Nothing();
                     return Some(mDefaultFB->mSamples);
                 }
 
                 if (!fb->IsCheckFramebufferStatusComplete(funcName))
                     return Some(0);
 
                 DoBindFB(fb, LOCAL_GL_FRAMEBUFFER);
--- a/dom/canvas/nsICanvasRenderingContextInternal.h
+++ b/dom/canvas/nsICanvasRenderingContextInternal.h
@@ -88,18 +88,18 @@ public:
   }
 
   void SetOffscreenCanvas(mozilla::dom::OffscreenCanvas* aOffscreenCanvas)
   {
     mOffscreenCanvas = aOffscreenCanvas;
   }
 
   // Dimensions of the canvas, in pixels.
-  virtual int32_t GetWidth() const = 0;
-  virtual int32_t GetHeight() const = 0;
+  virtual int32_t GetWidth() = 0;
+  virtual int32_t GetHeight() = 0;
 
   // Sets the dimensions of the canvas, in pixels.  Called
   // whenever the size of the element changes.
   NS_IMETHOD SetDimensions(int32_t width, int32_t height) = 0;
 
   // Initializes with an nsIDocShell and DrawTarget. The size is taken from the
   // DrawTarget.
   NS_IMETHOD InitializeWithDrawTarget(nsIDocShell *aDocShell,
--- a/gfx/gl/MozFramebuffer.cpp
+++ b/gfx/gl/MozFramebuffer.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MozFramebuffer.h"
 
 #include "GLContext.h"
+#include "mozilla/gfx/Logging.h"
 #include "ScopedGLHelpers.h"
 
 namespace mozilla {
 namespace gl {
 
 static void
 DeleteByTarget(GLContext* const gl, const GLenum target, const GLuint name)
 {
@@ -56,17 +57,20 @@ MozFramebuffer::Create(GLContext* const 
         gl->fBindBuffer(LOCAL_GL_PIXEL_UNPACK_BUFFER, 0);
         gl->fTexImage2D(colorTarget, 0, LOCAL_GL_RGBA,
                         size.width, size.height, 0,
                         LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, nullptr);
     }
 
     const auto err = errorScope.GetError();
     if (err) {
-        MOZ_ASSERT(err == LOCAL_GL_OUT_OF_MEMORY);
+        if (err != LOCAL_GL_OUT_OF_MEMORY) {
+            gfxCriticalNote << "Unexpected error: " << gfx::hexa(err) << ": "
+                            << GLContext::GLErrorToString(err);
+        }
         DeleteByTarget(gl, colorTarget, colorName);
         return nullptr;
     }
 
     return CreateWith(gl, size, samples, depthStencil, colorTarget, colorName);
 }
 
 UniquePtr<MozFramebuffer>