Support SharedTextureMemory from AHBs for the GL backend.

Create and store EGLImages in SharedTextureMemoryEGL and produce
native GL textures on SharedTextureMemory::CreaeteTexture.

Unknown format enums are treated as RGBA8 and all usages except
sampling are disabled. They are often vendor-specific YUV formats
that are unknowable.

Bug: 42241435
Change-Id: I47b5ecd653dc7efd0ec89207e96b3d5d45ba9ff2
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/211834
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Geoff Lang <geofflang@chromium.org>
diff --git a/src/dawn/native/AHBFunctions.cpp b/src/dawn/native/AHBFunctions.cpp
index 25d66e7..ec89898 100644
--- a/src/dawn/native/AHBFunctions.cpp
+++ b/src/dawn/native/AHBFunctions.cpp
@@ -29,6 +29,82 @@
 
 namespace dawn::native {
 
+namespace {
+
+// Convert from AHardwareBuffer_Format to wgpu::TextureFormat. Returns:
+//  - The exact corresponding wgpu::TextureFormat if the AHardwareBuffer format matches channel and
+//  bit count.
+//  - External if the format miss-matches in channel count or bit count but is still usable as an
+//  OpenGL or Vulkan texture in Dawn.
+//  - Undefined if the format is not applicable for WebGPU to import as a texture.
+wgpu::TextureFormat FormatFromAHardwareBufferFormat(uint32_t ahbFormat) {
+    switch (ahbFormat) {
+        // Formats with exact WebGPU representations
+        case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
+            return wgpu::TextureFormat::RGBA8Unorm;
+        case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
+            return wgpu::TextureFormat::RGBA16Float;
+        case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
+            return wgpu::TextureFormat::RGB10A2Unorm;
+        case AHARDWAREBUFFER_FORMAT_D16_UNORM:
+            return wgpu::TextureFormat::Depth16Unorm;
+        case AHARDWAREBUFFER_FORMAT_D24_UNORM:
+            return wgpu::TextureFormat::Depth24Plus;
+        case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
+            return wgpu::TextureFormat::Depth24PlusStencil8;
+        case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
+            return wgpu::TextureFormat::Depth32Float;
+        case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT:
+            return wgpu::TextureFormat::Depth32FloatStencil8;
+        case AHARDWAREBUFFER_FORMAT_S8_UINT:
+            return wgpu::TextureFormat::Stencil8;
+        case AHARDWAREBUFFER_FORMAT_R8_UNORM:
+            return wgpu::TextureFormat::R8Unorm;
+        case AHARDWAREBUFFER_FORMAT_R16_UINT:
+            return wgpu::TextureFormat::R16Uint;
+        case AHARDWAREBUFFER_FORMAT_R16G16_UINT:
+            return wgpu::TextureFormat::RG16Uint;
+
+        // R8G8B8X8 is used in Vulkan as VK_FORMAT_R8G8B8A8_UNORM and GLES as GL_RGB8. In Vulkan the
+        // alpha channel can be read and written but GL always treats it as opaque. Data can be
+        // uploaded as if it's RGBA8.
+        case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
+            return wgpu::TextureFormat::RGBA8Unorm;
+
+        // YUV formats are sampleable with external Vulkan samplers or as opaque samplers in GLES.
+        // Treat them as External.
+        case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
+            return wgpu::TextureFormat::External;
+        case AHARDWAREBUFFER_FORMAT_YCbCr_P010:
+            return wgpu::TextureFormat::External;
+
+        // RGB formats with no direct representation in WebGPU. Trivially sampleable and renderable
+        // in Vulkan and GLES but data uploads are not currently supported.
+        case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
+            return wgpu::TextureFormat::External;
+        case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
+            return wgpu::TextureFormat::External;
+
+        // R10G10B10A10 maps to Vulkan format VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 and no
+        // GLES format. Not supported with no known use case.
+        case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM:
+            return wgpu::TextureFormat::Undefined;
+
+        // Can be used as a Vulkan buffer or bound to a GLES buffer using GL_EXT_external_buffer.
+        // These use cases are not currently supported.
+        case AHARDWAREBUFFER_FORMAT_BLOB:
+            return wgpu::TextureFormat::Undefined;
+
+        // Android drivers make use of formats outside of the defined enums to represent internal
+        // YUV formats used by the camera and video decoder. Treat these all as external and
+        // sampleable.
+        default:
+            return wgpu::TextureFormat::External;
+    }
+}
+
+}  // anonymous namespace
+
 AHBFunctions::AHBFunctions() {
     if (!mNativeWindowLib.Open("libnativewindow.so") ||
         !mNativeWindowLib.GetProc(&Acquire, "AHardwareBuffer_acquire") ||
@@ -44,4 +120,28 @@
     return mNativeWindowLib.Valid();
 }
 
+SharedTextureMemoryProperties GetAHBSharedTextureMemoryProperties(
+    const AHBFunctions* ahbFunctions,
+    ::AHardwareBuffer* aHardwareBuffer) {
+    AHardwareBuffer_Desc aHardwareBufferDesc{};
+    ahbFunctions->Describe(aHardwareBuffer, &aHardwareBufferDesc);
+
+    SharedTextureMemoryProperties properties;
+    properties.size = {aHardwareBufferDesc.width, aHardwareBufferDesc.height,
+                       aHardwareBufferDesc.layers};
+    properties.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
+    if (aHardwareBufferDesc.usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) {
+        properties.usage |= wgpu::TextureUsage::RenderAttachment;
+    }
+    if (aHardwareBufferDesc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
+        properties.usage |= wgpu::TextureUsage::TextureBinding;
+    }
+    if (aHardwareBufferDesc.usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER) {
+        properties.usage |= wgpu::TextureUsage::StorageBinding;
+    }
+    properties.format = FormatFromAHardwareBufferFormat(aHardwareBufferDesc.format);
+
+    return properties;
+}
+
 }  // namespace dawn::native
diff --git a/src/dawn/native/AHBFunctions.h b/src/dawn/native/AHBFunctions.h
index 1d42b9b..bf1d50c 100644
--- a/src/dawn/native/AHBFunctions.h
+++ b/src/dawn/native/AHBFunctions.h
@@ -31,6 +31,7 @@
 #include <android/hardware_buffer.h>
 
 #include "dawn/common/DynamicLib.h"
+#include "dawn/native/dawn_platform.h"
 
 namespace dawn::native {
 
@@ -50,6 +51,10 @@
     DynamicLib mNativeWindowLib;
 };
 
+SharedTextureMemoryProperties GetAHBSharedTextureMemoryProperties(
+    const AHBFunctions* ahbFunctions,
+    ::AHardwareBuffer* aHardwareBuffer);
+
 }  // namespace dawn::native
 
 #endif  // SRC_DAWN_NATIVE_AHBFUNCTIONS_H_
diff --git a/src/dawn/native/BUILD.gn b/src/dawn/native/BUILD.gn
index 766688d..8089e30 100644
--- a/src/dawn/native/BUILD.gn
+++ b/src/dawn/native/BUILD.gn
@@ -757,6 +757,10 @@
       "opengl/SamplerGL.h",
       "opengl/ShaderModuleGL.cpp",
       "opengl/ShaderModuleGL.h",
+      "opengl/SharedTextureMemoryEGL.cpp",
+      "opengl/SharedTextureMemoryEGL.h",
+      "opengl/SharedTextureMemoryGL.cpp",
+      "opengl/SharedTextureMemoryGL.h",
       "opengl/SwapChainEGL.cpp",
       "opengl/SwapChainEGL.h",
       "opengl/TextureGL.cpp",
diff --git a/src/dawn/native/CMakeLists.txt b/src/dawn/native/CMakeLists.txt
index 1bcb6b2..35dffb2 100644
--- a/src/dawn/native/CMakeLists.txt
+++ b/src/dawn/native/CMakeLists.txt
@@ -611,6 +611,8 @@
         "opengl/RenderPipelineGL.h"
         "opengl/SamplerGL.h"
         "opengl/ShaderModuleGL.h"
+        "opengl/SharedTextureMemoryEGL.h"
+        "opengl/SharedTextureMemoryGL.h"
         "opengl/SwapChainEGL.h"
         "opengl/TextureGL.h"
         "opengl/UtilsEGL.h"
@@ -640,6 +642,8 @@
         "opengl/RenderPipelineGL.cpp"
         "opengl/SamplerGL.cpp"
         "opengl/ShaderModuleGL.cpp"
+        "opengl/SharedTextureMemoryEGL.cpp"
+        "opengl/SharedTextureMemoryGL.cpp"
         "opengl/SwapChainEGL.cpp"
         "opengl/TextureGL.cpp"
         "opengl/UtilsEGL.cpp"
diff --git a/src/dawn/native/opengl/DeviceGL.cpp b/src/dawn/native/opengl/DeviceGL.cpp
index 6ee1ca1..dd9568f 100644
--- a/src/dawn/native/opengl/DeviceGL.cpp
+++ b/src/dawn/native/opengl/DeviceGL.cpp
@@ -48,11 +48,16 @@
 #include "dawn/native/opengl/RenderPipelineGL.h"
 #include "dawn/native/opengl/SamplerGL.h"
 #include "dawn/native/opengl/ShaderModuleGL.h"
+#include "dawn/native/opengl/SharedTextureMemoryEGL.h"
 #include "dawn/native/opengl/SwapChainEGL.h"
 #include "dawn/native/opengl/TextureGL.h"
 #include "dawn/native/opengl/UtilsGL.h"
 #include "dawn/native/opengl/opengl_platform.h"
 
+#if DAWN_PLATFORM_IS(ANDROID)
+#include "dawn/native/AHBFunctions.h"
+#endif  // DAWN_PLATFORM_IS(ANDROID)
+
 namespace {
 
 void KHRONOS_APIENTRY OnGLDebugMessage(GLenum source,
@@ -281,6 +286,28 @@
     return AcquireRef(new TextureView(texture, descriptor));
 }
 
+ResultOrError<Ref<SharedTextureMemoryBase>> Device::ImportSharedTextureMemoryImpl(
+    const SharedTextureMemoryDescriptor* descriptor) {
+    UnpackedPtr<SharedTextureMemoryDescriptor> unpacked;
+    DAWN_TRY_ASSIGN(unpacked, ValidateAndUnpack(descriptor));
+
+    wgpu::SType type;
+    DAWN_TRY_ASSIGN(
+        type, (unpacked.ValidateBranches<Branch<SharedTextureMemoryAHardwareBufferDescriptor>>()));
+
+    switch (type) {
+        case wgpu::SType::SharedTextureMemoryAHardwareBufferDescriptor:
+            DAWN_INVALID_IF(!HasFeature(Feature::SharedTextureMemoryAHardwareBuffer),
+                            "%s is not enabled.",
+                            wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer);
+            return SharedTextureMemoryEGL::Create(
+                this, descriptor->label,
+                unpacked.Get<SharedTextureMemoryAHardwareBufferDescriptor>());
+        default:
+            DAWN_UNREACHABLE();
+    }
+}
+
 MaybeError Device::ValidateTextureCanBeWrapped(const UnpackedPtr<TextureDescriptor>& descriptor) {
     DAWN_INVALID_IF(descriptor->dimension != wgpu::TextureDimension::e2D,
                     "Texture dimension (%s) is not %s.", descriptor->dimension,
@@ -340,7 +367,7 @@
 
     // TODO(dawn:803): Validate the OpenGL texture format from the EGLImage against the format
     // in the passed-in TextureDescriptor.
-    auto result = AcquireRef(new Texture(this, textureDescriptor, tex));
+    auto result = AcquireRef(new Texture(this, textureDescriptor, tex, OwnsHandle::No));
     result->SetIsSubresourceContentInitialized(descriptor->isInitialized,
                                                result->GetAllSubresources());
     return result;
@@ -381,7 +408,7 @@
         return nullptr;
     }
 
-    auto result = AcquireRef(new Texture(this, textureDescriptor, texture));
+    auto result = AcquireRef(new Texture(this, textureDescriptor, texture, OwnsHandle::No));
     result->SetIsSubresourceContentInitialized(descriptor->isInitialized,
                                                result->GetAllSubresources());
     return result;
@@ -431,6 +458,17 @@
     return true;
 }
 
+const AHBFunctions* Device::GetOrLoadAHBFunctions() {
+#if DAWN_PLATFORM_IS(ANDROID)
+    if (mAHBFunctions == nullptr) {
+        mAHBFunctions = std::make_unique<AHBFunctions>();
+    }
+    return mAHBFunctions.get();
+#else
+    DAWN_UNREACHABLE();
+#endif  // DAWN_PLATFORM_IS(ANDROID)
+}
+
 const OpenGLFunctions& Device::GetGL() const {
     mContext->MakeCurrent();
     ToBackend(GetQueue())->OnGLUsed();
diff --git a/src/dawn/native/opengl/DeviceGL.h b/src/dawn/native/opengl/DeviceGL.h
index f4418f6..13a551c 100644
--- a/src/dawn/native/opengl/DeviceGL.h
+++ b/src/dawn/native/opengl/DeviceGL.h
@@ -41,6 +41,10 @@
 #include "dawn/native/opengl/GLFormat.h"
 #include "dawn/native/opengl/OpenGLFunctions.h"
 
+namespace dawn::native {
+class AHBFunctions;
+}  // namespace dawn::native
+
 namespace dawn::native::opengl {
 
 class ContextEGL;
@@ -101,6 +105,8 @@
     bool MayRequireDuplicationOfIndirectParameters() const override;
     bool ShouldApplyIndexBufferOffsetToFirstIndex() const override;
 
+    const AHBFunctions* GetOrLoadAHBFunctions();
+
   private:
     Device(AdapterBase* adapter,
            const UnpackedPtr<DeviceDescriptor>& descriptor,
@@ -138,6 +144,8 @@
         const UnpackedPtr<ComputePipelineDescriptor>& descriptor) override;
     Ref<RenderPipelineBase> CreateUninitializedRenderPipelineImpl(
         const UnpackedPtr<RenderPipelineDescriptor>& descriptor) override;
+    ResultOrError<Ref<SharedTextureMemoryBase>> ImportSharedTextureMemoryImpl(
+        const SharedTextureMemoryDescriptor* descriptor) override;
 
     GLenum GetBGRAInternalFormat(const OpenGLFunctions& gl) const;
     void DestroyImpl() override;
@@ -147,6 +155,10 @@
     GLFormatTable mFormatTable;
     std::unique_ptr<ContextEGL> mContext;
     int mMaxTextureMaxAnisotropy = 0;
+
+#if DAWN_PLATFORM_IS(ANDROID)
+    std::unique_ptr<AHBFunctions> mAHBFunctions;
+#endif  // DAWN_PLATFORM_IS(ANDROID)
 };
 
 }  // namespace dawn::native::opengl
diff --git a/src/dawn/native/opengl/EGLFunctions.cpp b/src/dawn/native/opengl/EGLFunctions.cpp
index 867d5ff..4c0d4bd 100644
--- a/src/dawn/native/opengl/EGLFunctions.cpp
+++ b/src/dawn/native/opengl/EGLFunctions.cpp
@@ -86,6 +86,9 @@
     {EGLExt::NoConfigContext, "EGL_KHR_no_config_context", NeverPromoted, ExtType::Display},
     {EGLExt::PixelFormatFloat, "EGL_EXT_pixel_format_float", NeverPromoted, ExtType::Display},
     {EGLExt::GLColorspace, "EGL_KHR_gl_colorspace", NeverPromoted, ExtType::Display},
+    {EGLExt::ImageNativeBuffer, "EGL_ANDROID_image_native_buffer", NeverPromoted, ExtType::Display},
+    {EGLExt::GetNativeClientBuffer, "EGL_ANDROID_get_native_client_buffer", NeverPromoted,
+     ExtType::Display},
     //
 }};
 
@@ -235,6 +238,10 @@
         GET_PROC_WITH_NAME(SignalSync, "eglSignalSyncKHR");
     }
 
+    if (HasExt(EGLExt::GetNativeClientBuffer)) {
+        GET_PROC_WITH_NAME(GetNativeClientBuffer, "eglGetNativeClientBufferANDROID");
+    }
+
     return {};
 }
 
diff --git a/src/dawn/native/opengl/EGLFunctions.h b/src/dawn/native/opengl/EGLFunctions.h
index a2b2aae..68ef1d1 100644
--- a/src/dawn/native/opengl/EGLFunctions.h
+++ b/src/dawn/native/opengl/EGLFunctions.h
@@ -61,6 +61,10 @@
     PixelFormatFloat,
     GLColorspace,
 
+    // EGL image creation extensions
+    ImageNativeBuffer,      // EGL_ANDROID_image_native_buffer
+    GetNativeClientBuffer,  // EGL_ANDROID_get_native_client_buffer
+
     EnumCount,
 };
 
@@ -135,6 +139,9 @@
     // EGL_KHR_reusable_sync
     PFNEGLSIGNALSYNCKHRPROC SignalSync;
 
+    // EGL_ANDROID_get_native_client_buffer
+    PFNEGLGETNATIVECLIENTBUFFERANDROIDPROC GetNativeClientBuffer;
+
   private:
     MaybeError LoadClientExtensions();
 
diff --git a/src/dawn/native/opengl/PhysicalDeviceGL.cpp b/src/dawn/native/opengl/PhysicalDeviceGL.cpp
index 1755178..ec61445 100644
--- a/src/dawn/native/opengl/PhysicalDeviceGL.cpp
+++ b/src/dawn/native/opengl/PhysicalDeviceGL.cpp
@@ -252,6 +252,11 @@
         EnableFeature(dawn::native::Feature::ANGLETextureSharing);
     }
 
+    if (mDisplay->egl.HasExt(EGLExt::ImageNativeBuffer) &&
+        mDisplay->egl.HasExt(EGLExt::GetNativeClientBuffer)) {
+        EnableFeature(dawn::native::Feature::SharedTextureMemoryAHardwareBuffer);
+    }
+
     // Non-zero baseInstance requires at least desktop OpenGL 4.2, and it is not supported in
     // OpenGL ES OpenGL:
     // https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawElementsIndirect.xhtml
diff --git a/src/dawn/native/opengl/SharedTextureMemoryEGL.cpp b/src/dawn/native/opengl/SharedTextureMemoryEGL.cpp
new file mode 100644
index 0000000..c45261c
--- /dev/null
+++ b/src/dawn/native/opengl/SharedTextureMemoryEGL.cpp
@@ -0,0 +1,111 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "dawn/native/opengl/SharedTextureMemoryEGL.h"
+
+#include <utility>
+
+#include "dawn/native/opengl/DeviceGL.h"
+#include "dawn/native/opengl/TextureGL.h"
+
+#if DAWN_PLATFORM_IS(ANDROID)
+#include <android/hardware_buffer.h>
+
+#include "dawn/native/AHBFunctions.h"
+#endif  // DAWN_PLATFORM_IS(ANDROID)
+
+namespace dawn::native::opengl {
+
+ResultOrError<Ref<SharedTextureMemory>> SharedTextureMemoryEGL::Create(
+    Device* device,
+    StringView label,
+    const SharedTextureMemoryAHardwareBufferDescriptor* descriptor) {
+#if DAWN_PLATFORM_IS(ANDROID)
+    const EGLFunctions& egl = device->GetEGL(false);
+    DAWN_ASSERT(egl.HasExt(EGLExt::ImageNativeBuffer) && egl.HasExt(EGLExt::GetNativeClientBuffer));
+
+    ::AHardwareBuffer* aHardwareBuffer = static_cast<struct AHardwareBuffer*>(descriptor->handle);
+    DAWN_INVALID_IF(aHardwareBuffer == nullptr, "AHardwareBuffer is missing.");
+
+    // Reflect the properties of the AHardwareBuffer.
+    SharedTextureMemoryProperties properties =
+        GetAHBSharedTextureMemoryProperties(device->GetOrLoadAHBFunctions(), aHardwareBuffer);
+    DAWN_INVALID_IF(properties.format == wgpu::TextureFormat::Undefined,
+                    "Unknown AHardwareBuffer format cannot be imported.");
+
+    // If the format of the AHB is unknown due to not having an equivalent wgpu::TextureFormat or
+    // being an unknowable Android video format, disable all usages except sampling.
+    if (properties.format == wgpu::TextureFormat::External) {
+        properties.usage &= wgpu::TextureUsage::TextureBinding;
+    }
+
+    const EGLAttrib attribs[] = {
+        EGL_NONE,
+    };
+    EGLClientBuffer clientBuffer = egl.GetNativeClientBuffer(aHardwareBuffer);
+    ::EGLImage image = egl.CreateImage(device->GetEGLDisplay(), EGL_NO_CONTEXT,
+                                       EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attribs);
+    DAWN_INVALID_IF(image == nullptr, "EGLImage creation failed, 0x%X", egl.GetError());
+
+    auto result = AcquireRef(new SharedTextureMemoryEGL(device, label, properties, image));
+    result->Initialize();
+    return result;
+#else
+    DAWN_UNREACHABLE();
+#endif  // DAWN_PLATFORM_IS(ANDROID)
+}
+
+SharedTextureMemoryEGL::SharedTextureMemoryEGL(Device* device,
+                                               StringView label,
+                                               const SharedTextureMemoryProperties& properties,
+                                               ::EGLImage image)
+    : SharedTextureMemory(device, label, properties), mEGLImage(image) {}
+
+void SharedTextureMemoryEGL::DestroyImpl() {
+    if (mEGLImage) {
+        Device* device = ToBackend(GetDevice());
+        const EGLFunctions& egl = device->GetEGL(false);
+
+        egl.DestroyImage(device->GetEGLDisplay(), mEGLImage);
+        mEGLImage = nullptr;
+    }
+}
+
+GLuint SharedTextureMemoryEGL::GenerateGLTexture() {
+    Device* device = ToBackend(GetDevice());
+    const OpenGLFunctions& gl = device->GetGL();
+
+    GLuint tex;
+    gl.GenTextures(1, &tex);
+    gl.BindTexture(GL_TEXTURE_2D, tex);
+    gl.EGLImageTargetTexture2DOES(GL_TEXTURE_2D, mEGLImage);
+    gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+
+    return tex;
+}
+
+}  // namespace dawn::native::opengl
diff --git a/src/dawn/native/opengl/SharedTextureMemoryEGL.h b/src/dawn/native/opengl/SharedTextureMemoryEGL.h
new file mode 100644
index 0000000..4fbffb2
--- /dev/null
+++ b/src/dawn/native/opengl/SharedTextureMemoryEGL.h
@@ -0,0 +1,61 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_DAWN_NATIVE_OPENGL_SHARED_TEXTURE_MEMORY_EGL_H_
+#define SRC_DAWN_NATIVE_OPENGL_SHARED_TEXTURE_MEMORY_EGL_H_
+
+#include "dawn/native/opengl/SharedTextureMemoryGL.h"
+#include "dawn/native/opengl/opengl_platform.h"
+
+namespace dawn::native::opengl {
+
+class Device;
+
+class SharedTextureMemoryEGL final : public SharedTextureMemory {
+  public:
+    static ResultOrError<Ref<SharedTextureMemory>> Create(
+        Device* device,
+        StringView label,
+        const SharedTextureMemoryAHardwareBufferDescriptor* descriptor);
+
+    GLuint GenerateGLTexture() override;
+
+  private:
+    SharedTextureMemoryEGL(Device* device,
+                           StringView label,
+                           const SharedTextureMemoryProperties& properties,
+                           ::EGLImage image);
+
+    void DestroyImpl() override;
+
+  private:
+    ::EGLImage mEGLImage = nullptr;
+};
+
+}  // namespace dawn::native::opengl
+
+#endif  // SRC_DAWN_NATIVE_OPENGL_SHARED_TEXTURE_MEMORY_EGL_H_
diff --git a/src/dawn/native/opengl/SharedTextureMemoryGL.cpp b/src/dawn/native/opengl/SharedTextureMemoryGL.cpp
new file mode 100644
index 0000000..0b8f21c
--- /dev/null
+++ b/src/dawn/native/opengl/SharedTextureMemoryGL.cpp
@@ -0,0 +1,60 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "dawn/native/opengl/SharedTextureMemoryGL.h"
+
+#include <utility>
+
+#include "dawn/native/opengl/DeviceGL.h"
+#include "dawn/native/opengl/TextureGL.h"
+
+namespace dawn::native::opengl {
+
+SharedTextureMemory::SharedTextureMemory(Device* device,
+                                         StringView label,
+                                         const SharedTextureMemoryProperties& properties)
+    : SharedTextureMemoryBase(device, label, properties) {}
+
+ResultOrError<Ref<TextureBase>> SharedTextureMemory::CreateTextureImpl(
+    const UnpackedPtr<TextureDescriptor>& descriptor) {
+    return Texture::CreateFromSharedTextureMemory(this, descriptor);
+}
+
+MaybeError SharedTextureMemory::BeginAccessImpl(
+    TextureBase* texture,
+    const UnpackedPtr<BeginAccessDescriptor>& descriptor) {
+    return {};
+}
+
+ResultOrError<FenceAndSignalValue> SharedTextureMemory::EndAccessImpl(
+    TextureBase* texture,
+    ExecutionSerial lastUsageSerial,
+    UnpackedPtr<EndAccessState>& state) {
+    return DAWN_VALIDATION_ERROR("No shared fence features supported.");
+}
+
+}  // namespace dawn::native::opengl
diff --git a/src/dawn/native/opengl/SharedTextureMemoryGL.h b/src/dawn/native/opengl/SharedTextureMemoryGL.h
new file mode 100644
index 0000000..7572bfc
--- /dev/null
+++ b/src/dawn/native/opengl/SharedTextureMemoryGL.h
@@ -0,0 +1,58 @@
+// Copyright 2024 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_DAWN_NATIVE_OPENGL_SHARED_TEXTURE_MEMORY_GL_H_
+#define SRC_DAWN_NATIVE_OPENGL_SHARED_TEXTURE_MEMORY_GL_H_
+
+#include "dawn/native/SharedTextureMemory.h"
+#include "dawn/native/opengl/opengl_platform.h"
+
+namespace dawn::native::opengl {
+
+class Device;
+
+class SharedTextureMemory : public SharedTextureMemoryBase {
+  public:
+    virtual GLuint GenerateGLTexture() = 0;
+
+  protected:
+    SharedTextureMemory(Device* device,
+                        StringView label,
+                        const SharedTextureMemoryProperties& properties);
+
+    ResultOrError<Ref<TextureBase>> CreateTextureImpl(
+        const UnpackedPtr<TextureDescriptor>& descriptor) override;
+    MaybeError BeginAccessImpl(TextureBase* texture,
+                               const UnpackedPtr<BeginAccessDescriptor>& descriptor) override;
+    ResultOrError<FenceAndSignalValue> EndAccessImpl(TextureBase* texture,
+                                                     ExecutionSerial lastUsageSerial,
+                                                     UnpackedPtr<EndAccessState>& state) override;
+};
+
+}  // namespace dawn::native::opengl
+
+#endif  // SRC_DAWN_NATIVE_OPENGL_SHARED_TEXTURE_MEMORY_GL_H_
diff --git a/src/dawn/native/opengl/TextureGL.cpp b/src/dawn/native/opengl/TextureGL.cpp
index 771adc6..1fd249d 100644
--- a/src/dawn/native/opengl/TextureGL.cpp
+++ b/src/dawn/native/opengl/TextureGL.cpp
@@ -38,6 +38,7 @@
 #include "dawn/native/opengl/BufferGL.h"
 #include "dawn/native/opengl/CommandBufferGL.h"
 #include "dawn/native/opengl/DeviceGL.h"
+#include "dawn/native/opengl/SharedTextureMemoryGL.h"
 #include "dawn/native/opengl/UtilsGL.h"
 
 namespace dawn::native::opengl {
@@ -192,12 +193,25 @@
     return std::move(texture);
 }
 
+// static
+ResultOrError<Ref<Texture>> Texture::CreateFromSharedTextureMemory(
+    SharedTextureMemory* memory,
+    const UnpackedPtr<TextureDescriptor>& descriptor) {
+    Device* device = ToBackend(memory->GetDevice());
+
+    GLuint textureId = memory->GenerateGLTexture();
+    DAWN_ASSERT(textureId != 0);
+
+    Ref<Texture> texture = AcquireRef(new Texture(device, descriptor, textureId, OwnsHandle::Yes));
+    texture->mSharedResourceMemoryContents = memory->GetContents();
+    return texture;
+}
+
 Texture::Texture(Device* device, const UnpackedPtr<TextureDescriptor>& descriptor)
-    : Texture(device, descriptor, 0) {
+    : Texture(device, descriptor, 0, OwnsHandle::Yes) {
     const OpenGLFunctions& gl = device->GetGL();
 
     gl.GenTextures(1, &mHandle);
-    mOwnsHandle = true;
     uint32_t levels = GetNumMipLevels();
 
     const GLFormat& glFormat = GetGLFormat();
@@ -211,8 +225,11 @@
     gl.TexParameteri(mTarget, GL_TEXTURE_MAX_LEVEL, levels - 1);
 }
 
-Texture::Texture(Device* device, const UnpackedPtr<TextureDescriptor>& descriptor, GLuint handle)
-    : TextureBase(device, descriptor), mHandle(handle) {
+Texture::Texture(Device* device,
+                 const UnpackedPtr<TextureDescriptor>& descriptor,
+                 GLuint handle,
+                 OwnsHandle ownsHandle)
+    : TextureBase(device, descriptor), mHandle(handle), mOwnsHandle(ownsHandle) {
     mTarget = TargetForTextureViewDimension(GetCompatibilityTextureBindingViewDimension(),
                                             descriptor->sampleCount);
 }
@@ -221,7 +238,7 @@
 
 void Texture::DestroyImpl() {
     TextureBase::DestroyImpl();
-    if (mOwnsHandle) {
+    if (mOwnsHandle == OwnsHandle::Yes) {
         const OpenGLFunctions& gl = ToBackend(GetDevice())->GetGL();
         gl.DeleteTextures(1, &mHandle);
         mHandle = 0;
diff --git a/src/dawn/native/opengl/TextureGL.h b/src/dawn/native/opengl/TextureGL.h
index f971dd4..d63a4f8 100644
--- a/src/dawn/native/opengl/TextureGL.h
+++ b/src/dawn/native/opengl/TextureGL.h
@@ -36,12 +36,25 @@
 
 class Device;
 struct GLFormat;
+class SharedTextureMemory;
+
+enum class OwnsHandle : uint8_t {
+    Yes,
+    No,
+};
 
 class Texture final : public TextureBase {
   public:
     static ResultOrError<Ref<Texture>> Create(Device* device,
                                               const UnpackedPtr<TextureDescriptor>& descriptor);
-    Texture(Device* device, const UnpackedPtr<TextureDescriptor>& descriptor, GLuint handle);
+    static ResultOrError<Ref<Texture>> CreateFromSharedTextureMemory(
+        SharedTextureMemory* memory,
+        const UnpackedPtr<TextureDescriptor>& descriptor);
+
+    Texture(Device* device,
+            const UnpackedPtr<TextureDescriptor>& descriptor,
+            GLuint handle,
+            OwnsHandle ownsHandle);
 
     GLuint GetHandle() const;
     GLenum GetGLTarget() const;
@@ -57,7 +70,7 @@
     MaybeError ClearTexture(const SubresourceRange& range, TextureBase::ClearValue clearValue);
 
     GLuint mHandle;
-    bool mOwnsHandle = false;
+    OwnsHandle mOwnsHandle = OwnsHandle::No;
     GLenum mTarget;
 };
 
diff --git a/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp b/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp
index 1122d45..6967083 100644
--- a/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp
+++ b/src/dawn/native/vulkan/SharedTextureMemoryVk.cpp
@@ -493,27 +493,12 @@
         VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID;
 
     // Reflect the properties of the AHardwareBuffer.
-    AHardwareBuffer_Desc aHardwareBufferDesc{};
-    ahbFunctions->Describe(aHardwareBuffer, &aHardwareBufferDesc);
+    SharedTextureMemoryProperties properties =
+        GetAHBSharedTextureMemoryProperties(ahbFunctions, aHardwareBuffer);
 
-    SharedTextureMemoryProperties properties;
-    properties.size = {aHardwareBufferDesc.width, aHardwareBufferDesc.height,
-                       aHardwareBufferDesc.layers};
     if (useExternalFormat) {
-        if (aHardwareBufferDesc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
-            properties.usage = wgpu::TextureUsage::TextureBinding;
-        }
-    } else {
-        properties.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
-        if (aHardwareBufferDesc.usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) {
-            properties.usage |= wgpu::TextureUsage::RenderAttachment;
-        }
-        if (aHardwareBufferDesc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
-            properties.usage |= wgpu::TextureUsage::TextureBinding;
-        }
-        if (aHardwareBufferDesc.usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER) {
-            properties.usage |= wgpu::TextureUsage::StorageBinding;
-        }
+        // When using the external YUV texture format, only TextureBinding usage is valid.
+        properties.usage &= wgpu::TextureUsage::TextureBinding;
     }
 
     VkFormat vkFormat;
diff --git a/src/dawn/tests/white_box/SharedTextureMemoryTests.cpp b/src/dawn/tests/white_box/SharedTextureMemoryTests.cpp
index 2e16605..d5666be 100644
--- a/src/dawn/tests/white_box/SharedTextureMemoryTests.cpp
+++ b/src/dawn/tests/white_box/SharedTextureMemoryTests.cpp
@@ -829,6 +829,9 @@
 // Test that it is an error to import a shared fence with no chained struct.
 // Also test that ExportInfo reports an Undefined type for the error fence.
 TEST_P(SharedTextureMemoryTests, ImportSharedFenceNoChain) {
+    // TODO(dawn/42241435): No shared texture memory extensions are supported yet.
+    DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
+
     wgpu::SharedFenceDescriptor desc;
     ASSERT_DEVICE_ERROR_MSG(wgpu::SharedFence fence = device.ImportSharedFence(&desc),
                             HasSubstr("chain"));
@@ -2242,6 +2245,9 @@
     // Not supported if using the same device. Not possible to lose one without losing the other.
     DAWN_TEST_UNSUPPORTED_IF(GetParam().mBackend->UseSameDevice());
 
+    // TODO(dawn/42241435): No shared texture memory extensions are supported yet.
+    DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
+
     // crbug.com/358166479
     DAWN_SUPPRESS_TEST_IF(IsLinux() && IsNvidia() && IsVulkan());
 
@@ -2309,6 +2315,9 @@
 TEST_P(SharedTextureMemoryTests, SeparateDevicesWriteThenConcurrentReadThenWrite) {
     DAWN_TEST_UNSUPPORTED_IF(!GetParam().mBackend->SupportsConcurrentRead());
 
+    // TODO(dawn/42241435): No shared texture memory extensions are supported yet.
+    DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
+
     // crbug.com/358166479
     DAWN_SUPPRESS_TEST_IF(IsLinux() && IsNvidia() && IsVulkan());
 
@@ -2592,6 +2601,9 @@
 
 // Test that textures created from SharedTextureMemory may perform sRGB reinterpretation.
 TEST_P(SharedTextureMemoryTests, SRGBReinterpretation) {
+    // Format reinterpretation is not available in compatibility mode.
+    DAWN_SUPPRESS_TEST_IF(IsCompatibilityMode());
+
     // crbug.com/358166479
     DAWN_SUPPRESS_TEST_IF(IsLinux() && IsNvidia() && IsVulkan());
 
diff --git a/src/dawn/tests/white_box/SharedTextureMemoryTests_android.cpp b/src/dawn/tests/white_box/SharedTextureMemoryTests_android.cpp
index cb1706c..7145f67 100644
--- a/src/dawn/tests/white_box/SharedTextureMemoryTests_android.cpp
+++ b/src/dawn/tests/white_box/SharedTextureMemoryTests_android.cpp
@@ -42,21 +42,11 @@
 namespace dawn {
 namespace {
 
-class Backend : public SharedTextureMemoryTestVulkanBackend {
+template <typename BackendBase>
+class SharedTextureMemoryTestAndroidBackend : public BackendBase {
   public:
-    static SharedTextureMemoryTestBackend* GetInstance() {
-        static Backend b;
-        return &b;
-    }
-
     std::string Name() const override { return "AHardwareBuffer"; }
 
-    std::vector<wgpu::FeatureName> RequiredFeatures(const wgpu::Adapter&) const override {
-        return {wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer,
-                wgpu::FeatureName::SharedFenceVkSemaphoreSyncFD,
-                wgpu::FeatureName::YCbCrVulkanSamplers};
-    }
-
     static std::string MakeLabel(const AHardwareBuffer_Desc& desc) {
         std::string label = std::to_string(desc.width) + "x" + std::to_string(desc.height);
         switch (desc.format) {
@@ -138,9 +128,11 @@
         int layerCount) override {
         std::vector<std::vector<wgpu::SharedTextureMemory>> memories;
 
+        // AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM behaves inconsistantly between GLES (alpha is
+        // always 1.0) and Vulkan (alpha is writeable) so it is omitted. There is no TextureFormat
+        // to represent the difference so it's undetectable when testing.
         for (auto format : {
                  AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM,
-                 AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM,
                  AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT,
                  AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM,
                  AHARDWAREBUFFER_FORMAT_R8_UNORM,
@@ -179,8 +171,38 @@
     }
 };
 
+class SharedTextureMemoryTestAndroidVulkanBackend
+    : public SharedTextureMemoryTestAndroidBackend<SharedTextureMemoryTestVulkanBackend> {
+  public:
+    static SharedTextureMemoryTestBackend* GetInstance() {
+        static SharedTextureMemoryTestAndroidVulkanBackend b;
+        return &b;
+    }
+
+    std::vector<wgpu::FeatureName> RequiredFeatures(const wgpu::Adapter&) const override {
+        return {wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer,
+                wgpu::FeatureName::SharedFenceVkSemaphoreSyncFD,
+                wgpu::FeatureName::YCbCrVulkanSamplers};
+    }
+};
+
+class SharedTextureMemoryTestAndroidOpenGLESBackend
+    : public SharedTextureMemoryTestAndroidBackend<SharedTextureMemoryTestBackend> {
+  public:
+    static SharedTextureMemoryTestBackend* GetInstance() {
+        static SharedTextureMemoryTestAndroidOpenGLESBackend b;
+        return &b;
+    }
+
+    std::vector<wgpu::FeatureName> RequiredFeatures(const wgpu::Adapter&) const override {
+        return {wgpu::FeatureName::SharedTextureMemoryAHardwareBuffer};
+    }
+};
+
 // Test clearing the texture memory on the device, then reading it on the CPU.
 TEST_P(SharedTextureMemoryTests, GPUWriteThenCPURead) {
+    DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::SharedFenceVkSemaphoreSyncFD}));
+
     AHardwareBuffer_Desc aHardwareBufferDesc = {
         .width = 4,
         .height = 4,
@@ -322,6 +344,8 @@
 // Test validation of an incorrectly-configured SharedTextureMemoryAHardwareBufferProperties
 // instance.
 TEST_P(SharedTextureMemoryTests, InvalidSharedTextureMemoryAHardwareBufferProperties) {
+    DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::YCbCrVulkanSamplers}));
+
     AHardwareBuffer_Desc aHardwareBufferDesc = {
         .width = 4,
         .height = 4,
@@ -353,6 +377,8 @@
 
 // Test querying YCbCr info from the Device.
 TEST_P(SharedTextureMemoryTests, QueryYCbCrInfoFromDevice) {
+    DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::YCbCrVulkanSamplers}));
+
     AHardwareBuffer_Desc aHardwareBufferDesc = {
         .width = 4,
         .height = 4,
@@ -414,6 +440,8 @@
 
 // Test querying YCbCr info from the SharedTextureMemory without external format.
 TEST_P(SharedTextureMemoryTests, QueryYCbCrInfoWithoutExternalFormat) {
+    DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::YCbCrVulkanSamplers}));
+
     AHardwareBuffer_Desc aHardwareBufferDesc = {
         .width = 4,
         .height = 4,
@@ -488,6 +516,8 @@
 
 // Test querying YCbCr info from the SharedTextureMemory with external format.
 TEST_P(SharedTextureMemoryTests, QueryYCbCrInfoWithExternalFormat) {
+    DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::YCbCrVulkanSamplers}));
+
     AHardwareBuffer_Desc aHardwareBufferDesc = {
         .width = 4,
         .height = 4,
@@ -563,6 +593,8 @@
 
 // Test BeginAccess on an uninitialized texture with external format fails.
 TEST_P(SharedTextureMemoryTests, GPUReadForUninitializedTextureWithExternalFormatFails) {
+    DAWN_TEST_UNSUPPORTED_IF(!SupportsFeatures({wgpu::FeatureName::YCbCrVulkanSamplers}));
+
     const AHardwareBuffer_Desc aHardwareBufferDesc = {
         .width = 4,
         .height = 4,
@@ -605,13 +637,25 @@
 DAWN_INSTANTIATE_PREFIXED_TEST_P(Vulkan,
                                  SharedTextureMemoryNoFeatureTests,
                                  {VulkanBackend()},
-                                 {Backend::GetInstance()},
+                                 {SharedTextureMemoryTestAndroidVulkanBackend::GetInstance()},
                                  {1});
 
 DAWN_INSTANTIATE_PREFIXED_TEST_P(Vulkan,
                                  SharedTextureMemoryTests,
                                  {VulkanBackend()},
-                                 {Backend::GetInstance()},
+                                 {SharedTextureMemoryTestAndroidVulkanBackend::GetInstance()},
+                                 {1});
+
+DAWN_INSTANTIATE_PREFIXED_TEST_P(OpenGLES,
+                                 SharedTextureMemoryNoFeatureTests,
+                                 {OpenGLESBackend()},
+                                 {SharedTextureMemoryTestAndroidOpenGLESBackend::GetInstance()},
+                                 {1});
+
+DAWN_INSTANTIATE_PREFIXED_TEST_P(OpenGLES,
+                                 SharedTextureMemoryTests,
+                                 {OpenGLESBackend()},
+                                 {SharedTextureMemoryTestAndroidOpenGLESBackend::GetInstance()},
                                  {1});
 
 }  // anonymous namespace