Factor RenderPass command validation

Validation of GPURenderBundle will share code with RenderPass
validation. Factor validation of commands for GPURenderBundle
into a separate function.

Bug: dawn:154
Change-Id: I79a229592ead27d462da0dd2d12fbdb95443ff19
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/9980
Commit-Queue: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp
index 0fff02f..13bea84 100644
--- a/src/dawn_native/CommandEncoder.cpp
+++ b/src/dawn_native/CommandEncoder.cpp
@@ -19,6 +19,7 @@
 #include "dawn_native/Buffer.h"
 #include "dawn_native/CommandBuffer.h"
 #include "dawn_native/CommandBufferStateTracker.h"
+#include "dawn_native/CommandValidation.h"
 #include "dawn_native/Commands.h"
 #include "dawn_native/ComputePassEncoder.h"
 #include "dawn_native/Device.h"
@@ -121,29 +122,6 @@
             return {};
         }
 
-        inline MaybeError PushDebugMarkerStack(unsigned int* counter) {
-            *counter += 1;
-            return {};
-        }
-
-        inline MaybeError PopDebugMarkerStack(unsigned int* counter) {
-            if (*counter == 0) {
-                return DAWN_VALIDATION_ERROR("Pop must be balanced by a corresponding Push.");
-            } else {
-                *counter -= 1;
-            }
-
-            return {};
-        }
-
-        inline MaybeError ValidateDebugGroups(const unsigned int counter) {
-            if (counter != 0) {
-                return DAWN_VALIDATION_ERROR("Each Push must be balanced by a corresponding Pop.");
-            }
-
-            return {};
-        }
-
         MaybeError ValidateTextureSampleCountInCopyCommands(const TextureBase* texture) {
             if (texture->GetSampleCount() > 1) {
                 return DAWN_VALIDATION_ERROR("The sample count of textures must be 1");
@@ -476,39 +454,6 @@
             return {};
         }
 
-        void TrackBindGroupResourceUsage(BindGroupBase* group, PassResourceUsageTracker* tracker) {
-            const auto& layoutInfo = group->GetLayout()->GetBindingInfo();
-
-            for (uint32_t i : IterateBitSet(layoutInfo.mask)) {
-                dawn::BindingType type = layoutInfo.types[i];
-
-                switch (type) {
-                    case dawn::BindingType::UniformBuffer: {
-                        BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer;
-                        tracker->BufferUsedAs(buffer, dawn::BufferUsageBit::Uniform);
-                    } break;
-
-                    case dawn::BindingType::StorageBuffer: {
-                        BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer;
-                        tracker->BufferUsedAs(buffer, dawn::BufferUsageBit::Storage);
-                    } break;
-
-                    case dawn::BindingType::SampledTexture: {
-                        TextureBase* texture = group->GetBindingAsTextureView(i)->GetTexture();
-                        tracker->TextureUsedAs(texture, dawn::TextureUsageBit::Sampled);
-                    } break;
-
-                    case dawn::BindingType::Sampler:
-                        break;
-
-                    case dawn::BindingType::StorageTexture:
-                    case dawn::BindingType::ReadonlyStorageBuffer:
-                        UNREACHABLE();
-                        break;
-                }
-            }
-        }
-
     }  // namespace
 
     CommandEncoderBase::CommandEncoderBase(DeviceBase* device, const CommandEncoderDescriptor*)
@@ -749,12 +694,12 @@
             switch (type) {
                 case Command::BeginComputePass: {
                     commands->NextCommand<BeginComputePassCmd>();
-                    DAWN_TRY(ValidateComputePass(commands));
+                    DAWN_TRY(ValidateComputePass(commands, &mResourceUsages.perPass));
                 } break;
 
                 case Command::BeginRenderPass: {
                     BeginRenderPassCmd* cmd = commands->NextCommand<BeginRenderPassCmd>();
-                    DAWN_TRY(ValidateRenderPass(commands, cmd));
+                    DAWN_TRY(ValidateRenderPass(commands, cmd, &mResourceUsages.perPass));
                 } break;
 
                 case Command::CopyBufferToBuffer: {
@@ -881,211 +826,4 @@
         return {};
     }
 
-    MaybeError CommandEncoderBase::ValidateComputePass(CommandIterator* commands) {
-        PassResourceUsageTracker usageTracker;
-        CommandBufferStateTracker persistentState;
-
-        Command type;
-        while (commands->NextCommandId(&type)) {
-            switch (type) {
-                case Command::EndComputePass: {
-                    commands->NextCommand<EndComputePassCmd>();
-
-                    DAWN_TRY(ValidateDebugGroups(mDebugGroupStackSize));
-
-                    DAWN_TRY(usageTracker.ValidateComputePassUsages());
-                    mResourceUsages.perPass.push_back(usageTracker.AcquireResourceUsage());
-                    return {};
-                } break;
-
-                case Command::Dispatch: {
-                    commands->NextCommand<DispatchCmd>();
-                    DAWN_TRY(persistentState.ValidateCanDispatch());
-                } break;
-
-                case Command::DispatchIndirect: {
-                    DispatchIndirectCmd* cmd = commands->NextCommand<DispatchIndirectCmd>();
-                    DAWN_TRY(persistentState.ValidateCanDispatch());
-                    usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(),
-                                              dawn::BufferUsageBit::Indirect);
-                } break;
-
-                case Command::InsertDebugMarker: {
-                    InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
-                    commands->NextData<char>(cmd->length + 1);
-                } break;
-
-                case Command::PopDebugGroup: {
-                    commands->NextCommand<PopDebugGroupCmd>();
-                    DAWN_TRY(PopDebugMarkerStack(&mDebugGroupStackSize));
-                } break;
-
-                case Command::PushDebugGroup: {
-                    PushDebugGroupCmd* cmd = commands->NextCommand<PushDebugGroupCmd>();
-                    commands->NextData<char>(cmd->length + 1);
-                    DAWN_TRY(PushDebugMarkerStack(&mDebugGroupStackSize));
-                } break;
-
-                case Command::SetComputePipeline: {
-                    SetComputePipelineCmd* cmd = commands->NextCommand<SetComputePipelineCmd>();
-                    ComputePipelineBase* pipeline = cmd->pipeline.Get();
-                    persistentState.SetComputePipeline(pipeline);
-                } break;
-
-                case Command::SetBindGroup: {
-                    SetBindGroupCmd* cmd = commands->NextCommand<SetBindGroupCmd>();
-                    if (cmd->dynamicOffsetCount > 0) {
-                        commands->NextData<uint64_t>(cmd->dynamicOffsetCount);
-                    }
-
-                    TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker);
-                    persistentState.SetBindGroup(cmd->index, cmd->group.Get());
-                } break;
-
-                default:
-                    return DAWN_VALIDATION_ERROR("Command disallowed inside a compute pass");
-            }
-        }
-
-        UNREACHABLE();
-        return DAWN_VALIDATION_ERROR("Unfinished compute pass");
-    }
-
-    MaybeError CommandEncoderBase::ValidateRenderPass(CommandIterator* commands,
-                                                      BeginRenderPassCmd* renderPass) {
-        PassResourceUsageTracker usageTracker;
-        CommandBufferStateTracker persistentState;
-
-        // Track usage of the render pass attachments
-        for (uint32_t i : IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
-            RenderPassColorAttachmentInfo* colorAttachment = &renderPass->colorAttachments[i];
-            TextureBase* texture = colorAttachment->view->GetTexture();
-            usageTracker.TextureUsedAs(texture, dawn::TextureUsageBit::OutputAttachment);
-
-            TextureViewBase* resolveTarget = colorAttachment->resolveTarget.Get();
-            if (resolveTarget != nullptr) {
-                usageTracker.TextureUsedAs(resolveTarget->GetTexture(),
-                                           dawn::TextureUsageBit::OutputAttachment);
-            }
-        }
-
-        if (renderPass->attachmentState->HasDepthStencilAttachment()) {
-            TextureBase* texture = renderPass->depthStencilAttachment.view->GetTexture();
-            usageTracker.TextureUsedAs(texture, dawn::TextureUsageBit::OutputAttachment);
-        }
-
-        Command type;
-        while (commands->NextCommandId(&type)) {
-            switch (type) {
-                case Command::EndRenderPass: {
-                    commands->NextCommand<EndRenderPassCmd>();
-
-                    DAWN_TRY(ValidateDebugGroups(mDebugGroupStackSize));
-
-                    DAWN_TRY(usageTracker.ValidateRenderPassUsages());
-                    mResourceUsages.perPass.push_back(usageTracker.AcquireResourceUsage());
-                    return {};
-                } break;
-
-                case Command::Draw: {
-                    commands->NextCommand<DrawCmd>();
-                    DAWN_TRY(persistentState.ValidateCanDraw());
-                } break;
-
-                case Command::DrawIndexed: {
-                    commands->NextCommand<DrawIndexedCmd>();
-                    DAWN_TRY(persistentState.ValidateCanDrawIndexed());
-                } break;
-
-                case Command::DrawIndirect: {
-                    DrawIndirectCmd* cmd = commands->NextCommand<DrawIndirectCmd>();
-                    DAWN_TRY(persistentState.ValidateCanDraw());
-                    usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(),
-                                              dawn::BufferUsageBit::Indirect);
-                } break;
-
-                case Command::DrawIndexedIndirect: {
-                    DrawIndexedIndirectCmd* cmd = commands->NextCommand<DrawIndexedIndirectCmd>();
-                    DAWN_TRY(persistentState.ValidateCanDrawIndexed());
-                    usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(),
-                                              dawn::BufferUsageBit::Indirect);
-                } break;
-
-                case Command::InsertDebugMarker: {
-                    InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
-                    commands->NextData<char>(cmd->length + 1);
-                } break;
-
-                case Command::PopDebugGroup: {
-                    commands->NextCommand<PopDebugGroupCmd>();
-                    DAWN_TRY(PopDebugMarkerStack(&mDebugGroupStackSize));
-                } break;
-
-                case Command::PushDebugGroup: {
-                    PushDebugGroupCmd* cmd = commands->NextCommand<PushDebugGroupCmd>();
-                    commands->NextData<char>(cmd->length + 1);
-                    DAWN_TRY(PushDebugMarkerStack(&mDebugGroupStackSize));
-                } break;
-
-                case Command::SetRenderPipeline: {
-                    SetRenderPipelineCmd* cmd = commands->NextCommand<SetRenderPipelineCmd>();
-                    RenderPipelineBase* pipeline = cmd->pipeline.Get();
-
-                    DAWN_TRY(pipeline->ValidateCompatibleWith(renderPass));
-                    persistentState.SetRenderPipeline(pipeline);
-                } break;
-
-                case Command::SetStencilReference: {
-                    commands->NextCommand<SetStencilReferenceCmd>();
-                } break;
-
-                case Command::SetBlendColor: {
-                    commands->NextCommand<SetBlendColorCmd>();
-                } break;
-
-                case Command::SetViewport: {
-                    commands->NextCommand<SetViewportCmd>();
-                } break;
-
-                case Command::SetScissorRect: {
-                    commands->NextCommand<SetScissorRectCmd>();
-                } break;
-
-                case Command::SetBindGroup: {
-                    SetBindGroupCmd* cmd = commands->NextCommand<SetBindGroupCmd>();
-                    if (cmd->dynamicOffsetCount > 0) {
-                        commands->NextData<uint64_t>(cmd->dynamicOffsetCount);
-                    }
-
-                    TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker);
-                    persistentState.SetBindGroup(cmd->index, cmd->group.Get());
-                } break;
-
-                case Command::SetIndexBuffer: {
-                    SetIndexBufferCmd* cmd = commands->NextCommand<SetIndexBufferCmd>();
-
-                    usageTracker.BufferUsedAs(cmd->buffer.Get(), dawn::BufferUsageBit::Index);
-                    persistentState.SetIndexBuffer();
-                } break;
-
-                case Command::SetVertexBuffers: {
-                    SetVertexBuffersCmd* cmd = commands->NextCommand<SetVertexBuffersCmd>();
-                    auto buffers = commands->NextData<Ref<BufferBase>>(cmd->count);
-                    commands->NextData<uint64_t>(cmd->count);
-
-                    for (uint32_t i = 0; i < cmd->count; ++i) {
-                        usageTracker.BufferUsedAs(buffers[i].Get(), dawn::BufferUsageBit::Vertex);
-                    }
-                    persistentState.SetVertexBuffer(cmd->startSlot, cmd->count);
-                } break;
-
-                default:
-                    return DAWN_VALIDATION_ERROR("Command disallowed inside a render pass");
-            }
-        }
-
-        UNREACHABLE();
-        return DAWN_VALIDATION_ERROR("Unfinished render pass");
-    }
-
 }  // namespace dawn_native
diff --git a/src/dawn_native/CommandEncoder.h b/src/dawn_native/CommandEncoder.h
index 3a59e8e..bcb1137 100644
--- a/src/dawn_native/CommandEncoder.h
+++ b/src/dawn_native/CommandEncoder.h
@@ -56,15 +56,11 @@
 
       private:
         MaybeError ValidateFinish(const CommandBufferDescriptor* descriptor);
-        MaybeError ValidateComputePass(CommandIterator* commands);
-        MaybeError ValidateRenderPass(CommandIterator* commands, BeginRenderPassCmd* renderPass);
 
         EncodingContext mEncodingContext;
 
         bool mWereResourceUsagesAcquired = false;
         CommandBufferResourceUsage mResourceUsages;
-
-        unsigned int mDebugGroupStackSize = 0;
     };
 
 }  // namespace dawn_native
diff --git a/src/dawn_native/CommandValidation.cpp b/src/dawn_native/CommandValidation.cpp
new file mode 100644
index 0000000..9099a9e
--- /dev/null
+++ b/src/dawn_native/CommandValidation.cpp
@@ -0,0 +1,321 @@
+// Copyright 2019 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.
+
+#include "dawn_native/CommandValidation.h"
+
+#include "common/BitSetIterator.h"
+#include "dawn_native/BindGroup.h"
+#include "dawn_native/CommandBufferStateTracker.h"
+#include "dawn_native/Commands.h"
+#include "dawn_native/PassResourceUsageTracker.h"
+#include "dawn_native/RenderPipeline.h"
+
+namespace dawn_native {
+
+    namespace {
+
+        inline MaybeError PushDebugMarkerStack(unsigned int* counter) {
+            *counter += 1;
+            return {};
+        }
+
+        inline MaybeError PopDebugMarkerStack(unsigned int* counter) {
+            if (*counter == 0) {
+                return DAWN_VALIDATION_ERROR("Pop must be balanced by a corresponding Push.");
+            } else {
+                *counter -= 1;
+            }
+
+            return {};
+        }
+
+        inline MaybeError ValidateDebugGroups(const unsigned int counter) {
+            if (counter != 0) {
+                return DAWN_VALIDATION_ERROR("Each Push must be balanced by a corresponding Pop.");
+            }
+
+            return {};
+        }
+
+        void TrackBindGroupResourceUsage(BindGroupBase* group,
+                                         PassResourceUsageTracker* usageTracker) {
+            const auto& layoutInfo = group->GetLayout()->GetBindingInfo();
+
+            for (uint32_t i : IterateBitSet(layoutInfo.mask)) {
+                dawn::BindingType type = layoutInfo.types[i];
+
+                switch (type) {
+                    case dawn::BindingType::UniformBuffer: {
+                        BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer;
+                        usageTracker->BufferUsedAs(buffer, dawn::BufferUsageBit::Uniform);
+                    } break;
+
+                    case dawn::BindingType::StorageBuffer: {
+                        BufferBase* buffer = group->GetBindingAsBufferBinding(i).buffer;
+                        usageTracker->BufferUsedAs(buffer, dawn::BufferUsageBit::Storage);
+                    } break;
+
+                    case dawn::BindingType::SampledTexture: {
+                        TextureBase* texture = group->GetBindingAsTextureView(i)->GetTexture();
+                        usageTracker->TextureUsedAs(texture, dawn::TextureUsageBit::Sampled);
+                    } break;
+
+                    case dawn::BindingType::Sampler:
+                        break;
+
+                    case dawn::BindingType::StorageTexture:
+                    case dawn::BindingType::ReadonlyStorageBuffer:
+                        UNREACHABLE();
+                        break;
+                }
+            }
+        }
+
+        inline MaybeError ValidateRenderBundleCommand(CommandIterator* commands,
+                                                      Command type,
+                                                      PassResourceUsageTracker* usageTracker,
+                                                      CommandBufferStateTracker* commandBufferState,
+                                                      const AttachmentState* attachmentState,
+                                                      unsigned int* debugGroupStackSize,
+                                                      const char* disallowedMessage) {
+            switch (type) {
+                case Command::Draw: {
+                    commands->NextCommand<DrawCmd>();
+                    DAWN_TRY(commandBufferState->ValidateCanDraw());
+                } break;
+
+                case Command::DrawIndexed: {
+                    commands->NextCommand<DrawIndexedCmd>();
+                    DAWN_TRY(commandBufferState->ValidateCanDrawIndexed());
+                } break;
+
+                case Command::DrawIndirect: {
+                    DrawIndirectCmd* cmd = commands->NextCommand<DrawIndirectCmd>();
+                    DAWN_TRY(commandBufferState->ValidateCanDraw());
+                    usageTracker->BufferUsedAs(cmd->indirectBuffer.Get(),
+                                               dawn::BufferUsageBit::Indirect);
+                } break;
+
+                case Command::DrawIndexedIndirect: {
+                    DrawIndexedIndirectCmd* cmd = commands->NextCommand<DrawIndexedIndirectCmd>();
+                    DAWN_TRY(commandBufferState->ValidateCanDrawIndexed());
+                    usageTracker->BufferUsedAs(cmd->indirectBuffer.Get(),
+                                               dawn::BufferUsageBit::Indirect);
+                } break;
+
+                case Command::InsertDebugMarker: {
+                    InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
+                    commands->NextData<char>(cmd->length + 1);
+                } break;
+
+                case Command::PopDebugGroup: {
+                    commands->NextCommand<PopDebugGroupCmd>();
+                    DAWN_TRY(PopDebugMarkerStack(debugGroupStackSize));
+                } break;
+
+                case Command::PushDebugGroup: {
+                    PushDebugGroupCmd* cmd = commands->NextCommand<PushDebugGroupCmd>();
+                    commands->NextData<char>(cmd->length + 1);
+                    DAWN_TRY(PushDebugMarkerStack(debugGroupStackSize));
+                } break;
+
+                case Command::SetRenderPipeline: {
+                    SetRenderPipelineCmd* cmd = commands->NextCommand<SetRenderPipelineCmd>();
+                    RenderPipelineBase* pipeline = cmd->pipeline.Get();
+
+                    if (DAWN_UNLIKELY(pipeline->GetAttachmentState() != attachmentState)) {
+                        return DAWN_VALIDATION_ERROR("Pipeline attachment state is not compatible");
+                    }
+                    commandBufferState->SetRenderPipeline(pipeline);
+                } break;
+
+                case Command::SetBindGroup: {
+                    SetBindGroupCmd* cmd = commands->NextCommand<SetBindGroupCmd>();
+                    if (cmd->dynamicOffsetCount > 0) {
+                        commands->NextData<uint64_t>(cmd->dynamicOffsetCount);
+                    }
+
+                    TrackBindGroupResourceUsage(cmd->group.Get(), usageTracker);
+                    commandBufferState->SetBindGroup(cmd->index, cmd->group.Get());
+                } break;
+
+                case Command::SetIndexBuffer: {
+                    SetIndexBufferCmd* cmd = commands->NextCommand<SetIndexBufferCmd>();
+
+                    usageTracker->BufferUsedAs(cmd->buffer.Get(), dawn::BufferUsageBit::Index);
+                    commandBufferState->SetIndexBuffer();
+                } break;
+
+                case Command::SetVertexBuffers: {
+                    SetVertexBuffersCmd* cmd = commands->NextCommand<SetVertexBuffersCmd>();
+                    auto buffers = commands->NextData<Ref<BufferBase>>(cmd->count);
+                    commands->NextData<uint64_t>(cmd->count);
+
+                    for (uint32_t i = 0; i < cmd->count; ++i) {
+                        usageTracker->BufferUsedAs(buffers[i].Get(), dawn::BufferUsageBit::Vertex);
+                    }
+                    commandBufferState->SetVertexBuffer(cmd->startSlot, cmd->count);
+                } break;
+
+                default:
+                    return DAWN_VALIDATION_ERROR(disallowedMessage);
+            }
+
+            return {};
+        }
+
+    }  // namespace
+
+    MaybeError ValidateRenderPass(CommandIterator* commands,
+                                  BeginRenderPassCmd* renderPass,
+                                  std::vector<PassResourceUsage>* perPassResourceUsages) {
+        PassResourceUsageTracker usageTracker;
+        CommandBufferStateTracker commandBufferState;
+        unsigned int debugGroupStackSize = 0;
+
+        // Track usage of the render pass attachments
+        for (uint32_t i : IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
+            RenderPassColorAttachmentInfo* colorAttachment = &renderPass->colorAttachments[i];
+            TextureBase* texture = colorAttachment->view->GetTexture();
+            usageTracker.TextureUsedAs(texture, dawn::TextureUsageBit::OutputAttachment);
+
+            TextureViewBase* resolveTarget = colorAttachment->resolveTarget.Get();
+            if (resolveTarget != nullptr) {
+                usageTracker.TextureUsedAs(resolveTarget->GetTexture(),
+                                           dawn::TextureUsageBit::OutputAttachment);
+            }
+        }
+
+        if (renderPass->attachmentState->HasDepthStencilAttachment()) {
+            TextureBase* texture = renderPass->depthStencilAttachment.view->GetTexture();
+            usageTracker.TextureUsedAs(texture, dawn::TextureUsageBit::OutputAttachment);
+        }
+
+        Command type;
+        while (commands->NextCommandId(&type)) {
+            switch (type) {
+                case Command::EndRenderPass: {
+                    commands->NextCommand<EndRenderPassCmd>();
+
+                    DAWN_TRY(ValidateDebugGroups(debugGroupStackSize));
+
+                    DAWN_TRY(usageTracker.ValidateRenderPassUsages());
+                    ASSERT(perPassResourceUsages != nullptr);
+                    perPassResourceUsages->push_back(usageTracker.AcquireResourceUsage());
+
+                    return {};
+                } break;
+
+                case Command::SetStencilReference: {
+                    commands->NextCommand<SetStencilReferenceCmd>();
+                } break;
+
+                case Command::SetBlendColor: {
+                    commands->NextCommand<SetBlendColorCmd>();
+                } break;
+
+                case Command::SetViewport: {
+                    commands->NextCommand<SetViewportCmd>();
+                } break;
+
+                case Command::SetScissorRect: {
+                    commands->NextCommand<SetScissorRectCmd>();
+                } break;
+
+                default:
+                    DAWN_TRY(ValidateRenderBundleCommand(
+                        commands, type, &usageTracker, &commandBufferState,
+                        renderPass->attachmentState.Get(), &debugGroupStackSize,
+                        "Command disallowed inside a render pass"));
+            }
+        }
+
+        UNREACHABLE();
+        return DAWN_VALIDATION_ERROR("Unfinished render pass");
+    }
+
+    MaybeError ValidateComputePass(CommandIterator* commands,
+                                   std::vector<PassResourceUsage>* perPassResourceUsages) {
+        PassResourceUsageTracker usageTracker;
+        CommandBufferStateTracker commandBufferState;
+        unsigned int debugGroupStackSize = 0;
+
+        Command type;
+        while (commands->NextCommandId(&type)) {
+            switch (type) {
+                case Command::EndComputePass: {
+                    commands->NextCommand<EndComputePassCmd>();
+
+                    DAWN_TRY(ValidateDebugGroups(debugGroupStackSize));
+
+                    DAWN_TRY(usageTracker.ValidateComputePassUsages());
+                    ASSERT(perPassResourceUsages != nullptr);
+                    perPassResourceUsages->push_back(usageTracker.AcquireResourceUsage());
+                    return {};
+                } break;
+
+                case Command::Dispatch: {
+                    commands->NextCommand<DispatchCmd>();
+                    DAWN_TRY(commandBufferState.ValidateCanDispatch());
+                } break;
+
+                case Command::DispatchIndirect: {
+                    DispatchIndirectCmd* cmd = commands->NextCommand<DispatchIndirectCmd>();
+                    DAWN_TRY(commandBufferState.ValidateCanDispatch());
+                    usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(),
+                                              dawn::BufferUsageBit::Indirect);
+                } break;
+
+                case Command::InsertDebugMarker: {
+                    InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
+                    commands->NextData<char>(cmd->length + 1);
+                } break;
+
+                case Command::PopDebugGroup: {
+                    commands->NextCommand<PopDebugGroupCmd>();
+                    DAWN_TRY(PopDebugMarkerStack(&debugGroupStackSize));
+                } break;
+
+                case Command::PushDebugGroup: {
+                    PushDebugGroupCmd* cmd = commands->NextCommand<PushDebugGroupCmd>();
+                    commands->NextData<char>(cmd->length + 1);
+                    DAWN_TRY(PushDebugMarkerStack(&debugGroupStackSize));
+                } break;
+
+                case Command::SetComputePipeline: {
+                    SetComputePipelineCmd* cmd = commands->NextCommand<SetComputePipelineCmd>();
+                    ComputePipelineBase* pipeline = cmd->pipeline.Get();
+                    commandBufferState.SetComputePipeline(pipeline);
+                } break;
+
+                case Command::SetBindGroup: {
+                    SetBindGroupCmd* cmd = commands->NextCommand<SetBindGroupCmd>();
+                    if (cmd->dynamicOffsetCount > 0) {
+                        commands->NextData<uint64_t>(cmd->dynamicOffsetCount);
+                    }
+
+                    TrackBindGroupResourceUsage(cmd->group.Get(), &usageTracker);
+                    commandBufferState.SetBindGroup(cmd->index, cmd->group.Get());
+                } break;
+
+                default:
+                    return DAWN_VALIDATION_ERROR("Command disallowed inside a compute pass");
+            }
+        }
+
+        UNREACHABLE();
+        return DAWN_VALIDATION_ERROR("Unfinished compute pass");
+    }
+
+}  // namespace dawn_native
diff --git a/src/dawn_native/CommandValidation.h b/src/dawn_native/CommandValidation.h
new file mode 100644
index 0000000..2d29cb3
--- /dev/null
+++ b/src/dawn_native/CommandValidation.h
@@ -0,0 +1,36 @@
+// Copyright 2019 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_COMMANDVALIDATION_H_
+#define DAWNNATIVE_COMMANDVALIDATION_H_
+
+#include "dawn_native/CommandAllocator.h"
+#include "dawn_native/Error.h"
+
+#include <vector>
+
+namespace dawn_native {
+
+    struct BeginRenderPassCmd;
+    struct PassResourceUsage;
+
+    MaybeError ValidateRenderPass(CommandIterator* commands,
+                                  BeginRenderPassCmd* renderPass,
+                                  std::vector<PassResourceUsage>* perPassResourceUsages);
+    MaybeError ValidateComputePass(CommandIterator* commands,
+                                   std::vector<PassResourceUsage>* perPassResourceUsages);
+
+}  // namespace dawn_native
+
+#endif  // DAWNNATIVE_COMMANDVALIDATION_H_
diff --git a/src/dawn_native/RenderPipeline.cpp b/src/dawn_native/RenderPipeline.cpp
index a9fd5d7..6626c59 100644
--- a/src/dawn_native/RenderPipeline.cpp
+++ b/src/dawn_native/RenderPipeline.cpp
@@ -514,16 +514,10 @@
         return mAttachmentState->GetSampleCount();
     }
 
-    MaybeError RenderPipelineBase::ValidateCompatibleWith(
-        const BeginRenderPassCmd* renderPass) const {
+    const AttachmentState* RenderPipelineBase::GetAttachmentState() const {
         ASSERT(!IsError());
 
-        if (renderPass->attachmentState.Get() != mAttachmentState.Get()) {
-            return DAWN_VALIDATION_ERROR(
-                "Pipeline attachment state is not compatible with render pass");
-        }
-
-        return {};
+        return mAttachmentState.Get();
     }
 
     std::bitset<kMaxVertexAttributes> RenderPipelineBase::GetAttributesUsingInput(
diff --git a/src/dawn_native/RenderPipeline.h b/src/dawn_native/RenderPipeline.h
index f8e884e..7a00e3a 100644
--- a/src/dawn_native/RenderPipeline.h
+++ b/src/dawn_native/RenderPipeline.h
@@ -78,9 +78,8 @@
         dawn::TextureFormat GetDepthStencilFormat() const;
         uint32_t GetSampleCount() const;
 
-        // A pipeline can be used in a render pass if its attachment info matches the actual
-        // attachments in the render pass. This returns whether it is the case.
-        MaybeError ValidateCompatibleWith(const BeginRenderPassCmd* renderPassCmd) const;
+        const AttachmentState* GetAttachmentState() const;
+
         std::bitset<kMaxVertexAttributes> GetAttributesUsingInput(uint32_t slot) const;
         std::array<std::bitset<kMaxVertexAttributes>, kMaxVertexBuffers> attributesUsingInput;