Add cross-platform Workarounds to Context.

These are distinct from the renderer-level workarounds.

Add the first workaround, loseContextOnOutOfMemory. This is enabled
when the application enables reset notifications via KHR_robustness,
and is intended for more robust handling of errors in key APIs for
Chromium's correctness (in particular, sync objects).

Change Context::getResetStatus to persistently return the "lost" status
if it was set by calling Context::markContextLost. Previously, if
markContextLost was called but the implementation hadn't actually
received an error like a lost device, the "lost" reset status would be
dropped. Returning it only once to the caller is also fragile. Tested
this by manually injecting a failure in FenceSync11::clientWait and
ensuring that Chromium detected it as expected.

BUG=chromium:650138

Change-Id: Ie53069eacd1754ad5d64936e3fef315af24605fa
Reviewed-on: https://chromium-review.googlesource.com/394233
Commit-Queue: Kenneth Russell <kbr@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 235a3a5..3bdcc60 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -35,6 +35,7 @@
 #include "libANGLE/VertexArray.h"
 #include "libANGLE/formatutils.h"
 #include "libANGLE/validationES.h"
+#include "libANGLE/Workarounds.h"
 #include "libANGLE/renderer/ContextImpl.h"
 #include "libANGLE/renderer/EGLImplFactory.h"
 #include "libANGLE/queryconversions.h"
@@ -247,6 +248,7 @@
       mHasBeenCurrent(false),
       mContextLost(false),
       mResetStatus(GL_NO_ERROR),
+      mContextLostForced(false),
       mResetStrategy(GetResetStrategy(attribs)),
       mRobustAccess(GetRobustAccess(attribs)),
       mCurrentSurface(nullptr),
@@ -255,6 +257,7 @@
     ASSERT(!mRobustAccess);  // Unimplemented
 
     initCaps(GetWebGLContext(attribs));
+    initWorkarounds();
 
     mGLState.initialize(mCaps, mExtensions, mClientMajorVersion, GetDebug(attribs),
                         GetBindGeneratesResource(attribs));
@@ -1916,7 +1919,12 @@
 {
     if (error.isError())
     {
-        mErrors.insert(error.getCode());
+        GLenum code = error.getCode();
+        mErrors.insert(code);
+        if (code == GL_OUT_OF_MEMORY && getWorkarounds().loseContextOnOutOfMemory)
+        {
+            markContextLost();
+        }
 
         if (!error.getMessage().empty())
         {
@@ -1947,7 +1955,10 @@
 void Context::markContextLost()
 {
     if (mResetStrategy == GL_LOSE_CONTEXT_ON_RESET_EXT)
+    {
         mResetStatus = GL_UNKNOWN_CONTEXT_RESET_EXT;
+        mContextLostForced = true;
+    }
     mContextLost     = true;
 }
 
@@ -1986,8 +1997,11 @@
             mContextLost = true;
         }
     }
-    else if (mResetStatus != GL_NO_ERROR)
+    else if (!mContextLostForced && mResetStatus != GL_NO_ERROR)
     {
+        // If markContextLost was used to mark the context lost then
+        // assume that is not recoverable, and continue to report the
+        // lost reset status for the lifetime of this context.
         mResetStatus = mImplementation->getResetStatus();
     }
 
@@ -2470,6 +2484,13 @@
     }
 }
 
+void Context::initWorkarounds()
+{
+    // Lose the context upon out of memory error if the application is
+    // expecting to watch for those events.
+    mWorkarounds.loseContextOnOutOfMemory = (mResetStrategy == GL_LOSE_CONTEXT_ON_RESET_EXT);
+}
+
 void Context::syncRendererState()
 {
     const State::DirtyBits &dirtyBits = mGLState.getDirtyBits();
@@ -3557,4 +3578,9 @@
     programObject->attachShader(shaderObject);
 }
 
+const Workarounds &Context::getWorkarounds() const
+{
+    return mWorkarounds;
+}
+
 }  // namespace gl
diff --git a/src/libANGLE/Context.h b/src/libANGLE/Context.h
index ae84144..935eca6 100644
--- a/src/libANGLE/Context.h
+++ b/src/libANGLE/Context.h
@@ -22,6 +22,7 @@
 #include "libANGLE/Error.h"
 #include "libANGLE/HandleAllocator.h"
 #include "libANGLE/VertexAttribute.h"
+#include "libANGLE/Workarounds.h"
 #include "libANGLE/angletypes.h"
 
 namespace rx
@@ -595,6 +596,7 @@
     size_t getExtensionStringCount() const;
 
     rx::ContextImpl *getImplementation() const { return mImplementation.get(); }
+    const Workarounds &getWorkarounds() const;
 
   private:
     void syncRendererState();
@@ -620,6 +622,7 @@
 
     void initCaps(bool webGLContext);
     void updateCaps();
+    void initWorkarounds();
 
     LabeledObject *getLabeledObject(GLenum identifier, GLuint name) const;
     LabeledObject *getLabeledObjectFromPtr(const void *ptr) const;
@@ -672,6 +675,7 @@
     bool mHasBeenCurrent;
     bool mContextLost;
     GLenum mResetStatus;
+    bool mContextLostForced;
     GLenum mResetStrategy;
     bool mRobustAccess;
     egl::Surface *mCurrentSurface;
@@ -686,6 +690,8 @@
     State::DirtyObjects mClearDirtyObjects;
     State::DirtyBits mBlitDirtyBits;
     State::DirtyObjects mBlitDirtyObjects;
+
+    Workarounds mWorkarounds;
 };
 
 }  // namespace gl
diff --git a/src/libANGLE/Workarounds.h b/src/libANGLE/Workarounds.h
new file mode 100644
index 0000000..c5533c1
--- /dev/null
+++ b/src/libANGLE/Workarounds.h
@@ -0,0 +1,25 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+
+// Workarounds.h: Workarounds for driver bugs and other behaviors seen
+// on all platforms.
+
+#ifndef LIBANGLE_WORKAROUNDS_H_
+#define LIBANGLE_WORKAROUNDS_H_
+
+namespace gl
+{
+
+struct Workarounds
+{
+    // Force the context to be lost (via KHR_robustness) if a GL_OUT_OF_MEMORY error occurs. The
+    // driver may be in an inconsistent state if this happens, and some users of ANGLE rely on this
+    // notification to prevent further execution.
+    bool loseContextOnOutOfMemory = false;
+};
+}  // namespace gl
+
+#endif  // LIBANGLE_WORKAROUNDS_H_