ES31: Add DispatchComputeIndirect suport for OpenGL backend

BUG=angleproject:2270
TEST=dEQP-GLES31.functional.compute.indirect_dispatch.*

Change-Id: Id062a80188b2a37f28833aaae8e6d31bac382276
Reviewed-on: https://chromium-review.googlesource.com/802763
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/validationES31.cpp b/src/libANGLE/validationES31.cpp
index 61d1adb..8403f68 100644
--- a/src/libANGLE/validationES31.cpp
+++ b/src/libANGLE/validationES31.cpp
@@ -1384,16 +1384,9 @@
     const State &state = context->getGLState();
     Program *program   = state.getProgram();
 
-    if (program == nullptr)
+    if (program == nullptr || !program->hasLinkedComputeShader())
     {
-        context->handleError(InvalidOperation()
-                             << "No active program object for the compute shader stage.");
-        return false;
-    }
-
-    if (!program->hasLinkedComputeShader())
-    {
-        context->handleError(InvalidOperation() << "Program contains no compute shaders.");
+        ANGLE_VALIDATION_ERR(context, InvalidOperation(), NoActiveProgramWithComputeShader);
         return false;
     }
 
@@ -1425,8 +1418,50 @@
 
 bool ValidateDispatchComputeIndirect(Context *context, GLintptr indirect)
 {
-    UNIMPLEMENTED();
-    return false;
+    if (context->getClientVersion() < ES_3_1)
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES31Required);
+        return false;
+    }
+
+    const State &state = context->getGLState();
+    Program *program   = state.getProgram();
+
+    if (program == nullptr || !program->hasLinkedComputeShader())
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidOperation(), NoActiveProgramWithComputeShader);
+        return false;
+    }
+
+    gl::Buffer *dispatchIndirectBuffer = state.getTargetBuffer(BufferBinding::DispatchIndirect);
+    if (!dispatchIndirectBuffer)
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidOperation(), DispatchIndirectBufferNotBound);
+        return false;
+    }
+
+    if (indirect < 0)
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeOffset);
+        return false;
+    }
+
+    if ((indirect & (sizeof(GLuint) - 1)) != 0)
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidOperation(), OffsetMustBeMultipleOfUint);
+        return false;
+    }
+
+    CheckedNumeric<GLuint64> checkedOffset(static_cast<GLuint64>(indirect));
+    auto checkedSum = checkedOffset + static_cast<GLuint64>(3 * sizeof(GLuint));
+    if (!checkedSum.IsValid() ||
+        checkedSum.ValueOrDie() > static_cast<GLuint64>(dispatchIndirectBuffer->getSize()))
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidOperation(), InsufficientBufferSize);
+        return false;
+    }
+
+    return true;
 }
 
 bool ValidateBindImageTexture(Context *context,