Add checks to TextureUsage::Storage

This patch adds validations to the texture usage "Storage" when
creating a texture.
1. "Storage" usage cannot be used when SampleCount > 1
2. "Storage" usage can only be used with some texture formats. The
list of the formats can be found through the following link:
https://github.com/gpuweb/gpuweb/issues/513

BUG=dawn:267
TEST=dawn_unittests

Change-Id: Ifc7296d966ac0c600433948a63c3dd6a436c8d8b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/15040
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
diff --git a/src/dawn_native/Format.cpp b/src/dawn_native/Format.cpp
index c553784..d4c0e16 100644
--- a/src/dawn_native/Format.cpp
+++ b/src/dawn_native/Format.cpp
@@ -118,12 +118,14 @@
         };
 
         auto AddColorFormat = [&AddFormat](wgpu::TextureFormat format, bool renderable,
-                                           uint32_t byteSize, Type type) {
+                                           bool supportsStorageUsage, uint32_t byteSize,
+                                           Type type) {
             Format internalFormat;
             internalFormat.format = format;
             internalFormat.isRenderable = renderable;
             internalFormat.isCompressed = false;
             internalFormat.isSupported = true;
+            internalFormat.supportsStorageUsage = supportsStorageUsage;
             internalFormat.aspect = Aspect::Color;
             internalFormat.type = type;
             internalFormat.blockByteSize = byteSize;
@@ -139,6 +141,7 @@
             internalFormat.isRenderable = true;
             internalFormat.isCompressed = false;
             internalFormat.isSupported = true;
+            internalFormat.supportsStorageUsage = false;
             internalFormat.aspect = aspect;
             internalFormat.type = Type::Other;
             internalFormat.blockByteSize = byteSize;
@@ -154,6 +157,7 @@
             internalFormat.isRenderable = false;
             internalFormat.isCompressed = true;
             internalFormat.isSupported = isSupported;
+            internalFormat.supportsStorageUsage = false;
             internalFormat.aspect = Aspect::Color;
             internalFormat.type = Type::Float;
             internalFormat.blockByteSize = byteSize;
@@ -165,50 +169,50 @@
         // clang-format off
 
         // 1 byte color formats
-        AddColorFormat(wgpu::TextureFormat::R8Unorm, true, 1, Type::Float);
-        AddColorFormat(wgpu::TextureFormat::R8Snorm, false, 1, Type::Float);
-        AddColorFormat(wgpu::TextureFormat::R8Uint, true, 1, Type::Uint);
-        AddColorFormat(wgpu::TextureFormat::R8Sint, true, 1, Type::Sint);
+        AddColorFormat(wgpu::TextureFormat::R8Unorm, true, false, 1, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::R8Snorm, false, false, 1, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::R8Uint, true, false, 1, Type::Uint);
+        AddColorFormat(wgpu::TextureFormat::R8Sint, true, false, 1, Type::Sint);
 
         // 2 bytes color formats
-        AddColorFormat(wgpu::TextureFormat::R16Uint, true, 2, Type::Uint);
-        AddColorFormat(wgpu::TextureFormat::R16Sint, true, 2, Type::Sint);
-        AddColorFormat(wgpu::TextureFormat::R16Float, true, 2, Type::Float);
-        AddColorFormat(wgpu::TextureFormat::RG8Unorm, true, 2, Type::Float);
-        AddColorFormat(wgpu::TextureFormat::RG8Snorm, false, 2, Type::Float);
-        AddColorFormat(wgpu::TextureFormat::RG8Uint, true, 2, Type::Uint);
-        AddColorFormat(wgpu::TextureFormat::RG8Sint, true, 2, Type::Sint);
+        AddColorFormat(wgpu::TextureFormat::R16Uint, true, false, 2, Type::Uint);
+        AddColorFormat(wgpu::TextureFormat::R16Sint, true, false, 2, Type::Sint);
+        AddColorFormat(wgpu::TextureFormat::R16Float, true, false, 2, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::RG8Unorm, true, false, 2, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::RG8Snorm, false, false, 2, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::RG8Uint, true, false, 2, Type::Uint);
+        AddColorFormat(wgpu::TextureFormat::RG8Sint, true, false, 2, Type::Sint);
 
         // 4 bytes color formats
-        AddColorFormat(wgpu::TextureFormat::R32Uint, true, 4, Type::Uint);
-        AddColorFormat(wgpu::TextureFormat::R32Sint, true, 4, Type::Sint);
-        AddColorFormat(wgpu::TextureFormat::R32Float, true, 4, Type::Float);
-        AddColorFormat(wgpu::TextureFormat::RG16Uint, true, 4, Type::Uint);
-        AddColorFormat(wgpu::TextureFormat::RG16Sint, true, 4, Type::Sint);
-        AddColorFormat(wgpu::TextureFormat::RG16Float, true, 4, Type::Float);
-        AddColorFormat(wgpu::TextureFormat::RGBA8Unorm, true, 4, Type::Float);
-        AddColorFormat(wgpu::TextureFormat::RGBA8UnormSrgb, true, 4, Type::Float);
-        AddColorFormat(wgpu::TextureFormat::RGBA8Snorm, false, 4, Type::Float);
-        AddColorFormat(wgpu::TextureFormat::RGBA8Uint, true, 4, Type::Uint);
-        AddColorFormat(wgpu::TextureFormat::RGBA8Sint, true, 4, Type::Sint);
-        AddColorFormat(wgpu::TextureFormat::BGRA8Unorm, true, 4, Type::Float);
-        AddColorFormat(wgpu::TextureFormat::BGRA8UnormSrgb, true, 4, Type::Float);
-        AddColorFormat(wgpu::TextureFormat::RGB10A2Unorm, true, 4, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::R32Uint, true, true, 4, Type::Uint);
+        AddColorFormat(wgpu::TextureFormat::R32Sint, true, true, 4, Type::Sint);
+        AddColorFormat(wgpu::TextureFormat::R32Float, true, true, 4, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::RG16Uint, true, false, 4, Type::Uint);
+        AddColorFormat(wgpu::TextureFormat::RG16Sint, true, false, 4, Type::Sint);
+        AddColorFormat(wgpu::TextureFormat::RG16Float, true, false, 4, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::RGBA8Unorm, true, true, 4, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::RGBA8UnormSrgb, true, false, 4, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::RGBA8Snorm, false, true, 4, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::RGBA8Uint, true, true, 4, Type::Uint);
+        AddColorFormat(wgpu::TextureFormat::RGBA8Sint, true, true, 4, Type::Sint);
+        AddColorFormat(wgpu::TextureFormat::BGRA8Unorm, true, false, 4, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::BGRA8UnormSrgb, true, false, 4, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::RGB10A2Unorm, true, false, 4, Type::Float);
 
-        AddColorFormat(wgpu::TextureFormat::RG11B10Float, false, 4, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::RG11B10Float, false, false, 4, Type::Float);
 
         // 8 bytes color formats
-        AddColorFormat(wgpu::TextureFormat::RG32Uint, true, 8, Type::Uint);
-        AddColorFormat(wgpu::TextureFormat::RG32Sint, true, 8, Type::Sint);
-        AddColorFormat(wgpu::TextureFormat::RG32Float, true, 8, Type::Float);
-        AddColorFormat(wgpu::TextureFormat::RGBA16Uint, true, 8, Type::Uint);
-        AddColorFormat(wgpu::TextureFormat::RGBA16Sint, true, 8, Type::Sint);
-        AddColorFormat(wgpu::TextureFormat::RGBA16Float, true, 8, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::RG32Uint, true, true, 8, Type::Uint);
+        AddColorFormat(wgpu::TextureFormat::RG32Sint, true, true, 8, Type::Sint);
+        AddColorFormat(wgpu::TextureFormat::RG32Float, true, true, 8, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::RGBA16Uint, true, true, 8, Type::Uint);
+        AddColorFormat(wgpu::TextureFormat::RGBA16Sint, true, true, 8, Type::Sint);
+        AddColorFormat(wgpu::TextureFormat::RGBA16Float, true, true, 8, Type::Float);
 
         // 16 bytes color formats
-        AddColorFormat(wgpu::TextureFormat::RGBA32Uint, true, 16, Type::Uint);
-        AddColorFormat(wgpu::TextureFormat::RGBA32Sint, true, 16, Type::Sint);
-        AddColorFormat(wgpu::TextureFormat::RGBA32Float, true, 16, Type::Float);
+        AddColorFormat(wgpu::TextureFormat::RGBA32Uint, true, true, 16, Type::Uint);
+        AddColorFormat(wgpu::TextureFormat::RGBA32Sint, true, true, 16, Type::Sint);
+        AddColorFormat(wgpu::TextureFormat::RGBA32Float, true, true, 16, Type::Float);
 
         // Depth stencil formats
         AddDepthStencilFormat(wgpu::TextureFormat::Depth32Float, Aspect::Depth, 4);
diff --git a/src/dawn_native/Format.h b/src/dawn_native/Format.h
index f1e4fbb..3d2c48a 100644
--- a/src/dawn_native/Format.h
+++ b/src/dawn_native/Format.h
@@ -50,6 +50,7 @@
         bool isCompressed;
         // A format can be known but not supported because it is part of a disabled extension.
         bool isSupported;
+        bool supportsStorageUsage;
         Aspect aspect;
         Type type;
 
diff --git a/src/dawn_native/Texture.cpp b/src/dawn_native/Texture.cpp
index e1b70b2..7b264c1 100644
--- a/src/dawn_native/Texture.cpp
+++ b/src/dawn_native/Texture.cpp
@@ -108,6 +108,11 @@
                     return DAWN_VALIDATION_ERROR(
                         "The sample counts of the textures in BC formats must be 1.");
                 }
+
+                if (descriptor->usage & wgpu::TextureUsage::Storage) {
+                    return DAWN_VALIDATION_ERROR(
+                        "The sample counts of the storage textures must be 1.");
+                }
             }
 
             return {};
@@ -184,8 +189,9 @@
                     "Non-renderable format used with OutputAttachment usage");
             }
 
-            if (descriptor->usage & wgpu::TextureUsage::Storage) {
-                return DAWN_VALIDATION_ERROR("storage textures aren't supported (yet)");
+            if (!format->supportsStorageUsage &&
+                (descriptor->usage & wgpu::TextureUsage::Storage)) {
+                return DAWN_VALIDATION_ERROR("Format cannot be used in storage textures");
             }
 
             return {};
diff --git a/src/tests/unittests/validation/TextureValidationTests.cpp b/src/tests/unittests/validation/TextureValidationTests.cpp
index 5e9cafa..4a996fc 100644
--- a/src/tests/unittests/validation/TextureValidationTests.cpp
+++ b/src/tests/unittests/validation/TextureValidationTests.cpp
@@ -93,6 +93,15 @@
 
         ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
     }
+
+    // It is an error to set TextureUsage::Storage when sampleCount > 1.
+    {
+        wgpu::TextureDescriptor descriptor = defaultDescriptor;
+        descriptor.sampleCount = 4;
+        descriptor.usage |= wgpu::TextureUsage::Storage;
+
+        ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
+    }
 }
 
 // Test the validation of the mip level count
@@ -329,6 +338,47 @@
     }
 }
 
+// Test it is an error to create a Storage texture with any format that doesn't support
+// TextureUsage::Storage texture usages.
+TEST_F(TextureValidationTest, TextureFormatNotSupportTextureUsageStorage) {
+    wgpu::TextureDescriptor descriptor;
+    descriptor.size = {1, 1, 1};
+    descriptor.usage = wgpu::TextureUsage::Storage;
+
+    wgpu::TextureFormat kSupportedFormatsWithStorageUsage[] = {
+        wgpu::TextureFormat::R32Uint,     wgpu::TextureFormat::R32Sint,
+        wgpu::TextureFormat::R32Uint,     wgpu::TextureFormat::RGBA8Unorm,
+        wgpu::TextureFormat::RGBA8Snorm,  wgpu::TextureFormat::RGBA8Uint,
+        wgpu::TextureFormat::RGBA8Sint,   wgpu::TextureFormat::RG32Uint,
+        wgpu::TextureFormat::RG32Sint,    wgpu::TextureFormat::RG32Float,
+        wgpu::TextureFormat::RGBA16Uint,  wgpu::TextureFormat::RGBA16Sint,
+        wgpu::TextureFormat::RGBA16Float, wgpu::TextureFormat::RGBA32Uint,
+        wgpu::TextureFormat::RGBA32Sint,  wgpu::TextureFormat::RGBA32Float};
+    for (wgpu::TextureFormat format : kSupportedFormatsWithStorageUsage) {
+        descriptor.format = format;
+        device.CreateTexture(&descriptor);
+    }
+
+    wgpu::TextureFormat kUnsupportedFormatsWithStorageUsage[] = {
+        wgpu::TextureFormat::R8Unorm,        wgpu::TextureFormat::R8Snorm,
+        wgpu::TextureFormat::R8Uint,         wgpu::TextureFormat::R8Sint,
+        wgpu::TextureFormat::R16Uint,        wgpu::TextureFormat::R16Sint,
+        wgpu::TextureFormat::R16Float,       wgpu::TextureFormat::RG8Unorm,
+        wgpu::TextureFormat::RG8Snorm,       wgpu::TextureFormat::RG8Uint,
+        wgpu::TextureFormat::RG8Sint,        wgpu::TextureFormat::RG16Uint,
+        wgpu::TextureFormat::RG16Sint,       wgpu::TextureFormat::RG16Float,
+        wgpu::TextureFormat::RGBA8UnormSrgb, wgpu::TextureFormat::BGRA8Unorm,
+        wgpu::TextureFormat::BGRA8UnormSrgb, wgpu::TextureFormat::RGB10A2Unorm,
+        wgpu::TextureFormat::RG11B10Float,
+
+        wgpu::TextureFormat::Depth24Plus,    wgpu::TextureFormat::Depth24PlusStencil8,
+        wgpu::TextureFormat::Depth32Float};
+    for (wgpu::TextureFormat format : kUnsupportedFormatsWithStorageUsage) {
+        descriptor.format = format;
+        ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
+    }
+}
+
 // Test it is an error to create a texture with format "Undefined".
 TEST_F(TextureValidationTest, TextureFormatUndefined) {
     wgpu::TextureDescriptor descriptor = CreateDefaultTextureDescriptor();