OpenGL: Implement support for GL texture as ExternalImage on ANGLE.

Implement functionality to wrap a GL texture as an ExternalImage,
using ANGLE's EGL_ANGLE_display_texture_share_group extension.

Bug: chromium:1414566
Change-Id: Ib61ec97578dca77e62c5d9a191073e837cc3d5ae
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/143426
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Stephen White <senorblanco@chromium.org>
diff --git a/include/dawn/native/DawnNative.h b/include/dawn/native/DawnNative.h
index a53218b..df44feb 100644
--- a/include/dawn/native/DawnNative.h
+++ b/include/dawn/native/DawnNative.h
@@ -258,6 +258,7 @@
     DXGISharedHandle,
     D3D11Texture,
     EGLImage,
+    GLTexture,
     AHardwareBuffer,
     Last = AHardwareBuffer,
 };
diff --git a/include/dawn/native/OpenGLBackend.h b/include/dawn/native/OpenGLBackend.h
index 2be897f..9a42bfd 100644
--- a/include/dawn/native/OpenGLBackend.h
+++ b/include/dawn/native/OpenGLBackend.h
@@ -17,6 +17,7 @@
 
 using EGLDisplay = void*;
 using EGLImage = void*;
+using GLuint = unsigned int;
 
 #include "dawn/native/DawnNative.h"
 #include "dawn/webgpu_cpp_chained_struct.h"
@@ -49,6 +50,16 @@
 DAWN_NATIVE_EXPORT WGPUTexture
 WrapExternalEGLImage(WGPUDevice device, const ExternalImageDescriptorEGLImage* descriptor);
 
+struct DAWN_NATIVE_EXPORT ExternalImageDescriptorGLTexture : ExternalImageDescriptor {
+  public:
+    ExternalImageDescriptorGLTexture();
+
+    GLuint texture;
+};
+
+DAWN_NATIVE_EXPORT WGPUTexture
+WrapExternalGLTexture(WGPUDevice device, const ExternalImageDescriptorGLTexture* descriptor);
+
 }  // namespace dawn::native::opengl
 
 #endif  // INCLUDE_DAWN_NATIVE_OPENGLBACKEND_H_
diff --git a/src/dawn/native/opengl/ContextEGL.cpp b/src/dawn/native/opengl/ContextEGL.cpp
index 40ec2e3..1eb05f3 100644
--- a/src/dawn/native/opengl/ContextEGL.cpp
+++ b/src/dawn/native/opengl/ContextEGL.cpp
@@ -66,10 +66,6 @@
         return DAWN_INTERNAL_ERROR("EGL_EXT_create_context_robustness must be supported");
     }
 
-    // Use ANGLE texture sharing iff the extension is available.
-    useANGLETextureSharing &=
-        strstr(extensions, "EGL_ANGLE_display_texture_share_group") != nullptr;
-
     std::vector<EGLint> attrib_list{
         EGL_CONTEXT_MAJOR_VERSION,
         major,
@@ -79,6 +75,10 @@
         EGL_TRUE,
     };
     if (useANGLETextureSharing) {
+        if (strstr(extensions, "EGL_ANGLE_display_texture_share_group") == nullptr) {
+            return DAWN_INTERNAL_ERROR(
+                "GL_ANGLE_display_texture_share_group must be supported to use GL texture sharing");
+        }
         attrib_list.push_back(EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE);
         attrib_list.push_back(EGL_TRUE);
     }
diff --git a/src/dawn/native/opengl/DeviceGL.cpp b/src/dawn/native/opengl/DeviceGL.cpp
index 1e59ed3..5c24a8e 100644
--- a/src/dawn/native/opengl/DeviceGL.cpp
+++ b/src/dawn/native/opengl/DeviceGL.cpp
@@ -274,8 +274,7 @@
     mHasPendingCommands = false;
 }
 
-MaybeError Device::ValidateEGLImageCanBeWrapped(const TextureDescriptor* descriptor,
-                                                ::EGLImage image) {
+MaybeError Device::ValidateTextureCanBeWrapped(const TextureDescriptor* descriptor) {
     DAWN_INVALID_IF(descriptor->dimension != wgpu::TextureDimension::e2D,
                     "Texture dimension (%s) is not %s.", descriptor->dimension,
                     wgpu::TextureDimension::e2D);
@@ -304,7 +303,7 @@
     if (ConsumedError(ValidateTextureDescriptor(this, textureDescriptor))) {
         return nullptr;
     }
-    if (ConsumedError(ValidateEGLImageCanBeWrapped(textureDescriptor, image))) {
+    if (ConsumedError(ValidateTextureCanBeWrapped(textureDescriptor))) {
         return nullptr;
     }
 
@@ -313,10 +312,9 @@
     gl.BindTexture(GL_TEXTURE_2D, tex);
     gl.EGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
 
-    GLint width, height, internalFormat;
+    GLint width, height;
     gl.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
     gl.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
-    gl.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &internalFormat);
 
     if (textureDescriptor->size.width != static_cast<uint32_t>(width) ||
         textureDescriptor->size.height != static_cast<uint32_t>(height) ||
@@ -333,6 +331,40 @@
     return new Texture(this, textureDescriptor, tex, TextureBase::TextureState::OwnedInternal);
 }
 
+TextureBase* Device::CreateTextureWrappingGLTexture(const ExternalImageDescriptor* descriptor,
+                                                    GLuint texture) {
+    const OpenGLFunctions& gl = GetGL();
+    const TextureDescriptor* textureDescriptor = FromAPI(descriptor->cTextureDescriptor);
+
+    if (!HasFeature(Feature::ANGLETextureSharing)) {
+        HandleError(DAWN_VALIDATION_ERROR("Device does not support ANGLE GL texture sharing."));
+        return nullptr;
+    }
+    if (ConsumedError(ValidateTextureDescriptor(this, textureDescriptor))) {
+        return nullptr;
+    }
+    if (ConsumedError(ValidateTextureCanBeWrapped(textureDescriptor))) {
+        return nullptr;
+    }
+
+    gl.BindTexture(GL_TEXTURE_2D, texture);
+
+    GLint width, height;
+    gl.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &width);
+    gl.GetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &height);
+
+    if (textureDescriptor->size.width != static_cast<uint32_t>(width) ||
+        textureDescriptor->size.height != static_cast<uint32_t>(height) ||
+        textureDescriptor->size.depthOrArrayLayers != 1) {
+        HandleError(DAWN_VALIDATION_ERROR(
+            "GL texture size (width: %u, height: %u, depth: 1) doesn't match descriptor size %s.",
+            width, height, &textureDescriptor->size));
+        return nullptr;
+    }
+
+    return new Texture(this, textureDescriptor, texture, TextureBase::TextureState::OwnedInternal);
+}
+
 MaybeError Device::TickImpl() {
     SubmitFenceSync();
     return {};
diff --git a/src/dawn/native/opengl/DeviceGL.h b/src/dawn/native/opengl/DeviceGL.h
index 2be89e1..507379f 100644
--- a/src/dawn/native/opengl/DeviceGL.h
+++ b/src/dawn/native/opengl/DeviceGL.h
@@ -33,7 +33,7 @@
 #include "dawn/common/windows_with_undefs.h"
 #endif
 
-typedef void* EGLImage;
+using EGLImage = void*;
 
 namespace dawn::native::opengl {
 
@@ -57,9 +57,11 @@
 
     void SubmitFenceSync();
 
-    MaybeError ValidateEGLImageCanBeWrapped(const TextureDescriptor* descriptor, ::EGLImage image);
+    MaybeError ValidateTextureCanBeWrapped(const TextureDescriptor* descriptor);
     TextureBase* CreateTextureWrappingEGLImage(const ExternalImageDescriptor* descriptor,
                                                ::EGLImage image);
+    TextureBase* CreateTextureWrappingGLTexture(const ExternalImageDescriptor* descriptor,
+                                                GLuint texture);
 
     ResultOrError<Ref<CommandBufferBase>> CreateCommandBuffer(
         CommandEncoder* encoder,
diff --git a/src/dawn/native/opengl/OpenGLBackend.cpp b/src/dawn/native/opengl/OpenGLBackend.cpp
index e3f37b1..b066910 100644
--- a/src/dawn/native/opengl/OpenGLBackend.cpp
+++ b/src/dawn/native/opengl/OpenGLBackend.cpp
@@ -31,6 +31,9 @@
 ExternalImageDescriptorEGLImage::ExternalImageDescriptorEGLImage()
     : ExternalImageDescriptor(ExternalImageType::EGLImage) {}
 
+ExternalImageDescriptorGLTexture::ExternalImageDescriptorGLTexture()
+    : ExternalImageDescriptor(ExternalImageType::GLTexture) {}
+
 WGPUTexture WrapExternalEGLImage(WGPUDevice device,
                                  const ExternalImageDescriptorEGLImage* descriptor) {
     Device* backendDevice = ToBackend(FromAPI(device));
@@ -39,4 +42,12 @@
     return ToAPI(texture);
 }
 
+WGPUTexture WrapExternalGLTexture(WGPUDevice device,
+                                  const ExternalImageDescriptorGLTexture* descriptor) {
+    Device* backendDevice = ToBackend(FromAPI(device));
+    TextureBase* texture =
+        backendDevice->CreateTextureWrappingGLTexture(descriptor, descriptor->texture);
+    return ToAPI(texture);
+}
+
 }  // namespace dawn::native::opengl
diff --git a/src/dawn/tests/BUILD.gn b/src/dawn/tests/BUILD.gn
index df65dfd..1aef11a 100644
--- a/src/dawn/tests/BUILD.gn
+++ b/src/dawn/tests/BUILD.gn
@@ -709,6 +709,7 @@
 
   if (dawn_enable_opengles) {
     sources += [ "white_box/EGLImageWrappingTests.cpp" ]
+    sources += [ "white_box/GLTextureWrappingTests.cpp" ]
     include_dirs = [ "//third_party/khronos" ]
   }
 
diff --git a/src/dawn/tests/white_box/GLTextureWrappingTests.cpp b/src/dawn/tests/white_box/GLTextureWrappingTests.cpp
new file mode 100644
index 0000000..16289f7
--- /dev/null
+++ b/src/dawn/tests/white_box/GLTextureWrappingTests.cpp
@@ -0,0 +1,333 @@
+// Copyright 2023 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 <utility>
+#include <vector>
+
+#include "dawn/common/DynamicLib.h"
+#include "dawn/native/OpenGLBackend.h"
+#include "dawn/native/opengl/DeviceGL.h"
+#include "dawn/tests/DawnTest.h"
+#include "dawn/utils/ComboRenderPipelineDescriptor.h"
+#include "dawn/utils/WGPUHelpers.h"
+
+namespace dawn {
+namespace {
+
+class ScopedGLTexture {
+  public:
+    ScopedGLTexture(PFNGLDELETETEXTURESPROC deleteTextures, GLuint texture)
+        : mDeleteTextures(deleteTextures), mTexture(texture) {}
+
+    ScopedGLTexture(ScopedGLTexture&& other) {
+        if (mTexture != 0) {
+            mDeleteTextures(1, &mTexture);
+        }
+        mDeleteTextures = std::move(other.mDeleteTextures);
+        mTexture = std::move(other.mTexture);
+    }
+
+    ~ScopedGLTexture() {
+        if (mTexture != 0) {
+            mDeleteTextures(1, &mTexture);
+        }
+    }
+
+    GLuint Get() const { return mTexture; }
+
+  private:
+    PFNGLDELETETEXTURESPROC mDeleteTextures = nullptr;
+    GLuint mTexture = 0;
+};
+
+class GLTextureTestBase : public DawnTest {
+  protected:
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        if (SupportsFeatures({wgpu::FeatureName::ANGLETextureSharing})) {
+            return {
+                wgpu::FeatureName::DawnInternalUsages,
+                wgpu::FeatureName::ANGLETextureSharing,
+            };
+        } else {
+            return {
+                wgpu::FeatureName::DawnInternalUsages,
+            };
+        }
+    }
+
+    void SetUp() override {
+        DawnTest::SetUp();
+        DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::ANGLETextureSharing}));
+        DAWN_TEST_UNSUPPORTED_IF(UsesWire());
+    }
+
+  public:
+    ScopedGLTexture CreateGLTexture(uint32_t width,
+                                    uint32_t height,
+                                    GLenum internalFormat,
+                                    GLenum format,
+                                    GLenum type,
+                                    void* data,
+                                    size_t size) {
+        native::opengl::Device* openglDevice =
+            native::opengl::ToBackend(native::FromAPI(device.Get()));
+        const native::opengl::OpenGLFunctions& gl = openglDevice->GetGL();
+        GLuint tex;
+        gl.GenTextures(1, &tex);
+        gl.BindTexture(GL_TEXTURE_2D, tex);
+        gl.TexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, data);
+
+        return ScopedGLTexture(gl.DeleteTextures, tex);
+    }
+    wgpu::Texture WrapGLTexture(const wgpu::TextureDescriptor* descriptor, GLuint texture) {
+        native::opengl::ExternalImageDescriptorGLTexture externDesc;
+        externDesc.cTextureDescriptor = reinterpret_cast<const WGPUTextureDescriptor*>(descriptor);
+        externDesc.texture = texture;
+        return wgpu::Texture::Acquire(
+            native::opengl::WrapExternalGLTexture(device.Get(), &externDesc));
+    }
+};
+
+// A small fixture used to initialize default data for the GLTexture validation tests.
+// These tests are skipped if the harness is using the wire.
+class GLTextureValidationTests : public GLTextureTestBase {
+  public:
+    GLTextureValidationTests() {
+        descriptor.dimension = wgpu::TextureDimension::e2D;
+        descriptor.format = wgpu::TextureFormat::RGBA8Unorm;
+        descriptor.size = {10, 10, 1};
+        descriptor.sampleCount = 1;
+        descriptor.mipLevelCount = 1;
+        descriptor.usage = wgpu::TextureUsage::RenderAttachment;
+    }
+
+    ScopedGLTexture CreateDefaultGLTexture() {
+        return CreateGLTexture(10, 10, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, nullptr, 0);
+    }
+
+  protected:
+    wgpu::TextureDescriptor descriptor;
+};
+
+class GLTextureValidationNoANGLETextureSharingTests : public GLTextureValidationTests {
+  protected:
+    std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+        return {wgpu::FeatureName::DawnInternalUsages};
+    }
+};
+
+// Test a successful wrapping of a GL texture in a WebGPU texture
+TEST_P(GLTextureValidationTests, Success) {
+    ScopedGLTexture glTexture = CreateDefaultGLTexture();
+    wgpu::Texture texture = WrapGLTexture(&descriptor, glTexture.Get());
+    ASSERT_NE(texture.Get(), nullptr);
+}
+
+// Test an unsuccessful wrapping of a GL texture in a WebGPU texture when
+// the device does not support the ANGLETextureSharing Feature
+TEST_P(GLTextureValidationNoANGLETextureSharingTests, Failure) {
+    ScopedGLTexture glTexture = CreateDefaultGLTexture();
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapGLTexture(&descriptor, glTexture.Get()));
+    ASSERT_EQ(texture.Get(), nullptr);
+}
+
+// Test a successful wrapping of a GL texture in a texture with DawnTextureInternalUsageDescriptor
+TEST_P(GLTextureValidationTests, SuccessWithInternalUsageDescriptor) {
+    wgpu::DawnTextureInternalUsageDescriptor internalDesc = {};
+    descriptor.nextInChain = &internalDesc;
+    internalDesc.internalUsage = wgpu::TextureUsage::CopySrc;
+    internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor;
+
+    ScopedGLTexture glTexture = CreateDefaultGLTexture();
+    wgpu::Texture texture = WrapGLTexture(&descriptor, glTexture.Get());
+    ASSERT_NE(texture.Get(), nullptr);
+}
+
+// Test an error occurs if an invalid sType is the nextInChain
+TEST_P(GLTextureValidationTests, InvalidTextureDescriptor) {
+    wgpu::ChainedStruct chainedDescriptor;
+    chainedDescriptor.sType = wgpu::SType::SurfaceDescriptorFromWindowsSwapChainPanel;
+    descriptor.nextInChain = &chainedDescriptor;
+
+    ScopedGLTexture glTexture = CreateDefaultGLTexture();
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapGLTexture(&descriptor, glTexture.Get()));
+
+    ASSERT_EQ(texture.Get(), nullptr);
+}
+
+// Test an error occurs if the descriptor dimension isn't 2D
+TEST_P(GLTextureValidationTests, InvalidTextureDimension) {
+    descriptor.dimension = wgpu::TextureDimension::e3D;
+
+    ScopedGLTexture glTexture = CreateDefaultGLTexture();
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapGLTexture(&descriptor, glTexture.Get()));
+
+    ASSERT_EQ(texture.Get(), nullptr);
+}
+
+// Test an error occurs if the texture usage is not RenderAttachment
+TEST_P(GLTextureValidationTests, InvalidTextureUsage) {
+    descriptor.usage = wgpu::TextureUsage::TextureBinding;
+
+    ScopedGLTexture glTexture = CreateDefaultGLTexture();
+    wgpu::Texture texture;
+    ASSERT_DEVICE_ERROR(texture = WrapGLTexture(&descriptor, glTexture.Get()));
+
+    ASSERT_EQ(texture.Get(), nullptr);
+    descriptor.usage = wgpu::TextureUsage::StorageBinding;
+
+    ASSERT_DEVICE_ERROR(texture = WrapGLTexture(&descriptor, glTexture.Get()));
+
+    ASSERT_EQ(texture.Get(), nullptr);
+}
+
+// Test an error occurs if the descriptor mip level count isn't 1
+TEST_P(GLTextureValidationTests, InvalidMipLevelCount) {
+    descriptor.mipLevelCount = 2;
+
+    ScopedGLTexture glTexture = CreateDefaultGLTexture();
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapGLTexture(&descriptor, glTexture.Get()));
+    ASSERT_EQ(texture.Get(), nullptr);
+}
+
+// Test an error occurs if the descriptor depth isn't 1
+TEST_P(GLTextureValidationTests, InvalidDepth) {
+    descriptor.size.depthOrArrayLayers = 2;
+
+    ScopedGLTexture glTexture = CreateDefaultGLTexture();
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapGLTexture(&descriptor, glTexture.Get()));
+    ASSERT_EQ(texture.Get(), nullptr);
+}
+
+// Test an error occurs if the descriptor sample count isn't 1
+TEST_P(GLTextureValidationTests, InvalidSampleCount) {
+    descriptor.sampleCount = 4;
+
+    ScopedGLTexture glTexture = CreateDefaultGLTexture();
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapGLTexture(&descriptor, glTexture.Get()));
+    ASSERT_EQ(texture.Get(), nullptr);
+}
+
+// Test an error occurs if the descriptor width doesn't match the surface's
+TEST_P(GLTextureValidationTests, InvalidWidth) {
+    descriptor.size.width = 11;
+
+    ScopedGLTexture glTexture = CreateDefaultGLTexture();
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapGLTexture(&descriptor, glTexture.Get()));
+    ASSERT_EQ(texture.Get(), nullptr);
+}
+
+// Test an error occurs if the descriptor height doesn't match the surface's
+TEST_P(GLTextureValidationTests, InvalidHeight) {
+    descriptor.size.height = 11;
+
+    ScopedGLTexture glTexture = CreateDefaultGLTexture();
+    ASSERT_DEVICE_ERROR(wgpu::Texture texture = WrapGLTexture(&descriptor, glTexture.Get()));
+    ASSERT_EQ(texture.Get(), nullptr);
+}
+
+// Fixture to test using GL textures through different usages.
+// These tests are skipped if the harness is using the wire.
+class GLTextureUsageTests : public GLTextureTestBase {
+  public:
+    // Test that clearing using BeginRenderPass writes correct data in the GL texture.
+    void DoClearTest(GLuint texture,
+                     wgpu::TextureFormat format,
+                     GLenum glFormat,
+                     GLenum glType,
+                     void* data,
+                     size_t dataSize) {
+        native::opengl::Device* openglDevice =
+            native::opengl::ToBackend(native::FromAPI(device.Get()));
+        const native::opengl::OpenGLFunctions& gl = openglDevice->GetGL();
+
+        // Get a texture view for the GL texture.
+        wgpu::TextureDescriptor textureDescriptor;
+        textureDescriptor.dimension = wgpu::TextureDimension::e2D;
+        textureDescriptor.format = format;
+        textureDescriptor.size = {1, 1, 1};
+        textureDescriptor.sampleCount = 1;
+        textureDescriptor.mipLevelCount = 1;
+        textureDescriptor.usage = wgpu::TextureUsage::RenderAttachment;
+
+        wgpu::DawnTextureInternalUsageDescriptor internalDesc = {};
+        textureDescriptor.nextInChain = &internalDesc;
+        internalDesc.internalUsage = wgpu::TextureUsage::CopySrc;
+        internalDesc.sType = wgpu::SType::DawnTextureInternalUsageDescriptor;
+
+        wgpu::Texture wrappedTexture = WrapGLTexture(&textureDescriptor, texture);
+        ASSERT_NE(wrappedTexture, nullptr);
+
+        wgpu::TextureView wrappedView = wrappedTexture.CreateView();
+
+        utils::ComboRenderPassDescriptor renderPassDescriptor({wrappedView}, {});
+        renderPassDescriptor.cColorAttachments[0].clearValue = {1 / 255.0f, 2 / 255.0f, 3 / 255.0f,
+                                                                4 / 255.0f};
+
+        // Execute commands to clear the wrapped texture
+        wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+        wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPassDescriptor);
+        pass.End();
+
+        wgpu::CommandBuffer commands = encoder.Finish();
+        queue.Submit(1, &commands);
+
+        // Check the correct data was written
+        std::vector<uint8_t> result(dataSize);
+        GLuint fbo;
+        gl.GenFramebuffers(1, &fbo);
+        gl.BindFramebuffer(GL_FRAMEBUFFER, fbo);
+        gl.FramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture,
+                                0);
+        gl.ReadPixels(0, 0, 1, 1, glFormat, glType, result.data());
+        gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
+        gl.DeleteFramebuffers(1, &fbo);
+        ASSERT_EQ(0, memcmp(result.data(), data, dataSize));
+    }
+};
+
+// Test clearing a R8 GL texture
+TEST_P(GLTextureUsageTests, ClearR8GLTexture) {
+    ScopedGLTexture glTexture = CreateGLTexture(1, 1, GL_R8, GL_RED, GL_UNSIGNED_BYTE, nullptr, 0);
+
+    uint8_t data = 0x01;
+    DoClearTest(glTexture.Get(), wgpu::TextureFormat::R8Unorm, GL_RED, GL_UNSIGNED_BYTE, &data,
+                sizeof(data));
+}
+
+// Test clearing a RG8 GL texture
+TEST_P(GLTextureUsageTests, ClearRG8GLTexture) {
+    ScopedGLTexture glTexture = CreateGLTexture(1, 1, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, nullptr, 0);
+
+    uint16_t data = 0x0201;
+    DoClearTest(glTexture.Get(), wgpu::TextureFormat::RG8Unorm, GL_RG, GL_UNSIGNED_BYTE, &data,
+                sizeof(data));
+}
+
+// Test clearing an RGBA8 GL texture
+TEST_P(GLTextureUsageTests, ClearRGBA8GLTexture) {
+    ScopedGLTexture glTexture =
+        CreateGLTexture(1, 1, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, nullptr, 0);
+
+    uint32_t data = 0x04030201;
+    DoClearTest(glTexture.Get(), wgpu::TextureFormat::RGBA8Unorm, GL_RGBA, GL_UNSIGNED_BYTE, &data,
+                sizeof(data));
+}
+
+DAWN_INSTANTIATE_TEST(GLTextureValidationTests, OpenGLESBackend());
+DAWN_INSTANTIATE_TEST(GLTextureValidationNoANGLETextureSharingTests, OpenGLESBackend());
+DAWN_INSTANTIATE_TEST(GLTextureUsageTests, OpenGLESBackend());
+
+}  // anonymous namespace
+}  // namespace dawn