CommandBuffer: have a state tracker per-pass

Also perform small code simplifications of the CommandBufferStateTracker
now that it only tracks aspects.
diff --git a/src/backend/CommandBuffer.cpp b/src/backend/CommandBuffer.cpp
index 9b74c3f..beb31ca 100644
--- a/src/backend/CommandBuffer.cpp
+++ b/src/backend/CommandBuffer.cpp
@@ -278,8 +278,7 @@
 
     // CommandBufferBuilder
 
-    CommandBufferBuilder::CommandBufferBuilder(DeviceBase* device)
-        : Builder(device), mState(std::make_unique<CommandBufferStateTracker>()) {
+    CommandBufferBuilder::CommandBufferBuilder(DeviceBase* device) : Builder(device) {
     }
 
     CommandBufferBuilder::~CommandBufferBuilder() {
@@ -392,6 +391,7 @@
 
     MaybeError CommandBufferBuilder::ValidateComputePass() {
         PassResourceUsageTracker usageTracker;
+        CommandBufferStateTracker persistentState;
 
         Command type;
         while (mIterator.NextCommandId(&type)) {
@@ -401,20 +401,18 @@
 
                     DAWN_TRY(usageTracker.ValidateUsages(PassType::Compute));
                     mPassResourceUsages.push_back(usageTracker.AcquireResourceUsage());
-
-                    mState->EndPass();
                     return {};
                 } break;
 
                 case Command::Dispatch: {
                     mIterator.NextCommand<DispatchCmd>();
-                    DAWN_TRY(mState->ValidateCanDispatch());
+                    DAWN_TRY(persistentState.ValidateCanDispatch());
                 } break;
 
                 case Command::SetComputePipeline: {
                     SetComputePipelineCmd* cmd = mIterator.NextCommand<SetComputePipelineCmd>();
                     ComputePipelineBase* pipeline = cmd->pipeline.Get();
-                    mState->SetComputePipeline(pipeline);
+                    persistentState.SetComputePipeline(pipeline);
                 } break;
 
                 case Command::SetPushConstants: {
@@ -433,7 +431,7 @@
                     SetBindGroupCmd* cmd = mIterator.NextCommand<SetBindGroupCmd>();
 
                     TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker);
-                    mState->SetBindGroup(cmd->index, cmd->group.Get());
+                    persistentState.SetBindGroup(cmd->index, cmd->group.Get());
                 } break;
 
                 default:
@@ -446,6 +444,7 @@
 
     MaybeError CommandBufferBuilder::ValidateRenderPass(RenderPassDescriptorBase* renderPass) {
         PassResourceUsageTracker usageTracker;
+        CommandBufferStateTracker persistentState;
 
         // Track usage of the render pass attachments
         for (uint32_t i : IterateBitSet(renderPass->GetColorAttachmentMask())) {
@@ -466,19 +465,17 @@
 
                     DAWN_TRY(usageTracker.ValidateUsages(PassType::Render));
                     mPassResourceUsages.push_back(usageTracker.AcquireResourceUsage());
-
-                    mState->EndPass();
                     return {};
                 } break;
 
                 case Command::DrawArrays: {
                     mIterator.NextCommand<DrawArraysCmd>();
-                    DAWN_TRY(mState->ValidateCanDrawArrays());
+                    DAWN_TRY(persistentState.ValidateCanDrawArrays());
                 } break;
 
                 case Command::DrawElements: {
                     mIterator.NextCommand<DrawElementsCmd>();
-                    DAWN_TRY(mState->ValidateCanDrawElements());
+                    DAWN_TRY(persistentState.ValidateCanDrawElements());
                 } break;
 
                 case Command::SetRenderPipeline: {
@@ -489,7 +486,7 @@
                         DAWN_RETURN_ERROR("Pipeline is incompatible with this render pass");
                     }
 
-                    mState->SetRenderPipeline(pipeline);
+                    persistentState.SetRenderPipeline(pipeline);
                 } break;
 
                 case Command::SetPushConstants: {
@@ -522,14 +519,14 @@
                     SetBindGroupCmd* cmd = mIterator.NextCommand<SetBindGroupCmd>();
 
                     TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker);
-                    mState->SetBindGroup(cmd->index, cmd->group.Get());
+                    persistentState.SetBindGroup(cmd->index, cmd->group.Get());
                 } break;
 
                 case Command::SetIndexBuffer: {
                     SetIndexBufferCmd* cmd = mIterator.NextCommand<SetIndexBufferCmd>();
 
                     usageTracker.BufferUsedAs(cmd->buffer.Get(), dawn::BufferUsageBit::Index);
-                    DAWN_TRY(mState->SetIndexBuffer());
+                    persistentState.SetIndexBuffer();
                 } break;
 
                 case Command::SetVertexBuffers: {
@@ -539,8 +536,8 @@
 
                     for (uint32_t i = 0; i < cmd->count; ++i) {
                         usageTracker.BufferUsedAs(buffers[i].Get(), dawn::BufferUsageBit::Vertex);
-                        DAWN_TRY(mState->SetVertexBuffer(cmd->startSlot + i));
                     }
+                    persistentState.SetVertexBuffer(cmd->startSlot, cmd->count);
                 } break;
 
                 default:
diff --git a/src/backend/CommandBuffer.h b/src/backend/CommandBuffer.h
index 4efd772..ce15623 100644
--- a/src/backend/CommandBuffer.h
+++ b/src/backend/CommandBuffer.h
@@ -31,7 +31,6 @@
 
     class BindGroupBase;
     class BufferBase;
-    class CommandBufferStateTracker;
     class FramebufferBase;
     class DeviceBase;
     class PipelineBase;
@@ -138,7 +137,6 @@
         MaybeError ValidateComputePass();
         MaybeError ValidateRenderPass(RenderPassDescriptorBase* renderPass);
 
-        std::unique_ptr<CommandBufferStateTracker> mState;
         CommandAllocator mAllocator;
         CommandIterator mIterator;
         bool mWasMovedToIterator = false;
diff --git a/src/backend/CommandBufferStateTracker.cpp b/src/backend/CommandBufferStateTracker.cpp
index 943cf8f..37b04c3 100644
--- a/src/backend/CommandBufferStateTracker.cpp
+++ b/src/backend/CommandBufferStateTracker.cpp
@@ -15,69 +15,124 @@
 #include "backend/CommandBufferStateTracker.h"
 
 #include "backend/BindGroup.h"
-#include "backend/BindGroupLayout.h"
-#include "backend/Buffer.h"
 #include "backend/ComputePipeline.h"
 #include "backend/Forward.h"
 #include "backend/InputState.h"
 #include "backend/PipelineLayout.h"
-#include "backend/RenderPassDescriptor.h"
 #include "backend/RenderPipeline.h"
-#include "backend/Texture.h"
 #include "common/Assert.h"
 #include "common/BitSetIterator.h"
 
 namespace backend {
 
-    MaybeError CommandBufferStateTracker::ValidateCanDispatch() {
-        constexpr ValidationAspects requiredAspects =
-            1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS;
-        if ((requiredAspects & ~mAspects).none()) {
-            // Fast return-true path if everything is good
-            return {};
-        }
+    enum ValidationAspect {
+        VALIDATION_ASPECT_PIPELINE,
+        VALIDATION_ASPECT_BIND_GROUPS,
+        VALIDATION_ASPECT_VERTEX_BUFFERS,
+        VALIDATION_ASPECT_INDEX_BUFFER,
 
-        if (!mAspects[VALIDATION_ASPECT_PIPELINE]) {
-            DAWN_RETURN_ERROR("No active compute pipeline");
-        }
-        // Compute the lazily computed mAspects
-        if (!RecomputeHaveAspectBindGroups()) {
-            DAWN_RETURN_ERROR("Bind group state not valid");
-        }
-        return {};
+        VALIDATION_ASPECT_COUNT
+    };
+    static_assert(VALIDATION_ASPECT_COUNT == CommandBufferStateTracker::kNumAspects, "");
+
+    static constexpr CommandBufferStateTracker::ValidationAspects kDispatchAspects =
+        1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS;
+
+    static constexpr CommandBufferStateTracker::ValidationAspects kDrawArraysAspects =
+        1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS |
+        1 << VALIDATION_ASPECT_VERTEX_BUFFERS;
+
+    static constexpr CommandBufferStateTracker::ValidationAspects kDrawElementsAspects =
+        1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS |
+        1 << VALIDATION_ASPECT_VERTEX_BUFFERS | 1 << VALIDATION_ASPECT_INDEX_BUFFER;
+
+    static constexpr CommandBufferStateTracker::ValidationAspects kLazyAspects =
+        1 << VALIDATION_ASPECT_BIND_GROUPS | 1 << VALIDATION_ASPECT_VERTEX_BUFFERS;
+
+    MaybeError CommandBufferStateTracker::ValidateCanDispatch() {
+        return ValidateOperation(kDispatchAspects);
     }
 
     MaybeError CommandBufferStateTracker::ValidateCanDrawArrays() {
-        constexpr ValidationAspects requiredAspects = 1 << VALIDATION_ASPECT_PIPELINE |
-                                                      1 << VALIDATION_ASPECT_BIND_GROUPS |
-                                                      1 << VALIDATION_ASPECT_VERTEX_BUFFERS;
-        if ((requiredAspects & ~mAspects).none()) {
-            // Fast return-true path if everything is good
-            return {};
-        }
-
-        return RevalidateCanDraw();
+        return ValidateOperation(kDrawArraysAspects);
     }
 
     MaybeError CommandBufferStateTracker::ValidateCanDrawElements() {
-        constexpr ValidationAspects requiredAspects =
-            1 << VALIDATION_ASPECT_PIPELINE | 1 << VALIDATION_ASPECT_BIND_GROUPS |
-            1 << VALIDATION_ASPECT_VERTEX_BUFFERS | 1 << VALIDATION_ASPECT_INDEX_BUFFER;
-        if ((requiredAspects & ~mAspects).none()) {
-            // Fast return-true path if everything is good
+        return ValidateOperation(kDrawElementsAspects);
+    }
+
+    MaybeError CommandBufferStateTracker::ValidateOperation(ValidationAspects requiredAspects) {
+        // Fast return-true path if everything is good
+        ValidationAspects missingAspects = requiredAspects & ~mAspects;
+        if (missingAspects.none()) {
             return {};
         }
 
-        if (!mAspects[VALIDATION_ASPECT_INDEX_BUFFER]) {
-            DAWN_RETURN_ERROR("Cannot DrawElements without index buffer set");
+        // Generate an error immediately if a non-lazy aspect is missing as computing lazy aspects
+        // requires the pipeline to be set.
+        if ((missingAspects & ~kLazyAspects).any()) {
+            return GenerateAspectError(missingAspects);
         }
-        return RevalidateCanDraw();
+
+        RecomputeLazyAspects(missingAspects);
+
+        missingAspects = requiredAspects & ~mAspects;
+        if (missingAspects.any()) {
+            return GenerateAspectError(missingAspects);
+        }
+
+        return {};
     }
 
-    void CommandBufferStateTracker::EndPass() {
-        mInputsSet.reset();
-        mAspects = 0;
-        mBindgroups.fill(nullptr);
+    void CommandBufferStateTracker::RecomputeLazyAspects(ValidationAspects aspects) {
+        ASSERT(mAspects[VALIDATION_ASPECT_PIPELINE]);
+        ASSERT((aspects & ~kLazyAspects).none());
+
+        if (aspects[VALIDATION_ASPECT_BIND_GROUPS]) {
+            bool matches = true;
+
+            for (uint32_t i : IterateBitSet(mLastPipelineLayout->GetBindGroupLayoutsMask())) {
+                if (mLastPipelineLayout->GetBindGroupLayout(i) != mBindgroups[i]->GetLayout()) {
+                    matches = false;
+                    break;
+                }
+            }
+
+            if (matches) {
+                mAspects.set(VALIDATION_ASPECT_BIND_GROUPS);
+            }
+        }
+
+        if (aspects[VALIDATION_ASPECT_VERTEX_BUFFERS]) {
+            ASSERT(mLastRenderPipeline != nullptr);
+
+            auto requiredInputs = mLastRenderPipeline->GetInputState()->GetInputsSetMask();
+            if ((mInputsSet & requiredInputs) == requiredInputs) {
+                mAspects.set(VALIDATION_ASPECT_VERTEX_BUFFERS);
+            }
+        }
+    }
+
+    MaybeError CommandBufferStateTracker::GenerateAspectError(ValidationAspects aspects) {
+        ASSERT(aspects.any());
+
+        if (aspects[VALIDATION_ASPECT_INDEX_BUFFER]) {
+            DAWN_RETURN_ERROR("Missing index buffer");
+        }
+
+        if (aspects[VALIDATION_ASPECT_VERTEX_BUFFERS]) {
+            DAWN_RETURN_ERROR("Missing vertex buffer");
+        }
+
+        if (aspects[VALIDATION_ASPECT_BIND_GROUPS]) {
+            DAWN_RETURN_ERROR("Missing bind group");
+        }
+
+        if (aspects[VALIDATION_ASPECT_PIPELINE]) {
+            DAWN_RETURN_ERROR("Missing pipeline");
+        }
+
+        UNREACHABLE();
     }
 
     void CommandBufferStateTracker::SetComputePipeline(ComputePipelineBase* pipeline) {
@@ -90,96 +145,26 @@
     }
 
     void CommandBufferStateTracker::SetBindGroup(uint32_t index, BindGroupBase* bindgroup) {
-        mBindgroupsSet.set(index);
         mBindgroups[index] = bindgroup;
     }
 
-    MaybeError CommandBufferStateTracker::SetIndexBuffer() {
-        if (!HavePipeline()) {
-            DAWN_RETURN_ERROR("Can't set the index buffer without a pipeline");
-        }
-
+    void CommandBufferStateTracker::SetIndexBuffer() {
         mAspects.set(VALIDATION_ASPECT_INDEX_BUFFER);
-        return {};
     }
 
-    MaybeError CommandBufferStateTracker::SetVertexBuffer(uint32_t index) {
-        if (!HavePipeline()) {
-            DAWN_RETURN_ERROR("Can't set vertex buffers without a pipeline");
+    void CommandBufferStateTracker::SetVertexBuffer(uint32_t start, uint32_t count) {
+        for (uint32_t i = 0; i < count; ++i) {
+            mInputsSet.set(start + i);
         }
-
-        mInputsSet.set(index);
-        return {};
-    }
-
-    bool CommandBufferStateTracker::RecomputeHaveAspectBindGroups() {
-        if (mAspects[VALIDATION_ASPECT_BIND_GROUPS]) {
-            return true;
-        }
-        // Assumes we have a pipeline already
-        if (!mBindgroupsSet.all()) {
-            return false;
-        }
-        for (size_t i = 0; i < mBindgroups.size(); ++i) {
-            if (auto* bindgroup = mBindgroups[i]) {
-                // TODO(kainino@chromium.org): bind group compatibility
-                auto* pipelineBGL = mLastPipeline->GetLayout()->GetBindGroupLayout(i);
-                if (pipelineBGL && bindgroup->GetLayout() != pipelineBGL) {
-                    return false;
-                }
-            }
-        }
-        mAspects.set(VALIDATION_ASPECT_BIND_GROUPS);
-        return true;
-    }
-
-    bool CommandBufferStateTracker::RecomputeHaveAspectVertexBuffers() {
-        if (mAspects[VALIDATION_ASPECT_VERTEX_BUFFERS]) {
-            return true;
-        }
-        // Assumes we have a pipeline already
-        auto requiredInputs = mLastRenderPipeline->GetInputState()->GetInputsSetMask();
-        if ((mInputsSet & requiredInputs) == requiredInputs) {
-            mAspects.set(VALIDATION_ASPECT_VERTEX_BUFFERS);
-            return true;
-        }
-        return false;
-    }
-
-    bool CommandBufferStateTracker::HavePipeline() const {
-        return mAspects[VALIDATION_ASPECT_PIPELINE];
-    }
-
-    MaybeError CommandBufferStateTracker::RevalidateCanDraw() {
-        if (!mAspects[VALIDATION_ASPECT_PIPELINE]) {
-            DAWN_RETURN_ERROR("No active render pipeline");
-        }
-        // Compute the lazily computed mAspects
-        if (!RecomputeHaveAspectBindGroups()) {
-            DAWN_RETURN_ERROR("Bind group state not valid");
-        }
-        if (!RecomputeHaveAspectVertexBuffers()) {
-            DAWN_RETURN_ERROR("Some vertex buffers are not set");
-        }
-        return {};
     }
 
     void CommandBufferStateTracker::SetPipelineCommon(PipelineBase* pipeline) {
-        PipelineLayoutBase* layout = pipeline->GetLayout();
+        mLastPipelineLayout = pipeline->GetLayout();
 
         mAspects.set(VALIDATION_ASPECT_PIPELINE);
 
-        mAspects.reset(VALIDATION_ASPECT_BIND_GROUPS);
-        mAspects.reset(VALIDATION_ASPECT_VERTEX_BUFFERS);
-        // Reset bindgroups but mark unused bindgroups as valid
-        mBindgroupsSet = ~layout->GetBindGroupLayoutsMask();
-
-        // Only bindgroups that were not the same layout in the last pipeline need to be set again.
-        if (mLastPipeline) {
-            mBindgroupsSet |= layout->InheritedGroupsMask(mLastPipeline->GetLayout());
-        }
-
-        mLastPipeline = pipeline;
+        // Reset lazy aspects so they get recomputed on the next operation.
+        mAspects &= ~kLazyAspects;
     }
 
 }  // namespace backend
diff --git a/src/backend/CommandBufferStateTracker.h b/src/backend/CommandBufferStateTracker.h
index 45079e2..7b4454b 100644
--- a/src/backend/CommandBufferStateTracker.h
+++ b/src/backend/CommandBufferStateTracker.h
@@ -28,45 +28,33 @@
     class CommandBufferStateTracker {
       public:
         // Non-state-modifying validation functions
-        MaybeError ValidateCanCopy() const;
         MaybeError ValidateCanDispatch();
         MaybeError ValidateCanDrawArrays();
         MaybeError ValidateCanDrawElements();
 
         // State-modifying methods
-        void EndPass();
         void SetComputePipeline(ComputePipelineBase* pipeline);
         void SetRenderPipeline(RenderPipelineBase* pipeline);
         void SetBindGroup(uint32_t index, BindGroupBase* bindgroup);
-        MaybeError SetIndexBuffer();
-        MaybeError SetVertexBuffer(uint32_t index);
+        void SetIndexBuffer();
+        void SetVertexBuffer(uint32_t start, uint32_t count);
+
+        static constexpr size_t kNumAspects = 4;
+        using ValidationAspects = std::bitset<kNumAspects>;
 
       private:
-        enum ValidationAspect {
-            VALIDATION_ASPECT_PIPELINE,
-            VALIDATION_ASPECT_BIND_GROUPS,
-            VALIDATION_ASPECT_VERTEX_BUFFERS,
-            VALIDATION_ASPECT_INDEX_BUFFER,
-
-            VALIDATION_ASPECT_COUNT
-        };
-        using ValidationAspects = std::bitset<VALIDATION_ASPECT_COUNT>;
-
-        // Queries for lazily evaluated aspects
-        bool RecomputeHaveAspectBindGroups();
-        bool RecomputeHaveAspectVertexBuffers();
-
-        bool HavePipeline() const;
-        MaybeError RevalidateCanDraw();
+        MaybeError ValidateOperation(ValidationAspects requiredAspects);
+        void RecomputeLazyAspects(ValidationAspects aspects);
+        MaybeError GenerateAspectError(ValidationAspects aspects);
 
         void SetPipelineCommon(PipelineBase* pipeline);
 
         ValidationAspects mAspects;
 
-        std::bitset<kMaxBindGroups> mBindgroupsSet;
         std::array<BindGroupBase*, kMaxBindGroups> mBindgroups = {};
         std::bitset<kMaxVertexInputs> mInputsSet;
-        PipelineBase* mLastPipeline = nullptr;
+
+        PipelineLayoutBase* mLastPipelineLayout = nullptr;
         RenderPipelineBase* mLastRenderPipeline = nullptr;
     };