Add validation on the creation of 4x Multisampled textures

This patch adds validations on the creation of the multisampled textures
with sampleCount == 4. The validations include:
1. Only accept 1 or 4 as valid value of sampleCount.
2. According to Vulkan SPEC, when sampleCount > 1:
- The value of mipLevelCount can only be 1
- We cannot create cube map or cube map array texture views on this
  texture.

BUG=dawn:56
TEST=dawn_unittests

Change-Id: Iac7cbe7cd7af16216b9185afd37a80eef0712f6b
Reviewed-on: https://dawn-review.googlesource.com/c/5160
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn_native/Texture.cpp b/src/dawn_native/Texture.cpp
index a97536a..5d21ad6 100644
--- a/src/dawn_native/Texture.cpp
+++ b/src/dawn_native/Texture.cpp
@@ -47,6 +47,22 @@
             }
         }
 
+        bool IsTextureViewDimensionCompatibleWithTextureSampleCount(
+            dawn::TextureViewDimension textureViewDimension,
+            const uint32_t sampleCount) {
+            switch (textureViewDimension) {
+                case dawn::TextureViewDimension::Cube:
+                case dawn::TextureViewDimension::CubeArray:
+                    return sampleCount == 1;
+                case dawn::TextureViewDimension::e2D:
+                case dawn::TextureViewDimension::e2DArray:
+                    return true;
+                default:
+                    UNREACHABLE();
+                    return false;
+            }
+        }
+
         // TODO(jiawei.shao@intel.com): support validation on all texture view dimensions
         bool IsArrayLayerValidForTextureViewDimension(
             dawn::TextureViewDimension textureViewDimension,
@@ -82,10 +98,20 @@
             }
         }
 
-        // TODO(jiawei.shao@intel.com): support multisampled textures
-        MaybeError ValidateSampleCount(uint32_t sampleCount) {
-            if (sampleCount != 1) {
-                return DAWN_VALIDATION_ERROR("The sample count of the texture is not supported.");
+        // TODO(jiawei.shao@intel.com): support more sample count.
+        MaybeError ValidateSampleCount(const TextureDescriptor* descriptor) {
+            switch (descriptor->sampleCount) {
+                case 1:
+                    break;
+                case 4:
+                    if (descriptor->mipLevelCount > 1) {
+                        return DAWN_VALIDATION_ERROR(
+                            "The mipmap level count of a multisampled texture must be 1.");
+                    }
+                    break;
+                default:
+                    return DAWN_VALIDATION_ERROR(
+                        "The sample count of the texture is not supported.");
             }
 
             return {};
@@ -107,6 +133,13 @@
                     "original texture");
             }
 
+            if (!IsTextureViewDimensionCompatibleWithTextureSampleCount(
+                    descriptor->dimension, texture->GetSampleCount())) {
+                return DAWN_VALIDATION_ERROR(
+                    "The dimension of the texture view is not compatible with the sample count of "
+                    "the original texture");
+            }
+
             if (!IsTextureSizeValidForTextureViewDimension(descriptor->dimension,
                                                            texture->GetSize())) {
                 return DAWN_VALIDATION_ERROR(
@@ -151,7 +184,7 @@
         DAWN_TRY(ValidateTextureUsageBit(descriptor->usage));
         DAWN_TRY(ValidateTextureDimension(descriptor->dimension));
         DAWN_TRY(ValidateTextureFormat(descriptor->format));
-        DAWN_TRY(ValidateSampleCount(descriptor->sampleCount));
+        DAWN_TRY(ValidateSampleCount(descriptor));
 
         // TODO(jiawei.shao@intel.com): check stuff based on the dimension
         if (descriptor->size.width == 0 || descriptor->size.height == 0 ||
@@ -290,6 +323,7 @@
           mSize(descriptor->size),
           mArrayLayerCount(descriptor->arrayLayerCount),
           mMipLevelCount(descriptor->mipLevelCount),
+          mSampleCount(descriptor->sampleCount),
           mUsage(descriptor->usage) {
     }
 
@@ -322,6 +356,10 @@
         ASSERT(!IsError());
         return mMipLevelCount;
     }
+    uint32_t TextureBase::GetSampleCount() const {
+        ASSERT(!IsError());
+        return mSampleCount;
+    }
     dawn::TextureUsageBit TextureBase::GetUsage() const {
         ASSERT(!IsError());
         return mUsage;
@@ -332,6 +370,11 @@
         return {};
     }
 
+    bool TextureBase::IsMultisampledTexture() const {
+        ASSERT(!IsError());
+        return mSampleCount > 1;
+    }
+
     TextureViewBase* TextureBase::CreateDefaultTextureView() {
         TextureViewDescriptor descriptor = {};
 
diff --git a/src/dawn_native/Texture.h b/src/dawn_native/Texture.h
index d7dd6d3..0c0222a 100644
--- a/src/dawn_native/Texture.h
+++ b/src/dawn_native/Texture.h
@@ -54,10 +54,13 @@
         const Extent3D& GetSize() const;
         uint32_t GetArrayLayers() const;
         uint32_t GetNumMipLevels() const;
+        uint32_t GetSampleCount() const;
         dawn::TextureUsageBit GetUsage() const;
 
         MaybeError ValidateCanUseInSubmitNow() const;
 
+        bool IsMultisampledTexture() const;
+
         // Dawn API
         TextureViewBase* CreateDefaultTextureView();
         TextureViewBase* CreateTextureView(const TextureViewDescriptor* descriptor);
@@ -70,6 +73,7 @@
         Extent3D mSize;
         uint32_t mArrayLayerCount;
         uint32_t mMipLevelCount;
+        uint32_t mSampleCount;
         dawn::TextureUsageBit mUsage = dawn::TextureUsageBit::None;
     };
 
diff --git a/src/tests/unittests/validation/TextureValidationTests.cpp b/src/tests/unittests/validation/TextureValidationTests.cpp
index 28743c3..dce5af1 100644
--- a/src/tests/unittests/validation/TextureValidationTests.cpp
+++ b/src/tests/unittests/validation/TextureValidationTests.cpp
@@ -56,6 +56,14 @@
         device.CreateTexture(&descriptor);
     }
 
+    // sampleCount == 4 is allowed.
+    {
+        dawn::TextureDescriptor descriptor = defaultDescriptor;
+        descriptor.sampleCount = 4;
+
+        device.CreateTexture(&descriptor);
+    }
+
     // It is an error to create a texture with an invalid sampleCount.
     {
         dawn::TextureDescriptor descriptor = defaultDescriptor;
@@ -63,5 +71,14 @@
 
         ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
     }
+
+    // It is an error to create a multisampled texture with mipLevelCount > 1.
+    {
+        dawn::TextureDescriptor descriptor = defaultDescriptor;
+        descriptor.sampleCount = 4;
+        descriptor.mipLevelCount = 2;
+
+        ASSERT_DEVICE_ERROR(device.CreateTexture(&descriptor));
+    }
 }
 }  // namespace
diff --git a/src/tests/unittests/validation/TextureViewValidationTests.cpp b/src/tests/unittests/validation/TextureViewValidationTests.cpp
index 9d8c31a..b330600 100644
--- a/src/tests/unittests/validation/TextureViewValidationTests.cpp
+++ b/src/tests/unittests/validation/TextureViewValidationTests.cpp
@@ -28,16 +28,18 @@
 dawn::Texture Create2DArrayTexture(dawn::Device& device,
                                    uint32_t arrayLayerCount,
                                    uint32_t width = kWidth,
-                                   uint32_t height = kHeight) {
+                                   uint32_t height = kHeight,
+                                   uint32_t mipLevelCount = kDefaultMipLevels,
+                                   uint32_t sampleCount = 1) {
     dawn::TextureDescriptor descriptor;
     descriptor.dimension = dawn::TextureDimension::e2D;
     descriptor.size.width = width;
     descriptor.size.height = height;
     descriptor.size.depth = 1;
     descriptor.arrayLayerCount = arrayLayerCount;
-    descriptor.sampleCount = 1;
+    descriptor.sampleCount = sampleCount;
     descriptor.format = kDefaultTextureFormat;
-    descriptor.mipLevelCount = kDefaultMipLevels;
+    descriptor.mipLevelCount = mipLevelCount;
     descriptor.usage = dawn::TextureUsageBit::Sampled;
     return device.CreateTexture(&descriptor);
 }
@@ -198,6 +200,30 @@
         descriptor.arrayLayerCount = 12;
         ASSERT_DEVICE_ERROR(nonSquareTexture.CreateTextureView(&descriptor));
     }
+
+    // It is an error to create a cube map texture view on a multisampled texture.
+    {
+        constexpr uint32_t kSampleCount = 4;
+        dawn::Texture multisampledTexture = Create2DArrayTexture(device, kDefaultArrayLayers,
+                                                                 kWidth, kHeight, 1, kSampleCount);
+        dawn::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor;
+        descriptor.dimension = dawn::TextureViewDimension::Cube;
+        descriptor.arrayLayerCount = 6;
+        descriptor.mipLevelCount = 1;
+        ASSERT_DEVICE_ERROR(multisampledTexture.CreateTextureView(&descriptor));
+    }
+
+    // It is an error to create a cube map array texture view on a multisampled texture.
+    {
+        constexpr uint32_t kSampleCount = 4;
+        dawn::Texture multisampledTexture = Create2DArrayTexture(device, kDefaultArrayLayers,
+                                                                 kWidth, kHeight, 1, kSampleCount);
+        dawn::TextureViewDescriptor descriptor = base2DArrayTextureViewDescriptor;
+        descriptor.dimension = dawn::TextureViewDimension::CubeArray;
+        descriptor.arrayLayerCount = 12;
+        descriptor.mipLevelCount = 1;
+        ASSERT_DEVICE_ERROR(multisampledTexture.CreateTextureView(&descriptor));
+    }
 }
 
 // Test the format compatibility rules when creating a texture view.