Bug 978418 - Split GetError flags for WebGL. - r=kamidphish
authorJeff Gilbert <jgilbert@mozilla.com>
Fri, 07 Mar 2014 13:16:34 -0800
changeset 190832 69cb6eddfc773f85df768fbc2de242b837ea13f1
parent 190831 653d9da6c5a2b2ff4b73f1d32f9fd99baccf52b5
child 190833 f89aaec00f73169650e7cd64bf5080448e2e66ce
push id474
push userasasaki@mozilla.com
push dateMon, 02 Jun 2014 21:01:02 +0000
treeherdermozilla-release@967f4cf1b31c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskamidphish
bugs978418
milestone30.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 978418 - Split GetError flags for WebGL. - r=kamidphish
content/canvas/src/WebGLContext.cpp
content/canvas/src/WebGLContext.h
content/canvas/src/WebGLContextBuffers.cpp
content/canvas/src/WebGLContextGL.cpp
content/canvas/src/WebGLContextUtils.cpp
content/canvas/src/WebGLContextValidate.cpp
content/canvas/src/WebGLTexture.cpp
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -111,17 +111,16 @@ WebGLContext::WebGLContext()
 
     mGeneration = 0;
     mInvalidated = false;
     mShouldPresent = true;
     mResetLayer = true;
     mOptionsFrozen = false;
 
     mActiveTexture = 0;
-    mWebGLError = LOCAL_GL_NO_ERROR;
     mPixelStoreFlipY = false;
     mPixelStorePremultiplyAlpha = false;
     mPixelStoreColorspaceConversion = BROWSER_DEFAULT_WEBGL;
 
     mShaderValidation = true;
 
     mFakeBlackStatus = WebGLContextFakeBlackStatus::NotNeeded;
 
@@ -1284,17 +1283,17 @@ WebGLContext::RobustnessTimerCallback(ns
         mContextStatus = ContextNotLost;
         nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
                                              static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
                                              NS_LITERAL_STRING("webglcontextrestored"),
                                              true,
                                              true);
         // Set all flags back to the state they were in before the context was
         // lost.
-        mContextLostErrorSet = false;
+        mEmitContextLostErrorOnce = true;
         mAllowRestore = true;
     }
 
     MaybeRestoreContext();
     return;
 }
 
 void
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -239,20 +239,16 @@ public:
     // amount of work it does.
     // It only clears the buffers we specify, and can reset its state without
     // first having to query anything, as WebGL knows its state at all times.
     void ForceClearFramebufferWithDefaultValues(GLbitfield mask, const bool colorAttachmentsMask[sMaxColorAttachments]);
 
     // Calls ForceClearFramebufferWithDefaultValues() for the Context's 'screen'.
     void ClearScreen();
 
-    // checks for GL errors, clears any pending GL error, stores the current GL error in currentGLError (if not nullptr),
-    // and copies it into mWebGLError if it doesn't already have an error set
-    void UpdateWebGLErrorAndClearGLError(GLenum *currentGLError = nullptr);
-
     bool MinCapabilityMode() const { return mMinCapability; }
 
     void RobustnessTimerCallback(nsITimer* timer);
     static void RobustnessTimerCallbackStatic(nsITimer* timer, void *thisPointer);
     void SetupContextLossTimer();
     void TerminateContextLossTimer();
 
     // WebIDL WebGLRenderingContext API
@@ -847,17 +843,22 @@ protected:
     bool mShouldPresent;
     bool mIsScreenCleared;
     bool mDisableFragHighP;
 
     template<typename WebGLObjectType>
     void DeleteWebGLObjectsArray(nsTArray<WebGLObjectType>& array);
 
     GLuint mActiveTexture;
+
+    // glGetError sources:
+    bool mEmitContextLostErrorOnce;
     GLenum mWebGLError;
+    GLenum mUnderlyingGLError;
+    GLenum GetAndFlushUnderlyingGLErrors();
 
     // whether shader validation is supported
     bool mShaderValidation;
 
     // some GL constants
     int32_t mGLMaxVertexAttribs;
     int32_t mGLMaxTextureUnits;
     int32_t mGLMaxTextureSize;
--- a/content/canvas/src/WebGLContextBuffers.cpp
+++ b/content/canvas/src/WebGLContextBuffers.cpp
@@ -480,18 +480,17 @@ WebGLContext::CheckedBufferData(GLenum t
         boundBuffer = mBoundArrayBuffer;
     } else if (target == LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
         boundBuffer = mBoundVertexArray->mBoundElementArrayBuffer;
     }
     MOZ_ASSERT(boundBuffer != nullptr, "no buffer bound for this target");
 
     bool sizeChanges = uint32_t(size) != boundBuffer->ByteLength();
     if (sizeChanges) {
-        UpdateWebGLErrorAndClearGLError();
+        GetAndFlushUnderlyingGLErrors();
         gl->fBufferData(target, size, data, usage);
-        GLenum error = LOCAL_GL_NO_ERROR;
-        UpdateWebGLErrorAndClearGLError(&error);
+        GLenum error = GetAndFlushUnderlyingGLErrors();
         return error;
     } else {
         gl->fBufferData(target, size, data, usage);
         return LOCAL_GL_NO_ERROR;
     }
 }
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -533,23 +533,22 @@ WebGLContext::CopyTexImage2D(GLenum targ
 
         sizeMayChange = width != imageInfo.Width() ||
                         height != imageInfo.Height() ||
                         internalformat != imageInfo.InternalFormat() ||
                         type != imageInfo.Type();
     }
 
     if (sizeMayChange)
-        UpdateWebGLErrorAndClearGLError();
+        GetAndFlushUnderlyingGLErrors();
 
     CopyTexSubImage2D_base(target, level, internalformat, 0, 0, x, y, width, height, false);
 
     if (sizeMayChange) {
-        GLenum error = LOCAL_GL_NO_ERROR;
-        UpdateWebGLErrorAndClearGLError(&error);
+        GLenum error = GetAndFlushUnderlyingGLErrors();
         if (error) {
             GenerateWarning("copyTexImage2D generated error %s", ErrorName(error));
             return;
         }
     }
 
     tex->SetImageInfo(target, level, width, height, internalformat, type,
                       WebGLImageDataStatus::InitializedImageData);
@@ -897,32 +896,31 @@ WebGLContext::DoFakeVertexAttrib0(GLuint
         mFakeVertexAttrib0BufferObjectSize = dataSize;
         mFakeVertexAttrib0BufferObjectVector[0] = mVertexAttrib0Vector[0];
         mFakeVertexAttrib0BufferObjectVector[1] = mVertexAttrib0Vector[1];
         mFakeVertexAttrib0BufferObjectVector[2] = mVertexAttrib0Vector[2];
         mFakeVertexAttrib0BufferObjectVector[3] = mVertexAttrib0Vector[3];
 
         gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mFakeVertexAttrib0BufferObject);
 
-        GLenum error = LOCAL_GL_NO_ERROR;
-        UpdateWebGLErrorAndClearGLError();
+        GetAndFlushUnderlyingGLErrors();
 
         if (mFakeVertexAttrib0BufferStatus == WebGLVertexAttrib0Status::EmulatedInitializedArray) {
             nsAutoArrayPtr<GLfloat> array(new GLfloat[4 * vertexCount]);
             for(size_t i = 0; i < vertexCount; ++i) {
                 array[4 * i + 0] = mVertexAttrib0Vector[0];
                 array[4 * i + 1] = mVertexAttrib0Vector[1];
                 array[4 * i + 2] = mVertexAttrib0Vector[2];
                 array[4 * i + 3] = mVertexAttrib0Vector[3];
             }
             gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, array, LOCAL_GL_DYNAMIC_DRAW);
         } else {
             gl->fBufferData(LOCAL_GL_ARRAY_BUFFER, dataSize, nullptr, LOCAL_GL_DYNAMIC_DRAW);
         }
-        UpdateWebGLErrorAndClearGLError(&error);
+        GLenum error = GetAndFlushUnderlyingGLErrors();
 
         gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0);
 
         // note that we do this error checking and early return AFTER having restored the buffer binding above
         if (error) {
             ErrorOutOfMemory("Ran out of memory trying to construct a fake vertex attrib 0 array for a draw-operation "
                              "with %d vertices. Try reducing the number of vertices.", vertexCount);
             return false;
@@ -1494,29 +1492,63 @@ already_AddRefed<WebGLTexture>
 WebGLContext::CreateTexture()
 {
     if (IsContextLost())
         return nullptr;
     nsRefPtr<WebGLTexture> globj = new WebGLTexture(this);
     return globj.forget();
 }
 
+static GLenum
+GetAndClearError(GLenum* errorVar)
+{
+    MOZ_ASSERT(errorVar);
+    GLenum ret = *errorVar;
+    *errorVar = LOCAL_GL_NO_ERROR;
+    return ret;
+}
+
 GLenum
 WebGLContext::GetError()
 {
-    if (mContextStatus == ContextNotLost) {
-        MakeContextCurrent();
-        UpdateWebGLErrorAndClearGLError();
-    } else if (!mContextLostErrorSet) {
-        mWebGLError = LOCAL_GL_CONTEXT_LOST;
-        mContextLostErrorSet = true;
+    /* WebGL 1.0: Section 5.14.3: Setting and getting state:
+     *   If the context's webgl context lost flag is set, returns
+     *   CONTEXT_LOST_WEBGL the first time this method is called.
+     *   Afterward, returns NO_ERROR until the context has been
+     *   restored.
+     *
+     * WEBGL_lose_context:
+     *   [When this extension is enabled: ] loseContext and
+     *   restoreContext are allowed to generate INVALID_OPERATION errors
+     *   even when the context is lost.
+     */
+
+    if (IsContextLost()) {
+        if (mEmitContextLostErrorOnce) {
+            mEmitContextLostErrorOnce = false;
+            return LOCAL_GL_CONTEXT_LOST;
+        }
+        // Don't return yet, since WEBGL_lose_contexts contradicts the
+        // original spec, and allows error generation while lost.
     }
 
-    GLenum err = mWebGLError;
-    mWebGLError = LOCAL_GL_NO_ERROR;
+    GLenum err = GetAndClearError(&mWebGLError);
+    if (err != LOCAL_GL_NO_ERROR)
+        return err;
+
+    if (IsContextLost())
+        return LOCAL_GL_NO_ERROR;
+
+    // Either no WebGL-side error, or it's already been cleared.
+    // UnderlyingGL-side errors, now.
+
+    MakeContextCurrent();
+    GetAndFlushUnderlyingGLErrors();
+
+    err = GetAndClearError(&mUnderlyingGLError);
     return err;
 }
 
 JS::Value
 WebGLContext::GetProgramParameter(WebGLProgram *prog, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
@@ -2491,20 +2523,19 @@ WebGLContext::RenderbufferStorage(GLenum
     }
 
     MakeContextCurrent();
 
     bool sizeChanges = width != mBoundRenderbuffer->Width() ||
                        height != mBoundRenderbuffer->Height() ||
                        internalformat != mBoundRenderbuffer->InternalFormat();
     if (sizeChanges) {
-        UpdateWebGLErrorAndClearGLError();
+        GetAndFlushUnderlyingGLErrors();
         mBoundRenderbuffer->RenderbufferStorage(internalformatForGL, width, height);
-        GLenum error = LOCAL_GL_NO_ERROR;
-        UpdateWebGLErrorAndClearGLError(&error);
+        GLenum error = GetAndFlushUnderlyingGLErrors();
         if (error) {
             GenerateWarning("renderbufferStorage generated error %s", ErrorName(error));
             return;
         }
     } else {
         mBoundRenderbuffer->RenderbufferStorage(internalformatForGL, width, height);
     }
 
@@ -3627,22 +3658,21 @@ GLenum WebGLContext::CheckedTexImage2D(G
 
     // convert type for half float if not on GLES2
     GLenum realType = type;
     if (realType == LOCAL_GL_HALF_FLOAT_OES && !gl->IsGLES2()) {
         realType = LOCAL_GL_HALF_FLOAT;
     }
 
     if (sizeMayChange) {
-        UpdateWebGLErrorAndClearGLError();
+        GetAndFlushUnderlyingGLErrors();
 
         gl->fTexImage2D(target, level, internalFormat, width, height, border, format, realType, data);
 
-        GLenum error = LOCAL_GL_NO_ERROR;
-        UpdateWebGLErrorAndClearGLError(&error);
+        GLenum error = GetAndFlushUnderlyingGLErrors();
         return error;
     }
 
     gl->fTexImage2D(target, level, internalFormat, width, height, border, format, realType, data);
 
     return LOCAL_GL_NO_ERROR;
 }
 
--- a/content/canvas/src/WebGLContextUtils.cpp
+++ b/content/canvas/src/WebGLContextUtils.cpp
@@ -101,25 +101,22 @@ WebGLContext::GetImageSize(GLsizei heigh
         = height <= 0 ? 0 : (height-1) * checked_alignedRowSize + checked_plainRowSize;
 
     return checked_neededByteLength;
 }
 
 void
 WebGLContext::SynthesizeGLError(GLenum err)
 {
-    // If there is already a pending error, don't overwrite it;
-    // but if there isn't, then we need to check for a gl error
-    // that may have occurred before this one and use that code
-    // instead.
-
-    MakeContextCurrent();
-
-    UpdateWebGLErrorAndClearGLError();
-
+    /* ES2 section 2.5 "GL Errors" states that implementations can have
+     * multiple 'flags', as errors might be caught in different parts of
+     * a distributed implementation.
+     * We're signing up as a distributed implementation here, with
+     * separate flags for WebGL and the underlying GLContext.
+     */
     if (!mWebGLError)
         mWebGLError = err;
 }
 
 void
 WebGLContext::SynthesizeGLError(GLenum err, const char *fmt, ...)
 {
     va_list va;
@@ -229,19 +226,21 @@ WebGLContext::IsTextureFormatCompressed(
         case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1:
         case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1:
             return true;
         default:
             return false;
     }
 }
 
-void
-WebGLContext::UpdateWebGLErrorAndClearGLError(GLenum *currentGLError)
+GLenum
+WebGLContext::GetAndFlushUnderlyingGLErrors()
 {
-    // get and clear GL error in ALL cases
+    // Get and clear GL error in ALL cases.
     GLenum error = gl->GetAndClearError();
-    if (currentGLError)
-        *currentGLError = error;
-    // only store in mWebGLError if is hasn't already recorded an error
-    if (!mWebGLError)
-        mWebGLError = error;
+
+    // Only store in mUnderlyingGLError if is hasn't already recorded an
+    // error.
+    if (!mUnderlyingGLError)
+        mUnderlyingGLError = error;
+
+    return error;
 }
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -1581,17 +1581,19 @@ WebGLContext::InitAndValidateGL()
     mLoseContextOnHeapMinimize = Preferences::GetBool("webgl.lose-context-on-heap-minimize", false);
     mCanLoseContextInForeground = Preferences::GetBool("webgl.can-lose-context-in-foreground", true);
 
     if (MinCapabilityMode()) {
       mDisableFragHighP = true;
     }
 
     mActiveTexture = 0;
+    mEmitContextLostErrorOnce = true;
     mWebGLError = LOCAL_GL_NO_ERROR;
+    mUnderlyingGLError = LOCAL_GL_NO_ERROR;
 
     mBound2DTextures.Clear();
     mBoundCubeMapTextures.Clear();
 
     mBoundArrayBuffer = nullptr;
     mBoundTransformFeedbackBuffer = nullptr;
     mCurrentProgram = nullptr;
 
--- a/content/canvas/src/WebGLTexture.cpp
+++ b/content/canvas/src/WebGLTexture.cpp
@@ -446,23 +446,22 @@ WebGLTexture::DoDeferredImageInitializat
                         imageInfo.mHeight,
                         imageInfo.mWidth,
                         texelsize,
                         mContext->mPixelStoreUnpackAlignment);
     MOZ_ASSERT(checked_byteLength.isValid()); // should have been checked earlier
     void *zeros = calloc(1, checked_byteLength.value());
 
     GLenum format = WebGLTexelConversions::GLFormatForTexelFormat(texelformat);
-    mContext->UpdateWebGLErrorAndClearGLError();
+    mContext->GetAndFlushUnderlyingGLErrors();
     mContext->gl->fTexImage2D(imageTarget, level, imageInfo.mInternalFormat,
                               imageInfo.mWidth, imageInfo.mHeight,
                               0, format, imageInfo.mType,
                               zeros);
-    GLenum error = LOCAL_GL_NO_ERROR;
-    mContext->UpdateWebGLErrorAndClearGLError(&error);
+    GLenum error = mContext->GetAndFlushUnderlyingGLErrors();
 
     free(zeros);
     SetImageDataStatus(imageTarget, level, WebGLImageDataStatus::InitializedImageData);
 
     if (error) {
       // Should only be OUT_OF_MEMORY. Anyway, there's no good way to recover from this here.
       MOZ_CRASH(); // errors on texture upload have been related to video memory exposure in the past.
       return;