Add support for OES_depth_texture

Note: Includes workaround for http://anglebug.com/3452 - some Android
devices do not indicate filtering support on VK_FORMAT_D16_UNORM.

Bug: angleproject:3103
Test: angle_end2end_tests --gtest_filter=DepthStencilFormatsTest.DepthTexture/*
angle_end2end_tests --gtest_filter=DepthStencilFormatsTest.PackedDepthStencil/*
angle_end2end_tests --gtest_filter=DepthStencilFormatsTest.DepthTextureRender/ES2_VULKAN

Change-Id: Ic325fb94ab0e619a17c2e149e0e0865fa4142f3a
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1575426
Commit-Queue: Courtney Goeltzenleuchter <courtneygo@google.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp
index e7b0b96..7379705 100644
--- a/src/libANGLE/Caps.cpp
+++ b/src/libANGLE/Caps.cpp
@@ -175,6 +175,7 @@
       compressedTextureETC(false),
       sRGB(false),
       depthTextureANGLE(false),
+      depthTextureOES(false),
       depth32(false),
       textureStorage(false),
       textureNPOT(false),
@@ -665,6 +666,17 @@
     return GetFormatSupport(textureCaps, requiredFormats, true, true, true, true);
 }
 
+// Check for GL_OES_depth_texture
+static bool DetermineDepthTextureOESSupport(const TextureCapsMap &textureCaps)
+{
+    constexpr GLenum requiredFormats[] = {
+        GL_DEPTH_COMPONENT16,
+        GL_DEPTH_COMPONENT32_OES,
+    };
+
+    return GetFormatSupport(textureCaps, requiredFormats, true, true, true, true);
+}
+
 // Check for GL_OES_depth32
 static bool DetermineDepth32Support(const TextureCapsMap &textureCaps)
 {
@@ -785,6 +797,7 @@
     compressedEACRG11SignedTexture   = DetermineEACRG11SignedTextureSupport(textureCaps);
     sRGB                             = DetermineSRGBTextureSupport(textureCaps);
     depthTextureANGLE                = DetermineDepthTextureANGLESupport(textureCaps);
+    depthTextureOES                  = DetermineDepthTextureOESSupport(textureCaps);
     depth32                          = DetermineDepth32Support(textureCaps);
     colorBufferFloatRGB              = DetermineColorBufferFloatRGBSupport(textureCaps);
     colorBufferFloatRGBA             = DetermineColorBufferFloatRGBASupport(textureCaps);
@@ -847,6 +860,7 @@
         map["GL_CHROMIUM_compressed_texture_etc"] = enableableExtension(&Extensions::compressedTextureETC);
         map["GL_EXT_sRGB"] = enableableExtension(&Extensions::sRGB);
         map["GL_ANGLE_depth_texture"] = esOnlyExtension(&Extensions::depthTextureANGLE);
+        map["GL_OES_depth_texture"] = esOnlyExtension(&Extensions::depthTextureOES);
         map["GL_OES_depth32"] = esOnlyExtension(&Extensions::depth32);
         map["GL_EXT_texture_storage"] = enableableExtension(&Extensions::textureStorage);
         map["GL_OES_texture_npot"] = enableableExtension(&Extensions::textureNPOT);
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index 08311de..db45c40 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -117,6 +117,9 @@
     // GL_EXT_texture_compression_bptc
     void setTextureExtensionSupport(const TextureCapsMap &textureCaps);
 
+    // indicate if any depth texture extension is available
+    bool depthTextureAny() const { return (depthTextureANGLE || depthTextureOES); }
+
     // ES2 Extension support
 
     // GL_OES_element_index_uint
@@ -240,6 +243,9 @@
     // GL_ANGLE_depth_texture
     bool depthTextureANGLE;
 
+    // OES_depth_texture
+    bool depthTextureOES;
+
     // GL_OES_depth32
     // Allows DEPTH_COMPONENT32_OES as a valid Renderbuffer format.
     bool depth32;
diff --git a/src/libANGLE/formatutils.cpp b/src/libANGLE/formatutils.cpp
index 1401322..9ab99d9 100644
--- a/src/libANGLE/formatutils.cpp
+++ b/src/libANGLE/formatutils.cpp
@@ -136,6 +136,13 @@
     return extensions.*bool1 || extensions.*bool2;
 }
 
+// Check support for any of three extensions
+template <ExtensionBool bool1, ExtensionBool bool2, ExtensionBool bool3>
+static bool RequireExtOrExtOrExt(const Version &, const Extensions &extensions)
+{
+    return extensions.*bool1 || extensions.*bool2 || extensions.*bool3;
+}
+
 // R8, RG8
 static bool SizedRGSupport(const Version &clientVersion, const Extensions &extensions)
 {
@@ -785,12 +792,12 @@
     AddRGBAFormat(&map, GL_RGB32F,        true, 32, 32, 32,  0, 0, GL_RGB,  GL_FLOAT,          GL_FLOAT,        false, SizedFloatRGBSupport,       RequireExt<&Extensions::textureFloatLinear>, RequireExt<&Extensions::colorBufferFloatRGB>, NeverSupported                           );
     AddRGBAFormat(&map, GL_RGBA32F,       true, 32, 32, 32, 32, 0, GL_RGBA, GL_FLOAT,          GL_FLOAT,        false, SizedFloatRGBASupport,      RequireExt<&Extensions::textureFloatLinear>, SizedFloatRGBARenderableSupport,              SizedFloatRGBARenderableSupport          );
 
-    // Depth stencil formats
+    // ANGLE Depth stencil formats
     //                         | Internal format         |sized| D |S | X | Format            | Type                             | Component type        | Texture supported                                                | Filterable                                      | Texture attachment                                                                   | Renderbuffer                                                                         |
-    AddDepthStencilFormat(&map, GL_DEPTH_COMPONENT16,     true, 16, 0,  0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT,                 GL_UNSIGNED_NORMALIZED, RequireES<1, 0>,                                                   RequireESOrExt<3, 0, &Extensions::depthTextureANGLE>, RequireES<1, 0>,                                                                   RequireES<1, 0>                                                                       );
+    AddDepthStencilFormat(&map, GL_DEPTH_COMPONENT16,     true, 16, 0,  0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT,                 GL_UNSIGNED_NORMALIZED, RequireES<1, 0>,                                                   RequireESOrExtOrExt<3, 0, &Extensions::depthTextureANGLE, &Extensions::depthTextureOES>, RequireES<1, 0>,                                RequireES<1, 0>                                                                       );
     AddDepthStencilFormat(&map, GL_DEPTH_COMPONENT24,     true, 24, 0,  0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT,                   GL_UNSIGNED_NORMALIZED, RequireES<3, 0>,                                                   RequireESOrExt<3, 0, &Extensions::depthTextureANGLE>, RequireES<3, 0>,                                                                   RequireES<3, 0>                                                                       );
     AddDepthStencilFormat(&map, GL_DEPTH_COMPONENT32F,    true, 32, 0,  0, GL_DEPTH_COMPONENT, GL_FLOAT,                          GL_FLOAT,               RequireES<3, 0>,                                                   RequireESOrExt<3, 0, &Extensions::depthTextureANGLE>, RequireES<3, 0>,                                                                   RequireES<3, 0>                                                                       );
-    AddDepthStencilFormat(&map, GL_DEPTH_COMPONENT32_OES, true, 32, 0,  0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT,                   GL_UNSIGNED_NORMALIZED, RequireExtOrExt<&Extensions::depthTextureANGLE, &Extensions::depth32>, AlwaysSupported,                              RequireExtOrExt<&Extensions::depthTextureANGLE, &Extensions::depth32>,                 RequireExtOrExt<&Extensions::depthTextureANGLE, &Extensions::depth32>                 );
+    AddDepthStencilFormat(&map, GL_DEPTH_COMPONENT32_OES, true, 32, 0,  0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT,                   GL_UNSIGNED_NORMALIZED, RequireExtOrExtOrExt<&Extensions::depthTextureANGLE, &Extensions::depthTextureOES, &Extensions::depth32>, AlwaysSupported, RequireExtOrExtOrExt<&Extensions::depthTextureANGLE, &Extensions::depthTextureOES, &Extensions::depth32>, RequireExtOrExtOrExt<&Extensions::depthTextureANGLE, &Extensions::depthTextureOES, &Extensions::depth32>);
     AddDepthStencilFormat(&map, GL_DEPTH24_STENCIL8,      true, 24, 8,  0, GL_DEPTH_STENCIL,   GL_UNSIGNED_INT_24_8,              GL_UNSIGNED_NORMALIZED, RequireESOrExt<3, 0, &Extensions::depthTextureANGLE>,              AlwaysSupported,                                  RequireESOrExtOrExt<3, 0, &Extensions::depthTextureANGLE, &Extensions::packedDepthStencil>, RequireESOrExtOrExt<3, 0, &Extensions::depthTextureANGLE, &Extensions::packedDepthStencil>);
     AddDepthStencilFormat(&map, GL_DEPTH32F_STENCIL8,     true, 32, 8, 24, GL_DEPTH_STENCIL,   GL_FLOAT_32_UNSIGNED_INT_24_8_REV, GL_FLOAT,               RequireES<3, 0>,                                                   AlwaysSupported,                                  RequireES<3, 0>,                                                                       RequireES<3, 0>                                                                       );
     // STENCIL_INDEX8 is special-cased, see around the bottom of the list.
diff --git a/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp b/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
index f973864..01a74e5 100644
--- a/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/renderer11_utils.cpp
@@ -1653,6 +1653,10 @@
     extensions->blendFuncExtended        = true;
     extensions->maxDualSourceDrawBuffers = 1;
 
+    // D3D11 cannot support reading depth texture as a luminance texture.
+    // It treats it as a red-channel-only texture.
+    extensions->depthTextureOES = false;
+
     // D3D11 Feature Level 10_0+ uses SV_IsFrontFace in HLSL to emulate gl_FrontFacing.
     // D3D11 Feature Level 9_3 doesn't support SV_IsFrontFace, and has no equivalent, so can't
     // support gl_FrontFacing.
diff --git a/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp b/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp
index e039289..622ee42 100644
--- a/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp
+++ b/src/libANGLE/renderer/d3d/d3d9/renderer9_utils.cpp
@@ -658,6 +658,10 @@
     extensions->mapBuffer         = false;
     extensions->mapBufferRange    = false;
 
+    // D3D does not allow depth textures to have more than one mipmap level OES_depth_texture
+    // allows for that so we can't implement full support with the D3D9 back end.
+    extensions->depthTextureOES = false;
+
     // textureRG is emulated and not performant.
     extensions->textureRG = false;
 
@@ -674,6 +678,7 @@
         if (IsAMD(adapterId.VendorId))
         {
             extensions->depthTextureANGLE = false;
+            extensions->depthTextureOES   = false;
         }
     }
     else
@@ -710,14 +715,14 @@
     extensions->robustBufferAccessBehavior = false;
     extensions->blendMinMax                = true;
     // https://docs.microsoft.com/en-us/windows/desktop/direct3ddxgi/format-support-for-direct3d-feature-level-9-1-hardware
-    extensions->floatBlend                 = false;
-    extensions->framebufferBlit            = true;
-    extensions->framebufferMultisample     = true;
-    extensions->instancedArraysANGLE       = deviceCaps.PixelShaderVersion >= D3DPS_VERSION(3, 0);
+    extensions->floatBlend             = false;
+    extensions->framebufferBlit        = true;
+    extensions->framebufferMultisample = true;
+    extensions->instancedArraysANGLE   = deviceCaps.PixelShaderVersion >= D3DPS_VERSION(3, 0);
     // D3D9 requires at least one attribute that has a divisor of 0, which isn't required by the EXT
     // extension
-    extensions->instancedArraysEXT         = false;
-    extensions->packReverseRowOrder        = true;
+    extensions->instancedArraysEXT  = false;
+    extensions->packReverseRowOrder = true;
     extensions->standardDerivatives =
         (deviceCaps.PS20Caps.Caps & D3DPS20CAPS_GRADIENTINSTRUCTIONS) != 0;
     extensions->shaderTextureLOD       = true;
diff --git a/src/libANGLE/renderer/vulkan/ProgramVk.cpp b/src/libANGLE/renderer/vulkan/ProgramVk.cpp
index 0cdb05f..feb6d15 100644
--- a/src/libANGLE/renderer/vulkan/ProgramVk.cpp
+++ b/src/libANGLE/renderer/vulkan/ProgramVk.cpp
@@ -221,9 +221,7 @@
 
 ProgramVk::DefaultUniformBlock::~DefaultUniformBlock() = default;
 
-ProgramVk::ProgramVk(const gl::ProgramState &state) : ProgramImpl(state), mUniformBlocksOffsets{}
-{
-}
+ProgramVk::ProgramVk(const gl::ProgramState &state) : ProgramImpl(state), mUniformBlocksOffsets{} {}
 
 ProgramVk::~ProgramVk() = default;
 
@@ -998,8 +996,10 @@
                 vk::CommandBuffer *srcLayoutChange;
                 ANGLE_TRY(image.recordCommands(contextVk, &srcLayoutChange));
 
-                image.changeLayout(VK_IMAGE_ASPECT_COLOR_BIT,
-                                   vk::ImageLayout::FragmentShaderReadOnly, srcLayoutChange);
+                VkImageAspectFlags aspectFlags = image.getAspectFlags();
+                ASSERT(aspectFlags != 0);
+                image.changeLayout(aspectFlags, vk::ImageLayout::FragmentShaderReadOnly,
+                                   srcLayoutChange);
             }
 
             image.addReadDependency(framebuffer);
@@ -1092,8 +1092,8 @@
             descSet = mEmptyDescriptorSets[descriptorSetIndex];
         }
 
-        constexpr uint32_t kShaderTypeMin = static_cast<uint32_t>(gl::kGLES2ShaderTypeMin);
-        constexpr uint32_t kShaderTypeMax = static_cast<uint32_t>(gl::kGLES2ShaderTypeMax);
+        constexpr uint32_t kShaderTypeMin   = static_cast<uint32_t>(gl::kGLES2ShaderTypeMin);
+        constexpr uint32_t kShaderTypeMax   = static_cast<uint32_t>(gl::kGLES2ShaderTypeMax);
         constexpr uint32_t kShaderTypeCount = kShaderTypeMax - kShaderTypeMin + 1;
 
         // Default uniforms are encompassed in a block per shader stage, and they are assigned
diff --git a/src/libANGLE/renderer/vulkan/RendererVk.cpp b/src/libANGLE/renderer/vulkan/RendererVk.cpp
index f46ff95..d8388a3 100644
--- a/src/libANGLE/renderer/vulkan/RendererVk.cpp
+++ b/src/libANGLE/renderer/vulkan/RendererVk.cpp
@@ -1320,6 +1320,11 @@
     {
         mFeatures.forceNonZeroScissor.enabled = true;
     }
+
+    if (IsAndroid() && IsQualcomm(mPhysicalDeviceProperties.vendorID))
+    {
+        mFeatures.forceD16TexFilter.enabled = true;
+    }
 }
 
 void RendererVk::initPipelineCacheVkKey()
@@ -2284,6 +2289,12 @@
 
         // Otherwise query the format features and cache it.
         vkGetPhysicalDeviceFormatProperties(mPhysicalDevice, format, &deviceProperties);
+        // Workaround for some Android devices that don't indicate filtering
+        // support on D16_UNORM and they should.
+        if (mFeatures.forceD16TexFilter.enabled && format == VK_FORMAT_D16_UNORM)
+        {
+            deviceProperties.*features |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT;
+        }
     }
 
     return deviceProperties.*features & featureBits;
diff --git a/src/libANGLE/renderer/vulkan/TextureVk.cpp b/src/libANGLE/renderer/vulkan/TextureVk.cpp
index d329907..27b4598 100644
--- a/src/libANGLE/renderer/vulkan/TextureVk.cpp
+++ b/src/libANGLE/renderer/vulkan/TextureVk.cpp
@@ -1340,7 +1340,7 @@
     // Lazily allocate the image view itself.
     // Note that these views are specifically made to be used as color attachments, and therefore
     // don't have swizzle.
-    return mImage->initLayerImageView(context, mState.getType(), VK_IMAGE_ASPECT_COLOR_BIT,
+    return mImage->initLayerImageView(context, mState.getType(), mImage->getAspectFlags(),
                                       gl::SwizzleState(), *imageViewOut, getNativeImageLevel(level),
                                       1, getNativeImageLayer(layer), 1);
 }
@@ -1357,14 +1357,18 @@
                                    const uint32_t levelCount,
                                    vk::CommandBuffer *commandBuffer)
 {
-    const RendererVk *renderer         = contextVk->getRenderer();
-    const angle::Format &textureFormat = format.imageFormat();
+    RendererVk *renderer = contextVk->getRenderer();
 
     VkImageUsageFlags imageUsageFlags = VK_IMAGE_USAGE_TRANSFER_DST_BIT |
                                         VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
                                         VK_IMAGE_USAGE_SAMPLED_BIT;
-
-    if (!textureFormat.isBlock)
+    if (renderer->hasImageFormatFeatureBits(format.vkImageFormat,
+                                            VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT))
+    {
+        imageUsageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
+    }
+    else if (renderer->hasImageFormatFeatureBits(format.vkImageFormat,
+                                                 VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
     {
         imageUsageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
     }
@@ -1410,16 +1414,22 @@
     uint32_t baseLevel  = getNativeImageLevel(0);
     uint32_t baseLayer  = getNativeImageLayer(0);
     uint32_t layerCount = mState.getType() == gl::TextureType::CubeMap ? gl::kCubeFaceCount : 1;
+    VkImageAspectFlags aspectFlags = vk::GetFormatAspectFlags(format.angleFormat());
+    // If we are reading a depth buffer, select only the depth component/aspect
+    if (aspectFlags & VK_IMAGE_ASPECT_DEPTH_BIT)
+    {
+        aspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT;
+    }
 
-    ANGLE_TRY(mImage->initLayerImageView(contextVk, mState.getType(), VK_IMAGE_ASPECT_COLOR_BIT,
-                                         mappedSwizzle, &mReadMipmapImageView, baseLevel,
-                                         levelCount, baseLayer, layerCount));
-    ANGLE_TRY(mImage->initLayerImageView(contextVk, mState.getType(), VK_IMAGE_ASPECT_COLOR_BIT,
-                                         mappedSwizzle, &mReadBaseLevelImageView, baseLevel, 1,
-                                         baseLayer, layerCount));
+    ANGLE_TRY(mImage->initLayerImageView(contextVk, mState.getType(), aspectFlags, mappedSwizzle,
+                                         &mReadMipmapImageView, baseLevel, levelCount, baseLayer,
+                                         layerCount));
+    ANGLE_TRY(mImage->initLayerImageView(contextVk, mState.getType(), aspectFlags, mappedSwizzle,
+                                         &mReadBaseLevelImageView, baseLevel, 1, baseLayer,
+                                         layerCount));
     if (!format.imageFormat().isBlock)
     {
-        ANGLE_TRY(mImage->initLayerImageView(contextVk, mState.getType(), VK_IMAGE_ASPECT_COLOR_BIT,
+        ANGLE_TRY(mImage->initLayerImageView(contextVk, mState.getType(), aspectFlags,
                                              gl::SwizzleState(), &mDrawBaseLevelImageView,
                                              baseLevel, 1, baseLayer, layerCount));
     }
diff --git a/src/libANGLE/renderer/vulkan/vk_format_map.json b/src/libANGLE/renderer/vulkan/vk_format_map.json
index f6d0b26..f643025 100644
--- a/src/libANGLE/renderer/vulkan/vk_format_map.json
+++ b/src/libANGLE/renderer/vulkan/vk_format_map.json
@@ -224,6 +224,10 @@
             "buffer": "NONE",
             "image": "D24_UNORM_S8_UINT"
         },
+        "D32_UNORM": {
+            "buffer": "NONE",
+            "image": "D24_UNORM_S8_UINT"
+        },
         "ETC1_R8G8B8_UNORM_BLOCK": {
             "buffer": "NONE",
             "image": "ETC2_R8G8B8_UNORM_BLOCK"
diff --git a/src/libANGLE/renderer/vulkan/vk_format_table_autogen.cpp b/src/libANGLE/renderer/vulkan/vk_format_table_autogen.cpp
index a49c852..4924520 100644
--- a/src/libANGLE/renderer/vulkan/vk_format_table_autogen.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_format_table_autogen.cpp
@@ -695,7 +695,19 @@
             break;
 
         case angle::FormatID::D32_UNORM:
-            // This format is not implemented in Vulkan.
+            internalFormat = GL_DEPTH_COMPONENT32_OES;
+            {
+                static constexpr ImageFormatInitInfo kInfo[] = {
+                    {angle::FormatID::D24_UNORM_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT, nullptr},
+                    {angle::FormatID::D32_FLOAT_S8X24_UINT, VK_FORMAT_D32_SFLOAT_S8_UINT, nullptr},
+                    {angle::FormatID::D24_UNORM_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT, nullptr}};
+                initImageFallback(renderer, kInfo, ArraySize(kInfo));
+            }
+            bufferFormatID               = angle::FormatID::NONE;
+            vkBufferFormat               = VK_FORMAT_UNDEFINED;
+            vkBufferFormatIsPacked       = false;
+            vertexLoadFunction           = nullptr;
+            vertexLoadRequiresConversion = true;
             break;
 
         case angle::FormatID::EAC_R11G11_SNORM_BLOCK:
diff --git a/src/libANGLE/renderer/vulkan/vk_format_utils.cpp b/src/libANGLE/renderer/vulkan/vk_format_utils.cpp
index 378f1db..b2bb0bf 100644
--- a/src/libANGLE/renderer/vulkan/vk_format_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_format_utils.cpp
@@ -321,15 +321,28 @@
             swizzleStateOut->swizzleAlpha = swizzleState.swizzleRed;
             break;
         default:
-            // Set any missing channel to default in case the emulated format has that channel.
-            swizzleStateOut->swizzleRed =
-                angleFormat.redBits > 0 ? swizzleState.swizzleRed : GL_ZERO;
-            swizzleStateOut->swizzleGreen =
-                angleFormat.greenBits > 0 ? swizzleState.swizzleGreen : GL_ZERO;
-            swizzleStateOut->swizzleBlue =
-                angleFormat.blueBits > 0 ? swizzleState.swizzleBlue : GL_ZERO;
-            swizzleStateOut->swizzleAlpha =
-                angleFormat.alphaBits > 0 ? swizzleState.swizzleAlpha : GL_ONE;
+            if (angleFormat.hasDepthOrStencilBits())
+            {
+                swizzleStateOut->swizzleRed =
+                    angleFormat.depthBits > 0 ? swizzleState.swizzleRed : GL_ZERO;
+                swizzleStateOut->swizzleGreen =
+                    angleFormat.depthBits > 0 ? swizzleState.swizzleRed : GL_ZERO;
+                swizzleStateOut->swizzleBlue =
+                    angleFormat.depthBits > 0 ? swizzleState.swizzleRed : GL_ZERO;
+                swizzleStateOut->swizzleAlpha = GL_ONE;
+            }
+            else
+            {
+                // Set any missing channel to default in case the emulated format has that channel.
+                swizzleStateOut->swizzleRed =
+                    angleFormat.redBits > 0 ? swizzleState.swizzleRed : GL_ZERO;
+                swizzleStateOut->swizzleGreen =
+                    angleFormat.greenBits > 0 ? swizzleState.swizzleGreen : GL_ZERO;
+                swizzleStateOut->swizzleBlue =
+                    angleFormat.blueBits > 0 ? swizzleState.swizzleBlue : GL_ZERO;
+                swizzleStateOut->swizzleAlpha =
+                    angleFormat.alphaBits > 0 ? swizzleState.swizzleAlpha : GL_ONE;
+            }
             break;
     }
 }
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.cpp b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
index bc6ad8a..7e094a9 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.cpp
@@ -1683,6 +1683,10 @@
 void ImageHelper::clearDepthStencil(VkImageAspectFlags imageAspectFlags,
                                     VkImageAspectFlags clearAspectFlags,
                                     const VkClearDepthStencilValue &depthStencil,
+                                    uint32_t baseMipLevel,
+                                    uint32_t levelCount,
+                                    uint32_t baseArrayLayer,
+                                    uint32_t layerCount,
                                     vk::CommandBuffer *commandBuffer)
 {
     ASSERT(valid());
@@ -1691,10 +1695,10 @@
 
     VkImageSubresourceRange clearRange = {
         /*aspectMask*/ clearAspectFlags,
-        /*baseMipLevel*/ 0,
-        /*levelCount*/ 1,
-        /*baseArrayLayer*/ 0,
-        /*layerCount*/ 1,
+        /*baseMipLevel*/ baseMipLevel,
+        /*levelCount*/ levelCount,
+        /*baseArrayLayer*/ baseArrayLayer,
+        /*layerCount*/ layerCount,
     };
 
     commandBuffer->clearDepthStencilImage(mImage, getCurrentLayout(), depthStencil, 1, &clearRange);
@@ -1711,9 +1715,9 @@
 
     if (isDepthStencil)
     {
-        ASSERT(mipLevel == 0 && baseArrayLayer == 0 && layerCount == 1);
         const VkImageAspectFlags aspect = vk::GetDepthStencilAspectFlags(mFormat->imageFormat());
-        clearDepthStencil(aspect, aspect, value.depthStencil, commandBuffer);
+        clearDepthStencil(aspect, aspect, value.depthStencil, mipLevel, 1, baseArrayLayer,
+                          layerCount, commandBuffer);
     }
     else
     {
@@ -1945,12 +1949,12 @@
     loadFunction.loadFunction(extents.width, extents.height, extents.depth, source, inputRowPitch,
                               inputDepthPitch, stagingPointer, outputRowPitch, outputDepthPitch);
 
-    VkBufferImageCopy copy = {};
+    VkBufferImageCopy copy         = {};
+    VkImageAspectFlags aspectFlags = GetFormatAspectFlags(vkFormat.imageFormat());
 
     copy.bufferOffset                    = stagingOffset;
     copy.bufferRowLength                 = bufferRowLength;
     copy.bufferImageHeight               = bufferImageHeight;
-    copy.imageSubresource.aspectMask     = VK_IMAGE_ASPECT_COLOR_BIT;
     copy.imageSubresource.mipLevel       = index.getLevelIndex();
     copy.imageSubresource.baseArrayLayer = index.hasLayer() ? index.getLayerIndex() : 0;
     copy.imageSubresource.layerCount     = index.getLayerCount();
@@ -1958,7 +1962,19 @@
     gl_vk::GetOffset(offset, &copy.imageOffset);
     gl_vk::GetExtent(extents, &copy.imageExtent);
 
-    mSubresourceUpdates.emplace_back(bufferHandle, copy);
+    // TODO: http://anglebug.com/3437 - need to split packed depth_stencil into
+    // staging buffers for upload.
+    // Ignore stencil for now.
+    if (aspectFlags & VK_IMAGE_ASPECT_STENCIL_BIT)
+    {
+        aspectFlags &= ~VK_IMAGE_ASPECT_STENCIL_BIT;
+    }
+
+    if (aspectFlags)
+    {
+        copy.imageSubresource.aspectMask = aspectFlags;
+        mSubresourceUpdates.emplace_back(bufferHandle, copy);
+    }
 
     return angle::Result::Continue;
 }
@@ -1984,6 +2000,9 @@
     copy.imageSubresource.baseArrayLayer = imageIndex.hasLayer() ? imageIndex.getLayerIndex() : 0;
     copy.imageSubresource.layerCount     = imageIndex.getLayerCount();
 
+    // Note: Only support color now
+    ASSERT(getAspectFlags() == VK_IMAGE_ASPECT_COLOR_BIT);
+
     gl_vk::GetOffset(offset, &copy.imageOffset);
     gl_vk::GetExtent(extents, &copy.imageExtent);
 
diff --git a/src/libANGLE/renderer/vulkan/vk_helpers.h b/src/libANGLE/renderer/vulkan/vk_helpers.h
index 9672faa..754bb98 100644
--- a/src/libANGLE/renderer/vulkan/vk_helpers.h
+++ b/src/libANGLE/renderer/vulkan/vk_helpers.h
@@ -754,6 +754,10 @@
     void clearDepthStencil(VkImageAspectFlags imageAspectFlags,
                            VkImageAspectFlags clearAspectFlags,
                            const VkClearDepthStencilValue &depthStencil,
+                           uint32_t baseMipLevel,
+                           uint32_t levelCount,
+                           uint32_t baseArrayLayer,
+                           uint32_t layerCount,
                            vk::CommandBuffer *commandBuffer);
 
     struct SubresourceUpdate
diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp
index 248a511..a6caffc 100644
--- a/src/libANGLE/validationES2.cpp
+++ b/src/libANGLE/validationES2.cpp
@@ -730,9 +730,7 @@
             case GL_DEPTH_COMPONENT:
             case GL_DEPTH_COMPONENT16:
             case GL_DEPTH_COMPONENT32_OES:
-            case GL_DEPTH_STENCIL_OES:
-            case GL_DEPTH24_STENCIL8_OES:
-                if (context->getExtensions().depthTextureANGLE)
+                if (context->getExtensions().depthTextureAny())
                 {
                     context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
                     return false;
@@ -742,6 +740,21 @@
                     context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
                     return false;
                 }
+                break;
+            case GL_DEPTH_STENCIL_OES:
+            case GL_DEPTH24_STENCIL8_OES:
+                if (context->getExtensions().depthTextureAny() ||
+                    context->getExtensions().packedDepthStencil)
+                {
+                    context->validationError(GL_INVALID_OPERATION, kInvalidFormat);
+                    return false;
+                }
+                else
+                {
+                    context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+                    return false;
+                }
+                break;
             default:
                 context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
                 return false;
@@ -1497,7 +1510,9 @@
                 break;
             case GL_DEPTH_COMPONENT:
             case GL_DEPTH_STENCIL_OES:
-                if (!context->getExtensions().depthTextureANGLE)
+                if (!context->getExtensions().depthTextureANGLE &&
+                    !(context->getExtensions().packedDepthStencil &&
+                      context->getExtensions().depthTextureOES))
                 {
                     context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
                     return false;
@@ -1509,15 +1524,18 @@
                 }
                 // OES_depth_texture supports loading depth data and multiple levels,
                 // but ANGLE_depth_texture does not
-                if (pixels != nullptr)
+                if (!context->getExtensions().depthTextureOES)
                 {
-                    context->validationError(GL_INVALID_OPERATION, kPixelDataNotNull);
-                    return false;
-                }
-                if (level != 0)
-                {
-                    context->validationError(GL_INVALID_OPERATION, kLevelNotZero);
-                    return false;
+                    if (pixels != nullptr)
+                    {
+                        context->validationError(GL_INVALID_OPERATION, kPixelDataNotNull);
+                        return false;
+                    }
+                    if (level != 0)
+                    {
+                        context->validationError(GL_INVALID_OPERATION, kLevelNotZero);
+                        return false;
+                    }
                 }
                 break;
             default:
@@ -1587,8 +1605,16 @@
                     break;
 
                 case GL_DEPTH_COMPONENT:
+                    if (!(context->getExtensions().depthTextureAny()))
+                    {
+                        context->validationError(GL_INVALID_ENUM, kInvalidFormat);
+                        return false;
+                    }
+                    break;
+
                 case GL_DEPTH_STENCIL:
-                    if (!context->getExtensions().depthTextureANGLE)
+                    if (!(context->getExtensions().depthTextureANGLE ||
+                          context->getExtensions().packedDepthStencil))
                     {
                         context->validationError(GL_INVALID_ENUM, kInvalidFormat);
                         return false;
@@ -1883,8 +1909,7 @@
             break;
         case GL_DEPTH_COMPONENT16:
         case GL_DEPTH_COMPONENT32_OES:
-        case GL_DEPTH24_STENCIL8_OES:
-            if (!context->getExtensions().depthTextureANGLE)
+            if (!(context->getExtensions().depthTextureAny()))
             {
                 context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
                 return false;
@@ -1895,12 +1920,39 @@
                 return false;
             }
             // ANGLE_depth_texture only supports 1-level textures
-            if (levels != 1)
+            if (!context->getExtensions().depthTextureOES)
             {
-                context->validationError(GL_INVALID_OPERATION, kInvalidMipLevels);
-                return false;
+                if (levels != 1)
+                {
+                    context->validationError(GL_INVALID_OPERATION, kInvalidMipLevels);
+                    return false;
+                }
             }
             break;
+        case GL_DEPTH24_STENCIL8_OES:
+            if (!(context->getExtensions().depthTextureANGLE ||
+                  (context->getExtensions().packedDepthStencil &&
+                   context->getExtensions().textureStorage)))
+            {
+                context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
+                return false;
+            }
+            if (target != TextureType::_2D)
+            {
+                context->validationError(GL_INVALID_OPERATION, kInvalidTextureTarget);
+                return false;
+            }
+            if (!context->getExtensions().packedDepthStencil)
+            {
+                // ANGLE_depth_texture only supports 1-level textures
+                if (levels != 1)
+                {
+                    context->validationError(GL_INVALID_OPERATION, kInvalidMipLevels);
+                    return false;
+                }
+            }
+            break;
+
         default:
             break;
     }
diff --git a/src/tests/gl_tests/DepthStencilFormatsTest.cpp b/src/tests/gl_tests/DepthStencilFormatsTest.cpp
index 67fdac9..eb3df59 100644
--- a/src/tests/gl_tests/DepthStencilFormatsTest.cpp
+++ b/src/tests/gl_tests/DepthStencilFormatsTest.cpp
@@ -117,7 +117,9 @@
 
 TEST_P(DepthStencilFormatsTest, DepthTexture)
 {
-    bool shouldHaveTextureSupport = IsGLExtensionEnabled("GL_ANGLE_depth_texture");
+    bool shouldHaveTextureSupport = (IsGLExtensionEnabled("GL_ANGLE_depth_texture") ||
+                                     IsGLExtensionEnabled("GL_OES_depth_texture"));
+
     EXPECT_EQ(shouldHaveTextureSupport,
               checkTexImageFormatSupport(GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT));
     EXPECT_EQ(shouldHaveTextureSupport,
@@ -140,14 +142,331 @@
     EXPECT_EQ(shouldHaveRenderbufferSupport,
               checkRenderbufferFormatSupport(GL_DEPTH24_STENCIL8_OES));
 
-    bool shouldHaveTextureSupport = IsGLExtensionEnabled("GL_OES_packed_depth_stencil") &&
+    bool shouldHaveTextureSupport = (IsGLExtensionEnabled("GL_OES_packed_depth_stencil") &&
+                                     IsGLExtensionEnabled("GL_OES_depth_texture")) ||
                                     IsGLExtensionEnabled("GL_ANGLE_depth_texture");
     EXPECT_EQ(shouldHaveTextureSupport,
               checkTexImageFormatSupport(GL_DEPTH_STENCIL_OES, GL_UNSIGNED_INT_24_8_OES));
 
     if (IsGLExtensionEnabled("GL_EXT_texture_storage"))
     {
-        EXPECT_EQ(shouldHaveTextureSupport, checkTexStorageFormatSupport(GL_DEPTH24_STENCIL8_OES));
+        bool shouldHaveTexStorageSupport = IsGLExtensionEnabled("GL_OES_packed_depth_stencil") ||
+                                           IsGLExtensionEnabled("GL_ANGLE_depth_texture");
+        EXPECT_EQ(shouldHaveTexStorageSupport,
+                  checkTexStorageFormatSupport(GL_DEPTH24_STENCIL8_OES));
+    }
+}
+
+// This test will initialize a depth texture and then render with it and verify
+// pixel correctness.
+// This is modeled after webgl-depth-texture.html
+TEST_P(DepthStencilFormatsTest, DepthTextureRender)
+{
+    constexpr char kVS[] = R"(attribute vec4 a_position;
+void main()
+{
+    gl_Position = a_position;
+})";
+
+    constexpr char kFS[] = R"(precision mediump float;
+uniform sampler2D u_texture;
+uniform vec2 u_resolution;
+void main()
+{
+    vec2 texcoord = (gl_FragCoord.xy - vec2(0.5)) / (u_resolution - vec2(1.0));
+    gl_FragColor = texture2D(u_texture, texcoord);
+})";
+
+    ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_depth_texture") &&
+                       !IsGLExtensionEnabled("GL_ANGLE_depth_texture"));
+
+    // http://anglebug.com/3454
+    ANGLE_SKIP_TEST_IF(IsIntel() && IsWindows() && IsD3D9());
+
+    const int res     = 2;
+    const int destRes = 4;
+    GLint resolution;
+
+    ANGLE_GL_PROGRAM(program, kVS, kFS);
+
+    glUseProgram(program);
+    resolution = glGetUniformLocation(program, "u_resolution");
+    ASSERT_NE(-1, resolution);
+    glUniform2f(resolution, static_cast<float>(destRes), static_cast<float>(destRes));
+
+    GLuint buffer;
+    glGenBuffers(1, &buffer);
+    glBindBuffer(GL_ARRAY_BUFFER, buffer);
+    float verts[] = {
+        1, 1, 1, -1, 1, 0, -1, -1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 0,
+    };
+    glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
+    glEnableVertexAttribArray(0);
+    glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
+
+    // OpenGL ES does not have a FLIPY PixelStore attribute
+    // glPixelStorei(GL_UNPACK_FLIP)
+
+    enum ObjType
+    {
+        GL,
+        EXT
+    };
+    struct TypeInfo
+    {
+        ObjType obj;
+        GLuint attachment;
+        GLuint format;
+        GLuint type;
+        void *data;
+        int depthBits;
+        int stencilBits;
+    };
+
+    GLuint fakeData[10] = {0};
+
+    std::vector<TypeInfo> types = {
+        {ObjType::GL, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, fakeData, 16, 0},
+        {ObjType::GL, GL_DEPTH_ATTACHMENT, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, fakeData, 16, 0},
+        {ObjType::EXT, GL_DEPTH_STENCIL_ATTACHMENT, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8_OES,
+         fakeData, 24, 8},
+    };
+
+    for (const TypeInfo &type : types)
+    {
+        GLTexture cubeTex;
+        glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTex);
+        ASSERT_GL_NO_ERROR();
+
+        std::vector<GLuint> targets{GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
+                                    GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
+                                    GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z};
+
+        for (const GLuint target : targets)
+        {
+            glTexImage2D(target, 0, type.format, 1, 1, 0, type.format, type.type, nullptr);
+            EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+        }
+
+        std::vector<GLuint> filterModes = {GL_LINEAR, GL_NEAREST};
+
+        const bool supportPackedDepthStencilFramebuffer = getClientMajorVersion() >= 3;
+
+        for (const GLuint filterMode : filterModes)
+        {
+            GLTexture tex;
+            glBindTexture(GL_TEXTURE_2D, tex);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filterMode);
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filterMode);
+
+            // test level > 0
+            glTexImage2D(GL_TEXTURE_2D, 1, type.format, 1, 1, 0, type.format, type.type, nullptr);
+            if (IsGLExtensionEnabled("GL_OES_depth_texture"))
+            {
+                EXPECT_GL_NO_ERROR();
+            }
+            else
+            {
+                EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+            }
+
+            // test with data
+            glTexImage2D(GL_TEXTURE_2D, 0, type.format, 1, 1, 0, type.format, type.type, type.data);
+            if (IsGLExtensionEnabled("GL_OES_depth_texture"))
+            {
+                EXPECT_GL_NO_ERROR();
+            }
+            else
+            {
+                EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+            }
+
+            // test copyTexImage2D
+            glCopyTexImage2D(GL_TEXTURE_2D, 0, type.format, 0, 0, 1, 1, 0);
+            GLuint error = glGetError();
+            ASSERT_TRUE(error == GL_INVALID_ENUM || error == GL_INVALID_OPERATION);
+
+            // test real thing
+            glTexImage2D(GL_TEXTURE_2D, 0, type.format, res, res, 0, type.format, type.type,
+                         nullptr);
+            EXPECT_GL_NO_ERROR();
+
+            // test texSubImage2D
+            glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, type.format, type.type, type.data);
+            if (IsGLExtensionEnabled("GL_OES_depth_texture"))
+            {
+                EXPECT_GL_NO_ERROR();
+            }
+            else
+            {
+                EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+            }
+
+            // test copyTexSubImage2D
+            glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
+            EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+            // test generateMipmap
+            glGenerateMipmap(GL_TEXTURE_2D);
+            EXPECT_GL_ERROR(GL_INVALID_OPERATION);
+
+            GLuint fbo = 0;
+            glGenFramebuffers(1, &fbo);
+            glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+            if (type.depthBits > 0 && type.stencilBits > 0 && !supportPackedDepthStencilFramebuffer)
+            {
+                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, tex, 0);
+                EXPECT_GL_NO_ERROR();
+                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, tex,
+                                       0);
+                EXPECT_GL_NO_ERROR();
+            }
+            else
+            {
+                glFramebufferTexture2D(GL_FRAMEBUFFER, type.attachment, GL_TEXTURE_2D, tex, 0);
+                EXPECT_GL_NO_ERROR();
+            }
+
+            // Ensure DEPTH_BITS returns >= 16 bits for UNSIGNED_SHORT and UNSIGNED_INT, >= 24
+            // UNSIGNED_INT_24_8_WEBGL. If there is stencil, ensure STENCIL_BITS reports >= 8 for
+            // UNSIGNED_INT_24_8_WEBGL.
+
+            GLint depthBits = 0;
+            glGetIntegerv(GL_DEPTH_BITS, &depthBits);
+            EXPECT_GE(depthBits, type.depthBits);
+
+            GLint stencilBits = 0;
+            glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
+            EXPECT_GE(stencilBits, type.stencilBits);
+
+            // TODO: remove this check if the spec is updated to require these combinations to work.
+            if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
+            {
+                // try adding a color buffer.
+                GLuint colorTex = 0;
+                glGenTextures(1, &colorTex);
+                glBindTexture(GL_TEXTURE_2D, colorTex);
+                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, res, res, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+                             nullptr);
+                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                                       colorTex, 0);
+                EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+            }
+
+            EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+            // use the default texture to render with while we return to the depth texture.
+            glBindTexture(GL_TEXTURE_2D, 0);
+
+            /* Setup 2x2 depth texture:
+             * 1 0.6 0.8
+             * |
+             * 0 0.2 0.4
+             *    0---1
+             */
+            const GLfloat d00 = 0.2;
+            const GLfloat d01 = 0.4;
+            const GLfloat d10 = 0.6;
+            const GLfloat d11 = 0.8;
+            glEnable(GL_SCISSOR_TEST);
+            glScissor(0, 0, 1, 1);
+            glClearDepthf(d00);
+            glClear(GL_DEPTH_BUFFER_BIT);
+            glScissor(1, 0, 1, 1);
+            glClearDepthf(d10);
+            glClear(GL_DEPTH_BUFFER_BIT);
+            glScissor(0, 1, 1, 1);
+            glClearDepthf(d01);
+            glClear(GL_DEPTH_BUFFER_BIT);
+            glScissor(1, 1, 1, 1);
+            glClearDepthf(d11);
+            glClear(GL_DEPTH_BUFFER_BIT);
+            glDisable(GL_SCISSOR_TEST);
+
+            // render the depth texture.
+            glBindFramebuffer(GL_FRAMEBUFFER, 0);
+            glViewport(0, 0, destRes, destRes);
+            glBindTexture(GL_TEXTURE_2D, tex);
+            glDisable(GL_DITHER);
+            glEnable(GL_DEPTH_TEST);
+            glClearColor(1, 0, 0, 1);
+            glClearDepthf(1.0);
+            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+            glDrawArrays(GL_TRIANGLES, 0, 6);
+
+            GLubyte actualPixels[destRes * destRes * 4];
+            glReadPixels(0, 0, destRes, destRes, GL_RGBA, GL_UNSIGNED_BYTE, actualPixels);
+            const GLfloat eps = 0.002;
+            std::vector<GLfloat> expectedMin;
+            std::vector<GLfloat> expectedMax;
+            if (filterMode == GL_NEAREST)
+            {
+                GLfloat init[] = {d00, d00, d10, d10, d00, d00, d10, d10,
+                                  d01, d01, d11, d11, d01, d01, d11, d11};
+                expectedMin.insert(expectedMin.begin(), init, init + 16);
+                expectedMax.insert(expectedMax.begin(), init, init + 16);
+
+                for (int i = 0; i < 16; i++)
+                {
+                    expectedMin[i] = expectedMin[i] - eps;
+                    expectedMax[i] = expectedMax[i] + eps;
+                }
+            }
+            else
+            {
+                GLfloat initMin[] = {
+                    d00 - eps, d00, d00, d10 - eps, d00,       d00, d00, d10,
+                    d00,       d00, d00, d10,       d01 - eps, d01, d01, d11 - eps,
+                };
+                GLfloat initMax[] = {
+                    d00 + eps, d10, d10, d10 + eps, d01,       d11, d11, d11,
+                    d01,       d11, d11, d11,       d01 + eps, d11, d11, d11 + eps,
+                };
+                expectedMin.insert(expectedMin.begin(), initMin, initMin + 16);
+                expectedMax.insert(expectedMax.begin(), initMax, initMax + 16);
+            }
+            for (int yy = 0; yy < destRes; ++yy)
+            {
+                for (int xx = 0; xx < destRes; ++xx)
+                {
+                    const int t        = xx + destRes * yy;
+                    const GLfloat was  = (GLfloat)(actualPixels[4 * t] / 255.0);  // 4bpp
+                    const GLfloat eMin = expectedMin[t];
+                    const GLfloat eMax = expectedMax[t];
+                    EXPECT_TRUE(was >= eMin && was <= eMax)
+                        << "At " << xx << ", " << yy << ", expected within [" << eMin << ", "
+                        << eMax << "] was " << was;
+                }
+            }
+
+            // check limitations
+            // Note: What happens if current attachment type is GL_DEPTH_STENCIL_ATTACHMENT
+            // and you try to call glFramebufferTexture2D with GL_DEPTH_ATTACHMENT?
+            // The webGL test this code came from expected that to fail.
+            // I think due to this line in ES3 spec:
+            // GL_INVALID_OPERATION is generated if textarget and texture are not compatible
+            // However, that's not the behavior I'm seeing, nor does it seem that a depth_stencil
+            // buffer isn't compatible with a depth attachment (e.g. stencil is unused).
+            if (type.attachment == GL_DEPTH_ATTACHMENT)
+            {
+                glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+                glFramebufferTexture2D(GL_FRAMEBUFFER, type.attachment, GL_TEXTURE_2D, 0, 0);
+                glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
+                                       tex, 0);
+                EXPECT_GLENUM_NE(GL_NO_ERROR, glGetError());
+                EXPECT_GLENUM_NE(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
+                glClear(GL_DEPTH_BUFFER_BIT);
+                EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
+                glBindFramebuffer(GL_FRAMEBUFFER, 0);
+                EXPECT_GL_NO_ERROR();
+            }
+        }
     }
 }