Skip validation during command recording when skip_validation is on

This patch factors resource usage tracking so it is separate from
command validation, allowing the bulk of command validation to be
completely skipped.

In DrawCallPerfRun/Vulkan_DynamicPipeline_DynamicBindGroup, disabling
validation cuts roughly 74 nanoseconds (20%) of CPU time, per draw.

In DrawCallPerfRun/Vulkan_DynamicBindGroup, disabling validation
cuts roughly 35 nanoseconds (17%) of CPU time, per draw.

In DrawCallPerfRun/Vulkan_MultipleBindGroups, disabling validation
cuts roughly 45 nanoseconds (14%) of CPU time, per draw.

Bug: dawn:271
Change-Id: I517b85840ba18c6a554b83f34a1d0aef1a8c56a6
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/13520
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/dawn_native/CommandEncoder.cpp b/src/dawn_native/CommandEncoder.cpp
index 5af8ba0..e0de3a2 100644
--- a/src/dawn_native/CommandEncoder.cpp
+++ b/src/dawn_native/CommandEncoder.cpp
@@ -24,7 +24,6 @@
 #include "dawn_native/ComputePassEncoder.h"
 #include "dawn_native/Device.h"
 #include "dawn_native/ErrorData.h"
-#include "dawn_native/PassResourceUsageTracker.h"
 #include "dawn_native/RenderPassEncoder.h"
 #include "dawn_native/RenderPipeline.h"
 #include "dawn_platform/DawnPlatform.h"
@@ -243,7 +242,7 @@
             return {};
         }
 
-        MaybeError ValidateCanUseAs(BufferBase* buffer, wgpu::BufferUsage usage) {
+        MaybeError ValidateCanUseAs(const BufferBase* buffer, wgpu::BufferUsage usage) {
             ASSERT(wgpu::HasZeroOrOneBits(usage));
             if (!(buffer->GetUsage() & usage)) {
                 return DAWN_VALIDATION_ERROR("buffer doesn't have the required usage.");
@@ -252,7 +251,7 @@
             return {};
         }
 
-        MaybeError ValidateCanUseAs(TextureBase* texture, wgpu::TextureUsage usage) {
+        MaybeError ValidateCanUseAs(const TextureBase* texture, wgpu::TextureUsage usage) {
             ASSERT(wgpu::HasZeroOrOneBits(usage));
             if (!(texture->GetUsage() & usage)) {
                 return DAWN_VALIDATION_ERROR("texture doesn't have the required usage.");
@@ -467,9 +466,9 @@
     }
 
     CommandBufferResourceUsage CommandEncoder::AcquireResourceUsages() {
-        ASSERT(!mWereResourceUsagesAcquired);
-        mWereResourceUsagesAcquired = true;
-        return std::move(mResourceUsages);
+        return CommandBufferResourceUsage{mEncodingContext.AcquirePassUsages(),
+                                          std::move(mTopLevelBuffers),
+                                          std::move(mTopLevelTextures)};
     }
 
     CommandIterator CommandEncoder::AcquireCommands() {
@@ -503,6 +502,7 @@
     RenderPassEncoder* CommandEncoder::BeginRenderPass(const RenderPassDescriptor* descriptor) {
         DeviceBase* device = GetDevice();
 
+        PassResourceUsageTracker usageTracker;
         bool success =
             mEncodingContext.TryEncode(this, [&](CommandAllocator* allocator) -> MaybeError {
                 uint32_t width = 0;
@@ -520,18 +520,29 @@
                 cmd->attachmentState = device->GetOrCreateAttachmentState(descriptor);
 
                 for (uint32_t i : IterateBitSet(cmd->attachmentState->GetColorAttachmentsMask())) {
-                    cmd->colorAttachments[i].view = descriptor->colorAttachments[i].attachment;
-                    cmd->colorAttachments[i].resolveTarget =
-                        descriptor->colorAttachments[i].resolveTarget;
+                    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 =
                         descriptor->colorAttachments[i].clearColor;
+
+                    usageTracker.TextureUsedAs(view->GetTexture(),
+                                               wgpu::TextureUsage::OutputAttachment);
+
+                    if (resolveTarget != nullptr) {
+                        usageTracker.TextureUsedAs(resolveTarget->GetTexture(),
+                                                   wgpu::TextureUsage::OutputAttachment);
+                    }
                 }
 
                 if (cmd->attachmentState->HasDepthStencilAttachment()) {
-                    cmd->depthStencilAttachment.view =
-                        descriptor->depthStencilAttachment->attachment;
+                    TextureViewBase* view = descriptor->depthStencilAttachment->attachment;
+
+                    cmd->depthStencilAttachment.view = view;
                     cmd->depthStencilAttachment.clearDepth =
                         descriptor->depthStencilAttachment->clearDepth;
                     cmd->depthStencilAttachment.clearStencil =
@@ -544,6 +555,9 @@
                         descriptor->depthStencilAttachment->stencilLoadOp;
                     cmd->depthStencilAttachment.stencilStoreOp =
                         descriptor->depthStencilAttachment->stencilStoreOp;
+
+                    usageTracker.TextureUsedAs(view->GetTexture(),
+                                               wgpu::TextureUsage::OutputAttachment);
                 }
 
                 cmd->width = width;
@@ -553,7 +567,8 @@
             });
 
         if (success) {
-            RenderPassEncoder* passEncoder = new RenderPassEncoder(device, this, &mEncodingContext);
+            RenderPassEncoder* passEncoder =
+                new RenderPassEncoder(device, this, &mEncodingContext, std::move(usageTracker));
             mEncodingContext.EnterPass(passEncoder);
             return passEncoder;
         }
@@ -578,6 +593,10 @@
             copy->destinationOffset = destinationOffset;
             copy->size = size;
 
+            if (GetDevice()->IsValidationEnabled()) {
+                mTopLevelBuffers.insert(source);
+                mTopLevelBuffers.insert(destination);
+            }
             return {};
         });
     }
@@ -610,6 +629,10 @@
                 copy->source.imageHeight = source->imageHeight;
             }
 
+            if (GetDevice()->IsValidationEnabled()) {
+                mTopLevelBuffers.insert(source->buffer);
+                mTopLevelTextures.insert(destination->texture);
+            }
             return {};
         });
     }
@@ -642,6 +665,10 @@
                 copy->destination.imageHeight = destination->imageHeight;
             }
 
+            if (GetDevice()->IsValidationEnabled()) {
+                mTopLevelTextures.insert(source->texture);
+                mTopLevelBuffers.insert(destination->buffer);
+            }
             return {};
         });
     }
@@ -665,6 +692,10 @@
             copy->destination.arrayLayer = destination->arrayLayer;
             copy->copySize = *copySize;
 
+            if (GetDevice()->IsValidationEnabled()) {
+                mTopLevelTextures.insert(source->texture);
+                mTopLevelTextures.insert(destination->texture);
+            }
             return {};
         });
     }
@@ -704,46 +735,49 @@
     }
 
     CommandBufferBase* CommandEncoder::Finish(const CommandBufferDescriptor* descriptor) {
-        if (GetDevice()->ConsumedError(ValidateFinish(descriptor))) {
-            // Even if finish validation fails, it is now invalid to call any encoding commands on
-            // this object, so we set its state to finished.
-            return CommandBufferBase::MakeError(GetDevice());
+        DeviceBase* device = GetDevice();
+        // Even if mEncodingContext.Finish() validation fails, calling it will mutate the internal
+        // state of the encoding context. The internal state is set to finished, and subsequent
+        // calls to encode commands will generate errors.
+        if (device->ConsumedError(mEncodingContext.Finish()) ||
+            (device->IsValidationEnabled() &&
+             device->ConsumedError(ValidateFinish(mEncodingContext.GetIterator(),
+                                                  mEncodingContext.GetPassUsages())))) {
+            return CommandBufferBase::MakeError(device);
         }
         ASSERT(!IsError());
-
-        return GetDevice()->CreateCommandBuffer(this, descriptor);
+        return device->CreateCommandBuffer(this, descriptor);
     }
 
     // Implementation of the command buffer validation that can be precomputed before submit
-
-    MaybeError CommandEncoder::ValidateFinish(const CommandBufferDescriptor*) {
+    MaybeError CommandEncoder::ValidateFinish(CommandIterator* commands,
+                                              const PerPassUsages& perPassUsages) const {
         TRACE_EVENT0(GetDevice()->GetPlatform(), Validation, "CommandEncoder::ValidateFinish");
         DAWN_TRY(GetDevice()->ValidateObject(this));
 
-        // Even if Finish() validation fails, calling it will mutate the internal state of the
-        // encoding context. Subsequent calls to encode commands will generate errors.
-        DAWN_TRY(mEncodingContext.Finish());
+        for (const PassResourceUsage& passUsage : perPassUsages) {
+            DAWN_TRY(ValidatePassResourceUsage(passUsage));
+        }
 
         uint64_t debugGroupStackSize = 0;
 
-        CommandIterator* commands = mEncodingContext.GetIterator();
         commands->Reset();
-
         Command type;
         while (commands->NextCommandId(&type)) {
             switch (type) {
                 case Command::BeginComputePass: {
                     commands->NextCommand<BeginComputePassCmd>();
-                    DAWN_TRY(ValidateComputePass(commands, &mResourceUsages.perPass));
+                    DAWN_TRY(ValidateComputePass(commands));
                 } break;
 
                 case Command::BeginRenderPass: {
-                    BeginRenderPassCmd* cmd = commands->NextCommand<BeginRenderPassCmd>();
-                    DAWN_TRY(ValidateRenderPass(commands, cmd, &mResourceUsages.perPass));
+                    const BeginRenderPassCmd* cmd = commands->NextCommand<BeginRenderPassCmd>();
+                    DAWN_TRY(ValidateRenderPass(commands, cmd));
                 } break;
 
                 case Command::CopyBufferToBuffer: {
-                    CopyBufferToBufferCmd* copy = commands->NextCommand<CopyBufferToBufferCmd>();
+                    const CopyBufferToBufferCmd* copy =
+                        commands->NextCommand<CopyBufferToBufferCmd>();
 
                     DAWN_TRY(
                         ValidateCopySizeFitsInBuffer(copy->source, copy->sourceOffset, copy->size));
@@ -754,13 +788,11 @@
 
                     DAWN_TRY(ValidateCanUseAs(copy->source.Get(), wgpu::BufferUsage::CopySrc));
                     DAWN_TRY(ValidateCanUseAs(copy->destination.Get(), wgpu::BufferUsage::CopyDst));
-
-                    mResourceUsages.topLevelBuffers.insert(copy->source.Get());
-                    mResourceUsages.topLevelBuffers.insert(copy->destination.Get());
                 } break;
 
                 case Command::CopyBufferToTexture: {
-                    CopyBufferToTextureCmd* copy = commands->NextCommand<CopyBufferToTextureCmd>();
+                    const CopyBufferToTextureCmd* copy =
+                        commands->NextCommand<CopyBufferToTextureCmd>();
 
                     DAWN_TRY(
                         ValidateTextureSampleCountInCopyCommands(copy->destination.texture.Get()));
@@ -789,13 +821,11 @@
                         ValidateCanUseAs(copy->source.buffer.Get(), wgpu::BufferUsage::CopySrc));
                     DAWN_TRY(ValidateCanUseAs(copy->destination.texture.Get(),
                                               wgpu::TextureUsage::CopyDst));
-
-                    mResourceUsages.topLevelBuffers.insert(copy->source.buffer.Get());
-                    mResourceUsages.topLevelTextures.insert(copy->destination.texture.Get());
                 } break;
 
                 case Command::CopyTextureToBuffer: {
-                    CopyTextureToBufferCmd* copy = commands->NextCommand<CopyTextureToBufferCmd>();
+                    const CopyTextureToBufferCmd* copy =
+                        commands->NextCommand<CopyTextureToBufferCmd>();
 
                     DAWN_TRY(ValidateTextureSampleCountInCopyCommands(copy->source.texture.Get()));
 
@@ -824,13 +854,10 @@
                         ValidateCanUseAs(copy->source.texture.Get(), wgpu::TextureUsage::CopySrc));
                     DAWN_TRY(ValidateCanUseAs(copy->destination.buffer.Get(),
                                               wgpu::BufferUsage::CopyDst));
-
-                    mResourceUsages.topLevelTextures.insert(copy->source.texture.Get());
-                    mResourceUsages.topLevelBuffers.insert(copy->destination.buffer.Get());
                 } break;
 
                 case Command::CopyTextureToTexture: {
-                    CopyTextureToTextureCmd* copy =
+                    const CopyTextureToTextureCmd* copy =
                         commands->NextCommand<CopyTextureToTextureCmd>();
 
                     DAWN_TRY(ValidateTextureToTextureCopyRestrictions(
@@ -852,13 +879,10 @@
                         ValidateCanUseAs(copy->source.texture.Get(), wgpu::TextureUsage::CopySrc));
                     DAWN_TRY(ValidateCanUseAs(copy->destination.texture.Get(),
                                               wgpu::TextureUsage::CopyDst));
-
-                    mResourceUsages.topLevelTextures.insert(copy->source.texture.Get());
-                    mResourceUsages.topLevelTextures.insert(copy->destination.texture.Get());
                 } break;
 
                 case Command::InsertDebugMarker: {
-                    InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
+                    const InsertDebugMarkerCmd* cmd = commands->NextCommand<InsertDebugMarkerCmd>();
                     commands->NextData<char>(cmd->length + 1);
                 } break;
 
@@ -869,7 +893,7 @@
                 } break;
 
                 case Command::PushDebugGroup: {
-                    PushDebugGroupCmd* cmd = commands->NextCommand<PushDebugGroupCmd>();
+                    const PushDebugGroupCmd* cmd = commands->NextCommand<PushDebugGroupCmd>();
                     commands->NextData<char>(cmd->length + 1);
                     debugGroupStackSize++;
                 } break;