Typeify ColorAttachmentIndex

Also moves BindingNumber, BindGroupIndex, and BindingIndex to
IntegerTypes.h. Future TypedIntegers should be declared here.

Bug: dawn:442
Change-Id: I5ba8de3412fb48b7957b67e7c413a5097f8ec00f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/27880
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/src/common/Constants.h b/src/common/Constants.h
index e281646..5a7c1df 100644
--- a/src/common/Constants.h
+++ b/src/common/Constants.h
@@ -29,7 +29,7 @@
 static constexpr uint32_t kMaxVertexBuffers = 16u;
 static constexpr uint32_t kMaxVertexBufferStride = 2048u;
 static constexpr uint32_t kNumStages = 3;
-static constexpr uint32_t kMaxColorAttachments = 4u;
+static constexpr uint8_t kMaxColorAttachments = 4u;
 static constexpr uint32_t kTextureBytesPerRowAlignment = 256u;
 // Dynamic buffer offsets require offset to be divisible by 256
 static constexpr uint64_t kMinDynamicBufferOffsetAlignment = 256u;
diff --git a/src/common/ityp_array.h b/src/common/ityp_array.h
index fc77217..48e080f 100644
--- a/src/common/ityp_array.h
+++ b/src/common/ityp_array.h
@@ -88,6 +88,7 @@
         using Base::back;
         using Base::data;
         using Base::empty;
+        using Base::fill;
         using Base::front;
     };
 
diff --git a/src/dawn_native/AttachmentState.cpp b/src/dawn_native/AttachmentState.cpp
index 5ff33b3..77b669c 100644
--- a/src/dawn_native/AttachmentState.cpp
+++ b/src/dawn_native/AttachmentState.cpp
@@ -24,18 +24,22 @@
     AttachmentStateBlueprint::AttachmentStateBlueprint(
         const RenderBundleEncoderDescriptor* descriptor)
         : mSampleCount(descriptor->sampleCount) {
-        for (uint32_t i = 0; i < descriptor->colorFormatsCount; ++i) {
+        ASSERT(descriptor->colorFormatsCount <= kMaxColorAttachments);
+        for (ColorAttachmentIndex i(uint8_t(0));
+             i < ColorAttachmentIndex(static_cast<uint8_t>(descriptor->colorFormatsCount)); ++i) {
             mColorAttachmentsSet.set(i);
-            mColorFormats[i] = descriptor->colorFormats[i];
+            mColorFormats[i] = descriptor->colorFormats[static_cast<uint8_t>(i)];
         }
         mDepthStencilFormat = descriptor->depthStencilFormat;
     }
 
     AttachmentStateBlueprint::AttachmentStateBlueprint(const RenderPipelineDescriptor* descriptor)
         : mSampleCount(descriptor->sampleCount) {
-        for (uint32_t i = 0; i < descriptor->colorStateCount; ++i) {
+        ASSERT(descriptor->colorStateCount <= kMaxColorAttachments);
+        for (ColorAttachmentIndex i(uint8_t(0));
+             i < ColorAttachmentIndex(static_cast<uint8_t>(descriptor->colorStateCount)); ++i) {
             mColorAttachmentsSet.set(i);
-            mColorFormats[i] = descriptor->colorStates[i].format;
+            mColorFormats[i] = descriptor->colorStates[static_cast<uint8_t>(i)].format;
         }
         if (descriptor->depthStencilState != nullptr) {
             mDepthStencilFormat = descriptor->depthStencilState->format;
@@ -43,8 +47,11 @@
     }
 
     AttachmentStateBlueprint::AttachmentStateBlueprint(const RenderPassDescriptor* descriptor) {
-        for (uint32_t i = 0; i < descriptor->colorAttachmentCount; ++i) {
-            TextureViewBase* attachment = descriptor->colorAttachments[i].attachment;
+        for (ColorAttachmentIndex i(uint8_t(0));
+             i < ColorAttachmentIndex(static_cast<uint8_t>(descriptor->colorAttachmentCount));
+             ++i) {
+            TextureViewBase* attachment =
+                descriptor->colorAttachments[static_cast<uint8_t>(i)].attachment;
             mColorAttachmentsSet.set(i);
             mColorFormats[i] = attachment->GetFormat().format;
             if (mSampleCount == 0) {
@@ -74,7 +81,7 @@
 
         // Hash color formats
         HashCombine(&hash, attachmentState->mColorAttachmentsSet);
-        for (uint32_t i : IterateBitSet(attachmentState->mColorAttachmentsSet)) {
+        for (ColorAttachmentIndex i : IterateBitSet(attachmentState->mColorAttachmentsSet)) {
             HashCombine(&hash, attachmentState->mColorFormats[i]);
         }
 
@@ -96,7 +103,7 @@
         }
 
         // Check color formats
-        for (uint32_t i : IterateBitSet(a->mColorAttachmentsSet)) {
+        for (ColorAttachmentIndex i : IterateBitSet(a->mColorAttachmentsSet)) {
             if (a->mColorFormats[i] != b->mColorFormats[i]) {
                 return false;
             }
@@ -123,11 +130,13 @@
         GetDevice()->UncacheAttachmentState(this);
     }
 
-    std::bitset<kMaxColorAttachments> AttachmentState::GetColorAttachmentsMask() const {
+    ityp::bitset<ColorAttachmentIndex, kMaxColorAttachments>
+    AttachmentState::GetColorAttachmentsMask() const {
         return mColorAttachmentsSet;
     }
 
-    wgpu::TextureFormat AttachmentState::GetColorAttachmentFormat(uint32_t index) const {
+    wgpu::TextureFormat AttachmentState::GetColorAttachmentFormat(
+        ColorAttachmentIndex index) const {
         ASSERT(mColorAttachmentsSet[index]);
         return mColorFormats[index];
     }
diff --git a/src/dawn_native/AttachmentState.h b/src/dawn_native/AttachmentState.h
index f5d0159..e332859 100644
--- a/src/dawn_native/AttachmentState.h
+++ b/src/dawn_native/AttachmentState.h
@@ -16,7 +16,10 @@
 #define DAWNNATIVE_ATTACHMENTSTATE_H_
 
 #include "common/Constants.h"
+#include "common/ityp_array.h"
+#include "common/ityp_bitset.h"
 #include "dawn_native/CachedObject.h"
+#include "dawn_native/IntegerTypes.h"
 
 #include "dawn_native/dawn_platform.h"
 
@@ -49,8 +52,8 @@
         };
 
       protected:
-        std::bitset<kMaxColorAttachments> mColorAttachmentsSet;
-        std::array<wgpu::TextureFormat, kMaxColorAttachments> mColorFormats;
+        ityp::bitset<ColorAttachmentIndex, kMaxColorAttachments> mColorAttachmentsSet;
+        ityp::array<ColorAttachmentIndex, wgpu::TextureFormat, kMaxColorAttachments> mColorFormats;
         // Default (texture format Undefined) indicates there is no depth stencil attachment.
         wgpu::TextureFormat mDepthStencilFormat = wgpu::TextureFormat::Undefined;
         uint32_t mSampleCount = 0;
@@ -60,8 +63,8 @@
       public:
         AttachmentState(DeviceBase* device, const AttachmentStateBlueprint& blueprint);
 
-        std::bitset<kMaxColorAttachments> GetColorAttachmentsMask() const;
-        wgpu::TextureFormat GetColorAttachmentFormat(uint32_t index) const;
+        ityp::bitset<ColorAttachmentIndex, kMaxColorAttachments> GetColorAttachmentsMask() const;
+        wgpu::TextureFormat GetColorAttachmentFormat(ColorAttachmentIndex index) const;
         bool HasDepthStencilAttachment() const;
         wgpu::TextureFormat GetDepthStencilFormat() const;
         uint32_t GetSampleCount() const;
diff --git a/src/dawn_native/BUILD.gn b/src/dawn_native/BUILD.gn
index b4d0ae5..6f9dbd5 100644
--- a/src/dawn_native/BUILD.gn
+++ b/src/dawn_native/BUILD.gn
@@ -212,6 +212,7 @@
     "Forward.h",
     "Instance.cpp",
     "Instance.h",
+    "IntegerTypes.h",
     "MapRequestTracker.cpp",
     "MapRequestTracker.h",
     "ObjectBase.cpp",
diff --git a/src/dawn_native/BindingInfo.h b/src/dawn_native/BindingInfo.h
index b6cfad1..94cd8e9 100644
--- a/src/dawn_native/BindingInfo.h
+++ b/src/dawn_native/BindingInfo.h
@@ -16,10 +16,10 @@
 #define DAWNNATIVE_BINDINGINFO_H_
 
 #include "common/Constants.h"
-#include "common/TypedInteger.h"
 #include "common/ityp_array.h"
 #include "dawn_native/Error.h"
 #include "dawn_native/Format.h"
+#include "dawn_native/IntegerTypes.h"
 #include "dawn_native/PerStage.h"
 
 #include "dawn_native/dawn_platform.h"
@@ -28,16 +28,6 @@
 
 namespace dawn_native {
 
-    // Binding numbers in the shader and BindGroup/BindGroupLayoutDescriptors
-    using BindingNumber = TypedInteger<struct BindingNumberT, uint32_t>;
-
-    // Binding numbers get mapped to a packed range of indices
-    using BindingIndex = TypedInteger<struct BindingIndexT, uint32_t>;
-
-    using BindGroupIndex = TypedInteger<struct BindGroupIndexT, uint32_t>;
-
-    static constexpr BindGroupIndex kMaxBindGroupsTyped = BindGroupIndex(kMaxBindGroups);
-
     // Not a real WebGPU limit, but the sum of the two limits is useful for internal optimizations.
     static constexpr uint32_t kMaxDynamicBuffersPerPipelineLayout =
         kMaxDynamicUniformBuffersPerPipelineLayout + kMaxDynamicStorageBuffersPerPipelineLayout;
diff --git a/src/dawn_native/CMakeLists.txt b/src/dawn_native/CMakeLists.txt
index 7f6b85b..feb9dc0 100644
--- a/src/dawn_native/CMakeLists.txt
+++ b/src/dawn_native/CMakeLists.txt
@@ -90,6 +90,7 @@
     "Forward.h"
     "Instance.cpp"
     "Instance.h"
+    "IntegerTypes.h"
     "MapRequestTracker.cpp"
     "MapRequestTracker.h"
     "ObjectBase.cpp"
diff --git a/src/dawn_native/CommandBuffer.cpp b/src/dawn_native/CommandBuffer.cpp
index 2dae6a2..6410374 100644
--- a/src/dawn_native/CommandBuffer.cpp
+++ b/src/dawn_native/CommandBuffer.cpp
@@ -85,7 +85,8 @@
     }
 
     void LazyClearRenderPassAttachments(BeginRenderPassCmd* renderPass) {
-        for (uint32_t i : IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
+        for (ColorAttachmentIndex i :
+             IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
             auto& attachmentInfo = renderPass->colorAttachments[i];
             TextureViewBase* view = attachmentInfo.view.Get();
             bool hasResolveTarget = attachmentInfo.resolveTarget.Get() != nullptr;
diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp
index e846e11..bc5b533 100644
--- a/src/dawn_native/CommandEncoder.cpp
+++ b/src/dawn_native/CommandEncoder.cpp
@@ -514,15 +514,17 @@
 
                 cmd->attachmentState = device->GetOrCreateAttachmentState(descriptor);
 
-                for (uint32_t i : IterateBitSet(cmd->attachmentState->GetColorAttachmentsMask())) {
+                for (ColorAttachmentIndex index :
+                     IterateBitSet(cmd->attachmentState->GetColorAttachmentsMask())) {
+                    uint8_t i = static_cast<uint8_t>(index);
                     TextureViewBase* view = descriptor->colorAttachments[i].attachment;
                     TextureViewBase* resolveTarget = descriptor->colorAttachments[i].resolveTarget;
 
-                    cmd->colorAttachments[i].view = view;
-                    cmd->colorAttachments[i].resolveTarget = resolveTarget;
-                    cmd->colorAttachments[i].loadOp = descriptor->colorAttachments[i].loadOp;
-                    cmd->colorAttachments[i].storeOp = descriptor->colorAttachments[i].storeOp;
-                    cmd->colorAttachments[i].clearColor =
+                    cmd->colorAttachments[index].view = view;
+                    cmd->colorAttachments[index].resolveTarget = resolveTarget;
+                    cmd->colorAttachments[index].loadOp = descriptor->colorAttachments[i].loadOp;
+                    cmd->colorAttachments[index].storeOp = descriptor->colorAttachments[i].storeOp;
+                    cmd->colorAttachments[index].clearColor =
                         descriptor->colorAttachments[i].clearColor;
 
                     usageTracker.TextureViewUsedAs(view, wgpu::TextureUsage::OutputAttachment);
diff --git a/src/dawn_native/Commands.h b/src/dawn_native/Commands.h
index 0d9f57c..3d34693 100644
--- a/src/dawn_native/Commands.h
+++ b/src/dawn_native/Commands.h
@@ -86,7 +86,8 @@
 
     struct BeginRenderPassCmd {
         Ref<AttachmentState> attachmentState;
-        RenderPassColorAttachmentInfo colorAttachments[kMaxColorAttachments];
+        ityp::array<ColorAttachmentIndex, RenderPassColorAttachmentInfo, kMaxColorAttachments>
+            colorAttachments;
         RenderPassDepthStencilAttachmentInfo depthStencilAttachment;
 
         // Cache the width and height of all attachments for convenience
diff --git a/src/dawn_native/IntegerTypes.h b/src/dawn_native/IntegerTypes.h
new file mode 100644
index 0000000..689cf8e
--- /dev/null
+++ b/src/dawn_native/IntegerTypes.h
@@ -0,0 +1,41 @@
+// Copyright 2020 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef DAWNNATIVE_INTEGERTYPES_H_
+#define DAWNNATIVE_INTEGERTYPES_H_
+
+#include "common/Constants.h"
+#include "common/TypedInteger.h"
+
+#include <cstdint>
+
+namespace dawn_native {
+    // Binding numbers in the shader and BindGroup/BindGroupLayoutDescriptors
+    using BindingNumber = TypedInteger<struct BindingNumberT, uint32_t>;
+
+    // Binding numbers get mapped to a packed range of indices
+    using BindingIndex = TypedInteger<struct BindingIndexT, uint32_t>;
+
+    using BindGroupIndex = TypedInteger<struct BindGroupIndexT, uint32_t>;
+
+    static constexpr BindGroupIndex kMaxBindGroupsTyped = BindGroupIndex(kMaxBindGroups);
+
+    using ColorAttachmentIndex = TypedInteger<struct ColorAttachmentIndexT, uint8_t>;
+
+    constexpr ColorAttachmentIndex kMaxColorAttachmentsTyped =
+        ColorAttachmentIndex(kMaxColorAttachments);
+
+}  // namespace dawn_native
+
+#endif  // DAWNNATIVE_INTEGERTYPES_H_
diff --git a/src/dawn_native/RenderPipeline.cpp b/src/dawn_native/RenderPipeline.cpp
index 62ea4b3..cbe99d0 100644
--- a/src/dawn_native/RenderPipeline.cpp
+++ b/src/dawn_native/RenderPipeline.cpp
@@ -356,10 +356,11 @@
         const EntryPointMetadata& fragmentMetadata =
             descriptor->fragmentStage->module->GetEntryPoint(descriptor->fragmentStage->entryPoint,
                                                              SingleShaderStage::Fragment);
-        for (uint32_t i = 0; i < descriptor->colorStateCount; ++i) {
-            DAWN_TRY(
-                ValidateColorStateDescriptor(device, descriptor->colorStates[i],
-                                             fragmentMetadata.fragmentOutputFormatBaseTypes[i]));
+        for (ColorAttachmentIndex i(uint8_t(0));
+             i < ColorAttachmentIndex(static_cast<uint8_t>(descriptor->colorStateCount)); ++i) {
+            DAWN_TRY(ValidateColorStateDescriptor(
+                device, descriptor->colorStates[static_cast<uint8_t>(i)],
+                fragmentMetadata.fragmentOutputFormatBaseTypes[i]));
         }
 
         if (descriptor->depthStencilState) {
@@ -460,8 +461,8 @@
             mDepthStencilState.stencilWriteMask = 0xff;
         }
 
-        for (uint32_t i : IterateBitSet(mAttachmentState->GetColorAttachmentsMask())) {
-            mColorStates[i] = descriptor->colorStates[i];
+        for (ColorAttachmentIndex i : IterateBitSet(mAttachmentState->GetColorAttachmentsMask())) {
+            mColorStates[i] = descriptor->colorStates[static_cast<uint8_t>(i)];
         }
 
         // TODO(cwallez@chromium.org): Check against the shader module that the correct color
@@ -511,7 +512,7 @@
     }
 
     const ColorStateDescriptor* RenderPipelineBase::GetColorStateDescriptor(
-        uint32_t attachmentSlot) const {
+        ColorAttachmentIndex attachmentSlot) const {
         ASSERT(!IsError());
         ASSERT(attachmentSlot < mColorStates.size());
         return &mColorStates[attachmentSlot];
@@ -537,7 +538,8 @@
         return mRasterizationState.frontFace;
     }
 
-    std::bitset<kMaxColorAttachments> RenderPipelineBase::GetColorAttachmentsMask() const {
+    ityp::bitset<ColorAttachmentIndex, kMaxColorAttachments>
+    RenderPipelineBase::GetColorAttachmentsMask() const {
         ASSERT(!IsError());
         return mAttachmentState->GetColorAttachmentsMask();
     }
@@ -547,7 +549,8 @@
         return mAttachmentState->HasDepthStencilAttachment();
     }
 
-    wgpu::TextureFormat RenderPipelineBase::GetColorAttachmentFormat(uint32_t attachment) const {
+    wgpu::TextureFormat RenderPipelineBase::GetColorAttachmentFormat(
+        ColorAttachmentIndex attachment) const {
         ASSERT(!IsError());
         return mColorStates[attachment].format;
     }
@@ -594,7 +597,8 @@
         HashCombine(&hash, pipeline->mAttachmentState.Get());
 
         // Hash attachments
-        for (uint32_t i : IterateBitSet(pipeline->mAttachmentState->GetColorAttachmentsMask())) {
+        for (ColorAttachmentIndex i :
+             IterateBitSet(pipeline->mAttachmentState->GetColorAttachmentsMask())) {
             const ColorStateDescriptor& desc = *pipeline->GetColorStateDescriptor(i);
             HashCombine(&hash, desc.writeMask);
             HashCombine(&hash, desc.colorBlend.operation, desc.colorBlend.srcFactor,
@@ -656,7 +660,8 @@
             return false;
         }
 
-        for (uint32_t i : IterateBitSet(a->mAttachmentState->GetColorAttachmentsMask())) {
+        for (ColorAttachmentIndex i :
+             IterateBitSet(a->mAttachmentState->GetColorAttachmentsMask())) {
             const ColorStateDescriptor& descA = *a->GetColorStateDescriptor(i);
             const ColorStateDescriptor& descB = *b->GetColorStateDescriptor(i);
             if (descA.writeMask != descB.writeMask) {
diff --git a/src/dawn_native/RenderPipeline.h b/src/dawn_native/RenderPipeline.h
index 002b330..a5d9cca 100644
--- a/src/dawn_native/RenderPipeline.h
+++ b/src/dawn_native/RenderPipeline.h
@@ -67,15 +67,16 @@
         const std::bitset<kMaxVertexBuffers>& GetVertexBufferSlotsUsed() const;
         const VertexBufferInfo& GetVertexBuffer(uint32_t slot) const;
 
-        const ColorStateDescriptor* GetColorStateDescriptor(uint32_t attachmentSlot) const;
+        const ColorStateDescriptor* GetColorStateDescriptor(
+            ColorAttachmentIndex attachmentSlot) const;
         const DepthStencilStateDescriptor* GetDepthStencilStateDescriptor() const;
         wgpu::PrimitiveTopology GetPrimitiveTopology() const;
         wgpu::CullMode GetCullMode() const;
         wgpu::FrontFace GetFrontFace() const;
 
-        std::bitset<kMaxColorAttachments> GetColorAttachmentsMask() const;
+        ityp::bitset<ColorAttachmentIndex, kMaxColorAttachments> GetColorAttachmentsMask() const;
         bool HasDepthStencilAttachment() const;
-        wgpu::TextureFormat GetColorAttachmentFormat(uint32_t attachment) const;
+        wgpu::TextureFormat GetColorAttachmentFormat(ColorAttachmentIndex attachment) const;
         wgpu::TextureFormat GetDepthStencilFormat() const;
         uint32_t GetSampleCount() const;
         uint32_t GetSampleMask() const;
@@ -108,7 +109,7 @@
         // Attachments
         Ref<AttachmentState> mAttachmentState;
         DepthStencilStateDescriptor mDepthStencilState;
-        std::array<ColorStateDescriptor, kMaxColorAttachments> mColorStates;
+        ityp::array<ColorAttachmentIndex, ColorStateDescriptor, kMaxColorAttachments> mColorStates;
 
         // Other state
         wgpu::PrimitiveTopology mPrimitiveTopology;
diff --git a/src/dawn_native/ShaderModule.cpp b/src/dawn_native/ShaderModule.cpp
index df98750..cdd2fbb 100644
--- a/src/dawn_native/ShaderModule.cpp
+++ b/src/dawn_native/ShaderModule.cpp
@@ -872,12 +872,14 @@
                     return DAWN_VALIDATION_ERROR(
                         "Unable to find Location decoration for Fragment output");
                 }
-                uint32_t location =
+                uint32_t unsanitizedAttachment =
                     compiler.get_decoration(fragmentOutput.id, spv::DecorationLocation);
-                if (location >= kMaxColorAttachments) {
+                if (unsanitizedAttachment >= kMaxColorAttachments) {
                     return DAWN_VALIDATION_ERROR(
-                        "Fragment output location over limits in the SPIRV");
+                        "Fragment output attachment index must be less than max number of color "
+                        "attachments");
                 }
+                ColorAttachmentIndex attachment(static_cast<uint8_t>(unsanitizedAttachment));
 
                 spirv_cross::SPIRType::BaseType shaderFragmentOutputBaseType =
                     compiler.get_type(fragmentOutput.base_type_id).basetype;
@@ -886,7 +888,7 @@
                 if (formatType == Format::Type::Other) {
                     return DAWN_VALIDATION_ERROR("Unexpected Fragment output type");
                 }
-                metadata->fragmentOutputFormatBaseTypes[location] = formatType;
+                metadata->fragmentOutputFormatBaseTypes[attachment] = formatType;
             }
         }
 
diff --git a/src/dawn_native/ShaderModule.h b/src/dawn_native/ShaderModule.h
index edce8f7..3fcd35b 100644
--- a/src/dawn_native/ShaderModule.h
+++ b/src/dawn_native/ShaderModule.h
@@ -22,6 +22,7 @@
 #include "dawn_native/Error.h"
 #include "dawn_native/Format.h"
 #include "dawn_native/Forward.h"
+#include "dawn_native/IntegerTypes.h"
 #include "dawn_native/PerStage.h"
 
 #include "dawn_native/dawn_platform.h"
@@ -77,7 +78,8 @@
 
         // An array to record the basic types (float, int and uint) of the fragment shader outputs
         // or Format::Type::Other means the fragment shader output is unused.
-        using FragmentOutputBaseTypes = std::array<Format::Type, kMaxColorAttachments>;
+        using FragmentOutputBaseTypes =
+            ityp::array<ColorAttachmentIndex, Format::Type, kMaxColorAttachments>;
         FragmentOutputBaseTypes fragmentOutputFormatBaseTypes;
 
         // The shader stage for this binding, TODO(dawn:216): can likely be removed once we
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index bc40529..82ad2d8 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -522,7 +522,7 @@
                                            BeginRenderPassCmd* renderPass) {
             ASSERT(renderPass != nullptr);
 
-            for (uint32_t i :
+            for (ColorAttachmentIndex i :
                  IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
                 TextureViewBase* resolveTarget =
                     renderPass->colorAttachments[i].resolveTarget.Get();
@@ -993,7 +993,8 @@
                                               RenderPassBuilder* renderPassBuilder) {
         Device* device = ToBackend(GetDevice());
 
-        for (uint32_t i : IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
+        for (ColorAttachmentIndex i :
+             IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
             RenderPassColorAttachmentInfo& attachmentInfo = renderPass->colorAttachments[i];
             TextureView* view = ToBackend(attachmentInfo.view.Get());
 
@@ -1084,7 +1085,8 @@
 
         // Clear framebuffer attachments as needed.
         {
-            for (uint32_t i = 0; i < renderPassBuilder->GetColorAttachmentCount(); i++) {
+            for (ColorAttachmentIndex i(uint8_t(0));
+                 i < renderPassBuilder->GetColorAttachmentCount(); i++) {
                 // Load op - color
                 if (renderPassBuilder->GetRenderPassRenderTargetDescriptors()[i]
                         .BeginningAccess.Type == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR) {
@@ -1128,8 +1130,8 @@
         }
 
         commandList->OMSetRenderTargets(
-            renderPassBuilder->GetColorAttachmentCount(), renderPassBuilder->GetRenderTargetViews(),
-            FALSE,
+            static_cast<uint8_t>(renderPassBuilder->GetColorAttachmentCount()),
+            renderPassBuilder->GetRenderTargetViews(), FALSE,
             renderPassBuilder->HasDepth()
                 ? &renderPassBuilder->GetRenderPassDepthStencilDescriptor()->cpuDescriptor
                 : nullptr);
@@ -1153,8 +1155,8 @@
         // beginning and ending access operations.
         if (useRenderPass) {
             commandContext->GetCommandList4()->BeginRenderPass(
-                renderPassBuilder.GetColorAttachmentCount(),
-                renderPassBuilder.GetRenderPassRenderTargetDescriptors(),
+                static_cast<uint8_t>(renderPassBuilder.GetColorAttachmentCount()),
+                renderPassBuilder.GetRenderPassRenderTargetDescriptors().data(),
                 renderPassBuilder.HasDepth()
                     ? renderPassBuilder.GetRenderPassDepthStencilDescriptor()
                     : nullptr,
diff --git a/src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp b/src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp
index 587f48a..5edf6ad 100644
--- a/src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp
+++ b/src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp
@@ -110,9 +110,9 @@
         }
     }
 
-    void RenderPassBuilder::SetRenderTargetView(uint32_t attachmentIndex,
+    void RenderPassBuilder::SetRenderTargetView(ColorAttachmentIndex attachmentIndex,
                                                 D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor) {
-        ASSERT(mColorAttachmentCount < kMaxColorAttachments);
+        ASSERT(mColorAttachmentCount < kMaxColorAttachmentsTyped);
         mRenderTargetViews[attachmentIndex] = baseDescriptor;
         mRenderPassRenderTargetDescriptors[attachmentIndex].cpuDescriptor = baseDescriptor;
         mColorAttachmentCount++;
@@ -122,7 +122,7 @@
         mRenderPassDepthStencilDesc.cpuDescriptor = baseDescriptor;
     }
 
-    uint32_t RenderPassBuilder::GetColorAttachmentCount() const {
+    ColorAttachmentIndex RenderPassBuilder::GetColorAttachmentCount() const {
         return mColorAttachmentCount;
     }
 
@@ -130,9 +130,9 @@
         return mHasDepth;
     }
 
-    const D3D12_RENDER_PASS_RENDER_TARGET_DESC*
+    ityp::span<ColorAttachmentIndex, const D3D12_RENDER_PASS_RENDER_TARGET_DESC>
     RenderPassBuilder::GetRenderPassRenderTargetDescriptors() const {
-        return mRenderPassRenderTargetDescriptors.data();
+        return {mRenderPassRenderTargetDescriptors.data(), mColorAttachmentCount};
     }
 
     const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC*
@@ -148,7 +148,7 @@
         return mRenderTargetViews.data();
     }
 
-    void RenderPassBuilder::SetRenderTargetBeginningAccess(uint32_t attachment,
+    void RenderPassBuilder::SetRenderTargetBeginningAccess(ColorAttachmentIndex attachment,
                                                            wgpu::LoadOp loadOp,
                                                            dawn_native::Color clearColor,
                                                            DXGI_FORMAT format) {
@@ -168,13 +168,13 @@
         }
     }
 
-    void RenderPassBuilder::SetRenderTargetEndingAccess(uint32_t attachment,
+    void RenderPassBuilder::SetRenderTargetEndingAccess(ColorAttachmentIndex attachment,
                                                         wgpu::StoreOp storeOp) {
         mRenderPassRenderTargetDescriptors[attachment].EndingAccess.Type =
             D3D12EndingAccessType(storeOp);
     }
 
-    void RenderPassBuilder::SetRenderTargetEndingAccessResolve(uint32_t attachment,
+    void RenderPassBuilder::SetRenderTargetEndingAccessResolve(ColorAttachmentIndex attachment,
                                                                wgpu::StoreOp storeOp,
                                                                TextureView* resolveSource,
                                                                TextureView* resolveDestination) {
diff --git a/src/dawn_native/d3d12/RenderPassBuilderD3D12.h b/src/dawn_native/d3d12/RenderPassBuilderD3D12.h
index cf61a18..20f70a4 100644
--- a/src/dawn_native/d3d12/RenderPassBuilderD3D12.h
+++ b/src/dawn_native/d3d12/RenderPassBuilderD3D12.h
@@ -16,6 +16,9 @@
 #define DAWNNATIVE_D3D12_RENDERPASSBUILDERD3D12_H_
 
 #include "common/Constants.h"
+#include "common/ityp_array.h"
+#include "common/ityp_span.h"
+#include "dawn_native/IntegerTypes.h"
 #include "dawn_native/d3d12/d3d12_platform.h"
 #include "dawn_native/dawn_platform.h"
 
@@ -34,11 +37,12 @@
       public:
         RenderPassBuilder(bool hasUAV);
 
-        uint32_t GetColorAttachmentCount() const;
+        ColorAttachmentIndex GetColorAttachmentCount() const;
 
         // Returns descriptors that are fed directly to BeginRenderPass, or are used as parameter
         // storage if D3D12 render pass API is unavailable.
-        const D3D12_RENDER_PASS_RENDER_TARGET_DESC* GetRenderPassRenderTargetDescriptors() const;
+        ityp::span<ColorAttachmentIndex, const D3D12_RENDER_PASS_RENDER_TARGET_DESC>
+        GetRenderPassRenderTargetDescriptors() const;
         const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC* GetRenderPassDepthStencilDescriptor() const;
 
         D3D12_RENDER_PASS_FLAGS GetRenderPassFlags() const;
@@ -55,12 +59,12 @@
                             DXGI_FORMAT format);
         void SetDepthNoAccess();
         void SetDepthStencilNoAccess();
-        void SetRenderTargetBeginningAccess(uint32_t attachment,
+        void SetRenderTargetBeginningAccess(ColorAttachmentIndex attachment,
                                             wgpu::LoadOp loadOp,
                                             dawn_native::Color clearColor,
                                             DXGI_FORMAT format);
-        void SetRenderTargetEndingAccess(uint32_t attachment, wgpu::StoreOp storeOp);
-        void SetRenderTargetEndingAccessResolve(uint32_t attachment,
+        void SetRenderTargetEndingAccess(ColorAttachmentIndex attachment, wgpu::StoreOp storeOp);
+        void SetRenderTargetEndingAccessResolve(ColorAttachmentIndex attachment,
                                                 wgpu::StoreOp storeOp,
                                                 TextureView* resolveSource,
                                                 TextureView* resolveDestination);
@@ -70,20 +74,23 @@
                               DXGI_FORMAT format);
         void SetStencilNoAccess();
 
-        void SetRenderTargetView(uint32_t attachmentIndex,
+        void SetRenderTargetView(ColorAttachmentIndex attachmentIndex,
                                  D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor);
         void SetDepthStencilView(D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor);
 
       private:
-        uint32_t mColorAttachmentCount = 0;
+        ColorAttachmentIndex mColorAttachmentCount{uint8_t(0)};
         bool mHasDepth = false;
         D3D12_RENDER_PASS_FLAGS mRenderPassFlags = D3D12_RENDER_PASS_FLAG_NONE;
         D3D12_RENDER_PASS_DEPTH_STENCIL_DESC mRenderPassDepthStencilDesc;
-        std::array<D3D12_RENDER_PASS_RENDER_TARGET_DESC, kMaxColorAttachments>
-            mRenderPassRenderTargetDescriptors;
-        std::array<D3D12_CPU_DESCRIPTOR_HANDLE, kMaxColorAttachments> mRenderTargetViews;
-        std::array<D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS,
-                   kMaxColorAttachments>
+        ityp::
+            array<ColorAttachmentIndex, D3D12_RENDER_PASS_RENDER_TARGET_DESC, kMaxColorAttachments>
+                mRenderPassRenderTargetDescriptors;
+        ityp::array<ColorAttachmentIndex, D3D12_CPU_DESCRIPTOR_HANDLE, kMaxColorAttachments>
+            mRenderTargetViews;
+        ityp::array<ColorAttachmentIndex,
+                    D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS,
+                    kMaxColorAttachments>
             mSubresourceParams;
     };
 }}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/d3d12/RenderPipelineD3D12.cpp b/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
index 1653154..ada9fc6 100644
--- a/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
+++ b/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
@@ -376,9 +376,10 @@
             descriptorD3D12.DSVFormat = D3D12TextureFormat(GetDepthStencilFormat());
         }
 
-        for (uint32_t i : IterateBitSet(GetColorAttachmentsMask())) {
-            descriptorD3D12.RTVFormats[i] = D3D12TextureFormat(GetColorAttachmentFormat(i));
-            descriptorD3D12.BlendState.RenderTarget[i] =
+        for (ColorAttachmentIndex i : IterateBitSet(GetColorAttachmentsMask())) {
+            descriptorD3D12.RTVFormats[static_cast<uint8_t>(i)] =
+                D3D12TextureFormat(GetColorAttachmentFormat(i));
+            descriptorD3D12.BlendState.RenderTarget[static_cast<uint8_t>(i)] =
                 ComputeColorDesc(GetColorStateDescriptor(i));
         }
         descriptorD3D12.NumRenderTargets = static_cast<uint32_t>(GetColorAttachmentsMask().count());
diff --git a/src/dawn_native/metal/CommandBufferMTL.mm b/src/dawn_native/metal/CommandBufferMTL.mm
index bee68d7..e0ca8f8 100644
--- a/src/dawn_native/metal/CommandBufferMTL.mm
+++ b/src/dawn_native/metal/CommandBufferMTL.mm
@@ -56,9 +56,10 @@
         MTLRenderPassDescriptor* CreateMTLRenderPassDescriptor(BeginRenderPassCmd* renderPass) {
             MTLRenderPassDescriptor* descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
 
-            for (uint32_t i :
+            for (ColorAttachmentIndex attachment :
                  IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
-                auto& attachmentInfo = renderPass->colorAttachments[i];
+                uint8_t i = static_cast<uint8_t>(attachment);
+                auto& attachmentInfo = renderPass->colorAttachments[attachment];
 
                 switch (attachmentInfo.loadOp) {
                     case wgpu::LoadOp::Clear:
diff --git a/src/dawn_native/metal/RenderPipelineMTL.mm b/src/dawn_native/metal/RenderPipelineMTL.mm
index 88e905c..ede97aa 100644
--- a/src/dawn_native/metal/RenderPipelineMTL.mm
+++ b/src/dawn_native/metal/RenderPipelineMTL.mm
@@ -370,12 +370,12 @@
 
         const EntryPointMetadata::FragmentOutputBaseTypes& fragmentOutputBaseTypes =
             GetStage(SingleShaderStage::Fragment).metadata->fragmentOutputFormatBaseTypes;
-        for (uint32_t i : IterateBitSet(GetColorAttachmentsMask())) {
-            descriptorMTL.colorAttachments[i].pixelFormat =
+        for (ColorAttachmentIndex i : IterateBitSet(GetColorAttachmentsMask())) {
+            descriptorMTL.colorAttachments[static_cast<uint8_t>(i)].pixelFormat =
                 MetalPixelFormat(GetColorAttachmentFormat(i));
             const ColorStateDescriptor* descriptor = GetColorStateDescriptor(i);
             bool isDeclaredInFragmentShader = fragmentOutputBaseTypes[i] != Format::Type::Other;
-            ComputeBlendDesc(descriptorMTL.colorAttachments[i], descriptor,
+            ComputeBlendDesc(descriptorMTL.colorAttachments[static_cast<uint8_t>(i)], descriptor,
                              isDeclaredInFragmentShader);
         }
 
diff --git a/src/dawn_native/opengl/CommandBufferGL.cpp b/src/dawn_native/opengl/CommandBufferGL.cpp
index 6776c02..aa17ff5 100644
--- a/src/dawn_native/opengl/CommandBufferGL.cpp
+++ b/src/dawn_native/opengl/CommandBufferGL.cpp
@@ -370,7 +370,7 @@
             GLuint readFbo = 0;
             GLuint writeFbo = 0;
 
-            for (uint32_t i :
+            for (ColorAttachmentIndex i :
                  IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
                 if (renderPass->colorAttachments[i].resolveTarget.Get() != nullptr) {
                     if (readFbo == 0) {
@@ -854,30 +854,33 @@
 
             // Mapping from attachmentSlot to GL framebuffer attachment points. Defaults to zero
             // (GL_NONE).
-            std::array<GLenum, kMaxColorAttachments> drawBuffers = {};
+            ityp::array<ColorAttachmentIndex, GLenum, kMaxColorAttachments> drawBuffers = {};
 
             // Construct GL framebuffer
 
-            unsigned int attachmentCount = 0;
-            for (uint32_t i :
+            ColorAttachmentIndex attachmentCount(uint8_t(0));
+            for (ColorAttachmentIndex i :
                  IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
                 TextureViewBase* textureView = renderPass->colorAttachments[i].view.Get();
                 GLuint texture = ToBackend(textureView->GetTexture())->GetHandle();
 
+                GLenum glAttachment = GL_COLOR_ATTACHMENT0 + static_cast<uint8_t>(i);
+
                 // Attach color buffers.
                 if (textureView->GetTexture()->GetArrayLayers() == 1) {
                     GLenum target = ToBackend(textureView->GetTexture())->GetGLTarget();
-                    gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, target,
-                                            texture, textureView->GetBaseMipLevel());
+                    gl.FramebufferTexture2D(GL_DRAW_FRAMEBUFFER, glAttachment, target, texture,
+                                            textureView->GetBaseMipLevel());
                 } else {
-                    gl.FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i,
-                                               texture, textureView->GetBaseMipLevel(),
+                    gl.FramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, glAttachment, texture,
+                                               textureView->GetBaseMipLevel(),
                                                textureView->GetBaseArrayLayer());
                 }
-                drawBuffers[i] = GL_COLOR_ATTACHMENT0 + i;
-                attachmentCount = i + 1;
+                drawBuffers[i] = glAttachment;
+                attachmentCount = i;
+                attachmentCount++;
             }
-            gl.DrawBuffers(attachmentCount, drawBuffers.data());
+            gl.DrawBuffers(static_cast<uint8_t>(attachmentCount), drawBuffers.data());
 
             if (renderPass->attachmentState->HasDepthStencilAttachment()) {
                 TextureViewBase* textureView = renderPass->depthStencilAttachment.view.Get();
@@ -922,9 +925,10 @@
 
         // Clear framebuffer attachments as needed
         {
-            for (uint32_t i :
+            for (ColorAttachmentIndex index :
                  IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
-                auto* attachmentInfo = &renderPass->colorAttachments[i];
+                uint8_t i = static_cast<uint8_t>(index);
+                auto* attachmentInfo = &renderPass->colorAttachments[index];
 
                 // Load op - color
                 if (attachmentInfo->loadOp == wgpu::LoadOp::Clear) {
diff --git a/src/dawn_native/opengl/RenderPipelineGL.cpp b/src/dawn_native/opengl/RenderPipelineGL.cpp
index 7c70396..10add8c 100644
--- a/src/dawn_native/opengl/RenderPipelineGL.cpp
+++ b/src/dawn_native/opengl/RenderPipelineGL.cpp
@@ -109,21 +109,23 @@
         }
 
         void ApplyColorState(const OpenGLFunctions& gl,
-                             uint32_t attachment,
+                             ColorAttachmentIndex attachment,
                              const ColorStateDescriptor* descriptor) {
+            GLuint colorBuffer = static_cast<GLuint>(static_cast<uint8_t>(attachment));
             if (BlendEnabled(descriptor)) {
-                gl.Enablei(GL_BLEND, attachment);
-                gl.BlendEquationSeparatei(attachment, GLBlendMode(descriptor->colorBlend.operation),
+                gl.Enablei(GL_BLEND, colorBuffer);
+                gl.BlendEquationSeparatei(colorBuffer,
+                                          GLBlendMode(descriptor->colorBlend.operation),
                                           GLBlendMode(descriptor->alphaBlend.operation));
-                gl.BlendFuncSeparatei(attachment,
+                gl.BlendFuncSeparatei(colorBuffer,
                                       GLBlendFactor(descriptor->colorBlend.srcFactor, false),
                                       GLBlendFactor(descriptor->colorBlend.dstFactor, false),
                                       GLBlendFactor(descriptor->alphaBlend.srcFactor, true),
                                       GLBlendFactor(descriptor->alphaBlend.dstFactor, true));
             } else {
-                gl.Disablei(GL_BLEND, attachment);
+                gl.Disablei(GL_BLEND, colorBuffer);
             }
-            gl.ColorMaski(attachment, descriptor->writeMask & wgpu::ColorWriteMask::Red,
+            gl.ColorMaski(colorBuffer, descriptor->writeMask & wgpu::ColorWriteMask::Red,
                           descriptor->writeMask & wgpu::ColorWriteMask::Green,
                           descriptor->writeMask & wgpu::ColorWriteMask::Blue,
                           descriptor->writeMask & wgpu::ColorWriteMask::Alpha);
@@ -265,7 +267,7 @@
             gl.Disable(GL_SAMPLE_ALPHA_TO_COVERAGE);
         }
 
-        for (uint32_t attachmentSlot : IterateBitSet(GetColorAttachmentsMask())) {
+        for (ColorAttachmentIndex attachmentSlot : IterateBitSet(GetColorAttachmentsMask())) {
             ApplyColorState(gl, attachmentSlot, GetColorStateDescriptor(attachmentSlot));
         }
     }
diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp
index cc05048..aad8d51 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn_native/vulkan/CommandBufferVk.cpp
@@ -232,7 +232,7 @@
             {
                 RenderPassCacheQuery query;
 
-                for (uint32_t i :
+                for (ColorAttachmentIndex i :
                      IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
                     const auto& attachmentInfo = renderPass->colorAttachments[i];
 
@@ -264,7 +264,7 @@
                 // Fill in the attachment info that will be chained in the framebuffer create info.
                 std::array<VkImageView, kMaxColorAttachments * 2 + 1> attachments;
 
-                for (uint32_t i :
+                for (ColorAttachmentIndex i :
                      IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
                     auto& attachmentInfo = renderPass->colorAttachments[i];
                     TextureView* view = ToBackend(attachmentInfo.view.Get());
@@ -308,7 +308,7 @@
                     attachmentCount++;
                 }
 
-                for (uint32_t i :
+                for (ColorAttachmentIndex i :
                      IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
                     if (renderPass->colorAttachments[i].resolveTarget.Get() != nullptr) {
                         TextureView* view =
diff --git a/src/dawn_native/vulkan/RenderPassCache.cpp b/src/dawn_native/vulkan/RenderPassCache.cpp
index 9e105fd..9ea9576 100644
--- a/src/dawn_native/vulkan/RenderPassCache.cpp
+++ b/src/dawn_native/vulkan/RenderPassCache.cpp
@@ -37,7 +37,7 @@
 
     // RenderPassCacheQuery
 
-    void RenderPassCacheQuery::SetColor(uint32_t index,
+    void RenderPassCacheQuery::SetColor(ColorAttachmentIndex index,
                                         wgpu::TextureFormat format,
                                         wgpu::LoadOp loadOp,
                                         bool hasResolveTarget) {
@@ -94,13 +94,13 @@
 
         // Contains the attachment description that will be chained in the create info
         // The order of all attachments in attachmentDescs is "color-depthstencil-resolve".
-        constexpr uint32_t kMaxAttachmentCount = kMaxColorAttachments * 2 + 1;
+        constexpr uint8_t kMaxAttachmentCount = kMaxColorAttachments * 2 + 1;
         std::array<VkAttachmentDescription, kMaxAttachmentCount> attachmentDescs = {};
 
         VkSampleCountFlagBits vkSampleCount = VulkanSampleCount(query.sampleCount);
 
         uint32_t colorAttachmentIndex = 0;
-        for (uint32_t i : IterateBitSet(query.colorMask)) {
+        for (ColorAttachmentIndex i : IterateBitSet(query.colorMask)) {
             auto& attachmentRef = colorAttachmentRefs[colorAttachmentIndex];
             auto& attachmentDesc = attachmentDescs[colorAttachmentIndex];
 
@@ -142,7 +142,7 @@
         }
 
         uint32_t resolveAttachmentIndex = 0;
-        for (uint32_t i : IterateBitSet(query.resolveTargetMask)) {
+        for (ColorAttachmentIndex i : IterateBitSet(query.resolveTargetMask)) {
             auto& attachmentRef = resolveAttachmentRefs[resolveAttachmentIndex];
             auto& attachmentDesc = attachmentDescs[attachmentCount];
 
@@ -211,7 +211,7 @@
 
         HashCombine(&hash, Hash(query.resolveTargetMask));
 
-        for (uint32_t i : IterateBitSet(query.colorMask)) {
+        for (ColorAttachmentIndex i : IterateBitSet(query.colorMask)) {
             HashCombine(&hash, query.colorFormats[i], query.colorLoadOp[i]);
         }
 
@@ -239,7 +239,7 @@
             return false;
         }
 
-        for (uint32_t i : IterateBitSet(a.colorMask)) {
+        for (ColorAttachmentIndex i : IterateBitSet(a.colorMask)) {
             if ((a.colorFormats[i] != b.colorFormats[i]) ||
                 (a.colorLoadOp[i] != b.colorLoadOp[i])) {
                 return false;
diff --git a/src/dawn_native/vulkan/RenderPassCache.h b/src/dawn_native/vulkan/RenderPassCache.h
index 6af675b..4ee9441 100644
--- a/src/dawn_native/vulkan/RenderPassCache.h
+++ b/src/dawn_native/vulkan/RenderPassCache.h
@@ -16,8 +16,11 @@
 #define DAWNNATIVE_VULKAN_RENDERPASSCACHE_H_
 
 #include "common/Constants.h"
+#include "common/ityp_array.h"
+#include "common/ityp_bitset.h"
 #include "common/vulkan_platform.h"
 #include "dawn_native/Error.h"
+#include "dawn_native/IntegerTypes.h"
 #include "dawn_native/dawn_platform.h"
 
 #include <array>
@@ -34,7 +37,7 @@
     struct RenderPassCacheQuery {
         // Use these helpers to build the query, they make sure all relevant data is initialized and
         // masks set.
-        void SetColor(uint32_t index,
+        void SetColor(ColorAttachmentIndex index,
                       wgpu::TextureFormat format,
                       wgpu::LoadOp loadOp,
                       bool hasResolveTarget);
@@ -43,10 +46,10 @@
                              wgpu::LoadOp stencilLoadOp);
         void SetSampleCount(uint32_t sampleCount);
 
-        std::bitset<kMaxColorAttachments> colorMask;
-        std::bitset<kMaxColorAttachments> resolveTargetMask;
-        std::array<wgpu::TextureFormat, kMaxColorAttachments> colorFormats;
-        std::array<wgpu::LoadOp, kMaxColorAttachments> colorLoadOp;
+        ityp::bitset<ColorAttachmentIndex, kMaxColorAttachments> colorMask;
+        ityp::bitset<ColorAttachmentIndex, kMaxColorAttachments> resolveTargetMask;
+        ityp::array<ColorAttachmentIndex, wgpu::TextureFormat, kMaxColorAttachments> colorFormats;
+        ityp::array<ColorAttachmentIndex, wgpu::LoadOp, kMaxColorAttachments> colorLoadOp;
 
         bool hasDepthStencil = false;
         wgpu::TextureFormat depthStencilFormat;
diff --git a/src/dawn_native/vulkan/RenderPipelineVk.cpp b/src/dawn_native/vulkan/RenderPipelineVk.cpp
index 2c18cfd..d23d515 100644
--- a/src/dawn_native/vulkan/RenderPipelineVk.cpp
+++ b/src/dawn_native/vulkan/RenderPipelineVk.cpp
@@ -424,10 +424,11 @@
 
         // Initialize the "blend state info" that will be chained in the "create info" from the data
         // pre-computed in the ColorState
-        std::array<VkPipelineColorBlendAttachmentState, kMaxColorAttachments> colorBlendAttachments;
+        ityp::array<ColorAttachmentIndex, VkPipelineColorBlendAttachmentState, kMaxColorAttachments>
+            colorBlendAttachments;
         const EntryPointMetadata::FragmentOutputBaseTypes& fragmentOutputBaseTypes =
             GetStage(SingleShaderStage::Fragment).metadata->fragmentOutputFormatBaseTypes;
-        for (uint32_t i : IterateBitSet(GetColorAttachmentsMask())) {
+        for (ColorAttachmentIndex i : IterateBitSet(GetColorAttachmentsMask())) {
             const ColorStateDescriptor* colorStateDescriptor = GetColorStateDescriptor(i);
             bool isDeclaredInFragmentShader = fragmentOutputBaseTypes[i] != Format::Type::Other;
             colorBlendAttachments[i] =
@@ -469,7 +470,7 @@
         {
             RenderPassCacheQuery query;
 
-            for (uint32_t i : IterateBitSet(GetColorAttachmentsMask())) {
+            for (ColorAttachmentIndex i : IterateBitSet(GetColorAttachmentsMask())) {
                 query.SetColor(i, GetColorAttachmentFormat(i), wgpu::LoadOp::Load, false);
             }
 
diff --git a/src/tests/unittests/validation/ShaderModuleValidationTests.cpp b/src/tests/unittests/validation/ShaderModuleValidationTests.cpp
index d7d1d95..60f1df0 100644
--- a/src/tests/unittests/validation/ShaderModuleValidationTests.cpp
+++ b/src/tests/unittests/validation/ShaderModuleValidationTests.cpp
@@ -99,11 +99,10 @@
     std::ostringstream stream;
     stream << R"(#version 450
               layout(location = )"
-           << kMaxColorAttachments << R"() out vec4 fragColor;
+           << static_cast<unsigned>(kMaxColorAttachments) << R"() out vec4 fragColor;
               void main() {
                   fragColor = vec4(0.0, 1.0, 0.0, 1.0);
               })";
-
     ASSERT_DEVICE_ERROR(utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment,
                                                   stream.str().c_str()));
 }