Plumb robust resource init extensions.

This also cleans up a few minor glitches in the extension texts,
and renames the EGL extension for consistency.

It incidentally fixes a bug in our EGL init where we were checking
the wrong client versions for KHR_create_context.

It also implements a new feature for tests which allow them to defer
Context creation until the test body. This allows tests to check for
EGL extension available before trying to create a context with certain
extensions.

BUG=angleproject:1635

Change-Id: I9311991332c357e36214082b16f2a4a57bfa8865
Reviewed-on: https://chromium-review.googlesource.com/450920
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp
index 3bfdd97..8888831 100644
--- a/src/libANGLE/Caps.cpp
+++ b/src/libANGLE/Caps.cpp
@@ -214,7 +214,8 @@
       textureNorm16(false),
       pathRendering(false),
       surfacelessContext(false),
-      clientArrays(false)
+      clientArrays(false),
+      robustResourceInitialization(false)
 {
 }
 
@@ -648,6 +649,7 @@
         map["GL_CHROMIUM_path_rendering"] = esOnlyExtension(&Extensions::pathRendering);
         map["GL_OES_surfaceless_context"] = esOnlyExtension(&Extensions::surfacelessContext);
         map["GL_ANGLE_client_arrays"] = esOnlyExtension(&Extensions::clientArrays);
+        map["GL_ANGLE_robust_resource_initialization"] = esOnlyExtension(&Extensions::robustResourceInitialization);
         // clang-format on
 
         return map;
@@ -1037,7 +1039,8 @@
       pixelFormatFloat(false),
       surfacelessContext(false),
       displayTextureShareGroup(false),
-      createContextClientArrays(false)
+      createContextClientArrays(false),
+      createContextRobustResourceInitialization(false)
 {
 }
 
@@ -1079,6 +1082,7 @@
     InsertExtensionString("EGL_KHR_surfaceless_context",                         surfacelessContext,                 &extensionStrings);
     InsertExtensionString("EGL_ANGLE_display_texture_share_group",               displayTextureShareGroup,           &extensionStrings);
     InsertExtensionString("EGL_ANGLE_create_context_client_arrays",              createContextClientArrays,          &extensionStrings);
+    InsertExtensionString("EGL_ANGLE_create_context_robust_resource_initialization", createContextRobustResourceInitialization, &extensionStrings);
     // TODO(jmadill): Enable this when complete.
     //InsertExtensionString("KHR_create_context_no_error",                       createContextNoError,               &extensionStrings);
     // clang-format on
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index 76ccaad..57dc0b8 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -354,6 +354,9 @@
 
     // GL_ANGLE_client_arrays
     bool clientArrays;
+
+    // GL_ANGLE_robust_resource_initialization
+    bool robustResourceInitialization;
 };
 
 struct ExtensionInfo
@@ -655,6 +658,9 @@
 
     // EGL_ANGLE_create_context_client_arrays
     bool createContextClientArrays;
+
+    // EGL_ANGLE_create_context_robust_resource_initialization
+    bool createContextRobustResourceInitialization;
 };
 
 struct DeviceExtensions
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index d3e4366..507618d 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -204,6 +204,11 @@
     return (attribs.get(EGL_CONTEXT_CLIENT_ARRAYS_ENABLED_ANGLE, EGL_TRUE) == EGL_TRUE);
 }
 
+bool GetRobustResourceInit(const egl::AttributeMap &attribs)
+{
+    return (attribs.get(EGL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE, EGL_FALSE) == EGL_TRUE);
+}
+
 std::string GetObjectLabelFromPointer(GLsizei length, const GLchar *label)
 {
     std::string labelName;
@@ -278,7 +283,8 @@
     initWorkarounds();
 
     mGLState.initialize(mCaps, mExtensions, getClientVersion(), GetDebug(attribs),
-                        GetBindGeneratesResource(attribs), GetClientArraysEnabled(attribs));
+                        GetBindGeneratesResource(attribs), GetClientArraysEnabled(attribs),
+                        GetRobustResourceInit(attribs));
 
     mFenceNVHandleAllocator.setBaseHandle(0);
 
@@ -2533,6 +2539,10 @@
     // Explicitly enable GL_ANGLE_robust_client_memory
     mExtensions.robustClientMemory = true;
 
+    // Determine robust resource init availability from EGL.
+    mExtensions.robustResourceInitialization =
+        displayExtensions.createContextRobustResourceInitialization;
+
     // Apply implementation limits
     mCaps.maxVertexAttributes = std::min<GLuint>(mCaps.maxVertexAttributes, MAX_VERTEX_ATTRIBS);
     mCaps.maxVertexAttribBindings =
diff --git a/src/libANGLE/ContextState.cpp b/src/libANGLE/ContextState.cpp
index 4694929..ad0c036 100644
--- a/src/libANGLE/ContextState.cpp
+++ b/src/libANGLE/ContextState.cpp
@@ -433,6 +433,14 @@
         }
     }
 
+    if (getExtensions().robustResourceInitialization &&
+        pname == GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE)
+    {
+        *type      = GL_BOOL;
+        *numParams = 1;
+        return true;
+    }
+
     // Check for ES3.0+ parameter names which are also exposed as ES2 extensions
     switch (pname)
     {
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index 5ec2090..c47824c 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -62,7 +62,8 @@
       mPrimitiveRestart(false),
       mMultiSampling(false),
       mSampleAlphaToOne(false),
-      mFramebufferSRGB(true)
+      mFramebufferSRGB(true),
+      mRobustResourceInit(false)
 {
 }
 
@@ -75,7 +76,8 @@
                        const Version &clientVersion,
                        bool debug,
                        bool bindGeneratesResource,
-                       bool clientArraysEnabled)
+                       bool clientArraysEnabled,
+                       bool robustResourceInit)
 {
     mMaxDrawBuffers = caps.maxDrawBuffers;
     mMaxCombinedTextureImageUnits = caps.maxCombinedTextureImageUnits;
@@ -214,6 +216,8 @@
     mPathStencilFunc = GL_ALWAYS;
     mPathStencilRef  = 0;
     mPathStencilMask = std::numeric_limits<GLuint>::max();
+
+    mRobustResourceInit = robustResourceInit;
 }
 
 void State::reset(const Context *context)
@@ -697,6 +701,8 @@
           return areClientArraysEnabled();
       case GL_FRAMEBUFFER_SRGB_EXT:
           return getFramebufferSRGB();
+      case GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
+          return mRobustResourceInit;
       default:                               UNREACHABLE(); return false;
     }
 }
@@ -1589,6 +1595,9 @@
       case GL_FRAMEBUFFER_SRGB_EXT:
           *params = getFramebufferSRGB() ? GL_TRUE : GL_FALSE;
           break;
+      case GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
+          *params = mRobustResourceInit ? GL_TRUE : GL_FALSE;
+          break;
       default:
         UNREACHABLE();
         break;
diff --git a/src/libANGLE/State.h b/src/libANGLE/State.h
index 1f13b87..b0dd0ed 100644
--- a/src/libANGLE/State.h
+++ b/src/libANGLE/State.h
@@ -45,7 +45,8 @@
                     const Version &clientVersion,
                     bool debug,
                     bool bindGeneratesResource,
-                    bool clientArraysEnabled);
+                    bool clientArraysEnabled,
+                    bool robustResourceInit);
     void reset(const Context *context);
 
     // State chunk getters
@@ -509,6 +510,9 @@
     // GL_EXT_sRGB_write_control
     bool mFramebufferSRGB;
 
+    // GL_ANGLE_robust_resource_intialization
+    bool mRobustResourceInit;
+
     DirtyBits mDirtyBits;
     DirtyObjects mDirtyObjects;
 };
diff --git a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
index a53fc41..a3dbcbc 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Renderer11.cpp
@@ -1103,6 +1103,8 @@
 
     // Contexts are virtualized so textures can be shared globally
     outExtensions->displayTextureShareGroup = true;
+
+    outExtensions->createContextRobustResourceInitialization = true;
 }
 
 gl::Error Renderer11::flush()
diff --git a/src/libANGLE/validationEGL.cpp b/src/libANGLE/validationEGL.cpp
index 621928d..2140907 100644
--- a/src/libANGLE/validationEGL.cpp
+++ b/src/libANGLE/validationEGL.cpp
@@ -464,6 +464,21 @@
               }
               break;
 
+          case EGL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
+              if (!display->getExtensions().createContextRobustResourceInitialization)
+              {
+                  return Error(EGL_BAD_ATTRIBUTE,
+                               "Attribute EGL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE "
+                               "requires EGL_ANGLE_create_context_robust_resource_initialization.");
+              }
+              if (value != EGL_TRUE && value != EGL_FALSE)
+              {
+                  return Error(EGL_BAD_ATTRIBUTE,
+                               "EGL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE must be "
+                               "either EGL_TRUE or EGL_FALSE.");
+              }
+              break;
+
           default:
               return Error(EGL_BAD_ATTRIBUTE, "Unknown attribute.");
         }
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index 20413f6..6901b54 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -216,6 +216,9 @@
         case GL_SAMPLE_MASK:
             return context->getClientVersion() >= Version(3, 1);
 
+        case GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE:
+            return queryOnly && context->getExtensions().robustResourceInitialization;
+
         default:
             return false;
     }
diff --git a/src/libANGLE/validationES_unittest.cpp b/src/libANGLE/validationES_unittest.cpp
index f1171a8..17452d8 100644
--- a/src/libANGLE/validationES_unittest.cpp
+++ b/src/libANGLE/validationES_unittest.cpp
@@ -78,7 +78,7 @@
     caps.maxElementIndex     = 100;
     caps.maxDrawBuffers      = 1;
     caps.maxColorAttachments = 1;
-    state.initialize(caps, extensions, Version(3, 0), false, true, true);
+    state.initialize(caps, extensions, Version(3, 0), false, true, true, false);
 
     NiceMock<MockTextureImpl> *textureImpl = new NiceMock<MockTextureImpl>();
     EXPECT_CALL(mockFactory, createTexture(_)).WillOnce(Return(textureImpl));
diff --git a/src/tests/angle_end2end_tests.gypi b/src/tests/angle_end2end_tests.gypi
index cb53229..d476c5a 100644
--- a/src/tests/angle_end2end_tests.gypi
+++ b/src/tests/angle_end2end_tests.gypi
@@ -67,6 +67,7 @@
             '<(angle_path)/src/tests/gl_tests/ReadPixelsTest.cpp',
             '<(angle_path)/src/tests/gl_tests/RendererTest.cpp',
             '<(angle_path)/src/tests/gl_tests/RobustClientMemoryTest.cpp',
+            '<(angle_path)/src/tests/gl_tests/RobustResourceInitTest.cpp',
             '<(angle_path)/src/tests/gl_tests/SimpleOperationTest.cpp',
             '<(angle_path)/src/tests/gl_tests/SixteenBppTextureTest.cpp',
             '<(angle_path)/src/tests/gl_tests/SRGBFramebufferTest.cpp',
diff --git a/src/tests/gl_tests/RobustResourceInitTest.cpp b/src/tests/gl_tests/RobustResourceInitTest.cpp
new file mode 100644
index 0000000..8ecb29f
--- /dev/null
+++ b/src/tests/gl_tests/RobustResourceInitTest.cpp
@@ -0,0 +1,122 @@
+//
+// Copyright 2017 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.
+//
+// RobustResourceInitTest: Tests for GL_ANGLE_robust_resource_initialization.
+
+#include "test_utils/ANGLETest.h"
+
+#include "test_utils/gl_raii.h"
+
+namespace angle
+{
+
+class RobustResourceInitTest : public ANGLETest
+{
+  protected:
+    RobustResourceInitTest()
+    {
+        setWindowWidth(128);
+        setWindowHeight(128);
+        setConfigRedBits(8);
+        setConfigGreenBits(8);
+        setConfigBlueBits(8);
+        setConfigAlphaBits(8);
+
+        // Defer context init until the test body.
+        setDeferContextInit(true);
+        setRobustResourceInit(true);
+    }
+};
+
+// Context creation should fail if EGL_ANGLE_create_context_robust_resource_initialization
+// is not available, and succeed otherwise.
+TEST_P(RobustResourceInitTest, ExtensionInit)
+{
+    EGLDisplay display = getEGLWindow()->getDisplay();
+    ASSERT_TRUE(display != EGL_NO_DISPLAY);
+
+    if (eglDisplayExtensionEnabled(display,
+                                   "EGL_ANGLE_create_context_robust_resource_initialization"))
+    {
+        // Context creation shold succeed with robust resource init enabled.
+        EXPECT_TRUE(getEGLWindow()->initializeContext());
+
+        // Robust resource init extension should be available.
+        EXPECT_TRUE(extensionEnabled("GL_ANGLE_robust_resource_initialization"));
+
+        // Querying the state value should return true.
+        GLboolean enabled = 0;
+        glGetBooleanv(GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE, &enabled);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GL_TRUE(enabled);
+
+        EXPECT_GL_TRUE(glIsEnabled(GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE));
+    }
+    else
+    {
+        // Context creation should fail with robust resource init enabled.
+        EXPECT_FALSE(getEGLWindow()->initializeContext());
+
+        // Context creation should succeed with robust resource init disabled.
+        setRobustResourceInit(false);
+        ASSERT_TRUE(getEGLWindow()->initializeGL(GetOSWindow()));
+
+        // If context extension string exposed, check queries.
+        if (extensionEnabled("GL_ANGLE_robust_resource_initialization"))
+        {
+            GLboolean enabled = 0;
+            glGetBooleanv(GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE, &enabled);
+            EXPECT_GL_FALSE(enabled);
+
+            EXPECT_GL_FALSE(glIsEnabled(GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE));
+            EXPECT_GL_NO_ERROR();
+        }
+        else
+        {
+            // Querying robust resource init should return INVALID_ENUM.
+            GLboolean enabled = 0;
+            glGetBooleanv(GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE, &enabled);
+            EXPECT_GL_ERROR(GL_INVALID_ENUM);
+        }
+    }
+}
+
+// Test queries on a normal, non-robust enabled context.
+TEST_P(RobustResourceInitTest, QueriesOnNonRobustContext)
+{
+    EGLDisplay display = getEGLWindow()->getDisplay();
+    ASSERT_TRUE(display != EGL_NO_DISPLAY);
+
+    if (!eglDisplayExtensionEnabled(display,
+                                    "EGL_ANGLE_create_context_robust_resource_initialization"))
+    {
+        return;
+    }
+
+    setRobustResourceInit(false);
+    EXPECT_TRUE(getEGLWindow()->initializeContext());
+
+    // If context extension string exposed, check queries.
+    ASSERT_TRUE(extensionEnabled("GL_ANGLE_robust_resource_initialization"));
+
+    // Querying robust resource init should return INVALID_ENUM.
+    GLboolean enabled = 0;
+    glGetBooleanv(GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE, &enabled);
+    EXPECT_GL_FALSE(enabled);
+
+    EXPECT_GL_FALSE(glIsEnabled(GL_CONTEXT_ROBUST_RESOURCE_INITIALIZATION_ANGLE));
+    EXPECT_GL_NO_ERROR();
+}
+
+ANGLE_INSTANTIATE_TEST(RobustResourceInitTest,
+                       ES2_D3D9(),
+                       ES2_D3D11(),
+                       ES3_D3D11(),
+                       ES2_D3D11_FL9_3(),
+                       ES2_OPENGL(),
+                       ES3_OPENGL(),
+                       ES2_OPENGLES(),
+                       ES3_OPENGLES());
+}  // namespace
diff --git a/src/tests/test_utils/ANGLETest.cpp b/src/tests/test_utils/ANGLETest.cpp
index aa9b365..79a9e5d 100644
--- a/src/tests/test_utils/ANGLETest.cpp
+++ b/src/tests/test_utils/ANGLETest.cpp
@@ -198,7 +198,8 @@
       mWidth(16),
       mHeight(16),
       mIgnoreD3D11SDKLayersWarnings(false),
-      mQuadVertexBuffer(0)
+      mQuadVertexBuffer(0),
+      mDeferContextInit(false)
 {
     mEGLWindow =
         new EGLWindow(GetParam().majorVersion, GetParam().minorVersion, GetParam().eglParameters);
@@ -252,9 +253,14 @@
         needSwap = true;
     }
 
-    if (!createEGLContext())
+    if (!mEGLWindow->initializeDisplayAndSurface(mOSWindow))
     {
-        FAIL() << "egl context creation failed.";
+        FAIL() << "egl display or surface init failed.";
+    }
+
+    if (!mDeferContextInit && !mEGLWindow->initializeContext())
+    {
+        FAIL() << "GL Context init failed.";
     }
 
     if (mGLESLibrary)
@@ -706,6 +712,16 @@
     mEGLWindow->setClientArraysEnabled(enabled);
 }
 
+void ANGLETest::setRobustResourceInit(bool enabled)
+{
+    mEGLWindow->setRobustResourceInit(enabled);
+}
+
+void ANGLETest::setDeferContextInit(bool enabled)
+{
+    mDeferContextInit = enabled;
+}
+
 int ANGLETest::getClientMajorVersion() const
 {
     return mEGLWindow->getClientMajorVersion();
@@ -736,11 +752,6 @@
     return mEGLWindow->isMultisample();
 }
 
-bool ANGLETest::createEGLContext()
-{
-    return mEGLWindow->initializeGL(mOSWindow);
-}
-
 bool ANGLETest::destroyEGLContext()
 {
     mEGLWindow->destroyGL();
diff --git a/src/tests/test_utils/ANGLETest.h b/src/tests/test_utils/ANGLETest.h
index 2e18819..88c3f5f 100644
--- a/src/tests/test_utils/ANGLETest.h
+++ b/src/tests/test_utils/ANGLETest.h
@@ -238,6 +238,10 @@
     void setBindGeneratesResource(bool bindGeneratesResource);
     void setVulkanLayersEnabled(bool enabled);
     void setClientArraysEnabled(bool enabled);
+    void setRobustResourceInit(bool enabled);
+
+    // Some EGL extension tests would like to defer the Context init until the test body.
+    void setDeferContextInit(bool enabled);
 
     int getClientMajorVersion() const;
     int getClientMinorVersion() const;
@@ -254,7 +258,6 @@
     static OSWindow *GetOSWindow() { return mOSWindow; }
 
   private:
-    bool createEGLContext();
     bool destroyEGLContext();
 
     void checkD3D11SDKLayersMessages();
@@ -270,6 +273,8 @@
 
     TestPlatformContext mPlatformContext;
 
+    bool mDeferContextInit;
+
     static OSWindow *mOSWindow;
 
     // Workaround for NVIDIA not being able to share a window with OpenGL and Vulkan.