Implement GL_CHROMIUM_sync_query for D3D9 and D3D11.

BUG=angleproject:1366

Change-Id: Iadde61968f45b969c76578a6dd9116a25d63fb4b
Reviewed-on: https://chromium-review.googlesource.com/341230
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp
index 1ffdcbb..56580b3 100644
--- a/src/libANGLE/Caps.cpp
+++ b/src/libANGLE/Caps.cpp
@@ -159,6 +159,7 @@
       noError(false),
       lossyETCDecode(false),
       bindUniformLocation(false),
+      syncQuery(false),
       colorBufferFloat(false)
 {
 }
@@ -232,6 +233,7 @@
 
     InsertExtensionString("GL_ANGLE_lossy_etc_decode",           lossyETCDecode,            &extensionStrings);
     InsertExtensionString("GL_CHROMIUM_bind_uniform_location",   bindUniformLocation,       &extensionStrings);
+    InsertExtensionString("GL_CHROMIUM_sync_query",              syncQuery,                 &extensionStrings);
     // clang-format on
 
     return extensionStrings;
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index 32963b3..2ac3ef4 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -284,6 +284,9 @@
     // GL_CHROMIUM_bind_uniform_location
     bool bindUniformLocation;
 
+    // GL_CHROMIUM_sync_query
+    bool syncQuery;
+
     // ES3 Extension support
 
     // GL_EXT_color_buffer_float
diff --git a/src/libANGLE/State.cpp b/src/libANGLE/State.cpp
index a1d2752..40f0656 100644
--- a/src/libANGLE/State.cpp
+++ b/src/libANGLE/State.cpp
@@ -159,6 +159,7 @@
     mActiveQueries[GL_ANY_SAMPLES_PASSED_CONSERVATIVE].set(nullptr);
     mActiveQueries[GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN].set(nullptr);
     mActiveQueries[GL_TIME_ELAPSED_EXT].set(nullptr);
+    mActiveQueries[GL_COMMANDS_COMPLETED_CHROMIUM].set(nullptr);
 
     mProgram = nullptr;
 
diff --git a/src/libANGLE/renderer/d3d/d3d11/Query11.cpp b/src/libANGLE/renderer/d3d/d3d11/Query11.cpp
index dad5f8c..861950d 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Query11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Query11.cpp
@@ -33,6 +33,9 @@
         case GL_TIMESTAMP_EXT:
             return newResult;
 
+        case GL_COMMANDS_COMPLETED_CHROMIUM:
+            return newResult;
+
         default:
             UNREACHABLE();
             return 0;
@@ -142,9 +145,10 @@
     if (mActiveQuery->query != nullptr)
     {
         ID3D11DeviceContext *context = mRenderer->getDeviceContext();
+        GLenum queryType             = getType();
 
         // If we are doing time elapsed query the end timestamp
-        if (getType() == GL_TIME_ELAPSED_EXT)
+        if (queryType == GL_TIME_ELAPSED_EXT)
         {
             context->End(mActiveQuery->endTimestamp);
         }
@@ -168,8 +172,11 @@
             return error;
         }
 
+        GLenum queryType         = getType();
+        D3D11_QUERY d3dQueryType = gl_d3d11::ConvertQueryType(queryType);
+
         D3D11_QUERY_DESC queryDesc;
-        queryDesc.Query     = gl_d3d11::ConvertQueryType(getType());
+        queryDesc.Query     = d3dQueryType;
         queryDesc.MiscFlags = 0;
 
         ID3D11Device *device = mRenderer->getDevice();
@@ -182,7 +189,7 @@
         }
 
         // If we are doing time elapsed we also need a query to actually query the timestamp
-        if (getType() == GL_TIME_ELAPSED_EXT)
+        if (queryType == GL_TIME_ELAPSED_EXT)
         {
             D3D11_QUERY_DESC desc;
             desc.Query     = D3D11_QUERY_TIMESTAMP;
@@ -203,10 +210,13 @@
 
         ID3D11DeviceContext *context = mRenderer->getDeviceContext();
 
-        context->Begin(mActiveQuery->query);
+        if (d3dQueryType != D3D11_QUERY_EVENT)
+        {
+            context->Begin(mActiveQuery->query);
+        }
 
         // If we are doing time elapsed, query the begin timestamp
-        if (getType() == GL_TIME_ELAPSED_EXT)
+        if (queryType == GL_TIME_ELAPSED_EXT)
         {
             context->End(mActiveQuery->beginTimestamp);
         }
@@ -358,6 +368,28 @@
             }
             break;
 
+            case GL_COMMANDS_COMPLETED_CHROMIUM:
+            {
+                ASSERT(queryState->query);
+                BOOL completed = 0;
+                HRESULT result =
+                    context->GetData(queryState->query, &completed, sizeof(completed), 0);
+                if (FAILED(result))
+                {
+                    return gl::Error(GL_OUT_OF_MEMORY,
+                                     "Failed to get the data of an internal query, result: 0x%X.",
+                                     result);
+                }
+
+                if (result == S_OK)
+                {
+                    queryState->finished = true;
+                    ASSERT(completed == TRUE);
+                    mResult = (completed == TRUE) ? GL_TRUE : GL_FALSE;
+                }
+            }
+            break;
+
         default:
             UNREACHABLE();
             break;
diff --git a/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp b/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
index 37f9faa..614d887 100644
--- a/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/StateManager11.cpp
@@ -130,7 +130,8 @@
 }
 
 static const GLenum QueryTypes[] = {GL_ANY_SAMPLES_PASSED, GL_ANY_SAMPLES_PASSED_CONSERVATIVE,
-                                    GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, GL_TIME_ELAPSED_EXT};
+                                    GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, GL_TIME_ELAPSED_EXT,
+                                    GL_COMMANDS_COMPLETED_CHROMIUM};
 
 StateManager11::StateManager11(Renderer11 *renderer)
     : mRenderer(renderer),
diff --git a/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp b/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
index e724ff5..a26152e 100644
--- a/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
@@ -224,6 +224,8 @@
       case GL_TIME_ELAPSED_EXT:
           // Two internal queries are also created for begin/end timestamps
           return D3D11_QUERY_TIMESTAMP_DISJOINT;
+      case GL_COMMANDS_COMPLETED_CHROMIUM:
+          return D3D11_QUERY_EVENT;
       default: UNREACHABLE();                        return D3D11_QUERY_EVENT;
     }
 }
@@ -1236,6 +1238,7 @@
     extensions->noError                  = true;
     extensions->lossyETCDecode           = true;
     extensions->bindUniformLocation      = true;
+    extensions->syncQuery                 = GetEventQuerySupport(featureLevel);
 
     // D3D11 Feature Level 10_0+ uses SV_IsFrontFace in HLSL to emulate gl_FrontFacing.
     // D3D11 Feature Level 9_3 doesn't support SV_IsFrontFace, and has no equivalent, so can't support gl_FrontFacing.
diff --git a/src/libANGLE/renderer/d3d/d3d9/Query9.cpp b/src/libANGLE/renderer/d3d/d3d9/Query9.cpp
index c826abf..9e5a90a 100644
--- a/src/libANGLE/renderer/d3d/d3d9/Query9.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/Query9.cpp
@@ -30,20 +30,25 @@
 
 gl::Error Query9::begin()
 {
-    if (mQuery == NULL)
+    D3DQUERYTYPE d3dQueryType = gl_d3d9::ConvertQueryType(getType());
+    if (mQuery == nullptr)
     {
-        HRESULT result = mRenderer->getDevice()->CreateQuery(D3DQUERYTYPE_OCCLUSION, &mQuery);
+        HRESULT result = mRenderer->getDevice()->CreateQuery(d3dQueryType, &mQuery);
         if (FAILED(result))
         {
             return gl::Error(GL_OUT_OF_MEMORY, "Internal query creation failed, result: 0x%X.", result);
         }
     }
 
-    HRESULT result = mQuery->Issue(D3DISSUE_BEGIN);
-    ASSERT(SUCCEEDED(result));
-    if (FAILED(result))
+    if (d3dQueryType != D3DQUERYTYPE_EVENT)
     {
-        return gl::Error(GL_OUT_OF_MEMORY, "Failed to begin internal query, result: 0x%X.", result);
+        HRESULT result = mQuery->Issue(D3DISSUE_BEGIN);
+        ASSERT(SUCCEEDED(result));
+        if (FAILED(result))
+        {
+            return gl::Error(GL_OUT_OF_MEMORY, "Failed to begin internal query, result: 0x%X.",
+                             result);
+        }
     }
 
     return gl::Error(GL_NO_ERROR);
@@ -133,26 +138,40 @@
     {
         ASSERT(mQuery);
 
-        DWORD numPixels = 0;
-
-        HRESULT hres = mQuery->GetData(&numPixels, sizeof(DWORD), D3DGETDATA_FLUSH);
-        if (hres == S_OK)
+        HRESULT result = S_OK;
+        switch (getType())
         {
-            mQueryFinished = true;
-
-            switch (getType())
+            case GL_ANY_SAMPLES_PASSED_EXT:
+            case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT:
             {
-              case GL_ANY_SAMPLES_PASSED_EXT:
-              case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT:
-                mResult = (numPixels > 0) ? GL_TRUE : GL_FALSE;
-                break;
-
-              default:
-                UNREACHABLE();
+                DWORD numPixels = 0;
+                result = mQuery->GetData(&numPixels, sizeof(numPixels), D3DGETDATA_FLUSH);
+                if (result == S_OK)
+                {
+                    mQueryFinished = true;
+                    mResult        = (numPixels > 0) ? GL_TRUE : GL_FALSE;
+                }
                 break;
             }
+
+            case GL_COMMANDS_COMPLETED_CHROMIUM:
+            {
+                BOOL completed = FALSE;
+                result = mQuery->GetData(&completed, sizeof(completed), D3DGETDATA_FLUSH);
+                if (result == S_OK)
+                {
+                    mQueryFinished = true;
+                    mResult        = (completed == TRUE) ? GL_TRUE : GL_FALSE;
+                }
+                break;
+            }
+
+            default:
+                UNREACHABLE();
+                break;
         }
-        else if (d3d9::isDeviceLostError(hres))
+
+        if (d3d9::isDeviceLostError(result))
         {
             mRenderer->notifyDeviceLost();
             return gl::Error(GL_OUT_OF_MEMORY, "Failed to test get query result, device is lost.");
diff --git a/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp b/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp
index 34efec7..b55f7dc 100644
--- a/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp
@@ -264,6 +264,21 @@
     }
 }
 
+D3DQUERYTYPE ConvertQueryType(GLenum queryType)
+{
+    switch (queryType)
+    {
+        case GL_ANY_SAMPLES_PASSED_EXT:
+        case GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT:
+            return D3DQUERYTYPE_OCCLUSION;
+        case GL_COMMANDS_COMPLETED_CHROMIUM:
+            return D3DQUERYTYPE_EVENT;
+        default:
+            UNREACHABLE();
+            return static_cast<D3DQUERYTYPE>(0);
+    }
+}
+
 D3DMULTISAMPLE_TYPE GetMultisampleType(GLuint samples)
 {
     return (samples > 1) ? static_cast<D3DMULTISAMPLE_TYPE>(samples) : D3DMULTISAMPLE_NONE;
@@ -580,6 +595,7 @@
     extensions->vertexArrayObject      = true;
     extensions->noError                = true;
     extensions->bindUniformLocation    = true;
+    extensions->syncQuery              = extensions->fence;
 
     // D3D9 has no concept of separate masks and refs for front and back faces in the depth stencil
     // state.
diff --git a/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.h b/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.h
index aa494ad..5e2b6d2 100644
--- a/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.h
+++ b/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.h
@@ -39,6 +39,7 @@
 D3DTEXTUREFILTERTYPE ConvertMagFilter(GLenum magFilter, float maxAnisotropy);
 void ConvertMinFilter(GLenum minFilter, D3DTEXTUREFILTERTYPE *d3dMinFilter, D3DTEXTUREFILTERTYPE *d3dMipFilter,
                       float *d3dLodBias, float maxAnisotropy, size_t baseLevel);
+D3DQUERYTYPE ConvertQueryType(GLenum queryType);
 
 D3DMULTISAMPLE_TYPE GetMultisampleType(GLuint samples);
 
diff --git a/src/libANGLE/renderer/gl/StateManagerGL.cpp b/src/libANGLE/renderer/gl/StateManagerGL.cpp
index aa189f4..a961653 100644
--- a/src/libANGLE/renderer/gl/StateManagerGL.cpp
+++ b/src/libANGLE/renderer/gl/StateManagerGL.cpp
@@ -28,7 +28,8 @@
 {
 
 static const GLenum QueryTypes[] = {GL_ANY_SAMPLES_PASSED, GL_ANY_SAMPLES_PASSED_CONSERVATIVE,
-                                    GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, GL_TIME_ELAPSED};
+                                    GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, GL_TIME_ELAPSED,
+                                    GL_COMMANDS_COMPLETED_CHROMIUM};
 
 StateManagerGL::IndexedBufferBinding::IndexedBufferBinding() : offset(0), size(0), buffer(0)
 {
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index a84d9eb..40db7aa 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -398,6 +398,8 @@
         return (context->getClientVersion() >= 3);
       case GL_TIME_ELAPSED_EXT:
           return context->getExtensions().disjointTimerQuery;
+      case GL_COMMANDS_COMPLETED_CHROMIUM:
+          return context->getExtensions().syncQuery;
       default:
         return false;
     }
@@ -1220,7 +1222,7 @@
 bool ValidateBeginQueryEXT(gl::Context *context, GLenum target, GLuint id)
 {
     if (!context->getExtensions().occlusionQueryBoolean &&
-        !context->getExtensions().disjointTimerQuery)
+        !context->getExtensions().disjointTimerQuery && !context->getExtensions().syncQuery)
     {
         context->handleError(Error(GL_INVALID_OPERATION, "Query extension not enabled"));
         return false;
@@ -1251,7 +1253,7 @@
 bool ValidateEndQueryEXT(gl::Context *context, GLenum target)
 {
     if (!context->getExtensions().occlusionQueryBoolean &&
-        !context->getExtensions().disjointTimerQuery)
+        !context->getExtensions().disjointTimerQuery && !context->getExtensions().syncQuery)
     {
         context->handleError(Error(GL_INVALID_OPERATION, "Query extension not enabled"));
         return false;
@@ -1327,7 +1329,7 @@
 bool ValidateGetQueryivEXT(Context *context, GLenum target, GLenum pname, GLint *params)
 {
     if (!context->getExtensions().occlusionQueryBoolean &&
-        !context->getExtensions().disjointTimerQuery)
+        !context->getExtensions().disjointTimerQuery && !context->getExtensions().syncQuery)
     {
         context->handleError(Error(GL_INVALID_OPERATION, "Query extension not enabled"));
         return false;
@@ -1379,7 +1381,7 @@
 bool ValidateGetQueryObjectuivEXT(Context *context, GLuint id, GLenum pname, GLuint *params)
 {
     if (!context->getExtensions().disjointTimerQuery &&
-        !context->getExtensions().occlusionQueryBoolean)
+        !context->getExtensions().occlusionQueryBoolean && !context->getExtensions().syncQuery)
     {
         context->handleError(Error(GL_INVALID_OPERATION, "Query extension not enabled"));
         return false;
diff --git a/src/tests/angle_end2end_tests.gypi b/src/tests/angle_end2end_tests.gypi
index 020a61b..37490b2 100644
--- a/src/tests/angle_end2end_tests.gypi
+++ b/src/tests/angle_end2end_tests.gypi
@@ -60,6 +60,7 @@
             '<(angle_path)/src/tests/gl_tests/SRGBTextureTest.cpp',
             '<(angle_path)/src/tests/gl_tests/StateChangeTest.cpp',
             '<(angle_path)/src/tests/gl_tests/SwizzleTest.cpp',
+            '<(angle_path)/src/tests/gl_tests/SyncQueriesTest.cpp',
             '<(angle_path)/src/tests/gl_tests/TextureTest.cpp',
             '<(angle_path)/src/tests/gl_tests/TimerQueriesTest.cpp',
             '<(angle_path)/src/tests/gl_tests/TransformFeedbackTest.cpp',
diff --git a/src/tests/gl_tests/SyncQueriesTest.cpp b/src/tests/gl_tests/SyncQueriesTest.cpp
new file mode 100644
index 0000000..b363112
--- /dev/null
+++ b/src/tests/gl_tests/SyncQueriesTest.cpp
@@ -0,0 +1,108 @@
+//
+// Copyright 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.
+//
+
+// SyncQueriesTest.cpp: Tests of the GL_CHROMIUM_sync_query extension
+
+#include "test_utils/ANGLETest.h"
+
+namespace angle
+{
+
+class SyncQueriesTest : public ANGLETest
+{
+  protected:
+    SyncQueriesTest()
+    {
+        setWindowWidth(128);
+        setWindowHeight(128);
+        setConfigRedBits(8);
+        setConfigGreenBits(8);
+        setConfigBlueBits(8);
+        setConfigAlphaBits(8);
+        setConfigDepthBits(24);
+    }
+
+    void TearDown() override
+    {
+        if (mQuery != 0)
+        {
+            glDeleteQueriesEXT(1, &mQuery);
+            mQuery = 0;
+        }
+
+        ANGLETest::TearDown();
+    }
+
+    GLuint mQuery = 0;
+};
+
+// Test basic usage of sync queries
+TEST_P(SyncQueriesTest, Basic)
+{
+    if (!extensionEnabled("GL_CHROMIUM_sync_query"))
+    {
+        std::cout << "Test skipped because GL_CHROMIUM_sync_query is not available." << std::endl;
+        return;
+    }
+
+    glGenQueriesEXT(1, &mQuery);
+    glBeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, mQuery);
+    EXPECT_GL_NO_ERROR();
+
+    glClearColor(0.0, 0.0, 1.0, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    glEndQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM);
+
+    glFlush();
+    GLuint result = 0;
+    glGetQueryObjectuivEXT(mQuery, GL_QUERY_RESULT_EXT, &result);
+    EXPECT_EQ(static_cast<GLuint>(GL_TRUE), result);
+    EXPECT_GL_NO_ERROR();
+}
+
+// Test that the sync query enums are not accepted unless the extension is available
+TEST_P(SyncQueriesTest, Validation)
+{
+    // Need the GL_EXT_occlusion_query_boolean extension for the entry points
+    if (!extensionEnabled("GL_EXT_occlusion_query_boolean"))
+    {
+        std::cout << "Test skipped because GL_EXT_occlusion_query_boolean is not available."
+                  << std::endl;
+        return;
+    }
+
+    bool extensionAvailable = extensionEnabled("GL_CHROMIUM_sync_query");
+
+    glGenQueriesEXT(1, &mQuery);
+
+    glBeginQueryEXT(GL_COMMANDS_COMPLETED_CHROMIUM, mQuery);
+    if (extensionAvailable)
+    {
+        EXPECT_GL_NO_ERROR();
+    }
+    else
+    {
+        EXPECT_GL_ERROR(GL_INVALID_ENUM);
+    }
+
+    glDeleteQueriesEXT(1, &mQuery);
+
+    EXPECT_GL_NO_ERROR();
+}
+
+// Use this to select which configurations (e.g. which renderer, which GLES major version) these
+// tests should be run against.
+ANGLE_INSTANTIATE_TEST(SyncQueriesTest,
+                       ES2_D3D9(),
+                       ES2_D3D11(),
+                       ES3_D3D11(),
+                       ES2_OPENGL(),
+                       ES3_OPENGL(),
+                       ES2_OPENGLES(),
+                       ES3_OPENGLES());
+
+}  // namespace angle