OpenGL: Add support for backend validation

This uses the OpenGL debug ouput functionality to make the driver call
us back when an error happens so we can ASSERT and fail.

BUG=dawn:190

Change-Id: I4b6d7a860384dfeccc1c37383fd4cbdc09d7dc05
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/9204
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/generator/templates/opengl/opengl_platform.h b/generator/templates/opengl/opengl_platform.h
index 182f50a..9b2a53a 100644
--- a/generator/templates/opengl/opengl_platform.h
+++ b/generator/templates/opengl/opengl_platform.h
@@ -44,20 +44,8 @@
                                             GLsizei length,
                                             const GLchar* message,
                                             const void* userParam);
-using GLDEBUGPROCARB = void(KHRONOS_APIENTRY*)(GLenum source,
-                                               GLenum type,
-                                               GLuint id,
-                                               GLenum severity,
-                                               GLsizei length,
-                                               const GLchar* message,
-                                               const void* userParam);
-using GLDEBUGPROCKHR = void(KHRONOS_APIENTRY*)(GLenum source,
-                                               GLenum type,
-                                               GLuint id,
-                                               GLenum severity,
-                                               GLsizei length,
-                                               const GLchar* message,
-                                               const void* userParam);
+using GLDEBUGPROCARB = GLDEBUGPROC;
+using GLDEBUGPROCKHR = GLDEBUGPROC;
 using GLDEBUGPROCAMD = void(KHRONOS_APIENTRY*)(GLuint id,
                                                GLenum category,
                                                GLenum severity,
diff --git a/src/dawn_native/opengl/BackendGL.cpp b/src/dawn_native/opengl/BackendGL.cpp
index 94fc2fa..c168913 100644
--- a/src/dawn_native/opengl/BackendGL.cpp
+++ b/src/dawn_native/opengl/BackendGL.cpp
@@ -15,10 +15,12 @@
 #include "dawn_native/opengl/BackendGL.h"
 
 #include "common/Constants.h"
+#include "dawn_native/Instance.h"
 #include "dawn_native/OpenGLBackend.h"
 #include "dawn_native/opengl/DeviceGL.h"
 
 #include <cstring>
+#include <iostream>
 
 namespace dawn_native { namespace opengl {
 
@@ -47,7 +49,71 @@
             }
             return vendorId;
         }
-    }  // namespace
+
+        void KHRONOS_APIENTRY OnGLDebugMessage(GLenum source,
+                                               GLenum type,
+                                               GLuint id,
+                                               GLenum severity,
+                                               GLsizei length,
+                                               const GLchar* message,
+                                               const void* userParam) {
+            const char* sourceText;
+            switch (source) {
+                case GL_DEBUG_SOURCE_API:
+                    sourceText = "OpenGL";
+                    break;
+                case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
+                    sourceText = "Window System";
+                    break;
+                case GL_DEBUG_SOURCE_SHADER_COMPILER:
+                    sourceText = "Shader Compiler";
+                    break;
+                case GL_DEBUG_SOURCE_THIRD_PARTY:
+                    sourceText = "Third Party";
+                    break;
+                case GL_DEBUG_SOURCE_APPLICATION:
+                    sourceText = "Application";
+                    break;
+                case GL_DEBUG_SOURCE_OTHER:
+                    sourceText = "Other";
+                    break;
+                default:
+                    sourceText = "UNKNOWN";
+                    break;
+            }
+
+            const char* severityText;
+            switch (severity) {
+                case GL_DEBUG_SEVERITY_HIGH:
+                    severityText = "High";
+                    break;
+                case GL_DEBUG_SEVERITY_MEDIUM:
+                    severityText = "Medium";
+                    break;
+                case GL_DEBUG_SEVERITY_LOW:
+                    severityText = "Low";
+                    break;
+                case GL_DEBUG_SEVERITY_NOTIFICATION:
+                    severityText = "Notification";
+                    break;
+                default:
+                    severityText = "UNKNOWN";
+                    break;
+            }
+
+            if (type == GL_DEBUG_TYPE_ERROR) {
+                std::cout << "OpenGL error:" << std::endl;
+                std::cout << "    Source: " << sourceText << std::endl;
+                std::cout << "    ID: " << id << std::endl;
+                std::cout << "    Severity: " << severityText << std::endl;
+                std::cout << "    Message: " << message << std::endl;
+
+                // Abort on an error when in Debug mode.
+                UNREACHABLE();
+            }
+        }
+
+    }  // anonymous namespace
 
     // The OpenGL backend's Adapter.
 
@@ -60,6 +126,36 @@
             // Use getProc to populate the dispatch table
             DAWN_TRY(mFunctions.Initialize(options->getProc));
 
+            // Use the debug output functionality to get notified about GL errors
+            // TODO(cwallez@chromium.org): add support for the KHR_debug and ARB_debug_output
+            // extensions
+            bool hasDebugOutput = mFunctions.IsAtLeastGL(4, 3) || mFunctions.IsAtLeastGLES(3, 2);
+
+            if (GetInstance()->IsBackendValidationEnabled() && hasDebugOutput) {
+                mFunctions.Enable(GL_DEBUG_OUTPUT);
+                mFunctions.Enable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
+
+                // Any GL error; dangerous undefined behavior; any shader compiler and linker errors
+                mFunctions.DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH,
+                                               0, nullptr, GL_TRUE);
+
+                // Severe performance warnings; GLSL or other shader compiler and linker warnings;
+                // use of currently deprecated behavior
+                mFunctions.DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM,
+                                               0, nullptr, GL_TRUE);
+
+                // Performance warnings from redundant state changes; trivial undefined behavior
+                // This is disabled because we do an incredible amount of redundant state changes.
+                mFunctions.DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0,
+                                               nullptr, GL_FALSE);
+
+                // Any message which is not an error or performance concern
+                mFunctions.DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE,
+                                               GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr,
+                                               GL_FALSE);
+                mFunctions.DebugMessageCallback(&OnGLDebugMessage, nullptr);
+            }
+
             // Set state that never changes between devices.
             mFunctions.Enable(GL_DEPTH_TEST);
             mFunctions.Enable(GL_SCISSOR_TEST);
diff --git a/src/dawn_native/opengl/OpenGLFunctions.cpp b/src/dawn_native/opengl/OpenGLFunctions.cpp
index d56aa24..5432715 100644
--- a/src/dawn_native/opengl/OpenGLFunctions.cpp
+++ b/src/dawn_native/opengl/OpenGLFunctions.cpp
@@ -56,4 +56,14 @@
         return {};
     }
 
+    bool OpenGLFunctions::IsAtLeastGL(uint32_t majorVersion, uint32_t minorVersion) {
+        return mStandard == Standard::Desktop &&
+               std::tie(mMajorVersion, mMinorVersion) >= std::tie(majorVersion, minorVersion);
+    }
+
+    bool OpenGLFunctions::IsAtLeastGLES(uint32_t majorVersion, uint32_t minorVersion) {
+        return mStandard == Standard::ES &&
+               std::tie(mMajorVersion, mMinorVersion) >= std::tie(majorVersion, minorVersion);
+    }
+
 }}  // namespace dawn_native::opengl
diff --git a/src/dawn_native/opengl/OpenGLFunctions.h b/src/dawn_native/opengl/OpenGLFunctions.h
index 6f737ec..9fe81a5 100644
--- a/src/dawn_native/opengl/OpenGLFunctions.h
+++ b/src/dawn_native/opengl/OpenGLFunctions.h
@@ -23,6 +23,9 @@
       public:
         MaybeError Initialize(GetProcAddress getProc);
 
+        bool IsAtLeastGL(uint32_t majorVersion, uint32_t minorVersion);
+        bool IsAtLeastGLES(uint32_t majorVersion, uint32_t minorVersion);
+
       private:
         uint32_t mMajorVersion;
         uint32_t mMinorVersion;