GL: implement EGL context creation in Dawn native.

An abstract base class (Device::Context) is used to avoid adding dependencies to Device, with ContextEGL derived from it. This also
leaves open the possibility of supporting other native GL contexts
in the future (e.g., glX). One temporary EGLContext is created by opengl::Backend during Adapter discovery, then one is created for and
owned by each Device.

Contexts for the desktop GL backend are also managed via EGL, which
works for most modern drivers. This also means that GLFW is now
always used in GLFW_NO_API mode.

Since contexts are now per-device, all of the default GL state
setting and debug output setup was moved from Adapter to Device.

Bug: dawn:810
Change-Id: Idfe30939f155d026fcad549787fc167cc43aa3cb
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/93981
Commit-Queue: Stephen White <senorblanco@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ad25ae0..62d3a93 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -174,6 +174,7 @@
 set_if_not_defined(DAWN_ABSEIL_DIR "${DAWN_THIRD_PARTY_DIR}/abseil-cpp" "Directory in which to find Abseil")
 set_if_not_defined(DAWN_GLFW_DIR "${DAWN_THIRD_PARTY_DIR}/glfw" "Directory in which to find GLFW")
 set_if_not_defined(DAWN_JINJA2_DIR "${DAWN_THIRD_PARTY_DIR}/jinja2" "Directory in which to find Jinja2")
+set_if_not_defined(DAWN_KHRONOS_DIR "${DAWN_THIRD_PARTY_DIR}/khronos" "Directory in which to find Khronos GL headers")
 set_if_not_defined(DAWN_SPIRV_HEADERS_DIR "${DAWN_THIRD_PARTY_DIR}/vulkan-deps/spirv-headers/src" "Directory in which to find SPIRV-Headers")
 set_if_not_defined(DAWN_SPIRV_TOOLS_DIR "${DAWN_THIRD_PARTY_DIR}/vulkan-deps/spirv-tools/src" "Directory in which to find SPIRV-Tools")
 set_if_not_defined(DAWN_SWIFTSHADER_DIR "${DAWN_THIRD_PARTY_DIR}/swiftshader" "Directory in which to find swiftshader")
diff --git a/include/dawn/native/OpenGLBackend.h b/include/dawn/native/OpenGLBackend.h
index bee9dae..cceaab8 100644
--- a/include/dawn/native/OpenGLBackend.h
+++ b/include/dawn/native/OpenGLBackend.h
@@ -23,15 +23,14 @@
 namespace dawn::native::opengl {
 
 struct DAWN_NATIVE_EXPORT AdapterDiscoveryOptions : public AdapterDiscoveryOptionsBase {
-    AdapterDiscoveryOptions();
+    explicit AdapterDiscoveryOptions(WGPUBackendType type);
 
     void* (*getProc)(const char*);
 };
 
-struct DAWN_NATIVE_EXPORT AdapterDiscoveryOptionsES : public AdapterDiscoveryOptionsBase {
+// TODO(crbug.com/dawn/810): This struct can be removed once Chrome is no longer using it.
+struct DAWN_NATIVE_EXPORT AdapterDiscoveryOptionsES : public AdapterDiscoveryOptions {
     AdapterDiscoveryOptionsES();
-
-    void* (*getProc)(const char*);
 };
 
 using PresentCallback = void (*)(void*);
diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn
index 137ce1b..8f9f059 100644
--- a/src/dawn/native/BUILD.gn
+++ b/src/dawn/native/BUILD.gn
@@ -540,8 +540,12 @@
       "opengl/CommandBufferGL.h",
       "opengl/ComputePipelineGL.cpp",
       "opengl/ComputePipelineGL.h",
+      "opengl/ContextEGL.cpp",
+      "opengl/ContextEGL.h",
       "opengl/DeviceGL.cpp",
       "opengl/DeviceGL.h",
+      "opengl/EGLFunctions.cpp",
+      "opengl/EGLFunctions.h",
       "opengl/Forward.h",
       "opengl/GLFormat.cpp",
       "opengl/GLFormat.h",
@@ -575,6 +579,7 @@
       "opengl/UtilsGL.h",
       "opengl/opengl_platform.h",
     ]
+    include_dirs = [ "//third_party/khronos" ]
   }
 
   if (dawn_enable_vulkan) {
diff --git a/src/dawn/native/CMakeLists.txt b/src/dawn/native/CMakeLists.txt
index 8542b33..a523393 100644
--- a/src/dawn/native/CMakeLists.txt
+++ b/src/dawn/native/CMakeLists.txt
@@ -418,8 +418,12 @@
         "opengl/CommandBufferGL.h"
         "opengl/ComputePipelineGL.cpp"
         "opengl/ComputePipelineGL.h"
+        "opengl/ContextEGL.cpp"
+        "opengl/ContextEGL.h"
         "opengl/DeviceGL.cpp"
         "opengl/DeviceGL.h"
+        "opengl/EGLFunctions.cpp"
+        "opengl/EGLFunctions.h"
         "opengl/Forward.h"
         "opengl/GLFormat.cpp"
         "opengl/GLFormat.h"
@@ -454,6 +458,7 @@
         "opengl/opengl_platform.h"
     )
 
+    target_include_directories(dawn_native PRIVATE ${DAWN_KHRONOS_DIR})
     target_link_libraries(dawn_native PRIVATE dawn_khronos_platform)
 endif()
 
diff --git a/src/dawn/native/opengl/AdapterGL.cpp b/src/dawn/native/opengl/AdapterGL.cpp
index eca46c1..9f9d4ba 100644
--- a/src/dawn/native/opengl/AdapterGL.cpp
+++ b/src/dawn/native/opengl/AdapterGL.cpp
@@ -14,11 +14,13 @@
 
 #include "dawn/native/opengl/AdapterGL.h"
 
+#include <memory>
 #include <string>
+#include <utility>
 
 #include "dawn/common/GPUInfo.h"
-#include "dawn/common/Log.h"
 #include "dawn/native/Instance.h"
+#include "dawn/native/opengl/ContextEGL.h"
 #include "dawn/native/opengl/DeviceGL.h"
 
 namespace dawn::native::opengl {
@@ -49,69 +51,6 @@
     return vendorId;
 }
 
-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) {
-        dawn::WarningLog() << "OpenGL error:"
-                           << "\n    Source: " << sourceText      //
-                           << "\n    ID: " << id                  //
-                           << "\n    Severity: " << severityText  //
-                           << "\n    Message: " << message;
-
-        // Abort on an error when in Debug mode.
-        UNREACHABLE();
-    }
-}
-
 }  // anonymous namespace
 
 Adapter::Adapter(InstanceBase* instance, wgpu::BackendType backendType)
@@ -119,6 +58,7 @@
 
 MaybeError Adapter::InitializeGLFunctions(void* (*getProc)(const char*)) {
     // Use getProc to populate the dispatch table
+    mEGLFunctions.Init(getProc);
     return mFunctions.Initialize(getProc);
 }
 
@@ -134,47 +74,6 @@
         ASSERT(GetBackendType() == wgpu::BackendType::OpenGL);
     }
 
-    // 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);
-    mFunctions.Enable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
-    if (mFunctions.GetVersion().IsDesktop()) {
-        // These are not necessary on GLES. The functionality is enabled by default, and
-        // works by specifying sample counts and SRGB textures, respectively.
-        mFunctions.Enable(GL_MULTISAMPLE);
-        mFunctions.Enable(GL_FRAMEBUFFER_SRGB);
-    }
-    mFunctions.Enable(GL_SAMPLE_MASK);
-
     mName = reinterpret_cast<const char*>(mFunctions.GetString(GL_RENDERER));
 
     // Workaroud to find vendor id from vendor name
@@ -251,9 +150,12 @@
 }
 
 ResultOrError<Ref<DeviceBase>> Adapter::CreateDeviceImpl(const DeviceDescriptor* descriptor) {
-    // There is no limit on the number of devices created from this adapter because they can
-    // all share the same backing OpenGL context.
-    return Device::Create(this, descriptor, mFunctions);
+    EGLenum api =
+        GetBackendType() == wgpu::BackendType::OpenGL ? EGL_OPENGL_API : EGL_OPENGL_ES_API;
+
+    std::unique_ptr<Device::Context> context = ContextEGL::Create(mEGLFunctions, api);
+
+    return Device::Create(this, descriptor, mFunctions, std::move(context));
 }
 
 }  // namespace dawn::native::opengl
diff --git a/src/dawn/native/opengl/AdapterGL.h b/src/dawn/native/opengl/AdapterGL.h
index 4101887..6e354b2 100644
--- a/src/dawn/native/opengl/AdapterGL.h
+++ b/src/dawn/native/opengl/AdapterGL.h
@@ -16,6 +16,7 @@
 #define SRC_DAWN_NATIVE_OPENGL_ADAPTERGL_H_
 
 #include "dawn/native/Adapter.h"
+#include "dawn/native/opengl/EGLFunctions.h"
 #include "dawn/native/opengl/OpenGLFunctions.h"
 
 namespace dawn::native::opengl {
@@ -38,6 +39,7 @@
     ResultOrError<Ref<DeviceBase>> CreateDeviceImpl(const DeviceDescriptor* descriptor) override;
 
     OpenGLFunctions mFunctions;
+    EGLFunctions mEGLFunctions;
 };
 
 }  // namespace dawn::native::opengl
diff --git a/src/dawn/native/opengl/BackendGL.cpp b/src/dawn/native/opengl/BackendGL.cpp
index c4b8776..3fad08f 100644
--- a/src/dawn/native/opengl/BackendGL.cpp
+++ b/src/dawn/native/opengl/BackendGL.cpp
@@ -14,10 +14,17 @@
 
 #include "dawn/native/opengl/BackendGL.h"
 
+#include <EGL/egl.h>
+
+#include <memory>
 #include <utility>
 
+#include "dawn/common/SystemUtils.h"
+#include "dawn/native/Instance.h"
 #include "dawn/native/OpenGLBackend.h"
 #include "dawn/native/opengl/AdapterGL.h"
+#include "dawn/native/opengl/ContextEGL.h"
+#include "dawn/native/opengl/EGLFunctions.h"
 
 namespace dawn::native::opengl {
 
@@ -27,8 +34,53 @@
     : BackendConnection(instance, backendType) {}
 
 std::vector<Ref<AdapterBase>> Backend::DiscoverDefaultAdapters() {
-    // The OpenGL backend needs at least "getProcAddress" to discover an adapter.
-    return {};
+    std::vector<Ref<AdapterBase>> adapters;
+#if DAWN_PLATFORM_IS(WINDOWS)
+    const char* eglLib = "libEGL.dll";
+#elif DAWN_PLATFORM_IS(MACOS)
+    const char* eglLib = "libEGL.dylib";
+#else
+    const char* eglLib = "libEGL.so";
+#endif
+    if (!mLibEGL.Valid() && !mLibEGL.Open(eglLib)) {
+        return {};
+    }
+
+    AdapterDiscoveryOptions options(ToAPI(GetType()));
+    options.getProc =
+        reinterpret_cast<void* (*)(const char*)>(mLibEGL.GetProc("eglGetProcAddress"));
+    if (!options.getProc) {
+        return {};
+    }
+
+    EGLFunctions egl;
+    egl.Init(options.getProc);
+
+    EGLenum api = GetType() == wgpu::BackendType::OpenGLES ? EGL_OPENGL_ES_API : EGL_OPENGL_API;
+    std::unique_ptr<Device::Context> context = ContextEGL::Create(egl, api);
+    if (!context) {
+        return {};
+    }
+
+    EGLDisplay prevDisplay = egl.GetCurrentDisplay();
+    EGLContext prevDrawSurface = egl.GetCurrentSurface(EGL_DRAW);
+    EGLContext prevReadSurface = egl.GetCurrentSurface(EGL_READ);
+    EGLContext prevContext = egl.GetCurrentContext();
+
+    context->MakeCurrent();
+
+    auto result = DiscoverAdapters(&options);
+
+    if (result.IsError()) {
+        GetInstance()->ConsumedError(result.AcquireError());
+    } else {
+        auto value = result.AcquireSuccess();
+        adapters.insert(adapters.end(), value.begin(), value.end());
+    }
+
+    egl.MakeCurrent(prevDisplay, prevDrawSurface, prevReadSurface, prevContext);
+
+    return adapters;
 }
 
 ResultOrError<std::vector<Ref<AdapterBase>>> Backend::DiscoverAdapters(
diff --git a/src/dawn/native/opengl/BackendGL.h b/src/dawn/native/opengl/BackendGL.h
index 5916728..dc8fea5 100644
--- a/src/dawn/native/opengl/BackendGL.h
+++ b/src/dawn/native/opengl/BackendGL.h
@@ -17,6 +17,7 @@
 
 #include <vector>
 
+#include "dawn/common/DynamicLib.h"
 #include "dawn/native/BackendConnection.h"
 
 namespace dawn::native::opengl {
@@ -31,6 +32,7 @@
 
   private:
     bool mCreatedAdapter = false;
+    DynamicLib mLibEGL;
 };
 
 }  // namespace dawn::native::opengl
diff --git a/src/dawn/native/opengl/ContextEGL.cpp b/src/dawn/native/opengl/ContextEGL.cpp
new file mode 100644
index 0000000..795b3c0
--- /dev/null
+++ b/src/dawn/native/opengl/ContextEGL.cpp
@@ -0,0 +1,78 @@
+// Copyright 2022 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dawn/native/opengl/ContextEGL.h"
+
+#include <memory>
+#include <vector>
+
+namespace dawn::native::opengl {
+
+std::unique_ptr<ContextEGL> ContextEGL::Create(const EGLFunctions& egl, EGLenum api) {
+    EGLDisplay display = egl.GetCurrentDisplay();
+
+    if (display == EGL_NO_DISPLAY) {
+        display = egl.GetDisplay(EGL_DEFAULT_DISPLAY);
+    }
+
+    if (display == EGL_NO_DISPLAY) {
+        return nullptr;
+    }
+
+    EGLint renderableType = api == EGL_OPENGL_ES_API ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_BIT;
+
+    egl.Initialize(display, nullptr, nullptr);
+
+    // Since we're creating a surfaceless context, the only thing we really care
+    // about is the RENDERABLE_TYPE.
+    EGLint config_attribs[] = {EGL_RENDERABLE_TYPE, renderableType, EGL_NONE};
+
+    EGLint num_config;
+    EGLConfig config;
+    if (egl.ChooseConfig(display, config_attribs, &config, 1, &num_config) == EGL_FALSE) {
+        return nullptr;
+    }
+
+    if (!egl.BindAPI(api)) {
+        return nullptr;
+    }
+
+    EGLint major, minor;
+    if (api == EGL_OPENGL_ES_API) {
+        major = 3;
+        minor = 1;
+    } else {
+        major = 4;
+        minor = 4;
+    }
+    EGLint attrib_list[] = {
+        EGL_CONTEXT_MAJOR_VERSION, major, EGL_CONTEXT_MINOR_VERSION, minor, EGL_NONE, EGL_NONE,
+    };
+    EGLContext context = egl.CreateContext(display, config, EGL_NO_CONTEXT, attrib_list);
+    if (!context) {
+        return nullptr;
+    }
+
+    return std::unique_ptr<ContextEGL>(new ContextEGL(egl, display, context));
+}
+
+void ContextEGL::MakeCurrent() {
+    egl.MakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, mContext);
+}
+
+ContextEGL::~ContextEGL() {
+    egl.DestroyContext(mDisplay, mContext);
+}
+
+}  // namespace dawn::native::opengl
diff --git a/src/dawn/native/opengl/ContextEGL.h b/src/dawn/native/opengl/ContextEGL.h
new file mode 100644
index 0000000..df5a146
--- /dev/null
+++ b/src/dawn/native/opengl/ContextEGL.h
@@ -0,0 +1,44 @@
+// Copyright 2022 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_DAWN_NATIVE_OPENGL_CONTEXTEGL_H_
+#define SRC_DAWN_NATIVE_OPENGL_CONTEXTEGL_H_
+
+#include <EGL/egl.h>
+
+#include <memory>
+
+#include "dawn/native/opengl/DeviceGL.h"
+#include "dawn/native/opengl/EGLFunctions.h"
+
+namespace dawn::native::opengl {
+
+class ContextEGL : public Device::Context {
+  public:
+    static std::unique_ptr<ContextEGL> Create(const EGLFunctions& functions, EGLenum api);
+    void MakeCurrent() override;
+    ~ContextEGL() override;
+
+  private:
+    ContextEGL(const EGLFunctions& functions, EGLDisplay display, EGLContext context)
+        : egl(functions), mDisplay(display), mContext(context) {}
+
+    const EGLFunctions egl;
+    EGLDisplay mDisplay;
+    EGLContext mContext;
+};
+
+}  // namespace dawn::native::opengl
+
+#endif  // SRC_DAWN_NATIVE_OPENGL_CONTEXTEGL_H_
diff --git a/src/dawn/native/opengl/DeviceGL.cpp b/src/dawn/native/opengl/DeviceGL.cpp
index fbceac9..35ff4ef 100644
--- a/src/dawn/native/opengl/DeviceGL.cpp
+++ b/src/dawn/native/opengl/DeviceGL.cpp
@@ -14,9 +14,12 @@
 
 #include "dawn/native/opengl/DeviceGL.h"
 
+#include "dawn/common/Log.h"
+
 #include "dawn/native/BackendConnection.h"
 #include "dawn/native/BindGroupLayout.h"
 #include "dawn/native/ErrorData.h"
+#include "dawn/native/Instance.h"
 #include "dawn/native/StagingBuffer.h"
 #include "dawn/native/opengl/BindGroupGL.h"
 #include "dawn/native/opengl/BindGroupLayoutGL.h"
@@ -32,30 +35,141 @@
 #include "dawn/native/opengl/SwapChainGL.h"
 #include "dawn/native/opengl/TextureGL.h"
 
+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) {
+        dawn::WarningLog() << "OpenGL error:"
+                           << "\n    Source: " << sourceText      //
+                           << "\n    ID: " << id                  //
+                           << "\n    Severity: " << severityText  //
+                           << "\n    Message: " << message;
+
+        // Abort on an error when in Debug mode.
+        UNREACHABLE();
+    }
+}
+
+}  // anonymous namespace
+
 namespace dawn::native::opengl {
 
 // static
 ResultOrError<Ref<Device>> Device::Create(AdapterBase* adapter,
                                           const DeviceDescriptor* descriptor,
-                                          const OpenGLFunctions& functions) {
-    Ref<Device> device = AcquireRef(new Device(adapter, descriptor, functions));
+                                          const OpenGLFunctions& functions,
+                                          std::unique_ptr<Context> context) {
+    Ref<Device> device = AcquireRef(new Device(adapter, descriptor, functions, std::move(context)));
     DAWN_TRY(device->Initialize(descriptor));
     return device;
 }
 
 Device::Device(AdapterBase* adapter,
                const DeviceDescriptor* descriptor,
-               const OpenGLFunctions& functions)
-    : DeviceBase(adapter, descriptor), mGL(functions) {}
+               const OpenGLFunctions& functions,
+               std::unique_ptr<Context> context)
+    : DeviceBase(adapter, descriptor), mGL(functions), mContext(std::move(context)) {}
 
 Device::~Device() {
     Destroy();
 }
 
 MaybeError Device::Initialize(const DeviceDescriptor* descriptor) {
+    const OpenGLFunctions& gl = GetGL();
     InitTogglesFromDriver();
     mFormatTable = BuildGLFormatTable(GetBGRAInternalFormat());
 
+    // Use the debug output functionality to get notified about GL errors
+    // TODO(crbug.com/dawn/1475): add support for the KHR_debug and ARB_debug_output
+    // extensions
+    bool hasDebugOutput = gl.IsAtLeastGL(4, 3) || gl.IsAtLeastGLES(3, 2);
+
+    if (GetAdapter()->GetInstance()->IsBackendValidationEnabled() && hasDebugOutput) {
+        gl.Enable(GL_DEBUG_OUTPUT);
+        gl.Enable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
+
+        // Any GL error; dangerous undefined behavior; any shader compiler and linker errors
+        gl.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
+        gl.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.
+        gl.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
+        gl.DebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0,
+                               nullptr, GL_FALSE);
+        gl.DebugMessageCallback(&OnGLDebugMessage, nullptr);
+    }
+
+    // Set initial state.
+    gl.Enable(GL_DEPTH_TEST);
+    gl.Enable(GL_SCISSOR_TEST);
+    gl.Enable(GL_PRIMITIVE_RESTART_FIXED_INDEX);
+    if (gl.GetVersion().IsDesktop()) {
+        // These are not necessary on GLES. The functionality is enabled by default, and
+        // works by specifying sample counts and SRGB textures, respectively.
+        gl.Enable(GL_MULTISAMPLE);
+        gl.Enable(GL_FRAMEBUFFER_SRGB);
+    }
+    gl.Enable(GL_SAMPLE_MASK);
+
     return DeviceBase::Initialize(AcquireRef(new Queue(this, &descriptor->defaultQueue)));
 }
 
@@ -340,6 +454,9 @@
 }
 
 const OpenGLFunctions& Device::GetGL() const {
+    if (mContext) {
+        mContext->MakeCurrent();
+    }
     return mGL;
 }
 
diff --git a/src/dawn/native/opengl/DeviceGL.h b/src/dawn/native/opengl/DeviceGL.h
index b80439f..78abf11 100644
--- a/src/dawn/native/opengl/DeviceGL.h
+++ b/src/dawn/native/opengl/DeviceGL.h
@@ -39,14 +39,17 @@
 
 class Device final : public DeviceBase {
   public:
+    class Context;
     static ResultOrError<Ref<Device>> Create(AdapterBase* adapter,
                                              const DeviceDescriptor* descriptor,
-                                             const OpenGLFunctions& functions);
+                                             const OpenGLFunctions& functions,
+                                             std::unique_ptr<Context> context);
     ~Device() override;
 
     MaybeError Initialize(const DeviceDescriptor* descriptor);
 
-    // Contains all the OpenGL entry points, glDoFoo is called via gl.DoFoo.
+    // Returns all the OpenGL entry points and ensures that the associated
+    // Context is current.
     const OpenGLFunctions& GetGL() const;
 
     const GLFormat& GetGLFormat(const Format& format);
@@ -80,10 +83,17 @@
 
     float GetTimestampPeriodInNS() const override;
 
+    class Context {
+      public:
+        virtual ~Context() {}
+        virtual void MakeCurrent() = 0;
+    };
+
   private:
     Device(AdapterBase* adapter,
            const DeviceDescriptor* descriptor,
-           const OpenGLFunctions& functions);
+           const OpenGLFunctions& functions,
+           std::unique_ptr<Context> context);
 
     ResultOrError<Ref<BindGroupBase>> CreateBindGroupImpl(
         const BindGroupDescriptor* descriptor) override;
@@ -126,6 +136,7 @@
     std::queue<std::pair<GLsync, ExecutionSerial>> mFencesInFlight;
 
     GLFormatTable mFormatTable;
+    std::unique_ptr<Context> mContext = nullptr;
 };
 
 }  // namespace dawn::native::opengl
diff --git a/src/dawn/native/opengl/EGLFunctions.cpp b/src/dawn/native/opengl/EGLFunctions.cpp
new file mode 100644
index 0000000..75fd25b
--- /dev/null
+++ b/src/dawn/native/opengl/EGLFunctions.cpp
@@ -0,0 +1,42 @@
+// Copyright 2022 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dawn/native/opengl/EGLFunctions.h"
+
+namespace dawn::native::opengl {
+
+void EGLFunctions::Init(void* (*getProc)(const char*)) {
+    GetProcAddress = reinterpret_cast<PFNEGLGETPROCADDRESSPROC>(getProc);
+    BindAPI = reinterpret_cast<PFNEGLBINDAPIPROC>(GetProcAddress("eglBindAPI"));
+    ChooseConfig = reinterpret_cast<PFNEGLCHOOSECONFIGPROC>(GetProcAddress("eglChooseConfig"));
+    CreateContext = reinterpret_cast<PFNEGLCREATECONTEXTPROC>(GetProcAddress("eglCreateContext"));
+    CreatePbufferSurface =
+        reinterpret_cast<PFNEGLCREATEPBUFFERSURFACEPROC>(GetProcAddress("eglCreatePbufferSurface"));
+    CreatePlatformWindowSurface = reinterpret_cast<PFNEGLCREATEPLATFORMWINDOWSURFACEPROC>(
+        GetProcAddress("eglCreatePlatformWindowSurface"));
+    DestroyContext =
+        reinterpret_cast<PFNEGLDESTROYCONTEXTPROC>(GetProcAddress("eglDestroyContext"));
+    GetConfigs = reinterpret_cast<PFNEGLGETCONFIGSPROC>(GetProcAddress("eglGetConfigs"));
+    GetCurrentContext =
+        reinterpret_cast<PFNEGLGETCURRENTCONTEXTPROC>(GetProcAddress("eglGetCurrentContext"));
+    GetCurrentDisplay =
+        reinterpret_cast<PFNEGLGETCURRENTDISPLAYPROC>(GetProcAddress("eglGetCurrentDisplay"));
+    GetCurrentSurface =
+        reinterpret_cast<PFNEGLGETCURRENTSURFACEPROC>(GetProcAddress("eglGetCurrentSurface"));
+    GetDisplay = reinterpret_cast<PFNEGLGETDISPLAYPROC>(GetProcAddress("eglGetDisplay"));
+    Initialize = reinterpret_cast<PFNEGLINITIALIZEPROC>(GetProcAddress("eglInitialize"));
+    MakeCurrent = reinterpret_cast<PFNEGLMAKECURRENTPROC>(GetProcAddress("eglMakeCurrent"));
+}
+
+}  // namespace dawn::native::opengl
diff --git a/src/dawn/native/opengl/EGLFunctions.h b/src/dawn/native/opengl/EGLFunctions.h
new file mode 100644
index 0000000..e73e6e9
--- /dev/null
+++ b/src/dawn/native/opengl/EGLFunctions.h
@@ -0,0 +1,42 @@
+// Copyright 2022 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_DAWN_NATIVE_OPENGL_EGLFUNCTIONS_H_
+#define SRC_DAWN_NATIVE_OPENGL_EGLFUNCTIONS_H_
+
+#include <EGL/egl.h>
+
+namespace dawn::native::opengl {
+
+struct EGLFunctions {
+    void Init(void* (*getProc)(const char*));
+    PFNEGLBINDAPIPROC BindAPI;
+    PFNEGLCHOOSECONFIGPROC ChooseConfig;
+    PFNEGLCREATECONTEXTPROC CreateContext;
+    PFNEGLCREATEPLATFORMWINDOWSURFACEPROC CreatePlatformWindowSurface;
+    PFNEGLCREATEPBUFFERSURFACEPROC CreatePbufferSurface;
+    PFNEGLDESTROYCONTEXTPROC DestroyContext;
+    PFNEGLGETCONFIGSPROC GetConfigs;
+    PFNEGLGETCURRENTCONTEXTPROC GetCurrentContext;
+    PFNEGLGETCURRENTDISPLAYPROC GetCurrentDisplay;
+    PFNEGLGETCURRENTSURFACEPROC GetCurrentSurface;
+    PFNEGLGETDISPLAYPROC GetDisplay;
+    PFNEGLGETPROCADDRESSPROC GetProcAddress;
+    PFNEGLINITIALIZEPROC Initialize;
+    PFNEGLMAKECURRENTPROC MakeCurrent;
+};
+
+}  // namespace dawn::native::opengl
+
+#endif  // SRC_DAWN_NATIVE_OPENGL_EGLFUNCTIONS_H_
diff --git a/src/dawn/native/opengl/OpenGLBackend.cpp b/src/dawn/native/opengl/OpenGLBackend.cpp
index c77c1d0..87cd37f 100644
--- a/src/dawn/native/opengl/OpenGLBackend.cpp
+++ b/src/dawn/native/opengl/OpenGLBackend.cpp
@@ -23,11 +23,11 @@
 
 namespace dawn::native::opengl {
 
-AdapterDiscoveryOptions::AdapterDiscoveryOptions()
-    : AdapterDiscoveryOptionsBase(WGPUBackendType_OpenGL) {}
+AdapterDiscoveryOptions::AdapterDiscoveryOptions(WGPUBackendType type)
+    : AdapterDiscoveryOptionsBase(type) {}
 
 AdapterDiscoveryOptionsES::AdapterDiscoveryOptionsES()
-    : AdapterDiscoveryOptionsBase(WGPUBackendType_OpenGLES) {}
+    : AdapterDiscoveryOptions(WGPUBackendType_OpenGLES) {}
 
 DawnSwapChainImplementation CreateNativeSwapChainImpl(WGPUDevice device,
                                                       PresentCallback present,
diff --git a/src/dawn/native/opengl/OpenGLVersion.cpp b/src/dawn/native/opengl/OpenGLVersion.cpp
index 297b5fb..2c3f0a9 100644
--- a/src/dawn/native/opengl/OpenGLVersion.cpp
+++ b/src/dawn/native/opengl/OpenGLVersion.cpp
@@ -26,9 +26,9 @@
         return DAWN_INTERNAL_ERROR("Couldn't load glGetString");
     }
 
-    std::string version = reinterpret_cast<const char*>(getString(GL_VERSION));
+    const char* version = reinterpret_cast<const char*>(getString(GL_VERSION));
 
-    if (version.find("OpenGL ES") != std::string::npos) {
+    if (strstr(version, "OpenGL ES") != nullptr) {
         // ES spec states that the GL_VERSION string will be in the following format:
         // "OpenGL ES N.M vendor-specific information"
         mStandard = Standard::ES;
@@ -36,7 +36,7 @@
         mMinorVersion = version[12] - '0';
 
         // The minor version shouldn't get to two digits.
-        ASSERT(version.size() <= 13 || !isdigit(version[13]));
+        ASSERT(strlen(version) <= 13 || !isdigit(version[13]));
     } else {
         // OpenGL spec states the GL_VERSION string will be in the following format:
         // <version number><space><vendor-specific information>
@@ -48,7 +48,7 @@
         mMinorVersion = version[2] - '0';
 
         // The minor version shouldn't get to two digits.
-        ASSERT(version.size() <= 3 || !isdigit(version[3]));
+        ASSERT(strlen(version) <= 3 || !isdigit(version[3]));
     }
 
     return {};
diff --git a/src/dawn/samples/SampleUtils.cpp b/src/dawn/samples/SampleUtils.cpp
index ac102a6..c6036c7 100644
--- a/src/dawn/samples/SampleUtils.cpp
+++ b/src/dawn/samples/SampleUtils.cpp
@@ -103,8 +103,8 @@
         return wgpu::Device();
     }
 
-    // Create the test window and discover adapters using it (esp. for OpenGL)
-    utils::SetupGLFWWindowHintsForBackend(backendType);
+    // Create the test window with no client API.
+    glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
     glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
     window = glfwCreateWindow(640, 480, "Dawn window", nullptr, nullptr);
     if (!window) {
diff --git a/src/dawn/tests/DawnTest.cpp b/src/dawn/tests/DawnTest.cpp
index dfd2c54..605ced5 100644
--- a/src/dawn/tests/DawnTest.cpp
+++ b/src/dawn/tests/DawnTest.cpp
@@ -422,31 +422,8 @@
     auto instance = std::make_unique<dawn::native::Instance>();
     instance->EnableBeginCaptureOnStartup(mBeginCaptureOnStartup);
     instance->SetBackendValidationLevel(mBackendValidationLevel);
-    instance->DiscoverDefaultAdapters();
-
-#ifdef DAWN_ENABLE_BACKEND_DESKTOP_GL
-    if (!glfwInit()) {
-        return instance;
-    }
-    glfwDefaultWindowHints();
-    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
-    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
-    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
-    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
-    glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
-
-    mOpenGLWindow = glfwCreateWindow(400, 400, "Dawn OpenGL test window", nullptr, nullptr);
-    if (mOpenGLWindow != nullptr) {
-        glfwMakeContextCurrent(mOpenGLWindow);
-        dawn::native::opengl::AdapterDiscoveryOptions adapterOptions;
-        adapterOptions.getProc = reinterpret_cast<void* (*)(const char*)>(glfwGetProcAddress);
-        instance->DiscoverAdapters(&adapterOptions);
-    }
-#endif  // DAWN_ENABLE_BACKEND_DESKTOP_GL
 
 #ifdef DAWN_ENABLE_BACKEND_OPENGLES
-
-    ScopedEnvironmentVar angleDefaultPlatform;
     if (GetEnvironmentVar("ANGLE_DEFAULT_PLATFORM").first.empty()) {
         const char* platform;
         if (!mANGLEBackend.empty()) {
@@ -458,40 +435,15 @@
             platform = "swiftshader";
 #endif
         }
-        angleDefaultPlatform.Set("ANGLE_DEFAULT_PLATFORM", platform);
-    }
-
-    if (!glfwInit()) {
-        return instance;
-    }
-    glfwDefaultWindowHints();
-    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
-    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
-    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
-    glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
-    glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
-
-    mOpenGLESWindow = glfwCreateWindow(400, 400, "Dawn OpenGLES test window", nullptr, nullptr);
-    if (mOpenGLESWindow != nullptr) {
-        glfwMakeContextCurrent(mOpenGLESWindow);
-        dawn::native::opengl::AdapterDiscoveryOptionsES adapterOptionsES;
-        adapterOptionsES.getProc = reinterpret_cast<void* (*)(const char*)>(glfwGetProcAddress);
-        instance->DiscoverAdapters(&adapterOptionsES);
-        glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);
+        SetEnvironmentVar("ANGLE_DEFAULT_PLATFORM", platform);
     }
 #endif  // DAWN_ENABLE_BACKEND_OPENGLES
 
+    instance->DiscoverDefaultAdapters();
+
     return instance;
 }
 
-GLFWwindow* DawnTestEnvironment::GetOpenGLWindow() const {
-    return mOpenGLWindow;
-}
-
-GLFWwindow* DawnTestEnvironment::GetOpenGLESWindow() const {
-    return mOpenGLESWindow;
-}
-
 void DawnTestEnvironment::SelectPreferredAdapterProperties(const dawn::native::Instance* instance) {
     // Get the first available preferred device type.
     wgpu::AdapterType preferredDeviceType = static_cast<wgpu::AdapterType>(-1);
@@ -1129,17 +1081,6 @@
     ASSERT(device);
 
     queue = device.GetQueue();
-
-#if defined(DAWN_ENABLE_BACKEND_DESKTOP_GL)
-    if (IsOpenGL()) {
-        glfwMakeContextCurrent(gTestEnv->GetOpenGLWindow());
-    }
-#endif  // defined(DAWN_ENABLE_BACKEND_DESKTOP_GL)
-#if defined(DAWN_ENABLE_BACKEND_OPENGLES)
-    if (IsOpenGLES()) {
-        glfwMakeContextCurrent(gTestEnv->GetOpenGLESWindow());
-    }
-#endif  // defined(DAWN_ENABLE_BACKEND_OPENGLES)
 }
 
 void DawnTestBase::TearDown() {
diff --git a/src/dawn/tests/DawnTest.h b/src/dawn/tests/DawnTest.h
index e54ff74..6cbbfe8 100644
--- a/src/dawn/tests/DawnTest.h
+++ b/src/dawn/tests/DawnTest.h
@@ -273,8 +273,6 @@
     std::vector<TestAdapterProperties> mAdapterProperties;
 
     std::unique_ptr<utils::PlatformDebugLogger> mPlatformDebugLogger;
-    GLFWwindow* mOpenGLWindow;
-    GLFWwindow* mOpenGLESWindow;
 };
 
 class DawnTestBase {
diff --git a/src/dawn/tests/end2end/AdapterDiscoveryTests.cpp b/src/dawn/tests/end2end/AdapterDiscoveryTests.cpp
index 7272903..7f3669a 100644
--- a/src/dawn/tests/end2end/AdapterDiscoveryTests.cpp
+++ b/src/dawn/tests/end2end/AdapterDiscoveryTests.cpp
@@ -155,81 +155,6 @@
 }
 #endif  // defined(DAWN_ENABLE_BACKEND_METAL)
 
-#if defined(DAWN_ENABLE_BACKEND_DESKTOP_GL)
-// Test discovering only desktop OpenGL adapters
-TEST(AdapterDiscoveryTests, OnlyDesktopGL) {
-    if (!glfwInit()) {
-        GTEST_SKIP() << "glfwInit() failed";
-    }
-    glfwDefaultWindowHints();
-    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
-    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
-    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
-    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
-    glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
-
-    GLFWwindow* window = glfwCreateWindow(400, 400, "Dawn OpenGL test window", nullptr, nullptr);
-    glfwMakeContextCurrent(window);
-
-    dawn::native::Instance instance;
-
-    dawn::native::opengl::AdapterDiscoveryOptions options;
-    options.getProc = reinterpret_cast<void* (*)(const char*)>(glfwGetProcAddress);
-    instance.DiscoverAdapters(&options);
-    glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);
-
-    const auto& adapters = instance.GetAdapters();
-    for (const auto& adapter : adapters) {
-        wgpu::AdapterProperties properties;
-        adapter.GetProperties(&properties);
-
-        EXPECT_EQ(properties.backendType, wgpu::BackendType::OpenGL);
-    }
-
-    glfwDestroyWindow(window);
-}
-#endif  // defined(DAWN_ENABLE_BACKEND_DESKTOP_GL)
-
-#if defined(DAWN_ENABLE_BACKEND_OPENGLES)
-// Test discovering only OpenGLES adapters
-TEST(AdapterDiscoveryTests, OnlyOpenGLES) {
-    ScopedEnvironmentVar angleDefaultPlatform;
-    if (GetEnvironmentVar("ANGLE_DEFAULT_PLATFORM").first.empty()) {
-        angleDefaultPlatform.Set("ANGLE_DEFAULT_PLATFORM", "swiftshader");
-    }
-
-    if (!glfwInit()) {
-        GTEST_SKIP() << "glfwInit() failed";
-    }
-    glfwDefaultWindowHints();
-    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
-    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
-    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
-    glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
-    glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
-
-    GLFWwindow* window = glfwCreateWindow(400, 400, "Dawn OpenGLES test window", nullptr, nullptr);
-    glfwMakeContextCurrent(window);
-
-    dawn::native::Instance instance;
-
-    dawn::native::opengl::AdapterDiscoveryOptionsES options;
-    options.getProc = reinterpret_cast<void* (*)(const char*)>(glfwGetProcAddress);
-    instance.DiscoverAdapters(&options);
-    glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE);
-
-    const auto& adapters = instance.GetAdapters();
-    for (const auto& adapter : adapters) {
-        wgpu::AdapterProperties properties;
-        adapter.GetProperties(&properties);
-
-        EXPECT_EQ(properties.backendType, wgpu::BackendType::OpenGLES);
-    }
-
-    glfwDestroyWindow(window);
-}
-#endif  // defined(DAWN_ENABLE_BACKEND_OPENGLES)
-
 #if defined(DAWN_ENABLE_BACKEND_METAL) && defined(DAWN_ENABLE_BACKEND_VULKAN)
 // Test discovering the Metal backend, then the Vulkan backend
 // does not duplicate adapters.
diff --git a/src/dawn/tests/end2end/SwapChainTests.cpp b/src/dawn/tests/end2end/SwapChainTests.cpp
index c55330d..a7c50e7 100644
--- a/src/dawn/tests/end2end/SwapChainTests.cpp
+++ b/src/dawn/tests/end2end/SwapChainTests.cpp
@@ -37,10 +37,7 @@
             GTEST_SKIP();
         }
 
-        // The SwapChainTests don't create OpenGL contexts so we don't need to call
-        // SetupGLFWWindowHintsForBackend. Set GLFW_NO_API anyway to avoid GLFW bringing up a GL
-        // context that we won't use.
-        ASSERT_TRUE(!IsOpenGL());
+        // Set GLFW_NO_API to avoid GLFW bringing up a GL context that we won't use.
         glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
         window = glfwCreateWindow(400, 400, "SwapChainValidationTests window", nullptr, nullptr);
 
diff --git a/src/dawn/tests/end2end/SwapChainValidationTests.cpp b/src/dawn/tests/end2end/SwapChainValidationTests.cpp
index b351be7..f0a2ff7 100644
--- a/src/dawn/tests/end2end/SwapChainValidationTests.cpp
+++ b/src/dawn/tests/end2end/SwapChainValidationTests.cpp
@@ -34,10 +34,7 @@
         });
         DAWN_TEST_UNSUPPORTED_IF(!glfwInit());
 
-        // The SwapChainValidationTests don't create devices so we don't need to call
-        // SetupGLFWWindowHintsForBackend. Set GLFW_NO_API anyway to avoid GLFW bringing up a GL
-        // context that we won't use.
-        ASSERT_TRUE(!IsOpenGL());
+        // Set GLFW_NO_API to avoid GLFW bringing up a GL context that we won't use.
         glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
         window = glfwCreateWindow(400, 400, "SwapChainValidationTests window", nullptr, nullptr);
 
diff --git a/src/dawn/tests/end2end/WindowSurfaceTests.cpp b/src/dawn/tests/end2end/WindowSurfaceTests.cpp
index b168089..763f19f 100644
--- a/src/dawn/tests/end2end/WindowSurfaceTests.cpp
+++ b/src/dawn/tests/end2end/WindowSurfaceTests.cpp
@@ -68,9 +68,7 @@
     }
 
     GLFWwindow* CreateWindow() {
-        // The WindowSurfaceInstance tests don't create devices so we don't need to call
-        // SetupGLFWWindowHintsForBackend. Set GLFW_NO_API anyway to avoid GLFW bringing up a GL
-        // context that we won't use.
+        // Set GLFW_NO_API to avoid GLFW bringing up a GL context that we won't use.
         glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
         mWindow = glfwCreateWindow(400, 400, "WindowSurfaceInstanceTests window", nullptr, nullptr);
         return mWindow;
diff --git a/src/dawn/utils/BackendBinding.cpp b/src/dawn/utils/BackendBinding.cpp
index 17bea5f..2aaa047 100644
--- a/src/dawn/utils/BackendBinding.cpp
+++ b/src/dawn/utils/BackendBinding.cpp
@@ -16,12 +16,6 @@
 
 #include "dawn/common/Compiler.h"
 
-#include "GLFW/glfw3.h"
-
-#if defined(DAWN_ENABLE_BACKEND_OPENGL)
-#include "dawn/native/OpenGLBackend.h"
-#endif  // defined(DAWN_ENABLE_BACKEND_OPENGL)
-
 namespace utils {
 
 #if defined(DAWN_ENABLE_BACKEND_D3D12)
@@ -43,29 +37,6 @@
 BackendBinding::BackendBinding(GLFWwindow* window, WGPUDevice device)
     : mWindow(window), mDevice(device) {}
 
-void DiscoverAdapter(dawn::native::Instance* instance, GLFWwindow* window, wgpu::BackendType type) {
-    DAWN_UNUSED(type);
-    DAWN_UNUSED(window);
-
-    if (type == wgpu::BackendType::OpenGL || type == wgpu::BackendType::OpenGLES) {
-#if defined(DAWN_ENABLE_BACKEND_OPENGL)
-        glfwMakeContextCurrent(window);
-        auto getProc = reinterpret_cast<void* (*)(const char*)>(glfwGetProcAddress);
-        if (type == wgpu::BackendType::OpenGL) {
-            dawn::native::opengl::AdapterDiscoveryOptions adapterOptions;
-            adapterOptions.getProc = getProc;
-            instance->DiscoverAdapters(&adapterOptions);
-        } else {
-            dawn::native::opengl::AdapterDiscoveryOptionsES adapterOptions;
-            adapterOptions.getProc = getProc;
-            instance->DiscoverAdapters(&adapterOptions);
-        }
-#endif  // defined(DAWN_ENABLE_BACKEND_OPENGL)
-    } else {
-        instance->DiscoverDefaultAdapters();
-    }
-}
-
 BackendBinding* CreateBinding(wgpu::BackendType type, GLFWwindow* window, WGPUDevice device) {
     switch (type) {
 #if defined(DAWN_ENABLE_BACKEND_D3D12)
diff --git a/src/dawn/utils/GLFWUtils.cpp b/src/dawn/utils/GLFWUtils.cpp
index 3ba577f..c18ca52 100644
--- a/src/dawn/utils/GLFWUtils.cpp
+++ b/src/dawn/utils/GLFWUtils.cpp
@@ -32,26 +32,6 @@
 
 namespace utils {
 
-void SetupGLFWWindowHintsForBackend(wgpu::BackendType type) {
-    if (type == wgpu::BackendType::OpenGL) {
-        // Ask for OpenGL 4.4 which is what the GL backend requires for compute shaders and
-        // texture views.
-        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
-        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
-        glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
-        glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
-    } else if (type == wgpu::BackendType::OpenGLES) {
-        glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
-        glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
-        glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
-        glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
-    } else {
-        // Without this GLFW will initialize a GL context on the window, which prevents using
-        // the window with other APIs (by crashing in weird ways).
-        glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
-    }
-}
-
 wgpu::Surface CreateSurfaceForWindow(const wgpu::Instance& instance, GLFWwindow* window) {
     std::unique_ptr<wgpu::ChainedStruct> chainedDescriptor =
         SetupWindowAndGetSurfaceDescriptor(window);
diff --git a/src/dawn/utils/GLFWUtils.h b/src/dawn/utils/GLFWUtils.h
index 2fe5b10..f42446d 100644
--- a/src/dawn/utils/GLFWUtils.h
+++ b/src/dawn/utils/GLFWUtils.h
@@ -23,10 +23,6 @@
 
 namespace utils {
 
-// Adds all the necessary glfwWindowHint calls for the next GLFWwindow created to be used with
-// the specified backend.
-void SetupGLFWWindowHintsForBackend(wgpu::BackendType type);
-
 // Does the necessary setup on the GLFWwindow to allow creating a wgpu::Surface with it and
 // calls `instance.CreateSurface` with the correct descriptor for this window.
 // Returns a null wgpu::Surface on failure.