Query API: QuerySet

- Add QuerySet w/o backends implementation.
- Add validation tests

Bug: dawn:434
Change-Id: Id9fed4e42fac464b1254cd2e9cf5337a1d803089
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/22440
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/dawn.json b/dawn.json
index a9f278b..aa26700 100644
--- a/dawn.json
+++ b/dawn.json
@@ -636,6 +636,13 @@
                     {"name": "callback", "type": "error callback"},
                     {"name": "userdata", "type": "void", "annotation": "*"}
                 ]
+            },
+            {
+                "name": "create query set",
+                "returns": "query set",
+                "args": [
+                    {"name": "descriptor", "type": "query set descriptor", "annotation": "const*"}
+                ]
             }
         ]
     },
@@ -854,6 +861,16 @@
             {"name": "bind group layouts", "type": "bind group layout", "annotation": "const*", "length": "bind group layout count"}
         ]
     },
+    "pipeline statistics name": {
+        "category": "enum",
+        "values": [
+            {"value": 0, "name": "vertex shader invocations"},
+            {"value": 1, "name": "clipper invocations"},
+            {"value": 2, "name": "clipper primitives out"},
+            {"value": 3, "name": "fragment shader invocations"},
+            {"value": 4, "name": "compute shader invocations"}
+        ]
+    },
     "present mode": {
         "category": "enum",
         "values": [
@@ -880,6 +897,33 @@
             {"value": 4, "name": "triangle strip"}
         ]
     },
+    "query set": {
+        "category": "object",
+        "methods": [
+            {
+                "name": "destroy"
+            }
+        ]
+    },
+    "query set descriptor": {
+        "category": "structure",
+        "extensible": true,
+        "members": [
+            {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true},
+            {"name": "type", "type": "query type"},
+            {"name": "count", "type": "uint32_t"},
+            {"name": "pipeline statistics count", "type": "uint32_t", "default": "0"},
+            {"name": "pipeline statistics", "type": "pipeline statistics name", "annotation": "const*", "length": "pipeline statistics count"}
+        ]
+    },
+    "query type": {
+        "category": "enum",
+        "values": [
+            {"value": 0, "name": "occlusion"},
+            {"value": 1, "name": "pipeline statistics"},
+            {"value": 2, "name": "timestamp"}
+        ]
+    },
     "queue": {
         "category": "object",
         "methods": [
diff --git a/src/dawn_native/BUILD.gn b/src/dawn_native/BUILD.gn
index 25f02f9..3f24ba3 100644
--- a/src/dawn_native/BUILD.gn
+++ b/src/dawn_native/BUILD.gn
@@ -230,6 +230,8 @@
     "PipelineLayout.h",
     "ProgrammablePassEncoder.cpp",
     "ProgrammablePassEncoder.h",
+    "QuerySet.cpp",
+    "QuerySet.h",
     "Queue.cpp",
     "Queue.h",
     "RenderBundle.cpp",
@@ -442,6 +444,8 @@
       "opengl/PipelineGL.h",
       "opengl/PipelineLayoutGL.cpp",
       "opengl/PipelineLayoutGL.h",
+      "opengl/QuerySetGL.cpp",
+      "opengl/QuerySetGL.h",
       "opengl/QueueGL.cpp",
       "opengl/QueueGL.h",
       "opengl/RenderPipelineGL.cpp",
diff --git a/src/dawn_native/CMakeLists.txt b/src/dawn_native/CMakeLists.txt
index c51ae82..7cee0d0 100644
--- a/src/dawn_native/CMakeLists.txt
+++ b/src/dawn_native/CMakeLists.txt
@@ -102,6 +102,8 @@
     "PipelineLayout.h"
     "ProgrammablePassEncoder.cpp"
     "ProgrammablePassEncoder.h"
+    "QuerySet.cpp"
+    "QuerySet.h"
     "Queue.cpp"
     "Queue.h"
     "RenderBundle.cpp"
@@ -333,6 +335,8 @@
         "opengl/PipelineGL.h"
         "opengl/PipelineLayoutGL.cpp"
         "opengl/PipelineLayoutGL.h"
+        "opengl/QuerySetGL.cpp"
+        "opengl/QuerySetGL.h"
         "opengl/QueueGL.cpp"
         "opengl/QueueGL.h"
         "opengl/RenderPipelineGL.cpp"
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index 01fc874..73bbcf9 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -32,6 +32,7 @@
 #include "dawn_native/Instance.h"
 #include "dawn_native/MapRequestTracker.h"
 #include "dawn_native/PipelineLayout.h"
+#include "dawn_native/QuerySet.h"
 #include "dawn_native/Queue.h"
 #include "dawn_native/RenderBundleEncoder.h"
 #include "dawn_native/RenderPipeline.h"
@@ -647,6 +648,15 @@
 
         return result;
     }
+    QuerySetBase* DeviceBase::CreateQuerySet(const QuerySetDescriptor* descriptor) {
+        QuerySetBase* result = nullptr;
+
+        if (ConsumedError(CreateQuerySetInternal(&result, descriptor))) {
+            return QuerySetBase::MakeError(this);
+        }
+
+        return result;
+    }
     QueueBase* DeviceBase::CreateQueue() {
         // TODO(dawn:22): Remove this once users use GetDefaultQueue
         EmitDeprecationWarning(
@@ -884,6 +894,16 @@
         return {};
     }
 
+    MaybeError DeviceBase::CreateQuerySetInternal(QuerySetBase** result,
+                                                  const QuerySetDescriptor* descriptor) {
+        DAWN_TRY(ValidateIsAlive());
+        if (IsValidationEnabled()) {
+            DAWN_TRY(ValidateQuerySetDescriptor(this, descriptor));
+        }
+        DAWN_TRY_ASSIGN(*result, CreateQuerySetImpl(descriptor));
+        return {};
+    }
+
     MaybeError DeviceBase::CreateRenderBundleEncoderInternal(
         RenderBundleEncoder** result,
         const RenderBundleEncoderDescriptor* descriptor) {
diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h
index 9e336ac..b94f762 100644
--- a/src/dawn_native/Device.h
+++ b/src/dawn_native/Device.h
@@ -146,6 +146,7 @@
         CommandEncoder* CreateCommandEncoder(const CommandEncoderDescriptor* descriptor);
         ComputePipelineBase* CreateComputePipeline(const ComputePipelineDescriptor* descriptor);
         PipelineLayoutBase* CreatePipelineLayout(const PipelineLayoutDescriptor* descriptor);
+        QuerySetBase* CreateQuerySet(const QuerySetDescriptor* descriptor);
         QueueBase* CreateQueue();
         RenderBundleEncoder* CreateRenderBundleEncoder(
             const RenderBundleEncoderDescriptor* descriptor);
@@ -239,6 +240,8 @@
             const ComputePipelineDescriptor* descriptor) = 0;
         virtual ResultOrError<PipelineLayoutBase*> CreatePipelineLayoutImpl(
             const PipelineLayoutDescriptor* descriptor) = 0;
+        virtual ResultOrError<QuerySetBase*> CreateQuerySetImpl(
+            const QuerySetDescriptor* descriptor) = 0;
         virtual ResultOrError<RenderPipelineBase*> CreateRenderPipelineImpl(
             const RenderPipelineDescriptor* descriptor) = 0;
         virtual ResultOrError<SamplerBase*> CreateSamplerImpl(
@@ -267,6 +270,8 @@
                                                  const ComputePipelineDescriptor* descriptor);
         MaybeError CreatePipelineLayoutInternal(PipelineLayoutBase** result,
                                                 const PipelineLayoutDescriptor* descriptor);
+        MaybeError CreateQuerySetInternal(QuerySetBase** result,
+                                          const QuerySetDescriptor* descriptor);
         MaybeError CreateRenderBundleEncoderInternal(
             RenderBundleEncoder** result,
             const RenderBundleEncoderDescriptor* descriptor);
diff --git a/src/dawn_native/Forward.h b/src/dawn_native/Forward.h
index be7c98f..66e07e4 100644
--- a/src/dawn_native/Forward.h
+++ b/src/dawn_native/Forward.h
@@ -34,6 +34,7 @@
     class InstanceBase;
     class PipelineBase;
     class PipelineLayoutBase;
+    class QuerySetBase;
     class QueueBase;
     class RenderBundleBase;
     class RenderBundleEncoder;
diff --git a/src/dawn_native/QuerySet.cpp b/src/dawn_native/QuerySet.cpp
new file mode 100644
index 0000000..513658d
--- /dev/null
+++ b/src/dawn_native/QuerySet.cpp
@@ -0,0 +1,153 @@
+// 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/QuerySet.h"
+
+#include "dawn_native/Device.h"
+#include "dawn_native/Extensions.h"
+#include "dawn_native/ValidationUtils_autogen.h"
+
+#include <set>
+
+namespace dawn_native {
+
+    namespace {
+
+        class ErrorQuerySet final : public QuerySetBase {
+          public:
+            ErrorQuerySet(DeviceBase* device) : QuerySetBase(device, ObjectBase::kError) {
+            }
+
+          private:
+            void DestroyImpl() override {
+                UNREACHABLE();
+            }
+        };
+
+    }  // anonymous namespace
+
+    MaybeError ValidateQuerySetDescriptor(DeviceBase* device,
+                                          const QuerySetDescriptor* descriptor) {
+        if (descriptor->nextInChain != nullptr) {
+            return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
+        }
+
+        DAWN_TRY(ValidateQueryType(descriptor->type));
+
+        switch (descriptor->type) {
+            case wgpu::QueryType::Occlusion:
+                if (descriptor->pipelineStatisticsCount != 0) {
+                    return DAWN_VALIDATION_ERROR(
+                        "The pipeline statistics should not be set if query type is Occlusion");
+                }
+                break;
+
+            case wgpu::QueryType::PipelineStatistics: {
+                if (!device->IsExtensionEnabled(Extension::PipelineStatisticsQuery)) {
+                    return DAWN_VALIDATION_ERROR(
+                        "The pipeline statistics query feature is not supported");
+                }
+
+                if (descriptor->pipelineStatisticsCount == 0) {
+                    return DAWN_VALIDATION_ERROR(
+                        "At least one pipeline statistics is set if query type is "
+                        "PipelineStatistics");
+                }
+
+                std::set<wgpu::PipelineStatisticsName> pipelineStatisticsSet;
+                for (uint32_t i = 0; i < descriptor->pipelineStatisticsCount; i++) {
+                    DAWN_TRY(ValidatePipelineStatisticsName(descriptor->pipelineStatistics[i]));
+
+                    std::pair<std::set<wgpu::PipelineStatisticsName>::iterator, bool> res =
+                        pipelineStatisticsSet.insert((descriptor->pipelineStatistics[i]));
+                    if (!res.second) {
+                        return DAWN_VALIDATION_ERROR("Duplicate pipeline statistics found");
+                    }
+                }
+            } break;
+
+            case wgpu::QueryType::Timestamp:
+                if (!device->IsExtensionEnabled(Extension::TimestampQuery)) {
+                    return DAWN_VALIDATION_ERROR("The timestamp query feature is not supported");
+                }
+
+                if (descriptor->pipelineStatisticsCount != 0) {
+                    return DAWN_VALIDATION_ERROR(
+                        "The pipeline statistics should not be set if query type is Timestamp");
+                }
+                break;
+
+            default:
+                break;
+        }
+
+        return {};
+    }
+
+    QuerySetBase::QuerySetBase(DeviceBase* device, const QuerySetDescriptor* descriptor)
+        : ObjectBase(device),
+          mQueryType(descriptor->type),
+          mQueryCount(descriptor->count),
+          mState(QuerySetState::Available) {
+        for (uint32_t i = 0; i < descriptor->pipelineStatisticsCount; i++) {
+            mPipelineStatistics.push_back(descriptor->pipelineStatistics[i]);
+        }
+    }
+
+    QuerySetBase::QuerySetBase(DeviceBase* device, ObjectBase::ErrorTag tag)
+        : ObjectBase(device, tag) {
+    }
+
+    QuerySetBase::~QuerySetBase() {
+        // Uninitialized or already destroyed
+        ASSERT(mState == QuerySetState::Unavailable || mState == QuerySetState::Destroyed);
+    }
+
+    // static
+    QuerySetBase* QuerySetBase::MakeError(DeviceBase* device) {
+        return new ErrorQuerySet(device);
+    }
+
+    wgpu::QueryType QuerySetBase::GetQueryType() const {
+        return mQueryType;
+    }
+
+    uint32_t QuerySetBase::GetQueryCount() const {
+        return mQueryCount;
+    }
+
+    const std::vector<wgpu::PipelineStatisticsName>& QuerySetBase::GetPipelineStatistics() const {
+        return mPipelineStatistics;
+    }
+
+    void QuerySetBase::Destroy() {
+        if (GetDevice()->ConsumedError(ValidateDestroy())) {
+            return;
+        }
+        DestroyInternal();
+    }
+
+    MaybeError QuerySetBase::ValidateDestroy() const {
+        DAWN_TRY(GetDevice()->ValidateObject(this));
+        return {};
+    }
+
+    void QuerySetBase::DestroyInternal() {
+        if (mState != QuerySetState::Destroyed) {
+            DestroyImpl();
+        }
+        mState = QuerySetState::Destroyed;
+    }
+
+}  // namespace dawn_native
diff --git a/src/dawn_native/QuerySet.h b/src/dawn_native/QuerySet.h
new file mode 100644
index 0000000..7883678
--- /dev/null
+++ b/src/dawn_native/QuerySet.h
@@ -0,0 +1,61 @@
+// 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_QUERYSET_H_
+#define DAWNNATIVE_QUERYSET_H_
+
+#include "dawn_native/Error.h"
+#include "dawn_native/Forward.h"
+#include "dawn_native/ObjectBase.h"
+
+#include "dawn_native/dawn_platform.h"
+
+namespace dawn_native {
+
+    MaybeError ValidateQuerySetDescriptor(DeviceBase* device, const QuerySetDescriptor* descriptor);
+
+    class QuerySetBase : public ObjectBase {
+      public:
+        QuerySetBase(DeviceBase* device, const QuerySetDescriptor* descriptor);
+
+        static QuerySetBase* MakeError(DeviceBase* device);
+
+        wgpu::QueryType GetQueryType() const;
+        uint32_t GetQueryCount() const;
+        const std::vector<wgpu::PipelineStatisticsName>& GetPipelineStatistics() const;
+
+        void Destroy();
+
+      protected:
+        QuerySetBase(DeviceBase* device, ObjectBase::ErrorTag tag);
+        ~QuerySetBase() override;
+
+        void DestroyInternal();
+
+      private:
+        virtual void DestroyImpl() = 0;
+
+        MaybeError ValidateDestroy() const;
+
+        wgpu::QueryType mQueryType;
+        uint32_t mQueryCount;
+        std::vector<wgpu::PipelineStatisticsName> mPipelineStatistics;
+
+        enum class QuerySetState { Unavailable, Available, Destroyed };
+        QuerySetState mState = QuerySetState::Unavailable;
+    };
+
+}  // namespace dawn_native
+
+#endif  // DAWNNATIVE_QUERYSET_H_
diff --git a/src/dawn_native/ToBackend.h b/src/dawn_native/ToBackend.h
index b9940ab..3cc0715 100644
--- a/src/dawn_native/ToBackend.h
+++ b/src/dawn_native/ToBackend.h
@@ -64,6 +64,11 @@
     };
 
     template <typename BackendTraits>
+    struct ToBackendTraits<QuerySetBase, BackendTraits> {
+        using BackendType = typename BackendTraits::QuerySetType;
+    };
+
+    template <typename BackendTraits>
     struct ToBackendTraits<QueueBase, BackendTraits> {
         using BackendType = typename BackendTraits::QueueType;
     };
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index a32b77c..dcb0227 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -285,6 +285,9 @@
         const PipelineLayoutDescriptor* descriptor) {
         return PipelineLayout::Create(this, descriptor);
     }
+    ResultOrError<QuerySetBase*> Device::CreateQuerySetImpl(const QuerySetDescriptor* descriptor) {
+        return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation");
+    }
     ResultOrError<RenderPipelineBase*> Device::CreateRenderPipelineImpl(
         const RenderPipelineDescriptor* descriptor) {
         return RenderPipeline::Create(this, descriptor);
diff --git a/src/dawn_native/d3d12/DeviceD3D12.h b/src/dawn_native/d3d12/DeviceD3D12.h
index 7ce3a95..1ee4092 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.h
+++ b/src/dawn_native/d3d12/DeviceD3D12.h
@@ -136,6 +136,8 @@
             const ComputePipelineDescriptor* descriptor) override;
         ResultOrError<PipelineLayoutBase*> CreatePipelineLayoutImpl(
             const PipelineLayoutDescriptor* descriptor) override;
+        ResultOrError<QuerySetBase*> CreateQuerySetImpl(
+            const QuerySetDescriptor* descriptor) override;
         ResultOrError<RenderPipelineBase*> CreateRenderPipelineImpl(
             const RenderPipelineDescriptor* descriptor) override;
         ResultOrError<SamplerBase*> CreateSamplerImpl(const SamplerDescriptor* descriptor) override;
diff --git a/src/dawn_native/d3d12/Forward.h b/src/dawn_native/d3d12/Forward.h
index 1143a4a..4e5368b 100644
--- a/src/dawn_native/d3d12/Forward.h
+++ b/src/dawn_native/d3d12/Forward.h
@@ -28,6 +28,7 @@
     class Device;
     class Heap;
     class PipelineLayout;
+    class QuerySet;
     class Queue;
     class RenderPipeline;
     class Sampler;
@@ -46,6 +47,7 @@
         using ComputePipelineType = ComputePipeline;
         using DeviceType = Device;
         using PipelineLayoutType = PipelineLayout;
+        using QuerySetType = QuerySet;
         using QueueType = Queue;
         using RenderPipelineType = RenderPipeline;
         using ResourceHeapType = Heap;
diff --git a/src/dawn_native/metal/DeviceMTL.h b/src/dawn_native/metal/DeviceMTL.h
index 33ce4b6..76e4151 100644
--- a/src/dawn_native/metal/DeviceMTL.h
+++ b/src/dawn_native/metal/DeviceMTL.h
@@ -76,6 +76,8 @@
             const ComputePipelineDescriptor* descriptor) override;
         ResultOrError<PipelineLayoutBase*> CreatePipelineLayoutImpl(
             const PipelineLayoutDescriptor* descriptor) override;
+        ResultOrError<QuerySetBase*> CreateQuerySetImpl(
+            const QuerySetDescriptor* descriptor) override;
         ResultOrError<RenderPipelineBase*> CreateRenderPipelineImpl(
             const RenderPipelineDescriptor* descriptor) override;
         ResultOrError<SamplerBase*> CreateSamplerImpl(const SamplerDescriptor* descriptor) override;
diff --git a/src/dawn_native/metal/DeviceMTL.mm b/src/dawn_native/metal/DeviceMTL.mm
index 34b66fc..d53e2e1 100644
--- a/src/dawn_native/metal/DeviceMTL.mm
+++ b/src/dawn_native/metal/DeviceMTL.mm
@@ -123,6 +123,9 @@
         const PipelineLayoutDescriptor* descriptor) {
         return new PipelineLayout(this, descriptor);
     }
+    ResultOrError<QuerySetBase*> Device::CreateQuerySetImpl(const QuerySetDescriptor* descriptor) {
+        return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation");
+    }
     ResultOrError<RenderPipelineBase*> Device::CreateRenderPipelineImpl(
         const RenderPipelineDescriptor* descriptor) {
         return RenderPipeline::Create(this, descriptor);
diff --git a/src/dawn_native/metal/Forward.h b/src/dawn_native/metal/Forward.h
index a773a18..9481348 100644
--- a/src/dawn_native/metal/Forward.h
+++ b/src/dawn_native/metal/Forward.h
@@ -28,6 +28,7 @@
     class Device;
     class Framebuffer;
     class PipelineLayout;
+    class QuerySet;
     class Queue;
     class RenderPipeline;
     class Sampler;
@@ -46,6 +47,7 @@
         using ComputePipelineType = ComputePipeline;
         using DeviceType = Device;
         using PipelineLayoutType = PipelineLayout;
+        using QuerySetType = QuerySet;
         using QueueType = Queue;
         using RenderPipelineType = RenderPipeline;
         using SamplerType = Sampler;
diff --git a/src/dawn_native/null/DeviceNull.cpp b/src/dawn_native/null/DeviceNull.cpp
index fb11d59..c19670e 100644
--- a/src/dawn_native/null/DeviceNull.cpp
+++ b/src/dawn_native/null/DeviceNull.cpp
@@ -116,6 +116,9 @@
         const PipelineLayoutDescriptor* descriptor) {
         return new PipelineLayout(this, descriptor);
     }
+    ResultOrError<QuerySetBase*> Device::CreateQuerySetImpl(const QuerySetDescriptor* descriptor) {
+        return new QuerySet(this, descriptor);
+    }
     ResultOrError<RenderPipelineBase*> Device::CreateRenderPipelineImpl(
         const RenderPipelineDescriptor* descriptor) {
         return new RenderPipeline(this, descriptor);
@@ -351,6 +354,19 @@
         FreeCommands(&mCommands);
     }
 
+    // QuerySet
+
+    QuerySet::QuerySet(Device* device, const QuerySetDescriptor* descriptor)
+        : QuerySetBase(device, descriptor) {
+    }
+
+    QuerySet::~QuerySet() {
+        DestroyInternal();
+    }
+
+    void QuerySet::DestroyImpl() {
+    }
+
     // Queue
 
     Queue::Queue(Device* device) : QueueBase(device) {
diff --git a/src/dawn_native/null/DeviceNull.h b/src/dawn_native/null/DeviceNull.h
index 7a93822..42fce4d 100644
--- a/src/dawn_native/null/DeviceNull.h
+++ b/src/dawn_native/null/DeviceNull.h
@@ -24,6 +24,7 @@
 #include "dawn_native/ComputePipeline.h"
 #include "dawn_native/Device.h"
 #include "dawn_native/PipelineLayout.h"
+#include "dawn_native/QuerySet.h"
 #include "dawn_native/Queue.h"
 #include "dawn_native/RenderPipeline.h"
 #include "dawn_native/RingBufferAllocator.h"
@@ -45,6 +46,7 @@
     using ComputePipeline = ComputePipelineBase;
     class Device;
     using PipelineLayout = PipelineLayoutBase;
+    class QuerySet;
     class Queue;
     using RenderPipeline = RenderPipelineBase;
     using Sampler = SamplerBase;
@@ -62,6 +64,7 @@
         using ComputePipelineType = ComputePipeline;
         using DeviceType = Device;
         using PipelineLayoutType = PipelineLayout;
+        using QuerySetType = QuerySet;
         using QueueType = Queue;
         using RenderPipelineType = RenderPipeline;
         using SamplerType = Sampler;
@@ -118,6 +121,8 @@
             const ComputePipelineDescriptor* descriptor) override;
         ResultOrError<PipelineLayoutBase*> CreatePipelineLayoutImpl(
             const PipelineLayoutDescriptor* descriptor) override;
+        ResultOrError<QuerySetBase*> CreateQuerySetImpl(
+            const QuerySetDescriptor* descriptor) override;
         ResultOrError<RenderPipelineBase*> CreateRenderPipelineImpl(
             const RenderPipelineDescriptor* descriptor) override;
         ResultOrError<SamplerBase*> CreateSamplerImpl(const SamplerDescriptor* descriptor) override;
@@ -216,6 +221,16 @@
         CommandIterator mCommands;
     };
 
+    class QuerySet final : public QuerySetBase {
+      public:
+        QuerySet(Device* device, const QuerySetDescriptor* descriptor);
+
+      private:
+        ~QuerySet() override;
+
+        void DestroyImpl() override;
+    };
+
     class Queue final : public QueueBase {
       public:
         Queue(Device* device);
diff --git a/src/dawn_native/opengl/DeviceGL.cpp b/src/dawn_native/opengl/DeviceGL.cpp
index 6550b48..f47474f 100644
--- a/src/dawn_native/opengl/DeviceGL.cpp
+++ b/src/dawn_native/opengl/DeviceGL.cpp
@@ -24,6 +24,7 @@
 #include "dawn_native/opengl/CommandBufferGL.h"
 #include "dawn_native/opengl/ComputePipelineGL.h"
 #include "dawn_native/opengl/PipelineLayoutGL.h"
+#include "dawn_native/opengl/QuerySetGL.h"
 #include "dawn_native/opengl/QueueGL.h"
 #include "dawn_native/opengl/RenderPipelineGL.h"
 #include "dawn_native/opengl/SamplerGL.h"
@@ -116,6 +117,9 @@
         const PipelineLayoutDescriptor* descriptor) {
         return new PipelineLayout(this, descriptor);
     }
+    ResultOrError<QuerySetBase*> Device::CreateQuerySetImpl(const QuerySetDescriptor* descriptor) {
+        return new QuerySet(this, descriptor);
+    }
     ResultOrError<RenderPipelineBase*> Device::CreateRenderPipelineImpl(
         const RenderPipelineDescriptor* descriptor) {
         return new RenderPipeline(this, descriptor);
diff --git a/src/dawn_native/opengl/DeviceGL.h b/src/dawn_native/opengl/DeviceGL.h
index c38fcf6..4a03f4a 100644
--- a/src/dawn_native/opengl/DeviceGL.h
+++ b/src/dawn_native/opengl/DeviceGL.h
@@ -19,6 +19,7 @@
 
 #include "common/Platform.h"
 #include "dawn_native/Device.h"
+#include "dawn_native/QuerySet.h"
 #include "dawn_native/opengl/Forward.h"
 #include "dawn_native/opengl/GLFormat.h"
 #include "dawn_native/opengl/OpenGLFunctions.h"
@@ -75,6 +76,8 @@
             const ComputePipelineDescriptor* descriptor) override;
         ResultOrError<PipelineLayoutBase*> CreatePipelineLayoutImpl(
             const PipelineLayoutDescriptor* descriptor) override;
+        ResultOrError<QuerySetBase*> CreateQuerySetImpl(
+            const QuerySetDescriptor* descriptor) override;
         ResultOrError<RenderPipelineBase*> CreateRenderPipelineImpl(
             const RenderPipelineDescriptor* descriptor) override;
         ResultOrError<SamplerBase*> CreateSamplerImpl(const SamplerDescriptor* descriptor) override;
diff --git a/src/dawn_native/opengl/Forward.h b/src/dawn_native/opengl/Forward.h
index bd2cc76..82d0766 100644
--- a/src/dawn_native/opengl/Forward.h
+++ b/src/dawn_native/opengl/Forward.h
@@ -28,6 +28,7 @@
     class Device;
     class PersistentPipelineState;
     class PipelineLayout;
+    class QuerySet;
     class Queue;
     class RenderPipeline;
     class Sampler;
@@ -45,6 +46,7 @@
         using ComputePipelineType = ComputePipeline;
         using DeviceType = Device;
         using PipelineLayoutType = PipelineLayout;
+        using QuerySetType = QuerySet;
         using QueueType = Queue;
         using RenderPipelineType = RenderPipeline;
         using SamplerType = Sampler;
diff --git a/src/dawn_native/opengl/QuerySetGL.cpp b/src/dawn_native/opengl/QuerySetGL.cpp
new file mode 100644
index 0000000..6ff5d20
--- /dev/null
+++ b/src/dawn_native/opengl/QuerySetGL.cpp
@@ -0,0 +1,32 @@
+// 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/opengl/QuerySetGL.h"
+
+#include "dawn_native/opengl/DeviceGL.h"
+
+namespace dawn_native { namespace opengl {
+
+    QuerySet::QuerySet(Device* device, const QuerySetDescriptor* descriptor)
+        : QuerySetBase(device, descriptor) {
+    }
+
+    QuerySet::~QuerySet() {
+        DestroyInternal();
+    }
+
+    void QuerySet::DestroyImpl() {
+    }
+
+}}  // namespace dawn_native::opengl
diff --git a/src/dawn_native/opengl/QuerySetGL.h b/src/dawn_native/opengl/QuerySetGL.h
new file mode 100644
index 0000000..2a83bdd
--- /dev/null
+++ b/src/dawn_native/opengl/QuerySetGL.h
@@ -0,0 +1,36 @@
+// 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_OPENGL_QUERYSETGL_H_
+#define DAWNNATIVE_OPENGL_QUERYSETGL_H_
+
+#include "dawn_native/QuerySet.h"
+
+namespace dawn_native { namespace opengl {
+
+    class Device;
+
+    class QuerySet final : public QuerySetBase {
+      public:
+        QuerySet(Device* device, const QuerySetDescriptor* descriptor);
+
+      private:
+        ~QuerySet() override;
+
+        void DestroyImpl() override;
+    };
+
+}}  // namespace dawn_native::opengl
+
+#endif  // DAWNNATIVE_OPENGL_QUERYSETGL_H_
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index bd3ba34..fb116a1 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -124,6 +124,9 @@
         const PipelineLayoutDescriptor* descriptor) {
         return PipelineLayout::Create(this, descriptor);
     }
+    ResultOrError<QuerySetBase*> Device::CreateQuerySetImpl(const QuerySetDescriptor* descriptor) {
+        return DAWN_UNIMPLEMENTED_ERROR("Waiting for implementation");
+    }
     ResultOrError<RenderPipelineBase*> Device::CreateRenderPipelineImpl(
         const RenderPipelineDescriptor* descriptor) {
         return RenderPipeline::Create(this, descriptor);
diff --git a/src/dawn_native/vulkan/DeviceVk.h b/src/dawn_native/vulkan/DeviceVk.h
index 799f7f2..b38eb77 100644
--- a/src/dawn_native/vulkan/DeviceVk.h
+++ b/src/dawn_native/vulkan/DeviceVk.h
@@ -108,6 +108,8 @@
             const ComputePipelineDescriptor* descriptor) override;
         ResultOrError<PipelineLayoutBase*> CreatePipelineLayoutImpl(
             const PipelineLayoutDescriptor* descriptor) override;
+        ResultOrError<QuerySetBase*> CreateQuerySetImpl(
+            const QuerySetDescriptor* descriptor) override;
         ResultOrError<RenderPipelineBase*> CreateRenderPipelineImpl(
             const RenderPipelineDescriptor* descriptor) override;
         ResultOrError<SamplerBase*> CreateSamplerImpl(const SamplerDescriptor* descriptor) override;
diff --git a/src/dawn_native/vulkan/Forward.h b/src/dawn_native/vulkan/Forward.h
index 9b5a7a1..e11a74f 100644
--- a/src/dawn_native/vulkan/Forward.h
+++ b/src/dawn_native/vulkan/Forward.h
@@ -27,6 +27,7 @@
     class ComputePipeline;
     class Device;
     class PipelineLayout;
+    class QuerySet;
     class Queue;
     class RenderPipeline;
     class ResourceHeap;
@@ -46,6 +47,7 @@
         using ComputePipelineType = ComputePipeline;
         using DeviceType = Device;
         using PipelineLayoutType = PipelineLayout;
+        using QuerySetType = QuerySet;
         using QueueType = Queue;
         using RenderPipelineType = RenderPipeline;
         using ResourceHeapType = ResourceHeap;
diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn
index b9ae2fe..e602269 100644
--- a/src/tests/BUILD.gn
+++ b/src/tests/BUILD.gn
@@ -185,6 +185,7 @@
     "unittests/validation/FenceValidationTests.cpp",
     "unittests/validation/GetBindGroupLayoutValidationTests.cpp",
     "unittests/validation/IndexBufferValidationTests.cpp",
+    "unittests/validation/QuerySetValidationTests.cpp",
     "unittests/validation/QueueSubmitValidationTests.cpp",
     "unittests/validation/RenderBundleValidationTests.cpp",
     "unittests/validation/RenderPassDescriptorValidationTests.cpp",
diff --git a/src/tests/unittests/validation/QuerySetValidationTests.cpp b/src/tests/unittests/validation/QuerySetValidationTests.cpp
new file mode 100644
index 0000000..311bc66
--- /dev/null
+++ b/src/tests/unittests/validation/QuerySetValidationTests.cpp
@@ -0,0 +1,150 @@
+// 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 "tests/unittests/validation/ValidationTest.h"
+
+#include "utils/WGPUHelpers.h"
+
+class QuerySetValidationTest : public ValidationTest {
+  protected:
+    void SetUp() override {
+        ValidationTest::SetUp();
+
+        // Initialize the device with required extensions
+        deviceWithPipelineStatistics =
+            CreateDeviceFromAdapter(adapter, {"pipeline_statistics_query"});
+        deviceWithTimestamp = CreateDeviceFromAdapter(adapter, {"timestamp_query"});
+    }
+
+    void CreateQuerySet(wgpu::Device cDevice,
+                        wgpu::QueryType queryType,
+                        uint32_t queryCount,
+                        std::vector<wgpu::PipelineStatisticsName> pipelineStatistics = {}) {
+        wgpu::QuerySetDescriptor descriptor;
+        descriptor.type = queryType;
+        descriptor.count = queryCount;
+
+        if (pipelineStatistics.size() > 0) {
+            descriptor.pipelineStatistics = pipelineStatistics.data();
+            descriptor.pipelineStatisticsCount = pipelineStatistics.size();
+        }
+
+        cDevice.CreateQuerySet(&descriptor);
+    }
+
+    wgpu::Device deviceWithPipelineStatistics;
+    wgpu::Device deviceWithTimestamp;
+};
+
+// Test creating query set with/without extensions
+TEST_F(QuerySetValidationTest, Creation) {
+    // Create query set for Occlusion query
+    {
+        // Success on default device without any extension enabled
+        // Occlusion query does not require any extension.
+        CreateQuerySet(device, wgpu::QueryType::Occlusion, 1);
+
+        // Success on the device with extension enabled.
+        CreateQuerySet(deviceWithPipelineStatistics, wgpu::QueryType::Occlusion, 1);
+        CreateQuerySet(deviceWithTimestamp, wgpu::QueryType::Occlusion, 1);
+    }
+
+    // Create query set for PipelineStatistics query
+    {
+        // Fail on default device without any extension enabled
+        ASSERT_DEVICE_ERROR(
+            CreateQuerySet(device, wgpu::QueryType::PipelineStatistics, 1,
+                           {wgpu::PipelineStatisticsName::VertexShaderInvocations}));
+
+        // Success on the device if the extension is enabled.
+        CreateQuerySet(deviceWithPipelineStatistics, wgpu::QueryType::PipelineStatistics, 1,
+                       {wgpu::PipelineStatisticsName::VertexShaderInvocations});
+    }
+
+    // Create query set for Timestamp query
+    {
+        // Fail on default device without any extension enabled
+        ASSERT_DEVICE_ERROR(CreateQuerySet(device, wgpu::QueryType::Timestamp, 1));
+
+        // Success on the device if the extension is enabled.
+        CreateQuerySet(deviceWithTimestamp, wgpu::QueryType::Timestamp, 1);
+    }
+}
+
+// Test creating query set with invalid type
+TEST_F(QuerySetValidationTest, InvalidQueryType) {
+    ASSERT_DEVICE_ERROR(CreateQuerySet(device, static_cast<wgpu::QueryType>(0xFFFFFFFF), 1));
+}
+
+// Test creating query set with unnecessary pipeline statistics
+TEST_F(QuerySetValidationTest, UnnecessaryPipelineStatistics) {
+    // Fail to create with pipeline statistics for Occlusion query
+    {
+        ASSERT_DEVICE_ERROR(
+            CreateQuerySet(device, wgpu::QueryType::Occlusion, 1,
+                           {wgpu::PipelineStatisticsName::VertexShaderInvocations}));
+    }
+
+    // Fail to create with pipeline statistics for Timestamp query
+    {
+        ASSERT_DEVICE_ERROR(
+            CreateQuerySet(deviceWithTimestamp, wgpu::QueryType::Timestamp, 1,
+                           {wgpu::PipelineStatisticsName::VertexShaderInvocations}));
+    }
+}
+
+// Test creating query set with invalid pipeline statistics
+TEST_F(QuerySetValidationTest, InvalidPipelineStatistics) {
+    // Success to create with all pipeline statistics names which are not in the same order as
+    // defined in webgpu header file
+    {
+        CreateQuerySet(deviceWithPipelineStatistics, wgpu::QueryType::PipelineStatistics, 1,
+                       {wgpu::PipelineStatisticsName::ClipperInvocations,
+                        wgpu::PipelineStatisticsName::ClipperPrimitivesOut,
+                        wgpu::PipelineStatisticsName::ComputeShaderInvocations,
+                        wgpu::PipelineStatisticsName::FragmentShaderInvocations,
+                        wgpu::PipelineStatisticsName::VertexShaderInvocations});
+    }
+
+    // Fail to create with empty pipeline statistics
+    {
+        ASSERT_DEVICE_ERROR(CreateQuerySet(deviceWithPipelineStatistics,
+                                           wgpu::QueryType::PipelineStatistics, 1, {}));
+    }
+
+    // Fail to create with invalid pipeline statistics
+    {
+        ASSERT_DEVICE_ERROR(
+            CreateQuerySet(deviceWithPipelineStatistics, wgpu::QueryType::PipelineStatistics, 1,
+                           {static_cast<wgpu::PipelineStatisticsName>(0xFFFFFFFF)}));
+    }
+
+    // Fail to create with duplicate pipeline statistics
+    {
+        ASSERT_DEVICE_ERROR(
+            CreateQuerySet(deviceWithPipelineStatistics, wgpu::QueryType::PipelineStatistics, 1,
+                           {wgpu::PipelineStatisticsName::VertexShaderInvocations,
+                            wgpu::PipelineStatisticsName::VertexShaderInvocations}));
+    }
+}
+
+// Test destroying a destroyed query set
+TEST_F(QuerySetValidationTest, DestroyDestroyedQuerySet) {
+    wgpu::QuerySetDescriptor descriptor;
+    descriptor.type = wgpu::QueryType::Occlusion;
+    descriptor.count = 1;
+    wgpu::QuerySet querySet = device.CreateQuerySet(&descriptor);
+    querySet.Destroy();
+    querySet.Destroy();
+}