Implement GL_KHR_debug.

BUG=angleproject:520

Change-Id: I9ced3e7ab1515feddf2ec103c26b2610a45b1784
Reviewed-on: https://chromium-review.googlesource.com/319830
Tryjob-Request: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Tested-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp
index 75d9096..b929078 100644
--- a/src/libANGLE/validationES2.cpp
+++ b/src/libANGLE/validationES2.cpp
@@ -1069,6 +1069,61 @@
     return ValidateGetProgramBinaryBase(context, program, bufSize, length, binaryFormat, binary);
 }
 
+static bool ValidDebugSource(GLenum source, bool mustBeThirdPartyOrApplication)
+{
+    switch (source)
+    {
+        case GL_DEBUG_SOURCE_API:
+        case GL_DEBUG_SOURCE_SHADER_COMPILER:
+        case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
+        case GL_DEBUG_SOURCE_OTHER:
+            // Only THIRD_PARTY and APPLICATION sources are allowed to be manually inserted
+            return !mustBeThirdPartyOrApplication;
+
+        case GL_DEBUG_SOURCE_THIRD_PARTY:
+        case GL_DEBUG_SOURCE_APPLICATION:
+            return true;
+
+        default:
+            return false;
+    }
+}
+
+static bool ValidDebugType(GLenum type)
+{
+    switch (type)
+    {
+        case GL_DEBUG_TYPE_ERROR:
+        case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
+        case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
+        case GL_DEBUG_TYPE_PERFORMANCE:
+        case GL_DEBUG_TYPE_PORTABILITY:
+        case GL_DEBUG_TYPE_OTHER:
+        case GL_DEBUG_TYPE_MARKER:
+        case GL_DEBUG_TYPE_PUSH_GROUP:
+        case GL_DEBUG_TYPE_POP_GROUP:
+            return true;
+
+        default:
+            return false;
+    }
+}
+
+static bool ValidDebugSeverity(GLenum severity)
+{
+    switch (severity)
+    {
+        case GL_DEBUG_SEVERITY_HIGH:
+        case GL_DEBUG_SEVERITY_MEDIUM:
+        case GL_DEBUG_SEVERITY_LOW:
+        case GL_DEBUG_SEVERITY_NOTIFICATION:
+            return true;
+
+        default:
+            return false;
+    }
+}
+
 bool ValidateDebugMessageControlKHR(Context *context,
                                     GLenum source,
                                     GLenum type,
@@ -1083,7 +1138,43 @@
         return false;
     }
 
-    UNIMPLEMENTED();
+    if (!ValidDebugSource(source, false) && source != GL_DONT_CARE)
+    {
+        context->recordError(Error(GL_INVALID_ENUM, "Invalid debug source."));
+        return false;
+    }
+
+    if (!ValidDebugType(type) && type != GL_DONT_CARE)
+    {
+        context->recordError(Error(GL_INVALID_ENUM, "Invalid debug type."));
+        return false;
+    }
+
+    if (!ValidDebugSeverity(severity) && severity != GL_DONT_CARE)
+    {
+        context->recordError(Error(GL_INVALID_ENUM, "Invalid debug severity."));
+        return false;
+    }
+
+    if (count > 0)
+    {
+        if (source == GL_DONT_CARE || type == GL_DONT_CARE)
+        {
+            context->recordError(Error(
+                GL_INVALID_OPERATION,
+                "If count is greater than zero, source and severity cannot be GL_DONT_CARE."));
+            return false;
+        }
+
+        if (severity != GL_DONT_CARE)
+        {
+            context->recordError(
+                Error(GL_INVALID_OPERATION,
+                      "If count is greater than zero, severity must be GL_DONT_CARE."));
+            return false;
+        }
+    }
+
     return true;
 }
 
@@ -1101,7 +1192,39 @@
         return false;
     }
 
-    UNIMPLEMENTED();
+    if (!context->getState().getDebug().isOutputEnabled())
+    {
+        // If the DEBUG_OUTPUT state is disabled calls to DebugMessageInsert are discarded and do
+        // not generate an error.
+        return false;
+    }
+
+    if (!ValidDebugSeverity(severity))
+    {
+        context->recordError(Error(GL_INVALID_ENUM, "Invalid debug severity."));
+        return false;
+    }
+
+    if (!ValidDebugType(type))
+    {
+        context->recordError(Error(GL_INVALID_ENUM, "Invalid debug type."));
+        return false;
+    }
+
+    if (!ValidDebugSource(source, true))
+    {
+        context->recordError(Error(GL_INVALID_ENUM, "Invalid debug source."));
+        return false;
+    }
+
+    size_t messageLength = (length < 0) ? strlen(buf) : length;
+    if (messageLength > context->getExtensions().maxDebugMessageLength)
+    {
+        context->recordError(
+            Error(GL_INVALID_VALUE, "Message length is larger than GL_MAX_DEBUG_MESSAGE_LENGTH."));
+        return false;
+    }
+
     return true;
 }
 
@@ -1115,7 +1238,6 @@
         return false;
     }
 
-    UNIMPLEMENTED();
     return true;
 }
 
@@ -1135,7 +1257,13 @@
         return false;
     }
 
-    UNIMPLEMENTED();
+    if (bufSize < 0 && messageLog != nullptr)
+    {
+        context->recordError(
+            Error(GL_INVALID_VALUE, "bufSize must be positive if messageLog is not null."));
+        return false;
+    }
+
     return true;
 }
 
@@ -1151,7 +1279,29 @@
         return false;
     }
 
-    UNIMPLEMENTED();
+    if (!ValidDebugSource(source, true))
+    {
+        context->recordError(Error(GL_INVALID_ENUM, "Invalid debug source."));
+        return false;
+    }
+
+    size_t messageLength = (length < 0) ? strlen(message) : length;
+    if (messageLength > context->getExtensions().maxDebugMessageLength)
+    {
+        context->recordError(
+            Error(GL_INVALID_VALUE, "Message length is larger than GL_MAX_DEBUG_MESSAGE_LENGTH."));
+        return false;
+    }
+
+    size_t currentStackSize = context->getState().getDebug().getGroupStackDepth();
+    if (currentStackSize >= context->getExtensions().maxDebugGroupStackDepth)
+    {
+        context->recordError(
+            Error(GL_STACK_OVERFLOW,
+                  "Cannot push more than GL_MAX_DEBUG_GROUP_STACK_DEPTH debug groups."));
+        return false;
+    }
+
     return true;
 }
 
@@ -1163,7 +1313,106 @@
         return false;
     }
 
-    UNIMPLEMENTED();
+    size_t currentStackSize = context->getState().getDebug().getGroupStackDepth();
+    if (currentStackSize <= 1)
+    {
+        context->recordError(Error(GL_STACK_UNDERFLOW, "Cannot pop the default debug group."));
+        return false;
+    }
+
+    return true;
+}
+
+static bool ValidateObjectIdentifierAndName(Context *context, GLenum identifier, GLuint name)
+{
+    switch (identifier)
+    {
+        case GL_BUFFER:
+            if (context->getBuffer(name) == nullptr)
+            {
+                context->recordError(Error(GL_INVALID_VALUE, "name is not a valid buffer."));
+                return false;
+            }
+            return true;
+
+        case GL_SHADER:
+            if (context->getShader(name) == nullptr)
+            {
+                context->recordError(Error(GL_INVALID_VALUE, "name is not a valid shader."));
+                return false;
+            }
+            return true;
+
+        case GL_PROGRAM:
+            if (context->getProgram(name) == nullptr)
+            {
+                context->recordError(Error(GL_INVALID_VALUE, "name is not a valid program."));
+                return false;
+            }
+            return true;
+
+        case GL_VERTEX_ARRAY:
+            if (context->getVertexArray(name) == nullptr)
+            {
+                context->recordError(Error(GL_INVALID_VALUE, "name is not a valid vertex array."));
+                return false;
+            }
+            return true;
+
+        case GL_QUERY:
+            if (context->getQuery(name) == nullptr)
+            {
+                context->recordError(Error(GL_INVALID_VALUE, "name is not a valid query."));
+                return false;
+            }
+            return true;
+
+        case GL_TRANSFORM_FEEDBACK:
+            if (context->getTransformFeedback(name) == nullptr)
+            {
+                context->recordError(
+                    Error(GL_INVALID_VALUE, "name is not a valid transform feedback."));
+                return false;
+            }
+            return true;
+
+        case GL_SAMPLER:
+            if (context->getSampler(name) == nullptr)
+            {
+                context->recordError(Error(GL_INVALID_VALUE, "name is not a valid sampler."));
+                return false;
+            }
+            return true;
+
+        case GL_TEXTURE:
+            if (context->getTexture(name) == nullptr)
+            {
+                context->recordError(Error(GL_INVALID_VALUE, "name is not a valid texture."));
+                return false;
+            }
+            return true;
+
+        case GL_RENDERBUFFER:
+            if (context->getRenderbuffer(name) == nullptr)
+            {
+                context->recordError(Error(GL_INVALID_VALUE, "name is not a valid renderbuffer."));
+                return false;
+            }
+            return true;
+
+        case GL_FRAMEBUFFER:
+            if (context->getFramebuffer(name) == nullptr)
+            {
+                context->recordError(Error(GL_INVALID_VALUE, "name is not a valid framebuffer."));
+                return false;
+            }
+            return true;
+
+        default:
+            context->recordError(Error(GL_INVALID_ENUM, "Invalid identifier."));
+            return false;
+    }
+
     return true;
 }
 
@@ -1179,7 +1428,19 @@
         return false;
     }
 
-    UNIMPLEMENTED();
+    if (!ValidateObjectIdentifierAndName(context, identifier, name))
+    {
+        return false;
+    }
+
+    size_t labelLength = (length < 0) ? strlen(label) : length;
+    if (labelLength > context->getExtensions().maxLabelLength)
+    {
+        context->recordError(
+            Error(GL_INVALID_VALUE, "Label length is larger than GL_MAX_LABEL_LENGTH."));
+        return false;
+    }
+
     return true;
 }
 
@@ -1196,7 +1457,29 @@
         return false;
     }
 
-    UNIMPLEMENTED();
+    if (bufSize < 0)
+    {
+        context->recordError(Error(GL_INVALID_VALUE, "bufSize cannot be negative."));
+        return false;
+    }
+
+    if (!ValidateObjectIdentifierAndName(context, identifier, name))
+    {
+        return false;
+    }
+
+    // Can no-op if bufSize is zero.
+    return bufSize > 0;
+}
+
+static bool ValidateObjectPtrName(Context *context, const void *ptr)
+{
+    if (context->getFenceSync(reinterpret_cast<GLsync>(const_cast<void *>(ptr))) == nullptr)
+    {
+        context->recordError(Error(GL_INVALID_VALUE, "name is not a valid sync."));
+        return false;
+    }
+
     return true;
 }
 
@@ -1211,7 +1494,19 @@
         return false;
     }
 
-    UNIMPLEMENTED();
+    if (!ValidateObjectPtrName(context, ptr))
+    {
+        return false;
+    }
+
+    size_t labelLength = (length < 0) ? strlen(label) : length;
+    if (labelLength > context->getExtensions().maxLabelLength)
+    {
+        context->recordError(
+            Error(GL_INVALID_VALUE, "Label length is larger than GL_MAX_LABEL_LENGTH."));
+        return false;
+    }
+
     return true;
 }
 
@@ -1227,8 +1522,19 @@
         return false;
     }
 
-    UNIMPLEMENTED();
-    return true;
+    if (bufSize < 0)
+    {
+        context->recordError(Error(GL_INVALID_VALUE, "bufSize cannot be negative."));
+        return false;
+    }
+
+    if (!ValidateObjectPtrName(context, ptr))
+    {
+        return false;
+    }
+
+    // Can no-op if bufSize is zero.
+    return bufSize > 0;
 }
 
 bool ValidateGetPointervKHR(Context *context, GLenum pname, void **params)
@@ -1239,7 +1545,18 @@
         return false;
     }
 
-    UNIMPLEMENTED();
+    // TODO: represent this in Context::getQueryParameterInfo.
+    switch (pname)
+    {
+        case GL_DEBUG_CALLBACK_FUNCTION:
+        case GL_DEBUG_CALLBACK_USER_PARAM:
+            break;
+
+        default:
+            context->recordError(Error(GL_INVALID_ENUM, "Invalid pname."));
+            return false;
+    }
+
     return true;
 }
 }