Support T2T copies between formats that have only difference on srgb-ness

In previous T2T copy, Dawn requires textures have the same formats. But
Vulkan/Metal/D3D12 have ability to copy between "compatible" formats textures.

Metal has the most restrict rules without setting interpreter flags when creating
textures. It defines "compatible" texture formats to the formats that only have
difference on srgb-ness.

This CL follow Metal's rule and release the validations for T2T copies. It supports
T2T copy between "compatible" texture format textures.

Bug: dawn:1204
Change-Id: I50bf04ea15e8026530b3a5bdb5725f56aa192d85
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/74301
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Shaobo Yan <shaobo.yan@intel.com>
diff --git a/src/dawn_native/CommandValidation.cpp b/src/dawn_native/CommandValidation.cpp
index ea2017d..25f49f2 100644
--- a/src/dawn_native/CommandValidation.cpp
+++ b/src/dawn_native/CommandValidation.cpp
@@ -429,11 +429,13 @@
     MaybeError ValidateTextureToTextureCopyRestrictions(const ImageCopyTexture& src,
                                                         const ImageCopyTexture& dst,
                                                         const Extent3D& copySize) {
-        // Metal requires texture-to-texture copies be the same format
-        DAWN_INVALID_IF(src.texture->GetFormat().format != dst.texture->GetFormat().format,
-                        "Source %s format (%s) and destination %s format (%s) do not match.",
-                        src.texture, src.texture->GetFormat().format, dst.texture,
-                        dst.texture->GetFormat().format);
+        // Metal requires texture-to-texture copies happens between texture formats that equal to
+        // each other or only have diff on srgb-ness.
+        DAWN_INVALID_IF(
+            !src.texture->GetFormat().CopyCompatibleWith(dst.texture->GetFormat()),
+            "Source %s format (%s) and destination %s format (%s) are not copy compatible.",
+            src.texture, src.texture->GetFormat().format, dst.texture,
+            dst.texture->GetFormat().format);
 
         return ValidateTextureToTextureCopyCommonRestrictions(src, dst, copySize);
     }
diff --git a/src/dawn_native/Format.cpp b/src/dawn_native/Format.cpp
index c8d0588..45f89f1 100644
--- a/src/dawn_native/Format.cpp
+++ b/src/dawn_native/Format.cpp
@@ -104,6 +104,10 @@
         return (aspects & (Aspect::Plane0 | Aspect::Plane1)) != 0;
     }
 
+    bool Format::CopyCompatibleWith(const Format& format) const {
+        return baseFormat == format.baseFormat;
+    }
+
     const AspectInfo& Format::GetAspectInfo(wgpu::TextureAspect aspect) const {
         return GetAspectInfo(SelectFormatAspects(*this, aspect));
     }
@@ -157,47 +161,57 @@
             formatsSet.set(index);
         };
 
-        auto AddColorFormat = [&AddFormat](wgpu::TextureFormat format, bool renderable,
-                                           bool supportsStorageUsage, uint32_t byteSize,
-                                           SampleTypeBit sampleTypes, uint8_t componentCount) {
-            Format internalFormat;
-            internalFormat.format = format;
-            internalFormat.isRenderable = renderable;
-            internalFormat.isCompressed = false;
-            internalFormat.isSupported = true;
-            internalFormat.supportsStorageUsage = supportsStorageUsage;
-            internalFormat.aspects = Aspect::Color;
-            internalFormat.componentCount = componentCount;
-            AspectInfo* firstAspect = internalFormat.aspectInfo.data();
-            firstAspect->block.byteSize = byteSize;
-            firstAspect->block.width = 1;
-            firstAspect->block.height = 1;
-            if (HasOneBit(sampleTypes)) {
-                switch (sampleTypes) {
-                    case SampleTypeBit::Float:
-                    case SampleTypeBit::UnfilterableFloat:
-                        firstAspect->baseType = wgpu::TextureComponentType::Float;
-                        break;
-                    case SampleTypeBit::Sint:
-                        firstAspect->baseType = wgpu::TextureComponentType::Sint;
-                        break;
-                    case SampleTypeBit::Uint:
-                        firstAspect->baseType = wgpu::TextureComponentType::Uint;
-                        break;
-                    default:
-                        UNREACHABLE();
-                }
-            } else {
-                ASSERT((sampleTypes & SampleTypeBit::Float) != 0);
-                firstAspect->baseType = wgpu::TextureComponentType::Float;
-            }
-            firstAspect->supportedSampleTypes = sampleTypes;
-            firstAspect->format = format;
-            AddFormat(internalFormat);
-        };
+        auto AddColorFormat =
+            [&AddFormat](wgpu::TextureFormat format, bool renderable, bool supportsStorageUsage,
+                         uint32_t byteSize, SampleTypeBit sampleTypes, uint8_t componentCount,
+                         wgpu::TextureFormat baseFormat = wgpu::TextureFormat::Undefined) {
+                Format internalFormat;
+                internalFormat.format = format;
+                internalFormat.isRenderable = renderable;
+                internalFormat.isCompressed = false;
+                internalFormat.isSupported = true;
+                internalFormat.supportsStorageUsage = supportsStorageUsage;
+                internalFormat.aspects = Aspect::Color;
+                internalFormat.componentCount = componentCount;
 
-        auto AddDepthFormat = [&AddFormat](wgpu::TextureFormat format, uint32_t byteSize,
-                                           bool isSupported) {
+                // Default baseFormat of each color formats should be themselves.
+                if (baseFormat == wgpu::TextureFormat::Undefined) {
+                    internalFormat.baseFormat = format;
+                } else {
+                    internalFormat.baseFormat = baseFormat;
+                }
+
+                AspectInfo* firstAspect = internalFormat.aspectInfo.data();
+                firstAspect->block.byteSize = byteSize;
+                firstAspect->block.width = 1;
+                firstAspect->block.height = 1;
+                if (HasOneBit(sampleTypes)) {
+                    switch (sampleTypes) {
+                        case SampleTypeBit::Float:
+                        case SampleTypeBit::UnfilterableFloat:
+                            firstAspect->baseType = wgpu::TextureComponentType::Float;
+                            break;
+                        case SampleTypeBit::Sint:
+                            firstAspect->baseType = wgpu::TextureComponentType::Sint;
+                            break;
+                        case SampleTypeBit::Uint:
+                            firstAspect->baseType = wgpu::TextureComponentType::Uint;
+                            break;
+                        default:
+                            UNREACHABLE();
+                    }
+                } else {
+                    ASSERT((sampleTypes & SampleTypeBit::Float) != 0);
+                    firstAspect->baseType = wgpu::TextureComponentType::Float;
+                }
+                firstAspect->supportedSampleTypes = sampleTypes;
+                firstAspect->format = format;
+                AddFormat(internalFormat);
+            };
+
+        auto AddDepthFormat = [&AddFormat](
+                                  wgpu::TextureFormat format, uint32_t byteSize, bool isSupported,
+                                  wgpu::TextureFormat baseFormat = wgpu::TextureFormat::Undefined) {
             Format internalFormat;
             internalFormat.format = format;
             internalFormat.isRenderable = true;
@@ -206,6 +220,14 @@
             internalFormat.supportsStorageUsage = false;
             internalFormat.aspects = Aspect::Depth;
             internalFormat.componentCount = 1;
+
+            // Default baseFormat of each depth formats should be themselves.
+            if (baseFormat == wgpu::TextureFormat::Undefined) {
+                internalFormat.baseFormat = format;
+            } else {
+                internalFormat.baseFormat = baseFormat;
+            }
+
             AspectInfo* firstAspect = internalFormat.aspectInfo.data();
             firstAspect->block.byteSize = byteSize;
             firstAspect->block.width = 1;
@@ -216,7 +238,9 @@
             AddFormat(internalFormat);
         };
 
-        auto AddStencilFormat = [&AddFormat](wgpu::TextureFormat format, bool isSupported) {
+        auto AddStencilFormat = [&AddFormat](wgpu::TextureFormat format, bool isSupported,
+                                             wgpu::TextureFormat baseFormat =
+                                                 wgpu::TextureFormat::Undefined) {
             Format internalFormat;
             internalFormat.format = format;
             internalFormat.isRenderable = true;
@@ -225,6 +249,15 @@
             internalFormat.supportsStorageUsage = false;
             internalFormat.aspects = Aspect::Stencil;
             internalFormat.componentCount = 1;
+            internalFormat.baseFormat = baseFormat;
+
+            // Default baseFormat of each stencil formats should be themselves.
+            if (baseFormat == wgpu::TextureFormat::Undefined) {
+                internalFormat.baseFormat = format;
+            } else {
+                internalFormat.baseFormat = baseFormat;
+            }
+
             AspectInfo* firstAspect = internalFormat.aspectInfo.data();
             firstAspect->block.byteSize = 1;
             firstAspect->block.width = 1;
@@ -235,31 +268,41 @@
             AddFormat(internalFormat);
         };
 
-        auto AddCompressedFormat = [&AddFormat](wgpu::TextureFormat format, uint32_t byteSize,
-                                                uint32_t width, uint32_t height, bool isSupported,
-                                                uint8_t componentCount) {
-            Format internalFormat;
-            internalFormat.format = format;
-            internalFormat.isRenderable = false;
-            internalFormat.isCompressed = true;
-            internalFormat.isSupported = isSupported;
-            internalFormat.supportsStorageUsage = false;
-            internalFormat.aspects = Aspect::Color;
-            internalFormat.componentCount = componentCount;
-            AspectInfo* firstAspect = internalFormat.aspectInfo.data();
-            firstAspect->block.byteSize = byteSize;
-            firstAspect->block.width = width;
-            firstAspect->block.height = height;
-            firstAspect->baseType = wgpu::TextureComponentType::Float;
-            firstAspect->supportedSampleTypes = kAnyFloat;
-            firstAspect->format = format;
-            AddFormat(internalFormat);
-        };
+        auto AddCompressedFormat =
+            [&AddFormat](wgpu::TextureFormat format, uint32_t byteSize, uint32_t width,
+                         uint32_t height, bool isSupported, uint8_t componentCount,
+                         wgpu::TextureFormat baseFormat = wgpu::TextureFormat::Undefined) {
+                Format internalFormat;
+                internalFormat.format = format;
+                internalFormat.isRenderable = false;
+                internalFormat.isCompressed = true;
+                internalFormat.isSupported = isSupported;
+                internalFormat.supportsStorageUsage = false;
+                internalFormat.aspects = Aspect::Color;
+                internalFormat.componentCount = componentCount;
+
+                // Default baseFormat of each compressed formats should be themselves.
+                if (baseFormat == wgpu::TextureFormat::Undefined) {
+                    internalFormat.baseFormat = format;
+                } else {
+                    internalFormat.baseFormat = baseFormat;
+                }
+
+                AspectInfo* firstAspect = internalFormat.aspectInfo.data();
+                firstAspect->block.byteSize = byteSize;
+                firstAspect->block.width = width;
+                firstAspect->block.height = height;
+                firstAspect->baseType = wgpu::TextureComponentType::Float;
+                firstAspect->supportedSampleTypes = kAnyFloat;
+                firstAspect->format = format;
+                AddFormat(internalFormat);
+            };
 
         auto AddMultiAspectFormat =
             [&AddFormat, &table](wgpu::TextureFormat format, Aspect aspects,
                                  wgpu::TextureFormat firstFormat, wgpu::TextureFormat secondFormat,
-                                 bool isRenderable, bool isSupported, uint8_t componentCount) {
+                                 bool isRenderable, bool isSupported, uint8_t componentCount,
+                                 wgpu::TextureFormat baseFormat = wgpu::TextureFormat::Undefined) {
                 Format internalFormat;
                 internalFormat.format = format;
                 internalFormat.isRenderable = isRenderable;
@@ -268,6 +311,14 @@
                 internalFormat.supportsStorageUsage = false;
                 internalFormat.aspects = aspects;
                 internalFormat.componentCount = componentCount;
+
+                // Default baseFormat of each multi aspect formats should be themselves.
+                if (baseFormat == wgpu::TextureFormat::Undefined) {
+                    internalFormat.baseFormat = format;
+                } else {
+                    internalFormat.baseFormat = baseFormat;
+                }
+
                 const size_t firstFormatIndex = ComputeFormatIndex(firstFormat);
                 const size_t secondFormatIndex = ComputeFormatIndex(secondFormat);
 
@@ -301,12 +352,12 @@
         AddColorFormat(wgpu::TextureFormat::RG16Sint, true, false, 4, SampleTypeBit::Sint, 2);
         AddColorFormat(wgpu::TextureFormat::RG16Float, true, false, 4, kAnyFloat, 2);
         AddColorFormat(wgpu::TextureFormat::RGBA8Unorm, true, true, 4, kAnyFloat, 4);
-        AddColorFormat(wgpu::TextureFormat::RGBA8UnormSrgb, true, false, 4, kAnyFloat, 4);
+        AddColorFormat(wgpu::TextureFormat::RGBA8UnormSrgb, true, false, 4, kAnyFloat, 4, wgpu::TextureFormat::RGBA8Unorm);
         AddColorFormat(wgpu::TextureFormat::RGBA8Snorm, false, true, 4, kAnyFloat, 4);
         AddColorFormat(wgpu::TextureFormat::RGBA8Uint, true, true, 4, SampleTypeBit::Uint, 4);
         AddColorFormat(wgpu::TextureFormat::RGBA8Sint, true, true, 4, SampleTypeBit::Sint, 4);
         AddColorFormat(wgpu::TextureFormat::BGRA8Unorm, true, false, 4, kAnyFloat, 4);
-        AddColorFormat(wgpu::TextureFormat::BGRA8UnormSrgb, true, false, 4, kAnyFloat, 4);
+        AddColorFormat(wgpu::TextureFormat::BGRA8UnormSrgb, true, false, 4, kAnyFloat, 4, wgpu::TextureFormat::BGRA8Unorm);
         AddColorFormat(wgpu::TextureFormat::RGB10A2Unorm, true, false, 4, kAnyFloat, 4);
 
         AddColorFormat(wgpu::TextureFormat::RG11B10Ufloat, false, false, 4, kAnyFloat, 3);
@@ -346,28 +397,28 @@
         // BC compressed formats
         bool isBCFormatSupported = device->IsFeatureEnabled(Feature::TextureCompressionBC);
         AddCompressedFormat(wgpu::TextureFormat::BC1RGBAUnorm, 8, 4, 4, isBCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::BC1RGBAUnormSrgb, 8, 4, 4, isBCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::BC1RGBAUnormSrgb, 8, 4, 4, isBCFormatSupported, 4, wgpu::TextureFormat::BC1RGBAUnorm);
         AddCompressedFormat(wgpu::TextureFormat::BC4RSnorm, 8, 4, 4, isBCFormatSupported, 1);
         AddCompressedFormat(wgpu::TextureFormat::BC4RUnorm, 8, 4, 4, isBCFormatSupported, 1);
         AddCompressedFormat(wgpu::TextureFormat::BC2RGBAUnorm, 16, 4, 4, isBCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::BC2RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::BC2RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported, 4, wgpu::TextureFormat::BC2RGBAUnorm);
         AddCompressedFormat(wgpu::TextureFormat::BC3RGBAUnorm, 16, 4, 4, isBCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::BC3RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::BC3RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported, 4, wgpu::TextureFormat::BC3RGBAUnorm);
         AddCompressedFormat(wgpu::TextureFormat::BC5RGSnorm, 16, 4, 4, isBCFormatSupported, 2);
         AddCompressedFormat(wgpu::TextureFormat::BC5RGUnorm, 16, 4, 4, isBCFormatSupported, 2);
         AddCompressedFormat(wgpu::TextureFormat::BC6HRGBFloat, 16, 4, 4, isBCFormatSupported, 3);
         AddCompressedFormat(wgpu::TextureFormat::BC6HRGBUfloat, 16, 4, 4, isBCFormatSupported, 3);
         AddCompressedFormat(wgpu::TextureFormat::BC7RGBAUnorm, 16, 4, 4, isBCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::BC7RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::BC7RGBAUnormSrgb, 16, 4, 4, isBCFormatSupported, 4, wgpu::TextureFormat::BC7RGBAUnorm);
 
         // ETC2/EAC compressed formats
         bool isETC2FormatSupported = device->IsFeatureEnabled(Feature::TextureCompressionETC2);
         AddCompressedFormat(wgpu::TextureFormat::ETC2RGB8Unorm, 8, 4, 4, isETC2FormatSupported, 3);
-        AddCompressedFormat(wgpu::TextureFormat::ETC2RGB8UnormSrgb, 8, 4, 4, isETC2FormatSupported, 3);
+        AddCompressedFormat(wgpu::TextureFormat::ETC2RGB8UnormSrgb, 8, 4, 4, isETC2FormatSupported, 3, wgpu::TextureFormat::ETC2RGB8Unorm);
         AddCompressedFormat(wgpu::TextureFormat::ETC2RGB8A1Unorm, 8, 4, 4, isETC2FormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ETC2RGB8A1UnormSrgb, 8, 4, 4, isETC2FormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ETC2RGB8A1UnormSrgb, 8, 4, 4, isETC2FormatSupported, 4, wgpu::TextureFormat::ETC2RGB8A1Unorm);
         AddCompressedFormat(wgpu::TextureFormat::ETC2RGBA8Unorm, 16, 4, 4, isETC2FormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ETC2RGBA8UnormSrgb, 16, 4, 4, isETC2FormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ETC2RGBA8UnormSrgb, 16, 4, 4, isETC2FormatSupported, 4, wgpu::TextureFormat::ETC2RGBA8Unorm);
         AddCompressedFormat(wgpu::TextureFormat::EACR11Unorm, 8, 4, 4, isETC2FormatSupported, 1);
         AddCompressedFormat(wgpu::TextureFormat::EACR11Snorm, 8, 4, 4, isETC2FormatSupported, 1);
         AddCompressedFormat(wgpu::TextureFormat::EACRG11Unorm, 16, 4, 4, isETC2FormatSupported, 2);
@@ -376,33 +427,33 @@
         // ASTC compressed formats
         bool isASTCFormatSupported = device->IsFeatureEnabled(Feature::TextureCompressionASTC);
         AddCompressedFormat(wgpu::TextureFormat::ASTC4x4Unorm, 16, 4, 4, isASTCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ASTC4x4UnormSrgb, 16, 4, 4, isASTCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ASTC4x4UnormSrgb, 16, 4, 4, isASTCFormatSupported, 4, wgpu::TextureFormat::ASTC4x4Unorm);
         AddCompressedFormat(wgpu::TextureFormat::ASTC5x4Unorm, 16, 5, 4, isASTCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ASTC5x4UnormSrgb, 16, 5, 4, isASTCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ASTC5x4UnormSrgb, 16, 5, 4, isASTCFormatSupported, 4, wgpu::TextureFormat::ASTC5x4Unorm);
         AddCompressedFormat(wgpu::TextureFormat::ASTC5x5Unorm, 16, 5, 5, isASTCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ASTC5x5UnormSrgb, 16, 5, 5, isASTCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ASTC5x5UnormSrgb, 16, 5, 5, isASTCFormatSupported, 4, wgpu::TextureFormat::ASTC5x5Unorm);
         AddCompressedFormat(wgpu::TextureFormat::ASTC6x5Unorm, 16, 6, 5, isASTCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ASTC6x5UnormSrgb, 16, 6, 5, isASTCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ASTC6x5UnormSrgb, 16, 6, 5, isASTCFormatSupported, 4, wgpu::TextureFormat::ASTC6x5Unorm);
         AddCompressedFormat(wgpu::TextureFormat::ASTC6x6Unorm, 16, 6, 6, isASTCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ASTC6x6UnormSrgb, 16, 6, 6, isASTCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ASTC6x6UnormSrgb, 16, 6, 6, isASTCFormatSupported, 4, wgpu::TextureFormat::ASTC6x6Unorm);
         AddCompressedFormat(wgpu::TextureFormat::ASTC8x5Unorm, 16, 8, 5, isASTCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ASTC8x5UnormSrgb, 16, 8, 5, isASTCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ASTC8x5UnormSrgb, 16, 8, 5, isASTCFormatSupported, 4, wgpu::TextureFormat::ASTC8x5Unorm);
         AddCompressedFormat(wgpu::TextureFormat::ASTC8x6Unorm, 16, 8, 6, isASTCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ASTC8x6UnormSrgb, 16, 8, 6, isASTCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ASTC8x6UnormSrgb, 16, 8, 6, isASTCFormatSupported, 4, wgpu::TextureFormat::ASTC8x6Unorm);
         AddCompressedFormat(wgpu::TextureFormat::ASTC8x8Unorm, 16, 8, 8, isASTCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ASTC8x8UnormSrgb, 16, 8, 8, isASTCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ASTC8x8UnormSrgb, 16, 8, 8, isASTCFormatSupported, 4, wgpu::TextureFormat::ASTC8x8Unorm);
         AddCompressedFormat(wgpu::TextureFormat::ASTC10x5Unorm, 16, 10, 5, isASTCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ASTC10x5UnormSrgb, 16, 10, 5, isASTCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ASTC10x5UnormSrgb, 16, 10, 5, isASTCFormatSupported, 4, wgpu::TextureFormat::ASTC10x5Unorm);
         AddCompressedFormat(wgpu::TextureFormat::ASTC10x6Unorm, 16, 10, 6, isASTCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ASTC10x6UnormSrgb, 16, 10, 6, isASTCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ASTC10x6UnormSrgb, 16, 10, 6, isASTCFormatSupported, 4, wgpu::TextureFormat::ASTC10x6Unorm);
         AddCompressedFormat(wgpu::TextureFormat::ASTC10x8Unorm, 16, 10, 8, isASTCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ASTC10x8UnormSrgb, 16, 10, 8, isASTCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ASTC10x8UnormSrgb, 16, 10, 8, isASTCFormatSupported, 4, wgpu::TextureFormat::ASTC10x8Unorm);
         AddCompressedFormat(wgpu::TextureFormat::ASTC10x10Unorm, 16, 10, 10, isASTCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ASTC10x10UnormSrgb, 16, 10, 10, isASTCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ASTC10x10UnormSrgb, 16, 10, 10, isASTCFormatSupported, 4, wgpu::TextureFormat::ASTC10x10Unorm);
         AddCompressedFormat(wgpu::TextureFormat::ASTC12x10Unorm, 16, 12, 10, isASTCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ASTC12x10UnormSrgb, 16, 12, 10, isASTCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ASTC12x10UnormSrgb, 16, 12, 10, isASTCFormatSupported, 4, wgpu::TextureFormat::ASTC12x10Unorm);
         AddCompressedFormat(wgpu::TextureFormat::ASTC12x12Unorm, 16, 12, 12, isASTCFormatSupported, 4);
-        AddCompressedFormat(wgpu::TextureFormat::ASTC12x12UnormSrgb, 16, 12, 12, isASTCFormatSupported, 4);
+        AddCompressedFormat(wgpu::TextureFormat::ASTC12x12UnormSrgb, 16, 12, 12, isASTCFormatSupported, 4, wgpu::TextureFormat::ASTC12x12Unorm);
 
         // multi-planar formats
         const bool isMultiPlanarFormatSupported = device->IsFeatureEnabled(Feature::MultiPlanarFormats);
diff --git a/src/dawn_native/Format.h b/src/dawn_native/Format.h
index 2f604d3..a0730c6 100644
--- a/src/dawn_native/Format.h
+++ b/src/dawn_native/Format.h
@@ -85,6 +85,7 @@
     // A wgpu::TextureFormat along with all the information about it necessary for validation.
     struct Format {
         wgpu::TextureFormat format;
+
         bool isRenderable;
         bool isCompressed;
         // A format can be known but not supported because it is part of a disabled extension.
@@ -110,6 +111,14 @@
         // in [0, kKnownFormatCount)
         size_t GetIndex() const;
 
+        // baseFormat represents the memory layout of the format.
+        // If two formats has the same baseFormat, they could copy to each other.
+        wgpu::TextureFormat baseFormat;
+
+        // CopyCompatibleWith() returns true if the input format has the same baseFormat
+        // with current format.
+        bool CopyCompatibleWith(const Format& format) const;
+
       private:
         // Used to store the aspectInfo for one or more planes. For single plane "color" formats,
         // only the first aspect info or aspectInfo[0] is valid. For depth-stencil, the first aspect
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index 5ae6149..aa83abb 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -64,7 +64,7 @@
                                 const Extent3D& copySize) {
             // Checked by validation
             ASSERT(src.texture->GetSampleCount() == dst.texture->GetSampleCount());
-            ASSERT(src.texture->GetFormat().format == dst.texture->GetFormat().format);
+            ASSERT(src.texture->GetFormat().CopyCompatibleWith(dst.texture->GetFormat()));
             ASSERT(src.aspect == dst.aspect);
 
             const Extent3D& srcSize = src.texture->GetSize();
@@ -168,7 +168,8 @@
                     Toggle::
                         UseTempBufferInSmallFormatTextureToTextureCopyFromGreaterToLessMipLevel)) {
                 bool copyToLesserLevel = srcCopy.mipLevel > dstCopy.mipLevel;
-                ASSERT(srcCopy.texture->GetFormat().format == dstCopy.texture->GetFormat().format);
+                ASSERT(
+                    srcCopy.texture->GetFormat().CopyCompatibleWith(dstCopy.texture->GetFormat()));
 
                 // GetAspectInfo(aspect) requires HasOneBit(aspect) == true, plus the texel block
                 // sizes of depth stencil formats are always no less than 4 bytes.
diff --git a/src/dawn_native/metal/CommandBufferMTL.mm b/src/dawn_native/metal/CommandBufferMTL.mm
index 45ff8d9..ec6296d 100644
--- a/src/dawn_native/metal/CommandBufferMTL.mm
+++ b/src/dawn_native/metal/CommandBufferMTL.mm
@@ -897,6 +897,10 @@
                         *sourceZPtr = copy->source.origin.z + z;
                         *destinationZPtr = copy->destination.origin.z + z;
 
+                        // Hold the ref until out of scope
+                        NSPRef<id<MTLTexture>> dstTextureView =
+                            dstTexture->CreateFormatView(srcTexture->GetFormat().format);
+
                         [commandContext->EnsureBlit()
                               copyFromTexture:srcTexture->GetMTLTexture()
                                   sourceSlice:sourceLayer
@@ -904,7 +908,7 @@
                                  sourceOrigin:MTLOriginMake(copy->source.origin.x,
                                                             copy->source.origin.y, sourceOriginZ)
                                    sourceSize:sizeOneSlice
-                                    toTexture:dstTexture->GetMTLTexture()
+                                    toTexture:dstTextureView.Get()
                              destinationSlice:destinationLayer
                              destinationLevel:copy->destination.mipLevel
                             destinationOrigin:MTLOriginMake(copy->destination.origin.x,
diff --git a/src/dawn_native/metal/TextureMTL.h b/src/dawn_native/metal/TextureMTL.h
index bfcf02f..8c9a30d 100644
--- a/src/dawn_native/metal/TextureMTL.h
+++ b/src/dawn_native/metal/TextureMTL.h
@@ -48,6 +48,7 @@
                                            NSPRef<id<MTLTexture>> wrapped);
 
         id<MTLTexture> GetMTLTexture();
+        NSPRef<id<MTLTexture>> CreateFormatView(wgpu::TextureFormat format);
 
         void EnsureSubresourceContentInitialized(CommandRecordingContext* commandContext,
                                                  const SubresourceRange& range);
diff --git a/src/dawn_native/metal/TextureMTL.mm b/src/dawn_native/metal/TextureMTL.mm
index 7a98886..496ca10 100644
--- a/src/dawn_native/metal/TextureMTL.mm
+++ b/src/dawn_native/metal/TextureMTL.mm
@@ -113,6 +113,49 @@
             return false;
         }
 
+        // Metal only allows format reinterpretation to happen on swizzle pattern or conversion
+        // between linear space and sRGB without setting MTLTextureUsagePixelFormatView flag. For
+        // example, creating bgra8Unorm texture view on rgba8Unorm texture or creating
+        // rgba8Unorm_srgb texture view on rgab8Unorm texture.
+        bool AllowFormatReinterpretationWithoutFlag(MTLPixelFormat origin,
+                                                    MTLPixelFormat reinterpretation) {
+            switch (origin) {
+                case MTLPixelFormatRGBA8Unorm:
+                    return reinterpretation == MTLPixelFormatBGRA8Unorm ||
+                           reinterpretation == MTLPixelFormatRGBA8Unorm_sRGB;
+                case MTLPixelFormatBGRA8Unorm:
+                    return reinterpretation == MTLPixelFormatRGBA8Unorm ||
+                           reinterpretation == MTLPixelFormatBGRA8Unorm_sRGB;
+                case MTLPixelFormatRGBA8Unorm_sRGB:
+                    return reinterpretation == MTLPixelFormatBGRA8Unorm_sRGB ||
+                           reinterpretation == MTLPixelFormatRGBA8Unorm;
+                case MTLPixelFormatBGRA8Unorm_sRGB:
+                    return reinterpretation == MTLPixelFormatRGBA8Unorm_sRGB ||
+                           reinterpretation == MTLPixelFormatBGRA8Unorm;
+#if defined(DAWN_PLATFORM_MACOS)
+                case MTLPixelFormatBC1_RGBA:
+                    return reinterpretation == MTLPixelFormatBC1_RGBA_sRGB;
+                case MTLPixelFormatBC1_RGBA_sRGB:
+                    return reinterpretation == MTLPixelFormatBC1_RGBA;
+                case MTLPixelFormatBC2_RGBA:
+                    return reinterpretation == MTLPixelFormatBC2_RGBA_sRGB;
+                case MTLPixelFormatBC2_RGBA_sRGB:
+                    return reinterpretation == MTLPixelFormatBC2_RGBA;
+                case MTLPixelFormatBC3_RGBA:
+                    return reinterpretation == MTLPixelFormatBC3_RGBA_sRGB;
+                case MTLPixelFormatBC3_RGBA_sRGB:
+                    return reinterpretation == MTLPixelFormatBC3_RGBA;
+                case MTLPixelFormatBC7_RGBAUnorm:
+                    return reinterpretation == MTLPixelFormatBC7_RGBAUnorm_sRGB;
+                case MTLPixelFormatBC7_RGBAUnorm_sRGB:
+                    return reinterpretation == MTLPixelFormatBC7_RGBAUnorm;
+#endif
+
+                default:
+                    return false;
+            }
+        }
+
         ResultOrError<wgpu::TextureFormat> GetFormatEquivalentToIOSurfaceFormat(uint32_t format) {
             switch (format) {
                 case kCVPixelFormatType_32RGBA:
@@ -382,7 +425,10 @@
         mtlDesc.width = GetWidth();
         mtlDesc.height = GetHeight();
         mtlDesc.sampleCount = GetSampleCount();
-        // TODO: add MTLTextureUsagePixelFormatView when needed when we support format
+        // Metal only allows format reinterpretation to happen on swizzle pattern or conversion
+        // between linear space and sRGB. For example, creating bgra8Unorm texture view on
+        // rgba8Unorm texture or creating rgba8Unorm_srgb texture view on rgab8Unorm texture.
+        // TODO: add MTLTextureUsagePixelFormatView when needed when we support other format
         // reinterpretation.
         mtlDesc.usage = MetalTextureUsage(GetFormat(), GetInternalUsage(), GetSampleCount());
         mtlDesc.pixelFormat = MetalPixelFormat(GetFormat().format);
@@ -509,6 +555,17 @@
         return mMtlTexture.Get();
     }
 
+    NSPRef<id<MTLTexture>> Texture::CreateFormatView(wgpu::TextureFormat format) {
+        if (GetFormat().format == format) {
+            return mMtlTexture;
+        }
+
+        ASSERT(AllowFormatReinterpretationWithoutFlag(MetalPixelFormat(GetFormat().format),
+                                                      MetalPixelFormat(format)));
+        return AcquireNSPRef(
+            [mMtlTexture.Get() newTextureViewWithPixelFormat:MetalPixelFormat(format)]);
+    }
+
     MaybeError Texture::ClearTexture(CommandRecordingContext* commandContext,
                                      const SubresourceRange& range,
                                      TextureBase::ClearValue clearValue) {
diff --git a/src/tests/end2end/CopyTests.cpp b/src/tests/end2end/CopyTests.cpp
index 1907ffd..20cacaa 100644
--- a/src/tests/end2end/CopyTests.cpp
+++ b/src/tests/end2end/CopyTests.cpp
@@ -327,9 +327,13 @@
     // CopyTextureToTextureInternal.
     using UsageCopySrc = bool;
     DAWN_TEST_PARAM_STRUCT(CopyTestsParams, UsageCopySrc);
+
+    using SrcColorFormat = wgpu::TextureFormat;
+    DAWN_TEST_PARAM_STRUCT(SrcColorFormatParams, SrcColorFormat);
 }  // namespace
 
-class CopyTests_T2T : public CopyTests, public DawnTestWithParams<CopyTestsParams> {
+template <typename Parent>
+class CopyTests_T2TBase : public CopyTests, public Parent {
   protected:
     std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
         return {wgpu::FeatureName::DawnInternalUsages};
@@ -338,24 +342,10 @@
     void DoTest(const TextureSpec& srcSpec,
                 const TextureSpec& dstSpec,
                 const wgpu::Extent3D& copySize,
-                bool copyWithinSameTexture = false,
-                wgpu::TextureDimension dimension = wgpu::TextureDimension::e2D) {
-        DoTest(srcSpec, dstSpec, copySize, dimension, dimension, copyWithinSameTexture);
-    }
-
-    void DoTest(const TextureSpec& srcSpec,
-                const TextureSpec& dstSpec,
-                const wgpu::Extent3D& copySize,
                 wgpu::TextureDimension srcDimension,
                 wgpu::TextureDimension dstDimension,
-                bool copyWithinSameTexture = false) {
-        const bool usageCopySrc = GetParam().mUsageCopySrc;
-        // If we do this test with a CopyWithinSameTexture, it will need to have usageCopySrc in the
-        // public usage of the texture as it will later use a CopyTextureToBuffer, that needs the
-        // public usage of it.
-        DAWN_TEST_UNSUPPORTED_IF(!usageCopySrc && copyWithinSameTexture);
-
-        ASSERT_EQ(srcSpec.format, dstSpec.format);
+                bool copyWithinSameTexture = false,
+                bool usageCopySrc = false) {
         const wgpu::TextureFormat format = srcSpec.format;
 
         wgpu::TextureDescriptor srcDescriptor;
@@ -375,7 +365,7 @@
         } else {
             internalDesc.internalUsage = wgpu::TextureUsage::CopySrc;
         }
-        wgpu::Texture srcTexture = device.CreateTexture(&srcDescriptor);
+        wgpu::Texture srcTexture = this->device.CreateTexture(&srcDescriptor);
 
         wgpu::Texture dstTexture;
         if (copyWithinSameTexture) {
@@ -385,10 +375,10 @@
             dstDescriptor.dimension = dstDimension;
             dstDescriptor.size = dstSpec.textureSize;
             dstDescriptor.sampleCount = 1;
-            dstDescriptor.format = format;
+            dstDescriptor.format = dstSpec.format;
             dstDescriptor.mipLevelCount = dstSpec.levelCount;
             dstDescriptor.usage = wgpu::TextureUsage::CopySrc | wgpu::TextureUsage::CopyDst;
-            dstTexture = device.CreateTexture(&dstDescriptor);
+            dstTexture = this->device.CreateTexture(&dstDescriptor);
         }
 
         // Create an upload buffer and use it to completely populate the subresources of the src
@@ -409,12 +399,12 @@
                 srcTexture, srcSpec.copyLevel, {0, 0, srcSpec.copyOrigin.z});
             wgpu::TextureDataLayout textureDataLayout = utils::CreateTextureDataLayout(
                 0, srcDataCopyLayout.bytesPerRow, srcDataCopyLayout.rowsPerImage);
-            queue.WriteTexture(&imageCopyTexture, srcTextureCopyData.data(),
-                               srcDataCopyLayout.byteLength, &textureDataLayout,
-                               &srcDataCopyLayout.mipSize);
+            this->queue.WriteTexture(&imageCopyTexture, srcTextureCopyData.data(),
+                                     srcDataCopyLayout.byteLength, &textureDataLayout,
+                                     &srcDataCopyLayout.mipSize);
         }
 
-        wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+        wgpu::CommandEncoder encoder = this->device.CreateCommandEncoder();
 
         // Perform the texture to texture copy
         wgpu::ImageCopyTexture srcImageCopyTexture =
@@ -442,7 +432,7 @@
         wgpu::BufferDescriptor outputBufferDescriptor;
         outputBufferDescriptor.size = dstDataCopyLayout.byteLength;
         outputBufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::CopyDst;
-        wgpu::Buffer outputBuffer = device.CreateBuffer(&outputBufferDescriptor);
+        wgpu::Buffer outputBuffer = this->device.CreateBuffer(&outputBufferDescriptor);
         const uint32_t bytesPerTexel = utils::GetTexelBlockSizeInBytes(format);
         const uint32_t expectedDstDataOffset = dstSpec.copyOrigin.x * bytesPerTexel +
                                                dstSpec.copyOrigin.y * dstDataCopyLayout.bytesPerRow;
@@ -452,7 +442,7 @@
         encoder.CopyTextureToBuffer(&dstImageCopyTexture, &outputImageCopyBuffer, &copySize);
 
         wgpu::CommandBuffer commands = encoder.Finish();
-        queue.Submit(1, &commands);
+        this->queue.Submit(1, &commands);
 
         // Validate if the data in outputBuffer is what we expected, including the untouched data
         // outside of the copy.
@@ -504,6 +494,67 @@
     }
 };
 
+class CopyTests_T2T : public CopyTests_T2TBase<DawnTestWithParams<CopyTestsParams>> {
+  protected:
+    void DoTest(const TextureSpec& srcSpec,
+                const TextureSpec& dstSpec,
+                const wgpu::Extent3D& copySize,
+                bool copyWithinSameTexture = false,
+                wgpu::TextureDimension dimension = wgpu::TextureDimension::e2D) {
+        DoTest(srcSpec, dstSpec, copySize, dimension, dimension, copyWithinSameTexture);
+    }
+
+    void DoTest(const TextureSpec& srcSpec,
+                const TextureSpec& dstSpec,
+                const wgpu::Extent3D& copySize,
+                wgpu::TextureDimension srcDimension,
+                wgpu::TextureDimension dstDimension,
+                bool copyWithinSameTexture = false) {
+        const bool usageCopySrc = GetParam().mUsageCopySrc;
+        // If we do this test with a CopyWithinSameTexture, it will need to have usageCopySrc in the
+        // public usage of the texture as it will later use a CopyTextureToBuffer, that needs the
+        // public usage of it.
+        DAWN_TEST_UNSUPPORTED_IF(!usageCopySrc && copyWithinSameTexture);
+
+        ASSERT_EQ(srcSpec.format, dstSpec.format);
+
+        CopyTests_T2TBase<DawnTestWithParams<CopyTestsParams>>::DoTest(
+            srcSpec, dstSpec, copySize, srcDimension, dstDimension, copyWithinSameTexture,
+            usageCopySrc);
+    }
+};
+
+class CopyTests_Formats : public CopyTests_T2TBase<DawnTestWithParams<SrcColorFormatParams>> {
+  protected:
+    // Texture format is compatible and could be copied to each other if the only diff is srgb-ness.
+    wgpu::TextureFormat GetCopyCompatibleFormat(wgpu::TextureFormat format) {
+        switch (format) {
+            case wgpu::TextureFormat::RGBA8Unorm:
+                return wgpu::TextureFormat::RGBA8UnormSrgb;
+            case wgpu::TextureFormat::RGBA8UnormSrgb:
+                return wgpu::TextureFormat::RGBA8Unorm;
+            case wgpu::TextureFormat::BGRA8Unorm:
+                return wgpu::TextureFormat::BGRA8UnormSrgb;
+            case wgpu::TextureFormat::BGRA8UnormSrgb:
+                return wgpu::TextureFormat::BGRA8Unorm;
+            default:
+                UNREACHABLE();
+        }
+    }
+
+    void DoTest(TextureSpec srcSpec,
+                TextureSpec dstSpec,
+                const wgpu::Extent3D& copySize,
+                wgpu::TextureDimension srcDimension = wgpu::TextureDimension::e2D,
+                wgpu::TextureDimension dstDimension = wgpu::TextureDimension::e2D) {
+        srcSpec.format = GetParam().mSrcColorFormat;
+        dstSpec.format = GetCopyCompatibleFormat(srcSpec.format);
+
+        CopyTests_T2TBase<DawnTestWithParams<SrcColorFormatParams>>::DoTest(
+            srcSpec, dstSpec, copySize, srcDimension, dstDimension);
+    }
+};
+
 class CopyTests_B2B : public DawnTest {
   protected:
     // This is the same signature as CopyBufferToBuffer except that the buffers are replaced by
@@ -2409,6 +2460,27 @@
                          MetalBackend(), OpenGLBackend(), OpenGLESBackend(), VulkanBackend()},
                         {true, false});
 
+// Test copying between textures that have srgb compatible texture formats;
+TEST_P(CopyTests_Formats, SrgbCompatibility) {
+    // Skip backends because which fails to support *-srgb formats
+    // and bgra* formats.
+    DAWN_SUPPRESS_TEST_IF(IsOpenGLES());
+    DAWN_SUPPRESS_TEST_IF(IsOpenGL() && IsLinux());
+
+    constexpr uint32_t kWidth = 256;
+    constexpr uint32_t kHeight = 128;
+
+    TextureSpec textureSpec;
+    textureSpec.textureSize = {kWidth, kHeight, 1};
+    DoTest(textureSpec, textureSpec, {kWidth, kHeight, 1});
+}
+
+DAWN_INSTANTIATE_TEST_P(CopyTests_Formats,
+                        {D3D12Backend(), MetalBackend(), OpenGLBackend(), OpenGLESBackend(),
+                         VulkanBackend()},
+                        {wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA8UnormSrgb,
+                         wgpu::TextureFormat::BGRA8Unorm, wgpu::TextureFormat::BGRA8UnormSrgb});
+
 static constexpr uint64_t kSmallBufferSize = 4;
 static constexpr uint64_t kLargeBufferSize = 1 << 16;
 
diff --git a/src/tests/unittests/validation/CopyCommandsValidationTests.cpp b/src/tests/unittests/validation/CopyCommandsValidationTests.cpp
index 7f57c7d..28d05e0 100644
--- a/src/tests/unittests/validation/CopyCommandsValidationTests.cpp
+++ b/src/tests/unittests/validation/CopyCommandsValidationTests.cpp
@@ -1646,6 +1646,21 @@
         descriptor.requiredFeaturesCount = 2;
         return adapter.CreateDevice(&descriptor);
     }
+
+    wgpu::TextureFormat GetCopyCompatibleFormat(wgpu::TextureFormat format) {
+        switch (format) {
+            case wgpu::TextureFormat::BGRA8Unorm:
+                return wgpu::TextureFormat::BGRA8UnormSrgb;
+            case wgpu::TextureFormat::BGRA8UnormSrgb:
+                return wgpu::TextureFormat::BGRA8Unorm;
+            case wgpu::TextureFormat::RGBA8Unorm:
+                return wgpu::TextureFormat::RGBA8UnormSrgb;
+            case wgpu::TextureFormat::RGBA8UnormSrgb:
+                return wgpu::TextureFormat::RGBA8Unorm;
+            default:
+                UNREACHABLE();
+        }
+    }
 };
 
 TEST_F(CopyCommandTest_T2T, Success) {
@@ -1892,6 +1907,23 @@
                 {0, 0, 1});
 }
 
+// Test copying between textures that have srgb compatible texture formats;
+TEST_F(CopyCommandTest_T2T, SrgbFormatsCompatibility) {
+    for (wgpu::TextureFormat srcTextureFormat :
+         {wgpu::TextureFormat::BGRA8Unorm, wgpu::TextureFormat::BGRA8UnormSrgb,
+          wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureFormat::RGBA8UnormSrgb}) {
+        wgpu::TextureFormat dstTextureFormat = GetCopyCompatibleFormat(srcTextureFormat);
+        wgpu::Texture source =
+            Create2DTexture(16, 16, 5, 2, srcTextureFormat, wgpu::TextureUsage::CopySrc);
+        wgpu::Texture destination =
+            Create2DTexture(16, 16, 5, 2, dstTextureFormat, wgpu::TextureUsage::CopyDst);
+
+        // Failure when formats don't match
+        TestT2TCopy(utils::Expectation::Success, source, 0, {0, 0, 0}, destination, 0, {0, 0, 0},
+                    {0, 0, 1});
+    }
+}
+
 TEST_F(CopyCommandTest_T2T, MultisampledCopies) {
     wgpu::Texture sourceMultiSampled1x = Create2DTexture(
         16, 16, 1, 1, wgpu::TextureFormat::RGBA8Unorm, wgpu::TextureUsage::CopySrc, 1);
@@ -2112,6 +2144,97 @@
         uint32_t height = utils::GetTextureFormatBlockHeight(format) * 4;
         return Create2DTexture(format, 1, width, height);
     }
+
+    wgpu::TextureFormat GetCopyCompatibleFormat(wgpu::TextureFormat format) {
+        switch (format) {
+            case wgpu::TextureFormat::BC1RGBAUnorm:
+                return wgpu::TextureFormat::BC1RGBAUnormSrgb;
+            case wgpu::TextureFormat::BC1RGBAUnormSrgb:
+                return wgpu::TextureFormat::BC1RGBAUnorm;
+            case wgpu::TextureFormat::BC2RGBAUnorm:
+                return wgpu::TextureFormat::BC2RGBAUnormSrgb;
+            case wgpu::TextureFormat::BC2RGBAUnormSrgb:
+                return wgpu::TextureFormat::BC2RGBAUnorm;
+            case wgpu::TextureFormat::BC3RGBAUnorm:
+                return wgpu::TextureFormat::BC3RGBAUnormSrgb;
+            case wgpu::TextureFormat::BC3RGBAUnormSrgb:
+                return wgpu::TextureFormat::BC3RGBAUnorm;
+            case wgpu::TextureFormat::BC7RGBAUnorm:
+                return wgpu::TextureFormat::BC7RGBAUnormSrgb;
+            case wgpu::TextureFormat::BC7RGBAUnormSrgb:
+                return wgpu::TextureFormat::BC7RGBAUnorm;
+            case wgpu::TextureFormat::ETC2RGB8Unorm:
+                return wgpu::TextureFormat::ETC2RGB8UnormSrgb;
+            case wgpu::TextureFormat::ETC2RGB8UnormSrgb:
+                return wgpu::TextureFormat::ETC2RGB8Unorm;
+            case wgpu::TextureFormat::ETC2RGB8A1Unorm:
+                return wgpu::TextureFormat::ETC2RGB8A1UnormSrgb;
+            case wgpu::TextureFormat::ETC2RGB8A1UnormSrgb:
+                return wgpu::TextureFormat::ETC2RGB8A1Unorm;
+            case wgpu::TextureFormat::ETC2RGBA8Unorm:
+                return wgpu::TextureFormat::ETC2RGBA8UnormSrgb;
+            case wgpu::TextureFormat::ETC2RGBA8UnormSrgb:
+                return wgpu::TextureFormat::ETC2RGBA8Unorm;
+            case wgpu::TextureFormat::ASTC4x4Unorm:
+                return wgpu::TextureFormat::ASTC4x4UnormSrgb;
+            case wgpu::TextureFormat::ASTC4x4UnormSrgb:
+                return wgpu::TextureFormat::ASTC4x4Unorm;
+            case wgpu::TextureFormat::ASTC5x4Unorm:
+                return wgpu::TextureFormat::ASTC5x4UnormSrgb;
+            case wgpu::TextureFormat::ASTC5x4UnormSrgb:
+                return wgpu::TextureFormat::ASTC5x4Unorm;
+            case wgpu::TextureFormat::ASTC5x5Unorm:
+                return wgpu::TextureFormat::ASTC5x5UnormSrgb;
+            case wgpu::TextureFormat::ASTC5x5UnormSrgb:
+                return wgpu::TextureFormat::ASTC5x5Unorm;
+            case wgpu::TextureFormat::ASTC6x5Unorm:
+                return wgpu::TextureFormat::ASTC6x5UnormSrgb;
+            case wgpu::TextureFormat::ASTC6x5UnormSrgb:
+                return wgpu::TextureFormat::ASTC6x5Unorm;
+            case wgpu::TextureFormat::ASTC6x6Unorm:
+                return wgpu::TextureFormat::ASTC6x6UnormSrgb;
+            case wgpu::TextureFormat::ASTC6x6UnormSrgb:
+                return wgpu::TextureFormat::ASTC6x6Unorm;
+            case wgpu::TextureFormat::ASTC8x5Unorm:
+                return wgpu::TextureFormat::ASTC8x5UnormSrgb;
+            case wgpu::TextureFormat::ASTC8x5UnormSrgb:
+                return wgpu::TextureFormat::ASTC8x5Unorm;
+            case wgpu::TextureFormat::ASTC8x6Unorm:
+                return wgpu::TextureFormat::ASTC8x6UnormSrgb;
+            case wgpu::TextureFormat::ASTC8x6UnormSrgb:
+                return wgpu::TextureFormat::ASTC8x6Unorm;
+            case wgpu::TextureFormat::ASTC8x8Unorm:
+                return wgpu::TextureFormat::ASTC8x8UnormSrgb;
+            case wgpu::TextureFormat::ASTC8x8UnormSrgb:
+                return wgpu::TextureFormat::ASTC8x8Unorm;
+            case wgpu::TextureFormat::ASTC10x5Unorm:
+                return wgpu::TextureFormat::ASTC10x5UnormSrgb;
+            case wgpu::TextureFormat::ASTC10x5UnormSrgb:
+                return wgpu::TextureFormat::ASTC10x5Unorm;
+            case wgpu::TextureFormat::ASTC10x6Unorm:
+                return wgpu::TextureFormat::ASTC10x6UnormSrgb;
+            case wgpu::TextureFormat::ASTC10x6UnormSrgb:
+                return wgpu::TextureFormat::ASTC10x6Unorm;
+            case wgpu::TextureFormat::ASTC10x8Unorm:
+                return wgpu::TextureFormat::ASTC10x8UnormSrgb;
+            case wgpu::TextureFormat::ASTC10x8UnormSrgb:
+                return wgpu::TextureFormat::ASTC10x8Unorm;
+            case wgpu::TextureFormat::ASTC10x10Unorm:
+                return wgpu::TextureFormat::ASTC10x10UnormSrgb;
+            case wgpu::TextureFormat::ASTC10x10UnormSrgb:
+                return wgpu::TextureFormat::ASTC10x10Unorm;
+            case wgpu::TextureFormat::ASTC12x10Unorm:
+                return wgpu::TextureFormat::ASTC12x10UnormSrgb;
+            case wgpu::TextureFormat::ASTC12x10UnormSrgb:
+                return wgpu::TextureFormat::ASTC12x10Unorm;
+            case wgpu::TextureFormat::ASTC12x12Unorm:
+                return wgpu::TextureFormat::ASTC12x12UnormSrgb;
+            case wgpu::TextureFormat::ASTC12x12UnormSrgb:
+                return wgpu::TextureFormat::ASTC12x12Unorm;
+            default:
+                UNREACHABLE();
+        }
+    }
 };
 
 // Tests to verify that bufferOffset must be a multiple of the compressed texture blocks in bytes
@@ -2358,6 +2481,48 @@
     }
 }
 
+// Test copying between textures that have srgb compatible texture formats;
+TEST_F(CopyCommandTest_CompressedTextureFormats, SrgbFormatCompatibility) {
+    constexpr std::array<wgpu::TextureFormat, 42> srcFormats = {
+        wgpu::TextureFormat::BC1RGBAUnorm,    wgpu::TextureFormat::BC1RGBAUnormSrgb,
+        wgpu::TextureFormat::BC2RGBAUnorm,    wgpu::TextureFormat::BC2RGBAUnormSrgb,
+        wgpu::TextureFormat::BC3RGBAUnorm,    wgpu::TextureFormat::BC3RGBAUnormSrgb,
+        wgpu::TextureFormat::BC7RGBAUnorm,    wgpu::TextureFormat::BC7RGBAUnormSrgb,
+        wgpu::TextureFormat::ETC2RGB8Unorm,   wgpu::TextureFormat::ETC2RGB8UnormSrgb,
+        wgpu::TextureFormat::ETC2RGB8A1Unorm, wgpu::TextureFormat::ETC2RGB8A1UnormSrgb,
+        wgpu::TextureFormat::ETC2RGBA8Unorm,  wgpu::TextureFormat::ETC2RGBA8UnormSrgb,
+        wgpu::TextureFormat::ASTC4x4Unorm,    wgpu::TextureFormat::ASTC4x4UnormSrgb,
+        wgpu::TextureFormat::ASTC5x4Unorm,    wgpu::TextureFormat::ASTC5x4UnormSrgb,
+        wgpu::TextureFormat::ASTC5x5Unorm,    wgpu::TextureFormat::ASTC5x5UnormSrgb,
+        wgpu::TextureFormat::ASTC6x5Unorm,    wgpu::TextureFormat::ASTC6x5UnormSrgb,
+        wgpu::TextureFormat::ASTC6x6Unorm,    wgpu::TextureFormat::ASTC6x6UnormSrgb,
+        wgpu::TextureFormat::ASTC8x5Unorm,    wgpu::TextureFormat::ASTC8x5UnormSrgb,
+        wgpu::TextureFormat::ASTC8x6Unorm,    wgpu::TextureFormat::ASTC8x6UnormSrgb,
+        wgpu::TextureFormat::ASTC8x8Unorm,    wgpu::TextureFormat::ASTC8x8UnormSrgb,
+        wgpu::TextureFormat::ASTC10x5Unorm,   wgpu::TextureFormat::ASTC10x5UnormSrgb,
+        wgpu::TextureFormat::ASTC10x6Unorm,   wgpu::TextureFormat::ASTC10x6UnormSrgb,
+        wgpu::TextureFormat::ASTC10x8Unorm,   wgpu::TextureFormat::ASTC10x8UnormSrgb,
+        wgpu::TextureFormat::ASTC10x10Unorm,  wgpu::TextureFormat::ASTC10x10UnormSrgb,
+        wgpu::TextureFormat::ASTC12x10Unorm,  wgpu::TextureFormat::ASTC12x10UnormSrgb,
+        wgpu::TextureFormat::ASTC12x12Unorm,  wgpu::TextureFormat::ASTC12x12UnormSrgb};
+
+    constexpr uint32_t kBlockPerDim = 2;
+    constexpr uint32_t kMipmapLevels = 1;
+    for (wgpu::TextureFormat srcFormat : srcFormats) {
+        uint32_t blockWidth = utils::GetTextureFormatBlockWidth(srcFormat);
+        uint32_t blockHeight = utils::GetTextureFormatBlockHeight(srcFormat);
+        uint32_t testWidth = blockWidth * kBlockPerDim;
+        uint32_t testHeight = blockHeight * kBlockPerDim;
+        wgpu::Texture texture = Create2DTexture(srcFormat, kMipmapLevels, testWidth, testHeight);
+        wgpu::Texture texture2 = Create2DTexture(GetCopyCompatibleFormat(srcFormat), kMipmapLevels,
+                                                 testWidth, testHeight);
+        wgpu::Extent3D extent3D = {testWidth, testHeight, 1};
+
+        TestBothT2TCopies(utils::Expectation::Success, texture, 0, {0, 0, 0}, texture2, 0,
+                          {0, 0, 0}, extent3D);
+    }
+}
+
 class CopyCommandTest_ClearBuffer : public CopyCommandTest {};
 
 TEST_F(CopyCommandTest_ClearBuffer, Success) {