dawn_native: Add Instance and Adapters

New objects are introduced to control what happens before device
creation in dawn_native:
 - Instance: a connection from the application to dawn_native that is
used for dependency injection and to discover adapters.
 - Adapters: represents the possibility of device creation for a specific
(GPU, backend) pair.
 - BackendConnection: an internal object that standardizes the interface
between the frontend and backends.

The BackendConnection interface is implemented for the Null backend and
stubbed out in other backends. This allows this change to port the
ValidationTests to use the new Instance and Adapters concept and deal
with other backends later.

BUG=dawn:29

Change-Id: I19719a9342b4af091accc0c02fb6b9697eadde7b
Reviewed-on: https://dawn-review.googlesource.com/c/3500
Reviewed-by: Stephen White <senorblanco@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 9a8fe4c..2673ebe 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -359,6 +359,10 @@
 
   sources = get_target_outputs(":libdawn_native_utils_gen")
   sources += [
+    "src/dawn_native/Adapter.cpp",
+    "src/dawn_native/Adapter.h",
+    "src/dawn_native/BackendConnection.cpp",
+    "src/dawn_native/BackendConnection.h",
     "src/dawn_native/BindGroup.cpp",
     "src/dawn_native/BindGroup.h",
     "src/dawn_native/BindGroupLayout.cpp",
@@ -393,6 +397,8 @@
     "src/dawn_native/Forward.h",
     "src/dawn_native/InputState.cpp",
     "src/dawn_native/InputState.h",
+    "src/dawn_native/Instance.cpp",
+    "src/dawn_native/Instance.h",
     "src/dawn_native/ObjectBase.cpp",
     "src/dawn_native/ObjectBase.h",
     "src/dawn_native/PassResourceUsage.h",
diff --git a/src/dawn_native/Adapter.cpp b/src/dawn_native/Adapter.cpp
new file mode 100644
index 0000000..137a1c7
--- /dev/null
+++ b/src/dawn_native/Adapter.cpp
@@ -0,0 +1,54 @@
+// 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 "dawn_native/Adapter.h"
+
+#include "dawn_native/Instance.h"
+
+namespace dawn_native {
+
+    AdapterBase::AdapterBase(InstanceBase* instance, BackendType backend)
+        : mInstance(instance), mBackend(backend) {
+    }
+
+    BackendType AdapterBase::GetBackendType() const {
+        return mBackend;
+    }
+
+    const PCIInfo& AdapterBase::GetPCIInfo() const {
+        return mPCIInfo;
+    }
+
+    InstanceBase* AdapterBase::GetInstance() const {
+        return mInstance;
+    }
+
+    DeviceBase* AdapterBase::CreateDevice() {
+        DeviceBase* result = nullptr;
+
+        if (mInstance->ConsumedError(CreateDeviceInternal(&result))) {
+            return nullptr;
+        }
+
+        return result;
+    }
+
+    MaybeError AdapterBase::CreateDeviceInternal(DeviceBase** result) {
+        // TODO(cwallez@chromium.org): This will eventually have validation that the device
+        // descriptor is valid and is a subset what's allowed on this adapter.
+        DAWN_TRY_ASSIGN(*result, CreateDeviceImpl());
+        return {};
+    }
+
+}  // namespace dawn_native
diff --git a/src/dawn_native/Adapter.h b/src/dawn_native/Adapter.h
new file mode 100644
index 0000000..afb7546
--- /dev/null
+++ b/src/dawn_native/Adapter.h
@@ -0,0 +1,49 @@
+// 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.
+
+#ifndef DAWNNATIVE_ADAPTER_H_
+#define DAWNNATIVE_ADAPTER_H_
+
+#include "dawn_native/DawnNative.h"
+
+#include "dawn_native/Error.h"
+
+namespace dawn_native {
+
+    class DeviceBase;
+
+    class AdapterBase {
+      public:
+        AdapterBase(InstanceBase* instance, BackendType backend);
+        virtual ~AdapterBase() = default;
+
+        BackendType GetBackendType() const;
+        const PCIInfo& GetPCIInfo() const;
+        InstanceBase* GetInstance() const;
+
+        DeviceBase* CreateDevice();
+
+      private:
+        virtual ResultOrError<DeviceBase*> CreateDeviceImpl() = 0;
+
+        MaybeError CreateDeviceInternal(DeviceBase** result);
+
+        InstanceBase* mInstance = nullptr;
+        BackendType mBackend;
+        PCIInfo mPCIInfo;
+    };
+
+}  // namespace dawn_native
+
+#endif  // DAWNNATIVE_ADAPTER_H_
diff --git a/src/dawn_native/BackendConnection.cpp b/src/dawn_native/BackendConnection.cpp
new file mode 100644
index 0000000..93356ff
--- /dev/null
+++ b/src/dawn_native/BackendConnection.cpp
@@ -0,0 +1,31 @@
+// 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 "dawn_native/BackendConnection.h"
+
+namespace dawn_native {
+
+    BackendConnection::BackendConnection(InstanceBase* instance, BackendType type)
+        : mInstance(instance), mType(type) {
+    }
+
+    BackendType BackendConnection::GetType() const {
+        return mType;
+    }
+
+    InstanceBase* BackendConnection::GetInstance() const {
+        return mInstance;
+    }
+
+}  // namespace dawn_native
diff --git a/src/dawn_native/BackendConnection.h b/src/dawn_native/BackendConnection.h
new file mode 100644
index 0000000..c3d6fe7
--- /dev/null
+++ b/src/dawn_native/BackendConnection.h
@@ -0,0 +1,44 @@
+// 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.
+
+#ifndef DAWNNATIVE_BACKENDCONNECTION_H_
+#define DAWNNATIVE_BACKENDCONNECTION_H_
+
+#include "dawn_native/Adapter.h"
+#include "dawn_native/DawnNative.h"
+
+namespace dawn_native {
+
+    // An common interface for all backends. Mostly used to create adapters for a particular
+    // backend.
+    class BackendConnection {
+      public:
+        BackendConnection(InstanceBase* instance, BackendType type);
+        virtual ~BackendConnection() = default;
+
+        BackendType GetType() const;
+        InstanceBase* GetInstance() const;
+
+        // Returns all the adapters for the system that can be created by the backend, without extra
+        // options (such as debug adapters, custom driver libraries, etc.)
+        virtual std::vector<std::unique_ptr<AdapterBase>> DiscoverDefaultAdapters() = 0;
+
+      private:
+        InstanceBase* mInstance = nullptr;
+        BackendType mType;
+    };
+
+}  // namespace dawn_native
+
+#endif  // DAWNNATIVE_BACKENDCONNECTION_H_
diff --git a/src/dawn_native/DawnNative.cpp b/src/dawn_native/DawnNative.cpp
index f1dd1aa..6bd7bb87 100644
--- a/src/dawn_native/DawnNative.cpp
+++ b/src/dawn_native/DawnNative.cpp
@@ -14,6 +14,7 @@
 
 #include "dawn_native/DawnNative.h"
 #include "dawn_native/Device.h"
+#include "dawn_native/Instance.h"
 
 // Contains the entry-points into dawn_native
 
@@ -30,4 +31,50 @@
         return deviceBase->GetPCIInfo();
     }
 
+    // Adapter
+
+    Adapter::Adapter() = default;
+
+    Adapter::Adapter(AdapterBase* impl) : mImpl(impl) {
+    }
+
+    Adapter::~Adapter() {
+        mImpl = nullptr;
+    }
+
+    BackendType Adapter::GetBackendType() const {
+        return mImpl->GetBackendType();
+    }
+
+    const PCIInfo& Adapter::GetPCIInfo() const {
+        return mImpl->GetPCIInfo();
+    }
+
+    dawnDevice Adapter::CreateDevice() {
+        return reinterpret_cast<dawnDevice>(mImpl->CreateDevice());
+    }
+
+    // Instance
+
+    Instance::Instance() : mImpl(new InstanceBase()) {
+    }
+
+    Instance::~Instance() {
+        delete mImpl;
+        mImpl = nullptr;
+    }
+
+    void Instance::DiscoverDefaultAdapters() {
+        mImpl->DiscoverDefaultAdapters();
+    }
+
+    std::vector<Adapter> Instance::GetAdapters() const {
+        // Adapters are owned by mImpl so it is safe to return non RAII pointers to them
+        std::vector<Adapter> adapters;
+        for (const std::unique_ptr<AdapterBase>& adapter : mImpl->GetAdapters()) {
+            adapters.push_back({adapter.get()});
+        }
+        return adapters;
+    }
+
 }  // namespace dawn_native
diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h
index 79e1a35..491ae19 100644
--- a/src/dawn_native/Device.h
+++ b/src/dawn_native/Device.h
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#ifndef DAWNNATIVE_DEVICEBASE_H_
-#define DAWNNATIVE_DEVICEBASE_H_
+#ifndef DAWNNATIVE_DEVICE_H_
+#define DAWNNATIVE_DEVICE_H_
 
 #include "common/Serial.h"
 #include "dawn_native/Error.h"
@@ -170,4 +170,4 @@
 
 }  // namespace dawn_native
 
-#endif  // DAWNNATIVE_DEVICEBASE_H_
+#endif  // DAWNNATIVE_DEVICE_H_
diff --git a/src/dawn_native/Instance.cpp b/src/dawn_native/Instance.cpp
new file mode 100644
index 0000000..595f442
--- /dev/null
+++ b/src/dawn_native/Instance.cpp
@@ -0,0 +1,119 @@
+// 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 "dawn_native/Instance.h"
+
+#include "common/Assert.h"
+#include "dawn_native/ErrorData.h"
+
+#include <iostream>
+
+namespace dawn_native {
+
+    // Forward definitions of each backend's "Connect" function that creates new BackendConnection.
+    // Conditionally compiled declarations are used to avoid using static constructors instead.
+#if defined(DAWN_ENABLE_BACKEND_D3D12)
+    namespace d3d12 {
+        BackendConnection* Connect(InstanceBase* instance);
+    }
+#endif  // defined(DAWN_ENABLE_BACKEND_D3D12)
+#if defined(DAWN_ENABLE_BACKEND_METAL)
+    namespace metal {
+        BackendConnection* Connect(InstanceBase* instance);
+    }
+#endif  // defined(DAWN_ENABLE_BACKEND_METAL)
+#if defined(DAWN_ENABLE_BACKEND_NULL)
+    namespace null {
+        BackendConnection* Connect(InstanceBase* instance);
+    }
+#endif  // defined(DAWN_ENABLE_BACKEND_NULL)
+#if defined(DAWN_ENABLE_BACKEND_OPENGL)
+    namespace opengl {
+        BackendConnection* Connect(InstanceBase* instance);
+    }
+#endif  // defined(DAWN_ENABLE_BACKEND_OPENGL)
+#if defined(DAWN_ENABLE_BACKEND_VULKAN)
+    namespace vulkan {
+        BackendConnection* Connect(InstanceBase* instance);
+    }
+#endif  // defined(DAWN_ENABLE_BACKEND_VULKAN)
+
+    // InstanceBase
+
+    void InstanceBase::DiscoverDefaultAdapters() {
+        EnsureBackendConnections();
+
+        // Query and merge all default adapters for all backends
+        for (std::unique_ptr<BackendConnection>& backend : mBackends) {
+            std::vector<std::unique_ptr<AdapterBase>> backendAdapters =
+                backend->DiscoverDefaultAdapters();
+
+            for (std::unique_ptr<AdapterBase>& adapter : backendAdapters) {
+                ASSERT(adapter->GetBackendType() == backend->GetType());
+                ASSERT(adapter->GetInstance() == this);
+                mAdapters.push_back(std::move(adapter));
+            }
+        }
+    }
+
+    const std::vector<std::unique_ptr<AdapterBase>>& InstanceBase::GetAdapters() const {
+        return mAdapters;
+    }
+
+    void InstanceBase::EnsureBackendConnections() {
+        if (mBackendsConnected) {
+            return;
+        }
+
+        auto Register = [this](BackendConnection* connection, BackendType expectedType) {
+            if (connection != nullptr) {
+                ASSERT(connection->GetType() == expectedType);
+                ASSERT(connection->GetInstance() == this);
+                mBackends.push_back(std::unique_ptr<BackendConnection>(connection));
+            }
+        };
+
+#if defined(DAWN_ENABLE_BACKEND_D3D12)
+        Register(d3d12::Connect(this), BackendType::D3D12);
+#endif  // defined(DAWN_ENABLE_BACKEND_D3D12)
+#if defined(DAWN_ENABLE_BACKEND_METAL)
+        Register(metal::Connect(this), BackendType::Metal);
+#endif  // defined(DAWN_ENABLE_BACKEND_METAL)
+#if defined(DAWN_ENABLE_BACKEND_NULL)
+        Register(null::Connect(this), BackendType::Null);
+#endif  // defined(DAWN_ENABLE_BACKEND_NULL)
+#if defined(DAWN_ENABLE_BACKEND_OPENGL)
+        Register(opengl::Connect(this), BackendType::OpenGL);
+#endif  // defined(DAWN_ENABLE_BACKEND_OPENGL)
+#if defined(DAWN_ENABLE_BACKEND_VULKAN)
+        Register(vulkan::Connect(this), BackendType::Vulkan);
+#endif  // defined(DAWN_ENABLE_BACKEND_VULKAN)
+
+        mBackendsConnected = true;
+    }
+
+    bool InstanceBase::ConsumedError(MaybeError maybeError) {
+        if (maybeError.IsError()) {
+            ErrorData* error = maybeError.AcquireError();
+
+            ASSERT(error != nullptr);
+            std::cout << error->GetMessage() << std::endl;
+            delete error;
+
+            return true;
+        }
+        return false;
+    }
+
+}  // namespace dawn_native
diff --git a/src/dawn_native/Instance.h b/src/dawn_native/Instance.h
new file mode 100644
index 0000000..7fc4128
--- /dev/null
+++ b/src/dawn_native/Instance.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef DAWNNATIVE_INSTANCE_H_
+#define DAWNNATIVE_INSTANCE_H_
+
+#include "dawn_native/Adapter.h"
+#include "dawn_native/BackendConnection.h"
+
+#include <memory>
+#include <vector>
+
+namespace dawn_native {
+
+    // This is called InstanceBase for consistency across the frontend, even if the backends don't
+    // specialize this class.
+    class InstanceBase final {
+      public:
+        InstanceBase() = default;
+        ~InstanceBase() = default;
+
+        InstanceBase(const InstanceBase& other) = delete;
+        InstanceBase& operator=(const InstanceBase& other) = delete;
+
+        void DiscoverDefaultAdapters();
+
+        const std::vector<std::unique_ptr<AdapterBase>>& GetAdapters() const;
+
+        // Used to handle error that happen up to device creation.
+        bool ConsumedError(MaybeError maybeError);
+
+      private:
+        void EnsureBackendConnections();
+        bool mBackendsConnected = false;
+
+        std::vector<std::unique_ptr<BackendConnection>> mBackends;
+        std::vector<std::unique_ptr<AdapterBase>> mAdapters;
+    };
+
+}  // namespace dawn_native
+
+#endif  // DAWNNATIVE_INSTANCE_H_
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index 2d9f6be..dcf7110 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -16,6 +16,7 @@
 
 #include "common/Assert.h"
 #include "common/SwapChainUtils.h"
+#include "dawn_native/BackendConnection.h"
 #include "dawn_native/D3D12Backend.h"
 #include "dawn_native/d3d12/BindGroupD3D12.h"
 #include "dawn_native/d3d12/BindGroupLayoutD3D12.h"
@@ -66,6 +67,10 @@
         ASSERT(SUCCEEDED(hr));
     }
 
+    BackendConnection* Connect(InstanceBase* instance) {
+        return nullptr;
+    }
+
     namespace {
         ComPtr<IDXGIFactory4> CreateFactory(const PlatformFunctions* functions) {
             ComPtr<IDXGIFactory4> factory;
diff --git a/src/dawn_native/metal/DeviceMTL.mm b/src/dawn_native/metal/DeviceMTL.mm
index b11018a..1e54f5f 100644
--- a/src/dawn_native/metal/DeviceMTL.mm
+++ b/src/dawn_native/metal/DeviceMTL.mm
@@ -14,6 +14,7 @@
 
 #include "dawn_native/metal/DeviceMTL.h"
 
+#include "dawn_native/BackendConnection.h"
 #include "dawn_native/BindGroup.h"
 #include "dawn_native/BindGroupLayout.h"
 #include "dawn_native/MetalBackend.h"
@@ -125,6 +126,10 @@
         return reinterpret_cast<dawnDevice>(new Device(metalDevice));
     }
 
+    BackendConnection* Connect(InstanceBase* instance) {
+        return nullptr;
+    }
+
     // Device
 
     Device::Device(id<MTLDevice> mtlDevice)
diff --git a/src/dawn_native/null/NullBackend.cpp b/src/dawn_native/null/NullBackend.cpp
index aa3ec7f..01ca980 100644
--- a/src/dawn_native/null/NullBackend.cpp
+++ b/src/dawn_native/null/NullBackend.cpp
@@ -14,6 +14,7 @@
 
 #include "dawn_native/null/NullBackend.h"
 
+#include "dawn_native/BackendConnection.h"
 #include "dawn_native/Commands.h"
 #include "dawn_native/NullBackend.h"
 
@@ -21,10 +22,42 @@
 
 namespace dawn_native { namespace null {
 
+    // Implementation of pre-Device objects: the null adapter, null backend connection and Connect()
+
+    class Adapter : public AdapterBase {
+      public:
+        Adapter(InstanceBase* instance) : AdapterBase(instance, BackendType::Null) {
+        }
+        virtual ~Adapter() = default;
+
+      private:
+        ResultOrError<DeviceBase*> CreateDeviceImpl() override {
+            return {new Device};
+        }
+    };
+
+    class NullBackend : public BackendConnection {
+      public:
+        NullBackend(InstanceBase* instance) : BackendConnection(instance, BackendType::Null) {
+        }
+
+        std::vector<std::unique_ptr<AdapterBase>> DiscoverDefaultAdapters() override {
+            // There is always a single Null adapter because it is purely CPU based and doesn't
+            // depend on the system.
+            std::vector<std::unique_ptr<AdapterBase>> adapters;
+            adapters.push_back(std::make_unique<Adapter>(GetInstance()));
+            return adapters;
+        }
+    };
+
     dawnDevice CreateDevice() {
         return reinterpret_cast<dawnDevice>(new Device);
     }
 
+    BackendConnection* Connect(InstanceBase* instance) {
+        return new NullBackend(instance);
+    }
+
     // Device
 
     Device::Device() {
diff --git a/src/dawn_native/opengl/DeviceGL.cpp b/src/dawn_native/opengl/DeviceGL.cpp
index 7afd520..833f7e2 100644
--- a/src/dawn_native/opengl/DeviceGL.cpp
+++ b/src/dawn_native/opengl/DeviceGL.cpp
@@ -14,6 +14,7 @@
 
 #include "dawn_native/opengl/DeviceGL.h"
 
+#include "dawn_native/BackendConnection.h"
 #include "dawn_native/BindGroup.h"
 #include "dawn_native/BindGroupLayout.h"
 #include "dawn_native/OpenGLBackend.h"
@@ -42,6 +43,10 @@
         return reinterpret_cast<dawnDevice>(new Device);
     }
 
+    BackendConnection* Connect(InstanceBase* instance) {
+        return nullptr;
+    }
+
     // Device
 
     Device::Device() {
diff --git a/src/dawn_native/vulkan/DeviceVk.cpp b/src/dawn_native/vulkan/DeviceVk.cpp
index 074f334..725df61 100644
--- a/src/dawn_native/vulkan/DeviceVk.cpp
+++ b/src/dawn_native/vulkan/DeviceVk.cpp
@@ -16,6 +16,7 @@
 
 #include "common/Platform.h"
 #include "common/SwapChainUtils.h"
+#include "dawn_native/BackendConnection.h"
 #include "dawn_native/Commands.h"
 #include "dawn_native/ErrorData.h"
 #include "dawn_native/VulkanBackend.h"
@@ -80,6 +81,10 @@
         return static_cast<dawnTextureFormat>(impl->GetPreferredFormat());
     }
 
+    BackendConnection* Connect(InstanceBase* instance) {
+        return nullptr;
+    }
+
     // Device
 
     Device::Device(const std::vector<const char*>& requiredInstanceExtensions) {
diff --git a/src/include/dawn_native/DawnNative.h b/src/include/dawn_native/DawnNative.h
index f90dc9e..842e554 100644
--- a/src/include/dawn_native/DawnNative.h
+++ b/src/include/dawn_native/DawnNative.h
@@ -19,14 +19,75 @@
 #include <dawn_native/dawn_native_export.h>
 
 #include <string>
+#include <vector>
 
 namespace dawn_native {
+
     struct PCIInfo {
         uint32_t deviceId = 0;
         uint32_t vendorId = 0;
         std::string name;
     };
 
+    enum class BackendType {
+        D3D12,
+        Metal,
+        Null,
+        OpenGL,
+        Vulkan,
+    };
+
+    class InstanceBase;
+    class AdapterBase;
+
+    // An adapter is an object that represent on possibility of creating devices in the system.
+    // Most of the time it will represent a combination of a physical GPU and an API. Not that the
+    // same GPU can be represented by multiple adapters but on different APIs.
+    //
+    // The underlying Dawn adapter is owned by the Dawn instance so this class is not RAII but just
+    // a reference to an underlying adapter.
+    class DAWN_NATIVE_EXPORT Adapter {
+      public:
+        Adapter();
+        Adapter(AdapterBase* impl);
+        ~Adapter();
+
+        BackendType GetBackendType() const;
+        const PCIInfo& GetPCIInfo() const;
+
+        // Create a device on this adapter, note that the interface will change to include at least
+        // a device descriptor and a pointer to backend specific options.
+        // On an error, nullptr is returned.
+        dawnDevice CreateDevice();
+
+      private:
+        AdapterBase* mImpl = nullptr;
+    };
+
+    // Represents a connection to dawn_native and is used for dependency injection, discovering
+    // system adapters and injecting custom adapters (like a Swiftshader Vulkan adapter).
+    //
+    // This is an RAII class for Dawn instances and also controls the lifetime of all adapters
+    // for this instance.
+    class DAWN_NATIVE_EXPORT Instance {
+      public:
+        Instance();
+        ~Instance();
+
+        Instance(const Instance& other) = delete;
+        Instance& operator=(const Instance& other) = delete;
+
+        // Gather all adapters in the system that can be accessed with no special options. These
+        // adapters will later be returned by GetAdapters.
+        void DiscoverDefaultAdapters();
+
+        // Returns all the adapters that the instance knows about.
+        std::vector<Adapter> GetAdapters() const;
+
+      private:
+        InstanceBase* mImpl = nullptr;
+    };
+
     // Backend-agnostic API for dawn_native
     DAWN_NATIVE_EXPORT dawnProcTable GetProcs();
 
diff --git a/src/tests/unittests/validation/ValidationTest.cpp b/src/tests/unittests/validation/ValidationTest.cpp
index 903057a..b821088 100644
--- a/src/tests/unittests/validation/ValidationTest.cpp
+++ b/src/tests/unittests/validation/ValidationTest.cpp
@@ -14,22 +14,33 @@
 
 #include "tests/unittests/validation/ValidationTest.h"
 
+#include "common/Assert.h"
 #include "dawn/dawn.h"
 #include "dawn_native/DawnNative.h"
 #include "dawn_native/NullBackend.h"
 
-namespace dawn_native {
-    namespace null {
-        void Init(dawnProcTable* procs, dawnDevice* device);
-    }
-}
-
 ValidationTest::ValidationTest() {
-    dawnProcTable procs = dawn_native::GetProcs();
-    dawnDevice cDevice = dawn_native::null::CreateDevice();
+    mInstance = std::make_unique<dawn_native::Instance>();
+    mInstance->DiscoverDefaultAdapters();
 
+    std::vector<dawn_native::Adapter> adapters = mInstance->GetAdapters();
+
+    // Validation tests run against the null backend, find the corresponding adapter
+    bool foundNullAdapter = false;
+    dawn_native::Adapter nullAdapter;
+    for (auto adapter : adapters) {
+        if (adapter.GetBackendType() == dawn_native::BackendType::Null) {
+            nullAdapter = adapter;
+            foundNullAdapter = true;
+            break;
+        }
+    }
+
+    ASSERT(foundNullAdapter);
+    device = dawn::Device::Acquire(nullAdapter.CreateDevice());
+
+    dawnProcTable procs = dawn_native::GetProcs();
     dawnSetProcs(&procs);
-    device = dawn::Device::Acquire(cDevice);
 
     device.SetErrorCallback(ValidationTest::OnDeviceError, static_cast<dawnCallbackUserdata>(reinterpret_cast<uintptr_t>(this)));
 }
diff --git a/src/tests/unittests/validation/ValidationTest.h b/src/tests/unittests/validation/ValidationTest.h
index c6bd908..1e16baf 100644
--- a/src/tests/unittests/validation/ValidationTest.h
+++ b/src/tests/unittests/validation/ValidationTest.h
@@ -19,6 +19,10 @@
 #include "dawn/dawncpp.h"
 #include "dawn/dawncpp_traits.h"
 
+namespace dawn_native {
+    class Instance;
+};
+
 #define ASSERT_DEVICE_ERROR(statement) \
     StartExpectDeviceError(); \
     statement; \
@@ -66,6 +70,8 @@
         dawn::Device device;
 
     private:
+        std::unique_ptr<dawn_native::Instance> mInstance;
+
         static void OnDeviceError(const char* message, dawnCallbackUserdata userdata);
         std::string mDeviceErrorMessage;
         bool mExpectError = false;