Query API: QuerySet on Vulkan

- Implement QuerySet on Vulkan backend.
- Enable end2end tests on Vulkan. The timestamp tests will be enabled
  in following CL which implement timestamp query.

Bug: dawn:434
Change-Id: I7ee04380c5f6b5af561cc23e28637dcae70bc7b9
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/26360
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Hao Li <hao.x.li@intel.com>
diff --git a/src/dawn_native/BUILD.gn b/src/dawn_native/BUILD.gn
index 0633915..cecfc1e 100644
--- a/src/dawn_native/BUILD.gn
+++ b/src/dawn_native/BUILD.gn
@@ -499,6 +499,8 @@
       "vulkan/NativeSwapChainImplVk.h",
       "vulkan/PipelineLayoutVk.cpp",
       "vulkan/PipelineLayoutVk.h",
+      "vulkan/QuerySetVk.cpp",
+      "vulkan/QuerySetVk.h",
       "vulkan/QueueVk.cpp",
       "vulkan/QueueVk.h",
       "vulkan/RenderPassCache.cpp",
diff --git a/src/dawn_native/CMakeLists.txt b/src/dawn_native/CMakeLists.txt
index c7f0808..afac9c5 100644
--- a/src/dawn_native/CMakeLists.txt
+++ b/src/dawn_native/CMakeLists.txt
@@ -396,6 +396,8 @@
         "vulkan/NativeSwapChainImplVk.h"
         "vulkan/PipelineLayoutVk.cpp"
         "vulkan/PipelineLayoutVk.h"
+        "vulkan/QuerySetVk.cpp"
+        "vulkan/QuerySetVk.h"
         "vulkan/QueueVk.cpp"
         "vulkan/QueueVk.h"
         "vulkan/RenderPassCache.cpp"
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index 818af70..01ba55c 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -28,6 +28,7 @@
 #include "dawn_native/vulkan/ComputePipelineVk.h"
 #include "dawn_native/vulkan/FencedDeleter.h"
 #include "dawn_native/vulkan/PipelineLayoutVk.h"
+#include "dawn_native/vulkan/QuerySetVk.h"
 #include "dawn_native/vulkan/QueueVk.h"
 #include "dawn_native/vulkan/RenderPassCache.h"
 #include "dawn_native/vulkan/RenderPipelineVk.h"
@@ -125,7 +126,7 @@
         return PipelineLayout::Create(this, descriptor);
     }
     ResultOrError<QuerySetBase*> Device::CreateQuerySetImpl(const QuerySetDescriptor* descriptor) {
-        return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation");
+        return QuerySet::Create(this, descriptor);
     }
     ResultOrError<RenderPipelineBase*> Device::CreateRenderPipelineImpl(
         const RenderPipelineDescriptor* descriptor) {
@@ -318,6 +319,12 @@
             usedKnobs.features.textureCompressionBC = VK_TRUE;
         }
 
+        if (IsExtensionEnabled(Extension::PipelineStatisticsQuery)) {
+            ASSERT(ToBackend(GetAdapter())->GetDeviceInfo().features.pipelineStatisticsQuery ==
+                   VK_TRUE);
+            usedKnobs.features.pipelineStatisticsQuery = VK_TRUE;
+        }
+
         if (IsExtensionEnabled(Extension::ShaderFloat16)) {
             const VulkanDeviceInfo& deviceInfo = ToBackend(GetAdapter())->GetDeviceInfo();
             ASSERT(deviceInfo.HasExt(DeviceExt::ShaderFloat16Int8) &&
diff --git a/src/dawn_native/vulkan/FencedDeleter.cpp b/src/dawn_native/vulkan/FencedDeleter.cpp
index 388eb93..42a4237 100644
--- a/src/dawn_native/vulkan/FencedDeleter.cpp
+++ b/src/dawn_native/vulkan/FencedDeleter.cpp
@@ -30,6 +30,7 @@
         ASSERT(mMemoriesToDelete.Empty());
         ASSERT(mPipelinesToDelete.Empty());
         ASSERT(mPipelineLayoutsToDelete.Empty());
+        ASSERT(mQueryPoolsToDelete.Empty());
         ASSERT(mRenderPassesToDelete.Empty());
         ASSERT(mSamplersToDelete.Empty());
         ASSERT(mSemaphoresToDelete.Empty());
@@ -70,6 +71,10 @@
         mPipelineLayoutsToDelete.Enqueue(layout, mDevice->GetPendingCommandSerial());
     }
 
+    void FencedDeleter::DeleteWhenUnused(VkQueryPool querypool) {
+        mQueryPoolsToDelete.Enqueue(querypool, mDevice->GetPendingCommandSerial());
+    }
+
     void FencedDeleter::DeleteWhenUnused(VkRenderPass renderPass) {
         mRenderPassesToDelete.Enqueue(renderPass, mDevice->GetPendingCommandSerial());
     }
@@ -164,6 +169,11 @@
         }
         mDescriptorPoolsToDelete.ClearUpTo(completedSerial);
 
+        for (VkQueryPool pool : mQueryPoolsToDelete.IterateUpTo(completedSerial)) {
+            mDevice->fn.DestroyQueryPool(vkDevice, pool, nullptr);
+        }
+        mQueryPoolsToDelete.ClearUpTo(completedSerial);
+
         for (VkSampler sampler : mSamplersToDelete.IterateUpTo(completedSerial)) {
             mDevice->fn.DestroySampler(vkDevice, sampler, nullptr);
         }
diff --git a/src/dawn_native/vulkan/FencedDeleter.h b/src/dawn_native/vulkan/FencedDeleter.h
index 7200eda..9e516b1 100644
--- a/src/dawn_native/vulkan/FencedDeleter.h
+++ b/src/dawn_native/vulkan/FencedDeleter.h
@@ -36,6 +36,7 @@
         void DeleteWhenUnused(VkPipelineLayout layout);
         void DeleteWhenUnused(VkRenderPass renderPass);
         void DeleteWhenUnused(VkPipeline pipeline);
+        void DeleteWhenUnused(VkQueryPool querypool);
         void DeleteWhenUnused(VkSampler sampler);
         void DeleteWhenUnused(VkSemaphore semaphore);
         void DeleteWhenUnused(VkShaderModule module);
@@ -54,6 +55,7 @@
         SerialQueue<VkImageView> mImageViewsToDelete;
         SerialQueue<VkPipeline> mPipelinesToDelete;
         SerialQueue<VkPipelineLayout> mPipelineLayoutsToDelete;
+        SerialQueue<VkQueryPool> mQueryPoolsToDelete;
         SerialQueue<VkRenderPass> mRenderPassesToDelete;
         SerialQueue<VkSampler> mSamplersToDelete;
         SerialQueue<VkSemaphore> mSemaphoresToDelete;
diff --git a/src/dawn_native/vulkan/QuerySetVk.cpp b/src/dawn_native/vulkan/QuerySetVk.cpp
new file mode 100644
index 0000000..0d740a6
--- /dev/null
+++ b/src/dawn_native/vulkan/QuerySetVk.cpp
@@ -0,0 +1,112 @@
+// 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.
+
+#include "dawn_native/vulkan/QuerySetVk.h"
+
+#include "dawn_native/vulkan/DeviceVk.h"
+#include "dawn_native/vulkan/FencedDeleter.h"
+#include "dawn_native/vulkan/VulkanError.h"
+#include "dawn_platform/DawnPlatform.h"
+
+namespace dawn_native { namespace vulkan {
+
+    namespace {
+        VkQueryType VulkanQueryType(wgpu::QueryType type) {
+            switch (type) {
+                case wgpu::QueryType::Occlusion:
+                    return VK_QUERY_TYPE_OCCLUSION;
+                case wgpu::QueryType::PipelineStatistics:
+                    return VK_QUERY_TYPE_PIPELINE_STATISTICS;
+                case wgpu::QueryType::Timestamp:
+                    return VK_QUERY_TYPE_TIMESTAMP;
+                default:
+                    UNREACHABLE();
+            }
+        }
+
+        VkQueryPipelineStatisticFlags VulkanQueryPipelineStatisticFlags(
+            std::vector<wgpu::PipelineStatisticName> pipelineStatisticsSet) {
+            VkQueryPipelineStatisticFlags pipelineStatistics = 0;
+            for (size_t i = 0; i < pipelineStatisticsSet.size(); ++i) {
+                switch (pipelineStatisticsSet[i]) {
+                    case wgpu::PipelineStatisticName::ClipperInvocations:
+                        pipelineStatistics |= VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT;
+                        break;
+                    case wgpu::PipelineStatisticName::ClipperPrimitivesOut:
+                        pipelineStatistics |= VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT;
+                        break;
+                    case wgpu::PipelineStatisticName::ComputeShaderInvocations:
+                        pipelineStatistics |=
+                            VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT;
+                        break;
+                    case wgpu::PipelineStatisticName::FragmentShaderInvocations:
+                        pipelineStatistics |=
+                            VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT;
+                        break;
+                    case wgpu::PipelineStatisticName::VertexShaderInvocations:
+                        pipelineStatistics |=
+                            VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT;
+                        break;
+                    default:
+                        UNREACHABLE();
+                        break;
+                }
+            }
+
+            return pipelineStatistics;
+        }
+    }  // anonymous namespace
+
+    // static
+    ResultOrError<QuerySet*> QuerySet::Create(Device* device,
+                                              const QuerySetDescriptor* descriptor) {
+        Ref<QuerySet> queryset = AcquireRef(new QuerySet(device, descriptor));
+        DAWN_TRY(queryset->Initialize());
+        return queryset.Detach();
+    }
+
+    MaybeError QuerySet::Initialize() {
+        VkQueryPoolCreateInfo createInfo;
+        createInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
+        createInfo.pNext = NULL;
+        createInfo.flags = 0;
+        createInfo.queryType = VulkanQueryType(GetQueryType());
+        createInfo.queryCount = GetQueryCount();
+        if (GetQueryType() == wgpu::QueryType::PipelineStatistics) {
+            createInfo.pipelineStatistics =
+                VulkanQueryPipelineStatisticFlags(GetPipelineStatistics());
+        }
+
+        Device* device = ToBackend(GetDevice());
+        return CheckVkOOMThenSuccess(
+            device->fn.CreateQueryPool(device->GetVkDevice(), &createInfo, nullptr, &*mHandle),
+            "vkCreateQueryPool");
+    }
+
+    VkQueryPool QuerySet::GetHandle() const {
+        return mHandle;
+    }
+
+    QuerySet::~QuerySet() {
+        DestroyInternal();
+    }
+
+    void QuerySet::DestroyImpl() {
+        if (mHandle != VK_NULL_HANDLE) {
+            ToBackend(GetDevice())->GetFencedDeleter()->DeleteWhenUnused(mHandle);
+            mHandle = VK_NULL_HANDLE;
+        }
+    }
+
+}}  // namespace dawn_native::vulkan
diff --git a/src/dawn_native/vulkan/QuerySetVk.h b/src/dawn_native/vulkan/QuerySetVk.h
new file mode 100644
index 0000000..18cd001
--- /dev/null
+++ b/src/dawn_native/vulkan/QuerySetVk.h
@@ -0,0 +1,45 @@
+// 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_VULKAN_QUERYSETVK_H_
+#define DAWNNATIVE_VULKAN_QUERYSETVK_H_
+
+#include "dawn_native/QuerySet.h"
+
+#include "common/vulkan_platform.h"
+
+namespace dawn_native { namespace vulkan {
+
+    class Device;
+
+    class QuerySet final : public QuerySetBase {
+      public:
+        static ResultOrError<QuerySet*> Create(Device* device,
+                                               const QuerySetDescriptor* descriptor);
+
+        VkQueryPool GetHandle() const;
+
+      private:
+        ~QuerySet() override;
+        using QuerySetBase::QuerySetBase;
+        MaybeError Initialize();
+
+        void DestroyImpl() override;
+
+        VkQueryPool mHandle = VK_NULL_HANDLE;
+    };
+
+}}  // namespace dawn_native::vulkan
+
+#endif  // DAWNNATIVE_VULKAN_QUERYSETVK_H_
diff --git a/src/tests/end2end/QueryTests.cpp b/src/tests/end2end/QueryTests.cpp
index 5436021..47ccdd0 100644
--- a/src/tests/end2end/QueryTests.cpp
+++ b/src/tests/end2end/QueryTests.cpp
@@ -57,7 +57,7 @@
     querySet.Destroy();
 }
 
-DAWN_INSTANTIATE_TEST(OcclusionQueryTests, D3D12Backend());
+DAWN_INSTANTIATE_TEST(OcclusionQueryTests, D3D12Backend(), VulkanBackend());
 
 class PipelineStatisticsQueryTests : public QueryTests {
   protected:
@@ -91,7 +91,7 @@
     device.CreateQuerySet(&descriptor);
 }
 
-DAWN_INSTANTIATE_TEST(PipelineStatisticsQueryTests, D3D12Backend());
+DAWN_INSTANTIATE_TEST(PipelineStatisticsQueryTests, D3D12Backend(), VulkanBackend());
 
 class TimestampExpectation : public detail::Expectation {
   public: