Validate that mapped buffers aren't used in submits.

Likewise "presented" textures won't be available for use in submits so
this sets up state-tracking and validation for textures too.

Command buffer resource usage is now stored in the frontend instead of
done per-backend because it is used to validate resources are allowed in
the submits.

Also adds a test.

BUG=dawn:9

Change-Id: I0537c5113bb33a089509b4f2af4ddf4eff8051ea
Reviewed-on: https://dawn-review.googlesource.com/c/2142
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index e235a3b..cc743e1 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -778,6 +778,7 @@
     "src/tests/unittests/validation/DynamicStateCommandValidationTests.cpp",
     "src/tests/unittests/validation/InputStateValidationTests.cpp",
     "src/tests/unittests/validation/PushConstantsValidationTests.cpp",
+    "src/tests/unittests/validation/QueueSubmitValidationTests.cpp",
     "src/tests/unittests/validation/RenderPassDescriptorValidationTests.cpp",
     "src/tests/unittests/validation/RenderPipelineValidationTests.cpp",
     "src/tests/unittests/validation/ShaderModuleValidationTests.cpp",
diff --git a/src/dawn_native/Buffer.cpp b/src/dawn_native/Buffer.cpp
index 145ed6e..e073491 100644
--- a/src/dawn_native/Buffer.cpp
+++ b/src/dawn_native/Buffer.cpp
@@ -72,6 +72,13 @@
         return mUsage;
     }
 
+    MaybeError BufferBase::ValidateCanUseInSubmitNow() const {
+        if (mIsMapped) {
+            return DAWN_VALIDATION_ERROR("Buffer used in a submit while mapped");
+        }
+        return {};
+    }
+
     void BufferBase::CallMapReadCallback(uint32_t serial,
                                          dawnBufferMapAsyncStatus status,
                                          const void* pointer) {
diff --git a/src/dawn_native/Buffer.h b/src/dawn_native/Buffer.h
index a80032f..ce10ea5 100644
--- a/src/dawn_native/Buffer.h
+++ b/src/dawn_native/Buffer.h
@@ -42,6 +42,8 @@
         uint32_t GetSize() const;
         dawn::BufferUsageBit GetUsage() const;
 
+        MaybeError ValidateCanUseInSubmitNow() const;
+
         // Dawn API
         BufferViewBuilder* CreateBufferViewBuilder();
         void SetSubData(uint32_t start, uint32_t count, const uint8_t* data);
diff --git a/src/dawn_native/CommandBuffer.cpp b/src/dawn_native/CommandBuffer.cpp
index 919f72c..2a9ffe5 100644
--- a/src/dawn_native/CommandBuffer.cpp
+++ b/src/dawn_native/CommandBuffer.cpp
@@ -286,7 +286,11 @@
     // CommandBuffer
 
     CommandBufferBase::CommandBufferBase(CommandBufferBuilder* builder)
-        : ObjectBase(builder->GetDevice()) {
+        : ObjectBase(builder->GetDevice()), mResourceUsages(builder->AcquireResourceUsages()) {
+    }
+
+    const CommandBufferResourceUsage& CommandBufferBase::GetResourceUsages() const {
+        return mResourceUsages;
     }
 
     // CommandBufferBuilder
@@ -308,10 +312,10 @@
         return std::move(mIterator);
     }
 
-    std::vector<PassResourceUsage> CommandBufferBuilder::AcquirePassResourceUsage() {
-        ASSERT(!mWerePassUsagesAcquired);
-        mWerePassUsagesAcquired = true;
-        return std::move(mPassResourceUsages);
+    CommandBufferResourceUsage CommandBufferBuilder::AcquireResourceUsages() {
+        ASSERT(!mWereResourceUsagesAcquired);
+        mWereResourceUsagesAcquired = true;
+        return std::move(mResourceUsages);
     }
 
     CommandBufferBase* CommandBufferBuilder::GetResultImpl() {
@@ -372,6 +376,9 @@
                                               dawn::BufferUsageBit::TransferSrc));
                     DAWN_TRY(ValidateCanUseAs(copy->destination.buffer.Get(),
                                               dawn::BufferUsageBit::TransferDst));
+
+                    mResourceUsages.topLevelBuffers.insert(copy->source.buffer.Get());
+                    mResourceUsages.topLevelBuffers.insert(copy->destination.buffer.Get());
                 } break;
 
                 case Command::CopyBufferToTexture: {
@@ -391,6 +398,9 @@
                                               dawn::BufferUsageBit::TransferSrc));
                     DAWN_TRY(ValidateCanUseAs(copy->destination.texture.Get(),
                                               dawn::TextureUsageBit::TransferDst));
+
+                    mResourceUsages.topLevelBuffers.insert(copy->source.buffer.Get());
+                    mResourceUsages.topLevelTextures.insert(copy->destination.texture.Get());
                 } break;
 
                 case Command::CopyTextureToBuffer: {
@@ -410,6 +420,9 @@
                                               dawn::TextureUsageBit::TransferSrc));
                     DAWN_TRY(ValidateCanUseAs(copy->destination.buffer.Get(),
                                               dawn::BufferUsageBit::TransferDst));
+
+                    mResourceUsages.topLevelTextures.insert(copy->source.texture.Get());
+                    mResourceUsages.topLevelBuffers.insert(copy->destination.buffer.Get());
                 } break;
 
                 default:
@@ -431,7 +444,7 @@
                     mIterator.NextCommand<EndComputePassCmd>();
 
                     DAWN_TRY(usageTracker.ValidateUsages(PassType::Compute));
-                    mPassResourceUsages.push_back(usageTracker.AcquireResourceUsage());
+                    mResourceUsages.perPass.push_back(usageTracker.AcquireResourceUsage());
                     return {};
                 } break;
 
@@ -495,7 +508,7 @@
                     mIterator.NextCommand<EndRenderPassCmd>();
 
                     DAWN_TRY(usageTracker.ValidateUsages(PassType::Render));
-                    mPassResourceUsages.push_back(usageTracker.AcquireResourceUsage());
+                    mResourceUsages.perPass.push_back(usageTracker.AcquireResourceUsage());
                     return {};
                 } break;
 
diff --git a/src/dawn_native/CommandBuffer.h b/src/dawn_native/CommandBuffer.h
index 553fe6e..3565085 100644
--- a/src/dawn_native/CommandBuffer.h
+++ b/src/dawn_native/CommandBuffer.h
@@ -42,6 +42,11 @@
     class CommandBufferBase : public ObjectBase {
       public:
         CommandBufferBase(CommandBufferBuilder* builder);
+
+        const CommandBufferResourceUsage& GetResourceUsages() const;
+
+      private:
+        CommandBufferResourceUsage mResourceUsages;
     };
 
     class CommandBufferBuilder : public Builder<CommandBufferBase> {
@@ -52,7 +57,7 @@
         MaybeError ValidateGetResult();
 
         CommandIterator AcquireCommands();
-        std::vector<PassResourceUsage> AcquirePassResourceUsage();
+        CommandBufferResourceUsage AcquireResourceUsages();
 
         // Dawn API
         ComputePassEncoderBase* BeginComputePass();
@@ -116,9 +121,9 @@
         CommandIterator mIterator;
         bool mWasMovedToIterator = false;
         bool mWereCommandsAcquired = false;
-        bool mWerePassUsagesAcquired = false;
 
-        std::vector<PassResourceUsage> mPassResourceUsages;
+        bool mWereResourceUsagesAcquired = false;
+        CommandBufferResourceUsage mResourceUsages;
     };
 
 }  // namespace dawn_native
diff --git a/src/dawn_native/PassResourceUsage.h b/src/dawn_native/PassResourceUsage.h
index 5bae2e4..9a8c2b0 100644
--- a/src/dawn_native/PassResourceUsage.h
+++ b/src/dawn_native/PassResourceUsage.h
@@ -17,6 +17,7 @@
 
 #include "dawn_native/dawn_platform.h"
 
+#include <set>
 #include <vector>
 
 namespace dawn_native {
@@ -35,6 +36,12 @@
         std::vector<dawn::TextureUsageBit> textureUsages;
     };
 
+    struct CommandBufferResourceUsage {
+        std::vector<PassResourceUsage> perPass;
+        std::set<BufferBase*> topLevelBuffers;
+        std::set<TextureBase*> topLevelTextures;
+    };
+
 }  // namespace dawn_native
 
 #endif  // DAWNNATIVE_PASSRESOURCEUSAGE_H
diff --git a/src/dawn_native/Queue.cpp b/src/dawn_native/Queue.cpp
index 46facf5..021a095 100644
--- a/src/dawn_native/Queue.cpp
+++ b/src/dawn_native/Queue.cpp
@@ -14,8 +14,10 @@
 
 #include "dawn_native/Queue.h"
 
+#include "dawn_native/Buffer.h"
 #include "dawn_native/CommandBuffer.h"
 #include "dawn_native/Device.h"
+#include "dawn_native/Texture.h"
 
 namespace dawn_native {
 
@@ -32,7 +34,27 @@
         SubmitImpl(numCommands, commands);
     }
 
-    MaybeError QueueBase::ValidateSubmit(uint32_t, CommandBufferBase* const*) {
+    MaybeError QueueBase::ValidateSubmit(uint32_t numCommands, CommandBufferBase* const* commands) {
+        for (uint32_t i = 0; i < numCommands; ++i) {
+            const CommandBufferResourceUsage& usages = commands[i]->GetResourceUsages();
+
+            for (const PassResourceUsage& passUsages : usages.perPass) {
+                for (const BufferBase* buffer : passUsages.buffers) {
+                    DAWN_TRY(buffer->ValidateCanUseInSubmitNow());
+                }
+                for (const TextureBase* texture : passUsages.textures) {
+                    DAWN_TRY(texture->ValidateCanUseInSubmitNow());
+                }
+            }
+
+            for (const BufferBase* buffer : usages.topLevelBuffers) {
+                DAWN_TRY(buffer->ValidateCanUseInSubmitNow());
+            }
+            for (const TextureBase* texture : usages.topLevelTextures) {
+                DAWN_TRY(texture->ValidateCanUseInSubmitNow());
+            }
+        }
+
         return {};
     }
 
diff --git a/src/dawn_native/Texture.cpp b/src/dawn_native/Texture.cpp
index ffc5da2..53e8d10 100644
--- a/src/dawn_native/Texture.cpp
+++ b/src/dawn_native/Texture.cpp
@@ -237,6 +237,10 @@
         return mUsage;
     }
 
+    MaybeError TextureBase::ValidateCanUseInSubmitNow() const {
+        return {};
+    }
+
     TextureViewBase* TextureBase::CreateDefaultTextureView() {
         TextureViewDescriptor descriptor = MakeDefaultTextureViewDescriptor(this);
         return GetDevice()->CreateTextureView(this, &descriptor);
diff --git a/src/dawn_native/Texture.h b/src/dawn_native/Texture.h
index 1817bc1..ab34e96 100644
--- a/src/dawn_native/Texture.h
+++ b/src/dawn_native/Texture.h
@@ -52,6 +52,8 @@
         uint32_t GetNumMipLevels() const;
         dawn::TextureUsageBit GetUsage() const;
 
+        MaybeError ValidateCanUseInSubmitNow() const;
+
         // Dawn API
         TextureViewBase* CreateDefaultTextureView();
         TextureViewBase* CreateTextureView(const TextureViewDescriptor* descriptor);
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.cpp b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
index 27bd589..65e7d6c 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.cpp
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.cpp
@@ -234,9 +234,7 @@
     }  // anonymous namespace
 
     CommandBuffer::CommandBuffer(CommandBufferBuilder* builder)
-        : CommandBufferBase(builder),
-          mCommands(builder->AcquireCommands()),
-          mPassResourceUsages(builder->AcquirePassResourceUsage()) {
+        : CommandBufferBase(builder), mCommands(builder->AcquireCommands()) {
     }
 
     CommandBuffer::~CommandBuffer() {
@@ -281,6 +279,7 @@
             }
         };
 
+        const std::vector<PassResourceUsage>& passResourceUsages = GetResourceUsages().perPass;
         uint32_t nextPassNumber = 0;
 
         Command type;
@@ -289,7 +288,7 @@
                 case Command::BeginComputePass: {
                     mCommands.NextCommand<BeginComputePassCmd>();
 
-                    TransitionForPass(commandList, mPassResourceUsages[nextPassNumber]);
+                    TransitionForPass(commandList, passResourceUsages[nextPassNumber]);
                     bindingTracker.SetInComputePass(true);
                     RecordComputePass(commandList, &bindingTracker);
 
@@ -300,7 +299,7 @@
                     BeginRenderPassCmd* beginRenderPassCmd =
                         mCommands.NextCommand<BeginRenderPassCmd>();
 
-                    TransitionForPass(commandList, mPassResourceUsages[nextPassNumber]);
+                    TransitionForPass(commandList, passResourceUsages[nextPassNumber]);
                     bindingTracker.SetInComputePass(false);
                     RecordRenderPass(commandList, &bindingTracker,
                                      ToBackend(beginRenderPassCmd->info.Get()));
diff --git a/src/dawn_native/d3d12/CommandBufferD3D12.h b/src/dawn_native/d3d12/CommandBufferD3D12.h
index e9e7bfe..95ac8bd 100644
--- a/src/dawn_native/d3d12/CommandBufferD3D12.h
+++ b/src/dawn_native/d3d12/CommandBufferD3D12.h
@@ -42,7 +42,6 @@
                               RenderPassDescriptor* renderPass);
 
         CommandIterator mCommands;
-        std::vector<PassResourceUsage> mPassResourceUsages;
     };
 
 }}  // namespace dawn_native::d3d12
diff --git a/src/dawn_native/vulkan/CommandBufferVk.cpp b/src/dawn_native/vulkan/CommandBufferVk.cpp
index fa07a9c5..8f5c462 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.cpp
+++ b/src/dawn_native/vulkan/CommandBufferVk.cpp
@@ -111,9 +111,7 @@
     }  // anonymous namespace
 
     CommandBuffer::CommandBuffer(CommandBufferBuilder* builder)
-        : CommandBufferBase(builder),
-          mCommands(builder->AcquireCommands()),
-          mPassResourceUsages(builder->AcquirePassResourceUsage()) {
+        : CommandBufferBase(builder), mCommands(builder->AcquireCommands()) {
     }
 
     CommandBuffer::~CommandBuffer() {
@@ -135,6 +133,7 @@
             }
         };
 
+        const std::vector<PassResourceUsage>& passResourceUsages = GetResourceUsages().perPass;
         size_t nextPassNumber = 0;
 
         Command type;
@@ -207,7 +206,7 @@
                 case Command::BeginRenderPass: {
                     BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
 
-                    TransitionForPass(commands, mPassResourceUsages[nextPassNumber]);
+                    TransitionForPass(commands, passResourceUsages[nextPassNumber]);
                     RecordRenderPass(commands, ToBackend(cmd->info.Get()));
 
                     nextPassNumber++;
@@ -216,7 +215,7 @@
                 case Command::BeginComputePass: {
                     mCommands.NextCommand<BeginComputePassCmd>();
 
-                    TransitionForPass(commands, mPassResourceUsages[nextPassNumber]);
+                    TransitionForPass(commands, passResourceUsages[nextPassNumber]);
                     RecordComputePass(commands);
 
                     nextPassNumber++;
diff --git a/src/dawn_native/vulkan/CommandBufferVk.h b/src/dawn_native/vulkan/CommandBufferVk.h
index b6499df..2ca5a62 100644
--- a/src/dawn_native/vulkan/CommandBufferVk.h
+++ b/src/dawn_native/vulkan/CommandBufferVk.h
@@ -35,7 +35,6 @@
         void RecordRenderPass(VkCommandBuffer commands, RenderPassDescriptor* renderPass);
 
         CommandIterator mCommands;
-        std::vector<PassResourceUsage> mPassResourceUsages;
     };
 
 }}  // namespace dawn_native::vulkan
diff --git a/src/tests/unittests/validation/QueueSubmitValidationTests.cpp b/src/tests/unittests/validation/QueueSubmitValidationTests.cpp
new file mode 100644
index 0000000..37c1d40
--- /dev/null
+++ b/src/tests/unittests/validation/QueueSubmitValidationTests.cpp
@@ -0,0 +1,69 @@
+// Copyright 2018 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 "tests/unittests/validation/ValidationTest.h"
+
+#include "utils/DawnHelpers.h"
+
+namespace {
+
+class QueueSubmitValidationTest : public ValidationTest {
+};
+
+static void StoreTrueMapWriteCallback(dawnBufferMapAsyncStatus status, void*, dawnCallbackUserdata userdata) {
+    bool* userdataPtr = reinterpret_cast<bool*>(static_cast<intptr_t>(userdata));
+    *userdataPtr = true;
+}
+
+// Test submitting with a mapped buffer is disallowed
+TEST_F(QueueSubmitValidationTest, SubmitWithMappedBuffer) {
+    // Create a map-write buffer.
+    dawn::BufferDescriptor descriptor;
+    descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc;
+    descriptor.size = 4;
+    dawn::Buffer buffer = device.CreateBuffer(&descriptor);
+
+    // Create a fake copy destination buffer
+    descriptor.usage = dawn::BufferUsageBit::TransferDst;
+    dawn::Buffer targetBuffer = device.CreateBuffer(&descriptor);
+
+    // Create a command buffer that reads from the mappable buffer.
+    dawn::CommandBuffer commands;
+    {
+        dawn::RenderPassDescriptor renderpass = CreateSimpleRenderPass();
+        dawn::CommandBufferBuilder builder = device.CreateCommandBufferBuilder();
+        builder.CopyBufferToBuffer(buffer, 0, targetBuffer, 0, 4);
+        commands = builder.GetResult();
+    }
+
+    dawn::Queue queue = device.CreateQueue();
+
+    // Submitting when the buffer has never been mapped should succeed
+    queue.Submit(1, &commands);
+
+    // Map the buffer, submitting when the buffer is mapped should fail
+    bool mapWriteFinished = false;
+    dawnCallbackUserdata userdata = static_cast<dawnCallbackUserdata>(reinterpret_cast<intptr_t>(&mapWriteFinished));
+    buffer.MapWriteAsync(0, 4, StoreTrueMapWriteCallback, userdata);
+    queue.Submit(0, nullptr);
+    ASSERT_TRUE(mapWriteFinished);
+
+    ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
+
+    // Unmap the buffer, queue submit should succeed
+    buffer.Unmap();
+    queue.Submit(1, &commands);
+}
+
+}  // anonymous namespace