Validate depthSlice for a 3D texture used in multiple color attachments

- Make depthSlice as an optional property in RenderPassColorAttachment
  and must be set for 3D attachments.
- depthSlice has been landed into the spec. We can remove the dawn only
  tag.
- Add validation for attachments' same subresource used multiple times
  in a render pass.

Bug: dawn:1020
Change-Id: Ibb5f66188dba4096f9186e53462bb36c300b1f56
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/155884
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Hao Li <hao.x.li@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/dawn.json b/dawn.json
index 4a51c93..7a80ae8 100644
--- a/dawn.json
+++ b/dawn.json
@@ -2545,7 +2545,7 @@
         "extensible": "in",
         "members": [
             {"name": "view", "type": "texture view", "optional": true},
-            {"name": "depth slice", "type": "uint32_t", "default": "0", "tags": ["dawn"]},
+            {"name": "depth slice", "type": "uint32_t", "default": "WGPU_DEPTH_SLICE_UNDEFINED"},
             {"name": "resolve target", "type": "texture view", "optional": true},
             {"name": "load op", "type": "load op"},
             {"name": "store op", "type": "store op"},
@@ -3675,6 +3675,11 @@
         "type": "uint32_t",
         "value":  "UINT32_MAX"
     },
+    "depth slice undefined" : {
+        "category": "constant",
+        "type": "uint32_t",
+        "value":  "(0xffffffffUL)"
+    },
     "query set index undefined" : {
         "category": "constant",
         "type": "uint32_t",
diff --git a/src/dawn/native/CommandEncoder.cpp b/src/dawn/native/CommandEncoder.cpp
index 128bbea..0bd5859 100644
--- a/src/dawn/native/CommandEncoder.cpp
+++ b/src/dawn/native/CommandEncoder.cpp
@@ -27,6 +27,7 @@
 
 #include "dawn/native/CommandEncoder.h"
 
+#include <string_view>
 #include <unordered_set>
 #include <utility>
 #include <vector>
@@ -62,6 +63,159 @@
 
 namespace {
 
+// Record the subresource range of a attachment used in render pass for checking overlaps.
+struct RecordedAttachment {
+    const TextureBase* texture;
+    uint32_t mipLevel;
+    // For 3d color attachment, it's the attachment's depthSlice.
+    uint32_t depthOrArrayLayer;
+
+    bool operator==(const RecordedAttachment& other) const {
+        return ((other.texture == texture) && (other.mipLevel == mipLevel) &&
+                (other.depthOrArrayLayer == depthOrArrayLayer));
+    }
+};
+
+enum class AttachmentType : uint8_t {
+    ColorAttachment,
+    ResolveTarget,
+    DepthStencilAttachment,
+    StorageAttachment
+};
+
+std::string_view GetAttachmentTypeStr(AttachmentType type) {
+    switch (type) {
+        case AttachmentType::ColorAttachment:
+            return "color attachment";
+        case AttachmentType::ResolveTarget:
+            return "resolve target";
+        case AttachmentType::DepthStencilAttachment:
+            return "depth stencil attachment";
+        case AttachmentType::StorageAttachment:
+            return "storage attachment";
+    }
+    DAWN_UNREACHABLE();
+}
+
+// The width, height, sample count and subresource range need to be validated between all
+// attachments in the render pass. This class records all the states and validate them for each
+// attachment.
+class RenderPassValidationState final : public NonMovable {
+  public:
+    RenderPassValidationState() = default;
+    ~RenderPassValidationState() = default;
+
+    // Record the attachment in the render pass if it passes all validations:
+    // - the attachment has same with, height and sample count with other attachments
+    // - no overlaps with other attachments
+    // TODO(dawn:1020): Improve the error messages to include the index information of the
+    // attachment in the render pass descriptor.
+    MaybeError AddAttachment(const TextureViewBase* attachment,
+                             AttachmentType attachmentType,
+                             uint32_t depthSlice = WGPU_DEPTH_SLICE_UNDEFINED) {
+        if (attachment == nullptr) {
+            return {};
+        }
+
+        DAWN_ASSERT(attachment->GetLevelCount() == 1);
+        DAWN_ASSERT(attachment->GetLayerCount() == 1);
+
+        const std::string_view attachmentTypeStr = GetAttachmentTypeStr(attachmentType);
+
+        std::string_view implicitPrefixStr;
+        // Not need to validate the implicit sample count for the depth stencil attachment.
+        if (mImplicitSampleCount > 1 && attachmentType != AttachmentType::DepthStencilAttachment) {
+            DAWN_INVALID_IF(attachment->GetTexture()->GetSampleCount() != 1,
+                            "The %s %s sample count (%u) is not 1 when it has implicit "
+                            "sample count (%u).",
+                            attachmentTypeStr, attachment,
+                            attachment->GetTexture()->GetSampleCount(), mImplicitSampleCount);
+
+            implicitPrefixStr = "implicit ";
+        }
+
+        Extent3D attachmentSize = attachment->GetSingleSubresourceVirtualSize();
+
+        if (HasAttachment()) {
+            DAWN_INVALID_IF(attachmentSize.width != mWidth || attachmentSize.height != mHeight,
+                            "The %s %s size (width: %u, height: %u) does not match the size of the "
+                            "other attachments (width: %u, height: %u).",
+                            attachmentTypeStr, attachment, attachmentSize.width,
+                            attachmentSize.height, mWidth, mHeight);
+
+            // Skip the sampleCount validation for resolve target
+            DAWN_INVALID_IF(attachmentType != AttachmentType::ResolveTarget &&
+                                attachment->GetTexture()->GetSampleCount() != mSampleCount,
+                            "The %s %s %ssample count (%u) does not match the sample count of the "
+                            "other attachments (%u).",
+                            attachmentTypeStr, attachment, implicitPrefixStr,
+                            attachment->GetTexture()->GetSampleCount(), mSampleCount);
+        } else {
+            mWidth = attachmentSize.width;
+            mHeight = attachmentSize.height;
+            mSampleCount = mImplicitSampleCount > 1 ? mImplicitSampleCount
+                                                    : attachment->GetTexture()->GetSampleCount();
+            DAWN_ASSERT(mWidth != 0);
+            DAWN_ASSERT(mHeight != 0);
+            DAWN_ASSERT(mSampleCount != 0);
+        }
+
+        RecordedAttachment record;
+        record.texture = attachment->GetTexture();
+        record.mipLevel = attachment->GetBaseMipLevel();
+        if (attachment->GetDimension() == wgpu::TextureViewDimension::e3D) {
+            DAWN_ASSERT(attachment->GetBaseArrayLayer() == 0);
+            record.depthOrArrayLayer = depthSlice;
+        } else {
+            // TODO(dawn:1020): Assert depthSlice should be the 'WGPU_DEPTH_SLICE_UNDEFINED' value
+            // if the attachment is a non-3d texture view after it's initialized correctly in Blink.
+            // DAWN_ASSERT(depthSlice == WGPU_DEPTH_SLICE_UNDEFINED);
+            record.depthOrArrayLayer = attachment->GetBaseArrayLayer();
+        }
+
+        for (size_t i = 0; i < mRecords->size(); i++) {
+            DAWN_INVALID_IF(
+                mRecords[i] == record,
+                "The %s %s has read-write or write-write conflict with another attachment.",
+                attachmentTypeStr, attachment);
+        }
+
+        mRecords->push_back(record);
+
+        return {};
+    }
+
+    bool HasAttachment() const { return mRecords->size() != 0; }
+
+    bool IsValidState() const {
+        return ((mWidth > 0) && (mHeight > 0) && (mSampleCount > 0) &&
+                (mImplicitSampleCount == 0 || mImplicitSampleCount == mSampleCount));
+    }
+
+    uint32_t GetWidth() const { return mWidth; }
+
+    uint32_t GetHeight() const { return mHeight; }
+
+    uint32_t GetSampleCount() const { return mSampleCount; }
+
+    uint32_t GetImplicitSampleCount() const { return mImplicitSampleCount; }
+
+    void SetImplicitSampleCount(uint32_t implicitSampleCount) {
+        mImplicitSampleCount = implicitSampleCount;
+    }
+
+  private:
+    // The attachment's width, height and sample count.
+    uint32_t mWidth = 0;
+    uint32_t mHeight = 0;
+    uint32_t mSampleCount = 0;
+    // The implicit multisample count used by MSAA render to single sampled.
+    uint32_t mImplicitSampleCount = 0;
+
+    // The records of the attachments that were validated in render pass.
+    StackVector<RecordedAttachment, kMaxColorAttachments> mRecords;
+};
+
 MaybeError ValidateB2BCopyAlignment(uint64_t dataSize, uint64_t srcOffset, uint64_t dstOffset) {
     // Copy size must be a multiple of 4 bytes on macOS.
     DAWN_INVALID_IF(dataSize % 4 != 0, "Copy size (%u) is not a multiple of 4.", dataSize);
@@ -154,58 +308,6 @@
     return {};
 }
 
-MaybeError ValidateOrSetAttachmentSize(const TextureViewBase* attachment,
-                                       uint32_t* width,
-                                       uint32_t* height) {
-    Extent3D attachmentSize = attachment->GetSingleSubresourceVirtualSize();
-
-    if (*width == 0) {
-        DAWN_ASSERT(*height == 0);
-        *width = attachmentSize.width;
-        *height = attachmentSize.height;
-        DAWN_ASSERT(*width != 0 && *height != 0);
-    } else {
-        DAWN_INVALID_IF(*width != attachmentSize.width || *height != attachmentSize.height,
-                        "Attachment %s size (width: %u, height: %u) does not match the size of the "
-                        "other attachments (width: %u, height: %u).",
-                        attachment, attachmentSize.width, attachmentSize.height, *width, *height);
-    }
-
-    return {};
-}
-
-MaybeError ValidateOrSetColorAttachmentSampleCount(const TextureViewBase* colorAttachment,
-                                                   uint32_t implicitSampleCount,
-                                                   uint32_t* sampleCount) {
-    uint32_t attachmentSampleCount = 0;
-    std::string implicitPrefixStr;
-    if (implicitSampleCount > 1) {
-        DAWN_INVALID_IF(colorAttachment->GetTexture()->GetSampleCount() != 1,
-                        "Color attachment %s sample count (%u) is not 1 when it has implicit "
-                        "sample count (%u).",
-                        colorAttachment, colorAttachment->GetTexture()->GetSampleCount(),
-                        implicitSampleCount);
-
-        attachmentSampleCount = implicitSampleCount;
-        implicitPrefixStr = "implicit ";
-    } else {
-        attachmentSampleCount = colorAttachment->GetTexture()->GetSampleCount();
-    }
-
-    if (*sampleCount == 0) {
-        *sampleCount = attachmentSampleCount;
-        DAWN_ASSERT(*sampleCount != 0);
-    } else {
-        DAWN_INVALID_IF(
-            *sampleCount != attachmentSampleCount,
-            "Color attachment %s %ssample count (%u) does not match the sample count of the "
-            "other attachments (%u).",
-            colorAttachment, implicitPrefixStr, attachmentSampleCount, *sampleCount);
-    }
-
-    return {};
-}
-
 MaybeError ValidateResolveTarget(const DeviceBase* device,
                                  const RenderPassColorAttachment& colorAttachment,
                                  UsageValidationMode usageValidationMode) {
@@ -236,15 +338,6 @@
                     "The resolve target %s mip level count (%u) is not 1.", resolveTarget,
                     resolveTarget->GetLevelCount());
 
-    const Extent3D& colorTextureSize = attachment->GetSingleSubresourceVirtualSize();
-    const Extent3D& resolveTextureSize = resolveTarget->GetSingleSubresourceVirtualSize();
-    DAWN_INVALID_IF(colorTextureSize.width != resolveTextureSize.width ||
-                        colorTextureSize.height != resolveTextureSize.height,
-                    "The Resolve target %s size (width: %u, height: %u) does not match the color "
-                    "attachment %s size (width: %u, height: %u).",
-                    resolveTarget, resolveTextureSize.width, resolveTextureSize.height, attachment,
-                    colorTextureSize.width, colorTextureSize.height);
-
     wgpu::TextureFormat resolveTargetFormat = resolveTarget->GetFormat().format;
     DAWN_INVALID_IF(
         resolveTargetFormat != attachment->GetFormat().format,
@@ -261,20 +354,25 @@
 
 MaybeError ValidateColorAttachmentDepthSlice(const TextureViewBase* attachment,
                                              uint32_t depthSlice) {
-    if (attachment->GetDimension() == wgpu::TextureViewDimension::e3D) {
-        const Extent3D& attachmentSize = attachment->GetSingleSubresourceVirtualSize();
-
-        DAWN_INVALID_IF(depthSlice >= attachmentSize.depthOrArrayLayers,
-                        "The depth slice index (%u) of 3D %s used as attachment is >= the "
-                        "depthOrArrayLayers (%u) of its subresource at mip level (%u).",
-                        depthSlice, attachment, attachmentSize.depthOrArrayLayers,
-                        attachment->GetBaseMipLevel());
-    } else {
-        DAWN_INVALID_IF(depthSlice != 0,
-                        "The depth slice index (%u) of non-3D %s used as attachment is not 0.",
-                        depthSlice, attachment);
+    if (attachment->GetDimension() != wgpu::TextureViewDimension::e3D) {
+        // TODO(dawn:1020): Validate depthSlice must not set for non-3d attachments. The depthSlice
+        // from WebGPU is not the 'WGPU_DEPTH_SLICE_UNDEFINED' value if it's not defined, we need to
+        // initialize it in Blink first, otherwise it will always be validated as set.
+        return {};
     }
 
+    DAWN_INVALID_IF(depthSlice == WGPU_DEPTH_SLICE_UNDEFINED,
+                    "depthSlice (%u) must be set and must not be undefined value (%u) for a 3D "
+                    "attachment (%s).",
+                    depthSlice, WGPU_DEPTH_SLICE_UNDEFINED, attachment);
+
+    const Extent3D& attachmentSize = attachment->GetSingleSubresourceVirtualSize();
+    DAWN_INVALID_IF(depthSlice >= attachmentSize.depthOrArrayLayers,
+                    "depthSlice (%u) of the attachment (%s) is >= the "
+                    "depthOrArrayLayers (%u) of the attachment's subresource at mip level (%u).",
+                    depthSlice, attachment, attachmentSize.depthOrArrayLayers,
+                    attachment->GetBaseMipLevel());
+
     return {};
 }
 
@@ -317,11 +415,8 @@
 
 MaybeError ValidateRenderPassColorAttachment(DeviceBase* device,
                                              const RenderPassColorAttachment& colorAttachment,
-                                             uint32_t* width,
-                                             uint32_t* height,
-                                             uint32_t* sampleCount,
-                                             uint32_t* implicitSampleCount,
-                                             UsageValidationMode usageValidationMode) {
+                                             UsageValidationMode usageValidationMode,
+                                             RenderPassValidationState* validationState) {
     TextureViewBase* attachment = colorAttachment.view;
     if (attachment == nullptr) {
         return {};
@@ -336,7 +431,7 @@
     if (msaaRenderToSingleSampledDesc) {
         DAWN_TRY(ValidateColorAttachmentRenderToSingleSampled(device, colorAttachment,
                                                               msaaRenderToSingleSampledDesc));
-        *implicitSampleCount = msaaRenderToSingleSampledDesc->implicitSampleCount;
+        validationState->SetImplicitSampleCount(msaaRenderToSingleSampledDesc->implicitSampleCount);
         // Note: we don't need to check whether the implicit sample count of different attachments
         // are the same. That already is done by indirectly comparing the sample count in
         // ValidateOrSetColorAttachmentSampleCount.
@@ -376,29 +471,30 @@
                         "Color clear value (%s) contains a NaN.", &clearValue);
     }
 
-    DAWN_TRY(
-        ValidateOrSetColorAttachmentSampleCount(attachment, *implicitSampleCount, sampleCount));
+    DAWN_TRY(ValidateColorAttachmentDepthSlice(attachment, colorAttachment.depthSlice));
+    DAWN_TRY(ValidateAttachmentArrayLayersAndLevelCount(attachment));
 
-    if (*implicitSampleCount <= 1) {
+    DAWN_TRY(validationState->AddAttachment(attachment, AttachmentType::ColorAttachment,
+                                            colorAttachment.depthSlice));
+
+    if (validationState->GetImplicitSampleCount() <= 1) {
         // This step is skipped if implicitSampleCount > 1, because in that case, there shoudn't be
         // any explicit resolveTarget specified.
         DAWN_TRY(ValidateResolveTarget(device, colorAttachment, usageValidationMode));
+        // Add resolve target after adding color attachment to make sure there is already a color
+        // attachment for the comparation of with and height.
+        DAWN_TRY(validationState->AddAttachment(colorAttachment.resolveTarget,
+                                                AttachmentType::ResolveTarget));
     }
 
-    DAWN_TRY(ValidateColorAttachmentDepthSlice(attachment, colorAttachment.depthSlice));
-    DAWN_TRY(ValidateAttachmentArrayLayersAndLevelCount(attachment));
-    DAWN_TRY(ValidateOrSetAttachmentSize(attachment, width, height));
-
     return {};
 }
 
 MaybeError ValidateRenderPassDepthStencilAttachment(
     DeviceBase* device,
     const RenderPassDepthStencilAttachment* depthStencilAttachment,
-    uint32_t* width,
-    uint32_t* height,
-    uint32_t* sampleCount,
-    UsageValidationMode usageValidationMode) {
+    UsageValidationMode usageValidationMode,
+    RenderPassValidationState* validationState) {
     DAWN_ASSERT(depthStencilAttachment != nullptr);
 
     TextureViewBase* attachment = depthStencilAttachment->view;
@@ -489,33 +585,19 @@
                         depthStencilAttachment->depthClearValue, attachment);
     }
 
-    // *sampleCount == 0 must only happen when there is no color attachment. In that case we
-    // do not need to validate the sample count of the depth stencil attachment.
-    const uint32_t depthStencilSampleCount = attachment->GetTexture()->GetSampleCount();
-    if (*sampleCount != 0) {
-        DAWN_INVALID_IF(
-            depthStencilSampleCount != *sampleCount,
-            "The depth stencil attachment %s sample count (%u) does not match the sample "
-            "count of the other attachments (%u).",
-            attachment, depthStencilSampleCount, *sampleCount);
-    } else {
-        *sampleCount = depthStencilSampleCount;
-    }
-
     DAWN_TRY(ValidateAttachmentArrayLayersAndLevelCount(attachment));
-    DAWN_TRY(ValidateOrSetAttachmentSize(attachment, width, height));
+
+    DAWN_TRY(validationState->AddAttachment(attachment, AttachmentType::DepthStencilAttachment));
 
     return {};
 }
 
 MaybeError ValidateRenderPassPLS(DeviceBase* device,
                                  const RenderPassPixelLocalStorage* pls,
-                                 uint32_t* width,
-                                 uint32_t* height,
-                                 uint32_t* sampleCount,
-                                 uint32_t implicitSampleCount,
-                                 UsageValidationMode usageValidationMode) {
+                                 UsageValidationMode usageValidationMode,
+                                 RenderPassValidationState* validationState) {
     StackVector<StorageAttachmentInfoForValidation, 4> attachments;
+
     for (size_t i = 0; i < pls->storageAttachmentCount; i++) {
         const RenderPassStorageAttachment& attachment = pls->storageAttachments[i];
 
@@ -524,9 +606,6 @@
         DAWN_TRY(ValidateCanUseAs(attachment.storage->GetTexture(),
                                   wgpu::TextureUsage::StorageAttachment, usageValidationMode));
         DAWN_TRY(ValidateAttachmentArrayLayersAndLevelCount(attachment.storage));
-        DAWN_TRY(ValidateOrSetColorAttachmentSampleCount(attachment.storage, implicitSampleCount,
-                                                         sampleCount));
-        DAWN_TRY(ValidateOrSetAttachmentSize(attachment.storage, width, height));
 
         // Validate the load/storeOp and the clearValue.
         DAWN_TRY(ValidateLoadOp(attachment.loadOp));
@@ -544,6 +623,9 @@
                             &clearValue);
         }
 
+        DAWN_TRY(
+            validationState->AddAttachment(attachment.storage, AttachmentType::StorageAttachment));
+
         attachments->push_back({attachment.offset, attachment.storage->GetFormat().format});
     }
 
@@ -553,11 +635,8 @@
 
 MaybeError ValidateRenderPassDescriptor(DeviceBase* device,
                                         const RenderPassDescriptor* descriptor,
-                                        uint32_t* width,
-                                        uint32_t* height,
-                                        uint32_t* sampleCount,
-                                        uint32_t* implicitSampleCount,
-                                        UsageValidationMode usageValidationMode) {
+                                        UsageValidationMode usageValidationMode,
+                                        RenderPassValidationState* validationState) {
     DAWN_TRY(
         ValidateSTypes(descriptor->nextInChain, {{wgpu::SType::RenderPassDescriptorMaxDrawCount},
                                                  {wgpu::SType::RenderPassPixelLocalStorage}}));
@@ -568,15 +647,12 @@
         "Color attachment count (%u) exceeds the maximum number of color attachments (%u).",
         descriptor->colorAttachmentCount, maxColorAttachments);
 
-    bool anyColorAttachment = false;
     ColorAttachmentFormats colorAttachmentFormats;
     for (uint32_t i = 0; i < descriptor->colorAttachmentCount; ++i) {
-        DAWN_TRY_CONTEXT(ValidateRenderPassColorAttachment(
-                             device, descriptor->colorAttachments[i], width, height, sampleCount,
-                             implicitSampleCount, usageValidationMode),
+        DAWN_TRY_CONTEXT(ValidateRenderPassColorAttachment(device, descriptor->colorAttachments[i],
+                                                           usageValidationMode, validationState),
                          "validating colorAttachments[%u].", i);
         if (descriptor->colorAttachments[i].view) {
-            anyColorAttachment = true;
             colorAttachmentFormats->push_back(&descriptor->colorAttachments[i].view->GetFormat());
         }
     }
@@ -584,10 +660,10 @@
                      "validating color attachment bytes per sample.");
 
     if (descriptor->depthStencilAttachment != nullptr) {
-        DAWN_TRY_CONTEXT(ValidateRenderPassDepthStencilAttachment(
-                             device, descriptor->depthStencilAttachment, width, height, sampleCount,
-                             usageValidationMode),
-                         "validating depthStencilAttachment.");
+        DAWN_TRY_CONTEXT(
+            ValidateRenderPassDepthStencilAttachment(device, descriptor->depthStencilAttachment,
+                                                     usageValidationMode, validationState),
+            "validating depthStencilAttachment.");
     }
 
     if (descriptor->occlusionQuerySet != nullptr) {
@@ -610,26 +686,21 @@
     }
 
     // Validation for any pixel local storage.
-    size_t storageAttachmentCount = 0;
     const RenderPassPixelLocalStorage* pls = nullptr;
     FindInChain(descriptor->nextInChain, &pls);
     if (pls != nullptr) {
-        storageAttachmentCount = pls->storageAttachmentCount;
-        DAWN_TRY(ValidateRenderPassPLS(device, pls, width, height, sampleCount,
-                                       *implicitSampleCount, usageValidationMode));
+        DAWN_TRY(ValidateRenderPassPLS(device, pls, usageValidationMode, validationState));
     }
 
-    DAWN_INVALID_IF(!anyColorAttachment && descriptor->depthStencilAttachment == nullptr &&
-                        storageAttachmentCount == 0,
-                    "Render pass has no attachments.");
+    DAWN_INVALID_IF(!validationState->HasAttachment(), "Render pass has no attachments.");
 
-    if (*implicitSampleCount > 1) {
+    if (validationState->GetImplicitSampleCount() > 1) {
         // TODO(dawn:1710): support multiple attachments.
         DAWN_INVALID_IF(
             descriptor->colorAttachmentCount != 1,
             "colorAttachmentCount (%u) is not supported when the render pass has implicit sample "
             "count (%u). (Currently) colorAttachmentCount = 1 is supported.",
-            descriptor->colorAttachmentCount, *implicitSampleCount);
+            descriptor->colorAttachmentCount, validationState->GetImplicitSampleCount());
         // TODO(dawn:1704): Consider supporting MSAARenderToSingleSampled + PLS
         DAWN_INVALID_IF(pls != nullptr,
                         "For now PLS is invalid to use with MSAARenderToSingleSampled.");
@@ -998,25 +1069,20 @@
 
     RenderPassResourceUsageTracker usageTracker;
 
-    uint32_t width = 0;
-    uint32_t height = 0;
-    uint32_t sampleCount = 0;
-    // The implicit multisample count used by MSAA render to single sampled.
-    uint32_t implicitSampleCount = 0;
     bool depthReadOnly = false;
     bool stencilReadOnly = false;
     Ref<AttachmentState> attachmentState;
+    RenderPassValidationState validationState;
 
     std::function<void()> passEndCallback = nullptr;
 
     bool success = mEncodingContext.TryEncode(
         this,
         [&](CommandAllocator* allocator) -> MaybeError {
-            DAWN_TRY(ValidateRenderPassDescriptor(device, descriptor, &width, &height, &sampleCount,
-                                                  &implicitSampleCount, mUsageValidationMode));
+            DAWN_TRY(ValidateRenderPassDescriptor(device, descriptor, mUsageValidationMode,
+                                                  &validationState));
 
-            DAWN_ASSERT(width > 0 && height > 0 && sampleCount > 0 &&
-                        (implicitSampleCount == 0 || implicitSampleCount == sampleCount));
+            DAWN_ASSERT(validationState.IsValidState());
 
             mEncodingContext.WillBeginRenderPass();
             BeginRenderPassCmd* cmd =
@@ -1032,7 +1098,7 @@
                 TextureViewBase* colorTarget;
                 TextureViewBase* resolveTarget;
 
-                if (implicitSampleCount <= 1) {
+                if (validationState.GetImplicitSampleCount() <= 1) {
                     colorTarget = descriptor->colorAttachments[i].view;
                     resolveTarget = descriptor->colorAttachments[i].resolveTarget;
 
@@ -1048,10 +1114,11 @@
                     Ref<TextureViewBase> implicitMSAATargetRef;
                     DAWN_TRY_ASSIGN(implicitMSAATargetRef,
                                     device->CreateImplicitMSAARenderTextureViewFor(
-                                        resolveTarget, implicitSampleCount));
+                                        resolveTarget, validationState.GetImplicitSampleCount()));
                     colorTarget = implicitMSAATargetRef.Get();
 
                     cmd->colorAttachments[index].view = std::move(implicitMSAATargetRef);
+                    // Without explicitly setting depthSlice to zero, its value would be undefined.
                     cmd->colorAttachments[index].depthSlice = 0;
                     cmd->colorAttachments[index].loadOp = wgpu::LoadOp::Clear;
                     cmd->colorAttachments[index].storeOp = wgpu::StoreOp::Discard;
@@ -1153,8 +1220,8 @@
                 }
             }
 
-            cmd->width = width;
-            cmd->height = height;
+            cmd->width = validationState.GetWidth();
+            cmd->height = validationState.GetHeight();
 
             cmd->occlusionQuerySet = descriptor->occlusionQuerySet;
 
@@ -1208,18 +1275,18 @@
         "encoding %s.BeginRenderPass(%s).", this, descriptor);
 
     if (success) {
-        Ref<RenderPassEncoder> passEncoder =
-            RenderPassEncoder::Create(device, descriptor, this, &mEncodingContext,
-                                      std::move(usageTracker), std::move(attachmentState), width,
-                                      height, depthReadOnly, stencilReadOnly, passEndCallback);
+        Ref<RenderPassEncoder> passEncoder = RenderPassEncoder::Create(
+            device, descriptor, this, &mEncodingContext, std::move(usageTracker),
+            std::move(attachmentState), validationState.GetWidth(), validationState.GetHeight(),
+            depthReadOnly, stencilReadOnly, passEndCallback);
 
         mEncodingContext.EnterPass(passEncoder.Get());
 
         MaybeError error;
 
-        if (implicitSampleCount > 1) {
+        if (validationState.GetImplicitSampleCount() > 1) {
             error = ApplyMSAARenderToSingleSampledLoadOp(device, passEncoder.Get(), descriptor,
-                                                         implicitSampleCount);
+                                                         validationState.GetImplicitSampleCount());
         } else if (ShouldApplyClearBigIntegerColorValueWithDraw(device, descriptor)) {
             // This is skipped if implicitSampleCount > 1. Because implicitSampleCount > 1 is only
             // supported for non-integer textures.
diff --git a/src/dawn/native/PassResourceUsageTracker.cpp b/src/dawn/native/PassResourceUsageTracker.cpp
index c5d100d..08c0c46 100644
--- a/src/dawn/native/PassResourceUsageTracker.cpp
+++ b/src/dawn/native/PassResourceUsageTracker.cpp
@@ -79,20 +79,6 @@
 
     textureSyncInfo.Update(
         range, [usage, shaderStages](const SubresourceRange&, TextureSyncInfo* storedSyncInfo) {
-            // TODO(crbug.com/dawn/1001): Consider optimizing to have fewer branches.
-
-            // Using the same subresource for two different attachments is a write-write or
-            // read-write hazard. Add an internal kAgainAsAttachment usage to fail the later check
-            // that a subresource with a writable usage has a single usage.
-            constexpr wgpu::TextureUsage kAgainAsAttachment =
-                kReservedTextureUsage | static_cast<wgpu::TextureUsage>(1);
-            constexpr wgpu::TextureUsage kWritableAttachmentUsages =
-                wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::StorageAttachment;
-            if ((usage & kWritableAttachmentUsages) &&
-                (storedSyncInfo->usage & kWritableAttachmentUsages)) {
-                storedSyncInfo->usage |= kAgainAsAttachment;
-            }
-
             storedSyncInfo->usage |= usage;
             storedSyncInfo->shaderStages |= shaderStages;
         });
diff --git a/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp b/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp
index cf5812e..8444f2d 100644
--- a/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp
@@ -440,15 +440,17 @@
     }
 }
 
-// Depth slice index must be within the depth range of 3D color attachment and must be 0 for non-3D
-// color attachment.
+// Check that depthSlice must be set correctly for 3D color attachments and must not be set for
+// non-3D color attachments.
 TEST_F(RenderPassDescriptorValidationTest, TextureViewDepthSliceForColor) {
     constexpr uint32_t kSize = 8;
     constexpr uint32_t kDepthOrArrayLayers = 4;
+    constexpr uint32_t kMipLevelCounts = 4;
     constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm;
 
-    wgpu::Texture colorTexture3D = CreateTexture(device, wgpu::TextureDimension::e3D, kColorFormat,
-                                                 kSize, kSize, kDepthOrArrayLayers, 2);
+    wgpu::Texture colorTexture3D =
+        CreateTexture(device, wgpu::TextureDimension::e3D, kColorFormat, kSize, kSize,
+                      kDepthOrArrayLayers, kMipLevelCounts);
 
     wgpu::TextureView colorView2D = Create2DAttachment(device, kSize, kSize, kColorFormat);
 
@@ -459,7 +461,8 @@
     baseDescriptor.baseMipLevel = 0;
     baseDescriptor.mipLevelCount = 1;
 
-    // Control case: Depth slice index within the depth range of 3D color attachment is valid.
+    // Control case: It's valid if depthSlice is set within the depth range of a 3D color
+    // attachment.
     {
         wgpu::TextureView view = colorTexture3D.CreateView(&baseDescriptor);
         utils::ComboRenderPassDescriptor renderPass({view});
@@ -467,7 +470,14 @@
         AssertBeginRenderPassSuccess(&renderPass);
     }
 
-    // Depth slice index out of the depth range of 3D color attachment is invalid.
+    // It's invalid if depthSlice is not set for a 3D color attachment.
+    {
+        wgpu::TextureView view = colorTexture3D.CreateView(&baseDescriptor);
+        utils::ComboRenderPassDescriptor renderPass({view});
+        AssertBeginRenderPassError(&renderPass);
+    }
+
+    // It's invalid if depthSlice is out of the depth range of a 3D color attachment.
     {
         wgpu::TextureView view = colorTexture3D.CreateView(&baseDescriptor);
         utils::ComboRenderPassDescriptor renderPass({view});
@@ -475,28 +485,84 @@
         AssertBeginRenderPassError(&renderPass);
     }
 
-    // Depth slice index out of the depth range of 3D color attachment with non-zero mip level is
-    // invalid.
+    // It's invalid if depthSlice is out of the depth range of a 3D color attachment with non-zero
+    // mip level.
     {
         wgpu::TextureViewDescriptor descriptor = baseDescriptor;
-        descriptor.baseMipLevel = 1;
+        descriptor.baseMipLevel = 2;
         wgpu::TextureView view = colorTexture3D.CreateView(&descriptor);
         utils::ComboRenderPassDescriptor renderPass({view});
-        renderPass.cColorAttachments[0].depthSlice = kDepthOrArrayLayers >> 1;
+        renderPass.cColorAttachments[0].depthSlice = kDepthOrArrayLayers >> 2;
         AssertBeginRenderPassError(&renderPass);
     }
 
-    // Control case: Depth slice must be 0 for non-3D color attachment.
+    // TODO(dawn:1020): Add tests to check depthSlice must not be set for non-3D attachments.
+}
+
+// Check that the depth slices of a 3D color attachment cannot overlap in same render pass.
+TEST_F(RenderPassDescriptorValidationTest, TextureViewDepthSliceOverlaps) {
+    constexpr uint32_t kSize = 8;
+    constexpr uint32_t kDepthOrArrayLayers = 2;
+    constexpr uint32_t kMipLevelCounts = 2;
+    constexpr wgpu::TextureFormat kColorFormat = wgpu::TextureFormat::RGBA8Unorm;
+
+    wgpu::Texture colorTexture3D =
+        CreateTexture(device, wgpu::TextureDimension::e3D, kColorFormat, kSize, kSize,
+                      kDepthOrArrayLayers, kMipLevelCounts);
+
+    wgpu::TextureViewDescriptor baseDescriptor;
+    baseDescriptor.dimension = wgpu::TextureViewDimension::e3D;
+    baseDescriptor.baseArrayLayer = 0;
+    baseDescriptor.arrayLayerCount = 1;
+    baseDescriptor.baseMipLevel = 0;
+    baseDescriptor.mipLevelCount = 1;
+
+    // Control case: It's valid if different depth slices of a texture are set in a render pass.
     {
-        utils::ComboRenderPassDescriptor renderPass({colorView2D});
+        wgpu::TextureView view = colorTexture3D.CreateView(&baseDescriptor);
+        utils::ComboRenderPassDescriptor renderPass({view, view});
         renderPass.cColorAttachments[0].depthSlice = 0;
+        renderPass.cColorAttachments[1].depthSlice = 1;
         AssertBeginRenderPassSuccess(&renderPass);
     }
 
-    // Non-zero depth slice is invalid for non-3D color attachment.
+    // It's valid if same depth slice of different mip levels from a texture with size [1, 1, n] is
+    // set in a render pass.
     {
-        utils::ComboRenderPassDescriptor renderPass({colorView2D});
-        renderPass.cColorAttachments[0].depthSlice = 1;
+        wgpu::Texture texture = CreateTexture(device, wgpu::TextureDimension::e3D, kColorFormat, 1,
+                                              1, kDepthOrArrayLayers, kMipLevelCounts);
+        wgpu::TextureView view = texture.CreateView(&baseDescriptor);
+        wgpu::TextureViewDescriptor descriptor = baseDescriptor;
+        descriptor.baseMipLevel = 1;
+        wgpu::TextureView view2 = texture.CreateView(&descriptor);
+
+        utils::ComboRenderPassDescriptor renderPass({view, view2});
+        renderPass.cColorAttachments[0].depthSlice = 0;
+        renderPass.cColorAttachments[1].depthSlice = 0;
+        AssertBeginRenderPassSuccess(&renderPass);
+    }
+
+    // It's valid if same depth slice of different textures is set in a render pass.
+    {
+        wgpu::Texture otherColorTexture3D =
+            CreateTexture(device, wgpu::TextureDimension::e3D, kColorFormat, kSize, kSize,
+                          kDepthOrArrayLayers, kMipLevelCounts);
+
+        wgpu::TextureView view = colorTexture3D.CreateView(&baseDescriptor);
+        wgpu::TextureView view2 = otherColorTexture3D.CreateView(&baseDescriptor);
+
+        utils::ComboRenderPassDescriptor renderPass({view, view2});
+        renderPass.cColorAttachments[0].depthSlice = 0;
+        renderPass.cColorAttachments[1].depthSlice = 0;
+        AssertBeginRenderPassSuccess(&renderPass);
+    }
+
+    // It's invalid if same depth slice of a texture is set twice in a render pass.
+    {
+        wgpu::TextureView view = colorTexture3D.CreateView(&baseDescriptor);
+        utils::ComboRenderPassDescriptor renderPass({view, view});
+        renderPass.cColorAttachments[0].depthSlice = 0;
+        renderPass.cColorAttachments[1].depthSlice = 0;
         AssertBeginRenderPassError(&renderPass);
     }
 }
@@ -1023,6 +1089,34 @@
     }
 }
 
+// Test the overlaps of multiple resolve target.
+TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetUsedTwice) {
+    wgpu::TextureView resolveTextureView = CreateNonMultisampledColorTextureView();
+    wgpu::TextureView colorTextureView1 = CreateMultisampledColorTextureView();
+    wgpu::TextureView colorTextureView2 = CreateMultisampledColorTextureView();
+
+    // It is allowed to use different resolve targets in a render pass.
+    {
+        wgpu::TextureView anotherResolveTextureView = CreateNonMultisampledColorTextureView();
+        utils::ComboRenderPassDescriptor renderPass =
+            utils::ComboRenderPassDescriptor({colorTextureView1, colorTextureView2});
+        renderPass.cColorAttachments[0].resolveTarget = resolveTextureView;
+        renderPass.cColorAttachments[1].resolveTarget = anotherResolveTextureView;
+
+        AssertBeginRenderPassSuccess(&renderPass);
+    }
+
+    // It is not allowed to use a resolve target twice in a render pass.
+    {
+        utils::ComboRenderPassDescriptor renderPass =
+            utils::ComboRenderPassDescriptor({colorTextureView1, colorTextureView2});
+        renderPass.cColorAttachments[0].resolveTarget = resolveTextureView;
+        renderPass.cColorAttachments[1].resolveTarget = resolveTextureView;
+
+        AssertBeginRenderPassError(&renderPass);
+    }
+}
+
 // Tests the texture format of the resolve target must support being used as resolve target.
 TEST_F(MultisampledRenderPassDescriptorValidationTest, ResolveTargetFormat) {
     for (wgpu::TextureFormat format : utils::kAllTextureFormats) {