Add basic or stub implementations of upstream instance/adapter APIs

Adds upstream instance/adapter APIs. In dawn_native, the basic APIs
to get limits and properties are implemented, but requestAdapter and
requestDevice are not. In dawn_wire, nothing is implemented, but the
stub definitions are put in place, as well the mechanism to inject
WGPUInstance into the wire.

There is a lifetime concern with WGPUInstance and WGPUAdapter on the
wire in that we need to ensure that the client cannot free the
instance or adapter while they are in use. In the near term, this is
not a problem because Chromium will always hold ownership of the
instance and adapters outside of the wire - i.e. it won't inject and
then release ownership.

Bug: dawn:160, dawn:689
Change-Id: Id904272983f23babc9177bc163d78c4fa1044da0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/71520
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/dawn.json b/dawn.json
index 1ef3fc1..884b5e6 100644
--- a/dawn.json
+++ b/dawn.json
@@ -49,16 +49,14 @@
     "request adapter options": {
         "category": "structure",
         "extensible": "in",
-        "tags": ["upstream"],
         "members": [
-            {"name": "compatible surface", "type": "surface"},
-            {"name": "power preference", "type": "power preference", "tags": ["upstream"]},
-            {"name": "force fallback adapter", "type": "bool", "tags": ["upstream"]}
+            {"name": "compatible surface", "type": "surface", "optional": true},
+            {"name": "power preference", "type": "power preference", "default": "undefined"},
+            {"name": "force fallback adapter", "type": "bool", "default": "false"}
         ]
     },
     "request adapter status": {
         "category": "enum",
-        "tags": ["upstream"],
         "emscripten_no_enum_table": true,
         "values": [
             {"value": 0, "name": "success"},
@@ -69,29 +67,25 @@
     },
     "request adapter callback": {
         "category": "function pointer",
-        "tags": ["upstream"],
         "args": [
             {"name": "status", "type": "request adapter status"},
             {"name": "adapter", "type": "adapter"},
-            {"name": "message", "type": "char", "annotation": "const*"},
+            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true},
             {"name": "userdata", "type": "void", "annotation": "*"}
         ]
     },
     "adapter": {
         "category": "object",
-        "tags": ["upstream"],
         "methods": [
             {
                 "name": "get limits",
                 "returns": "bool",
-                "tags": ["upstream"],
                 "args": [
                     {"name": "limits", "type": "supported limits", "annotation": "*"}
                 ]
             },
             {
                 "name": "get properties",
-                "tags": ["upstream"],
                 "args": [
                     {"name": "properties", "type": "adapter properties", "annotation": "*"}
                 ]
@@ -99,7 +93,6 @@
             {
                 "name": "has feature",
                 "returns": "bool",
-                "tags": ["upstream"],
                 "args": [
                     {"name": "feature", "type": "feature name"}
                 ]
@@ -139,13 +132,11 @@
     "device descriptor": {
         "category": "structure",
         "extensible": "in",
-        "tags": ["upstream"],
-        "_TODO": "Add requiredFeatures and requiredLimits support",
         "members": [
             {"name": "label", "type": "char", "annotation": "const*", "length": "strlen", "optional": true},
-            {"name": "required features count", "type": "uint32_t"},
-            {"name": "required features", "type": "feature name", "annotation": "const*", "length": "required features count"},
-            {"name": "required limits", "type": "required limits", "annotation": "const*"}
+            {"name": "required features count", "type": "uint32_t", "default": 0},
+            {"name": "required features", "type": "feature name", "annotation": "const*", "length": "required features count", "default": "nullptr"},
+            {"name": "required limits", "type": "required limits", "annotation": "const*", "optional": true}
         ]
     },
     "address mode": {
@@ -1295,7 +1286,6 @@
     },
     "feature name": {
         "category": "enum",
-        "tags": ["upstream"],
         "values": [
             {"value": 0, "name": "undefined", "jsrepr": "undefined"},
             {"value": 1, "name": "depth clip control", "tags": ["upstream"]},
@@ -1369,7 +1359,6 @@
             },
             {
                 "name": "request adapter",
-                "tags": ["upstream"],
                 "args": [
                     {"name": "options", "type": "request adapter options", "annotation": "const*"},
                     {"name": "callback", "type": "request adapter callback"},
@@ -1475,7 +1464,6 @@
     },
     "power preference": {
         "category": "enum",
-        "tags": ["upstream"],
         "values": [
             {"value": 0, "name": "undefined"},
             {"value": 1, "name": "low power"},
@@ -1994,7 +1982,7 @@
         "args": [
             {"name": "status", "type": "request device status"},
             {"name": "device", "type": "device"},
-            {"name": "message", "type": "char", "annotation": "const*"},
+            {"name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true},
             {"name": "userdata", "type": "void", "annotation": "*"}
         ]
     },
diff --git a/dawn_wire.json b/dawn_wire.json
index 41abdd8..5083751 100644
--- a/dawn_wire.json
+++ b/dawn_wire.json
@@ -148,6 +148,10 @@
             "SurfaceDescriptorFromWindowsSwapChainPanel"
         ],
         "client_side_commands": [
+            "AdapterGetProperties",
+            "AdapterGetLimits",
+            "AdapterHasFeature",
+            "AdapterRequestDevice",
             "BufferMapAsync",
             "BufferGetConstMappedRange",
             "BufferGetMappedRange",
@@ -159,6 +163,7 @@
             "DeviceSetDeviceLostCallback",
             "DeviceSetUncapturedErrorCallback",
             "DeviceSetLoggingCallback",
+            "InstanceRequestAdapter",
             "ShaderModuleGetCompilationInfo",
             "QueueOnSubmittedWorkDone",
             "QueueWriteBuffer",
@@ -173,8 +178,10 @@
             "DevicePushErrorScope"
         ],
         "client_special_objects": [
+            "Adapter",
             "Buffer",
             "Device",
+            "Instance",
             "Queue",
             "ShaderModule"
         ],
diff --git a/generator/templates/api_cpp.h b/generator/templates/api_cpp.h
index f9b6bf6..d54dcc3 100644
--- a/generator/templates/api_cpp.h
+++ b/generator/templates/api_cpp.h
@@ -140,7 +140,7 @@
     };
 
 {% macro render_cpp_default_value(member, is_struct=True) -%}
-    {%- if member.annotation in ["*", "const*"] and member.optional -%}
+    {%- if member.annotation in ["*", "const*"] and member.optional or member.default_value == "nullptr" -%}
         {{" "}}= nullptr
     {%- elif member.type.category == "object" and member.optional and is_struct -%}
         {{" "}}= nullptr
diff --git a/generator/templates/dawn_native/wgpu_structs.h b/generator/templates/dawn_native/wgpu_structs.h
index acaed3a..2249540 100644
--- a/generator/templates/dawn_native/wgpu_structs.h
+++ b/generator/templates/dawn_native/wgpu_structs.h
@@ -21,7 +21,7 @@
 namespace dawn_native {
 
 {% macro render_cpp_default_value(member) -%}
-    {%- if member.annotation in ["*", "const*"] and member.optional -%}
+    {%- if member.annotation in ["*", "const*"] and member.optional or member.default_value == "nullptr" -%}
         {{" "}}= nullptr
     {%- elif member.type.category == "object" and member.optional -%}
         {{" "}}= nullptr
diff --git a/src/dawn_native/Adapter.cpp b/src/dawn_native/Adapter.cpp
index 77082d7..c3ac8de 100644
--- a/src/dawn_native/Adapter.cpp
+++ b/src/dawn_native/Adapter.cpp
@@ -69,6 +69,29 @@
         return {};
     }
 
+    bool AdapterBase::APIGetLimits(SupportedLimits* limits) const {
+        return GetLimits(limits);
+    }
+
+    void AdapterBase::APIGetProperties(AdapterProperties* properties) const {
+        properties->vendorID = mPCIInfo.vendorId;
+        properties->deviceID = mPCIInfo.deviceId;
+        properties->name = mPCIInfo.name.c_str();
+        properties->driverDescription = mDriverDescription.c_str();
+        properties->adapterType = mAdapterType;
+        properties->backendType = mBackend;
+    }
+
+    bool AdapterBase::APIHasFeature(wgpu::FeatureName feature) const {
+        return mSupportedFeatures.IsEnabled(feature);
+    }
+
+    void AdapterBase::APIRequestDevice(const DeviceDescriptor* descriptor,
+                                       WGPURequestDeviceCallback callback,
+                                       void* userdata) {
+        callback(WGPURequestDeviceStatus_Error, nullptr, "Not implemented", userdata);
+    }
+
     wgpu::BackendType AdapterBase::GetBackendType() const {
         return mBackend;
     }
diff --git a/src/dawn_native/Adapter.h b/src/dawn_native/Adapter.h
index 1b9286e..36930db 100644
--- a/src/dawn_native/Adapter.h
+++ b/src/dawn_native/Adapter.h
@@ -17,6 +17,7 @@
 
 #include "dawn_native/DawnNative.h"
 
+#include "common/RefCounted.h"
 #include "dawn_native/Error.h"
 #include "dawn_native/Features.h"
 #include "dawn_native/Limits.h"
@@ -28,13 +29,21 @@
 
     class DeviceBase;
 
-    class AdapterBase {
+    class AdapterBase : public RefCounted {
       public:
         AdapterBase(InstanceBase* instance, wgpu::BackendType backend);
         virtual ~AdapterBase() = default;
 
         MaybeError Initialize();
 
+        // WebGPU API
+        bool APIGetLimits(SupportedLimits* limits) const;
+        void APIGetProperties(AdapterProperties* properties) const;
+        bool APIHasFeature(wgpu::FeatureName feature) const;
+        void APIRequestDevice(const DeviceDescriptor* descriptor,
+                              WGPURequestDeviceCallback callback,
+                              void* userdata);
+
         wgpu::BackendType GetBackendType() const;
         wgpu::AdapterType GetAdapterType() const;
         const std::string& GetDriverDescription() const;
diff --git a/src/dawn_native/Features.cpp b/src/dawn_native/Features.cpp
index b09caf2..ad427d3 100644
--- a/src/dawn_native/Features.cpp
+++ b/src/dawn_native/Features.cpp
@@ -85,6 +85,34 @@
                "https://bugs.chromium.org/p/dawn/issues/detail?id=551"},
               &WGPUDeviceProperties::multiPlanarFormats}}};
 
+        Feature FromAPIFeature(wgpu::FeatureName feature) {
+            switch (feature) {
+                case wgpu::FeatureName::Undefined:
+                    return Feature::InvalidEnum;
+
+                case wgpu::FeatureName::TimestampQuery:
+                    return Feature::TimestampQuery;
+                case wgpu::FeatureName::PipelineStatisticsQuery:
+                    return Feature::PipelineStatisticsQuery;
+                case wgpu::FeatureName::TextureCompressionBC:
+                    return Feature::TextureCompressionBC;
+                case wgpu::FeatureName::TextureCompressionETC2:
+                    return Feature::TextureCompressionETC2;
+                case wgpu::FeatureName::TextureCompressionASTC:
+                    return Feature::TextureCompressionASTC;
+                case wgpu::FeatureName::DepthClamping:
+                    return Feature::DepthClamping;
+                case wgpu::FeatureName::Depth24UnormStencil8:
+                    return Feature::Depth24UnormStencil8;
+                case wgpu::FeatureName::Depth32FloatStencil8:
+                    return Feature::Depth32FloatStencil8;
+
+                case wgpu::FeatureName::IndirectFirstInstance:
+                    return Feature::InvalidEnum;
+            }
+            return Feature::InvalidEnum;
+        }
+
     }  // anonymous namespace
 
     void FeaturesSet::EnableFeature(Feature feature) {
@@ -99,6 +127,11 @@
         return featuresBitSet[featureIndex];
     }
 
+    bool FeaturesSet::IsEnabled(wgpu::FeatureName feature) const {
+        Feature f = FromAPIFeature(feature);
+        return f != Feature::InvalidEnum && IsEnabled(f);
+    }
+
     std::vector<const char*> FeaturesSet::GetEnabledFeatureNames() const {
         std::vector<const char*> enabledFeatureNames(featuresBitSet.count());
 
diff --git a/src/dawn_native/Features.h b/src/dawn_native/Features.h
index 699ddc7..4c4a79a 100644
--- a/src/dawn_native/Features.h
+++ b/src/dawn_native/Features.h
@@ -19,6 +19,7 @@
 #include <unordered_map>
 #include <vector>
 
+#include "dawn/webgpu_cpp.h"
 #include "dawn_native/DawnNative.h"
 
 namespace dawn_native {
@@ -50,6 +51,7 @@
 
         void EnableFeature(Feature feature);
         bool IsEnabled(Feature feature) const;
+        bool IsEnabled(wgpu::FeatureName feature) const;
         std::vector<const char*> GetEnabledFeatureNames() const;
         void InitializeDeviceProperties(WGPUDeviceProperties* properties) const;
     };
diff --git a/src/dawn_native/Instance.cpp b/src/dawn_native/Instance.cpp
index ce0d85b..d8e107f 100644
--- a/src/dawn_native/Instance.cpp
+++ b/src/dawn_native/Instance.cpp
@@ -99,6 +99,12 @@
         return true;
     }
 
+    void InstanceBase::APIRequestAdapter(const RequestAdapterOptions* options,
+                                         WGPURequestAdapterCallback callback,
+                                         void* userdata) {
+        callback(WGPURequestAdapterStatus_Error, nullptr, "Not implemented", userdata);
+    }
+
     void InstanceBase::DiscoverDefaultAdapters() {
         for (wgpu::BackendType b : IterateBitSet(GetEnabledBackends())) {
             EnsureBackendConnection(b);
diff --git a/src/dawn_native/Instance.h b/src/dawn_native/Instance.h
index a636875..a36255a 100644
--- a/src/dawn_native/Instance.h
+++ b/src/dawn_native/Instance.h
@@ -45,6 +45,10 @@
       public:
         static InstanceBase* Create(const InstanceDescriptor* descriptor = nullptr);
 
+        void APIRequestAdapter(const RequestAdapterOptions* options,
+                               WGPURequestAdapterCallback callback,
+                               void* userdata);
+
         void DiscoverDefaultAdapters();
         bool DiscoverAdapters(const AdapterDiscoveryOptionsBase* options);
 
diff --git a/src/dawn_wire/BUILD.gn b/src/dawn_wire/BUILD.gn
index b678fa1..4137652 100644
--- a/src/dawn_wire/BUILD.gn
+++ b/src/dawn_wire/BUILD.gn
@@ -71,6 +71,8 @@
     "WireDeserializeAllocator.h",
     "WireResult.h",
     "WireServer.cpp",
+    "client/Adapter.cpp",
+    "client/Adapter.h",
     "client/ApiObjects.h",
     "client/Buffer.cpp",
     "client/Buffer.h",
@@ -80,6 +82,8 @@
     "client/ClientInlineMemoryTransferService.cpp",
     "client/Device.cpp",
     "client/Device.h",
+    "client/Instance.cpp",
+    "client/Instance.h",
     "client/ObjectAllocator.h",
     "client/Queue.cpp",
     "client/Queue.h",
diff --git a/src/dawn_wire/CMakeLists.txt b/src/dawn_wire/CMakeLists.txt
index e970367..8ba7571 100644
--- a/src/dawn_wire/CMakeLists.txt
+++ b/src/dawn_wire/CMakeLists.txt
@@ -43,6 +43,8 @@
     "WireDeserializeAllocator.h"
     "WireResult.h"
     "WireServer.cpp"
+    "client/Adapter.cpp"
+    "client/Adapter.h"
     "client/ApiObjects.h"
     "client/Buffer.cpp"
     "client/Buffer.h"
@@ -52,6 +54,8 @@
     "client/ClientInlineMemoryTransferService.cpp"
     "client/Device.cpp"
     "client/Device.h"
+    "client/Instance.cpp"
+    "client/Instance.h"
     "client/ObjectAllocator.h"
     "client/Queue.cpp"
     "client/Queue.h"
diff --git a/src/dawn_wire/WireClient.cpp b/src/dawn_wire/WireClient.cpp
index 01ab45b..a23e2f7 100644
--- a/src/dawn_wire/WireClient.cpp
+++ b/src/dawn_wire/WireClient.cpp
@@ -41,6 +41,10 @@
         return mImpl->ReserveDevice();
     }
 
+    ReservedInstance WireClient::ReserveInstance() {
+        return mImpl->ReserveInstance();
+    }
+
     void WireClient::ReclaimTextureReservation(const ReservedTexture& reservation) {
         mImpl->ReclaimTextureReservation(reservation);
     }
@@ -53,6 +57,10 @@
         mImpl->ReclaimDeviceReservation(reservation);
     }
 
+    void WireClient::ReclaimInstanceReservation(const ReservedInstance& reservation) {
+        mImpl->ReclaimInstanceReservation(reservation);
+    }
+
     void WireClient::Disconnect() {
         mImpl->Disconnect();
     }
diff --git a/src/dawn_wire/WireServer.cpp b/src/dawn_wire/WireServer.cpp
index bad5957..c998e44 100644
--- a/src/dawn_wire/WireServer.cpp
+++ b/src/dawn_wire/WireServer.cpp
@@ -51,6 +51,10 @@
         return mImpl->InjectDevice(device, id, generation);
     }
 
+    bool WireServer::InjectInstance(WGPUInstance instance, uint32_t id, uint32_t generation) {
+        return mImpl->InjectInstance(instance, id, generation);
+    }
+
     WGPUDevice WireServer::GetDevice(uint32_t id, uint32_t generation) {
         return mImpl->GetDevice(id, generation);
     }
diff --git a/src/dawn_wire/client/Adapter.cpp b/src/dawn_wire/client/Adapter.cpp
new file mode 100644
index 0000000..ee789f9
--- /dev/null
+++ b/src/dawn_wire/client/Adapter.cpp
@@ -0,0 +1,37 @@
+// Copyright 2021 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_wire/client/Adapter.h"
+
+namespace dawn_wire { namespace client {
+
+    bool Adapter::GetLimits(WGPUSupportedLimits* limits) const {
+        UNREACHABLE();
+    }
+
+    void Adapter::GetProperties(WGPUAdapterProperties* properties) const {
+        UNREACHABLE();
+    }
+
+    bool Adapter::HasFeature(WGPUFeatureName feature) const {
+        UNREACHABLE();
+    }
+
+    void Adapter::RequestDevice(const WGPUDeviceDescriptor* descriptor,
+                                WGPURequestDeviceCallback callback,
+                                void* userdata) {
+        callback(WGPURequestDeviceStatus_Error, nullptr, "Not implemented", nullptr);
+    }
+
+}}  // namespace dawn_wire::client
diff --git a/src/dawn_wire/client/Adapter.h b/src/dawn_wire/client/Adapter.h
new file mode 100644
index 0000000..d939220
--- /dev/null
+++ b/src/dawn_wire/client/Adapter.h
@@ -0,0 +1,39 @@
+// Copyright 2021 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 DAWNWIRE_CLIENT_ADAPTER_H_
+#define DAWNWIRE_CLIENT_ADAPTER_H_
+
+#include <dawn/webgpu.h>
+
+#include "dawn_wire/WireClient.h"
+#include "dawn_wire/client/ObjectBase.h"
+
+namespace dawn_wire { namespace client {
+
+    class Adapter final : public ObjectBase {
+      public:
+        using ObjectBase::ObjectBase;
+
+        bool GetLimits(WGPUSupportedLimits* limits) const;
+        void GetProperties(WGPUAdapterProperties* properties) const;
+        bool HasFeature(WGPUFeatureName feature) const;
+        void RequestDevice(const WGPUDeviceDescriptor* descriptor,
+                           WGPURequestDeviceCallback callback,
+                           void* userdata);
+    };
+
+}}  // namespace dawn_wire::client
+
+#endif  // DAWNWIRE_CLIENT_ADAPTER_H_
diff --git a/src/dawn_wire/client/ApiObjects.h b/src/dawn_wire/client/ApiObjects.h
index 71dbc82d..cfe4cb5 100644
--- a/src/dawn_wire/client/ApiObjects.h
+++ b/src/dawn_wire/client/ApiObjects.h
@@ -17,8 +17,10 @@
 
 #include "dawn_wire/client/ObjectBase.h"
 
+#include "dawn_wire/client/Adapter.h"
 #include "dawn_wire/client/Buffer.h"
 #include "dawn_wire/client/Device.h"
+#include "dawn_wire/client/Instance.h"
 #include "dawn_wire/client/Queue.h"
 #include "dawn_wire/client/ShaderModule.h"
 
diff --git a/src/dawn_wire/client/Client.cpp b/src/dawn_wire/client/Client.cpp
index 2d4445e..628232d 100644
--- a/src/dawn_wire/client/Client.cpp
+++ b/src/dawn_wire/client/Client.cpp
@@ -118,6 +118,16 @@
         return result;
     }
 
+    ReservedInstance Client::ReserveInstance() {
+        auto* allocation = InstanceAllocator().New(this);
+
+        ReservedInstance result;
+        result.instance = ToAPI(allocation->object.get());
+        result.id = allocation->object->id;
+        result.generation = allocation->generation;
+        return result;
+    }
+
     void Client::ReclaimTextureReservation(const ReservedTexture& reservation) {
         TextureAllocator().Free(FromAPI(reservation.texture));
     }
@@ -130,6 +140,10 @@
         DeviceAllocator().Free(FromAPI(reservation.device));
     }
 
+    void Client::ReclaimInstanceReservation(const ReservedInstance& reservation) {
+        InstanceAllocator().Free(FromAPI(reservation.instance));
+    }
+
     void Client::Disconnect() {
         mDisconnected = true;
         mSerializer = ChunkedCommandSerializer(NoopCommandSerializer::GetInstance());
diff --git a/src/dawn_wire/client/Client.h b/src/dawn_wire/client/Client.h
index fc3758a..aebf953 100644
--- a/src/dawn_wire/client/Client.h
+++ b/src/dawn_wire/client/Client.h
@@ -47,10 +47,12 @@
         ReservedTexture ReserveTexture(WGPUDevice device);
         ReservedSwapChain ReserveSwapChain(WGPUDevice device);
         ReservedDevice ReserveDevice();
+        ReservedInstance ReserveInstance();
 
         void ReclaimTextureReservation(const ReservedTexture& reservation);
         void ReclaimSwapChainReservation(const ReservedSwapChain& reservation);
         void ReclaimDeviceReservation(const ReservedDevice& reservation);
+        void ReclaimInstanceReservation(const ReservedInstance& reservation);
 
         template <typename Cmd>
         void SerializeCommand(const Cmd& cmd) {
diff --git a/src/dawn_wire/client/Instance.cpp b/src/dawn_wire/client/Instance.cpp
new file mode 100644
index 0000000..74d0517
--- /dev/null
+++ b/src/dawn_wire/client/Instance.cpp
@@ -0,0 +1,25 @@
+// Copyright 2021 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_wire/client/Instance.h"
+
+namespace dawn_wire { namespace client {
+
+    void Instance::RequestAdapter(const WGPURequestAdapterOptions* options,
+                                  WGPURequestAdapterCallback callback,
+                                  void* userdata) {
+        callback(WGPURequestAdapterStatus_Error, nullptr, "Not implemented", nullptr);
+    }
+
+}}  // namespace dawn_wire::client
diff --git a/src/dawn_wire/client/Instance.h b/src/dawn_wire/client/Instance.h
new file mode 100644
index 0000000..3d55ac9
--- /dev/null
+++ b/src/dawn_wire/client/Instance.h
@@ -0,0 +1,36 @@
+// Copyright 2021 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 DAWNWIRE_CLIENT_INSTANCE_H_
+#define DAWNWIRE_CLIENT_INSTANCE_H_
+
+#include <dawn/webgpu.h>
+
+#include "dawn_wire/WireClient.h"
+#include "dawn_wire/client/ObjectBase.h"
+
+namespace dawn_wire { namespace client {
+
+    class Instance final : public ObjectBase {
+      public:
+        using ObjectBase::ObjectBase;
+
+        void RequestAdapter(const WGPURequestAdapterOptions* options,
+                            WGPURequestAdapterCallback callback,
+                            void* userdata);
+    };
+
+}}  // namespace dawn_wire::client
+
+#endif  // DAWNWIRE_CLIENT_INSTANCE_H_
diff --git a/src/dawn_wire/server/Server.cpp b/src/dawn_wire/server/Server.cpp
index 8297cbd..a4dbd8c 100644
--- a/src/dawn_wire/server/Server.cpp
+++ b/src/dawn_wire/server/Server.cpp
@@ -153,6 +153,24 @@
         return true;
     }
 
+    bool Server::InjectInstance(WGPUInstance instance, uint32_t id, uint32_t generation) {
+        ASSERT(instance != nullptr);
+        ObjectData<WGPUInstance>* data = InstanceObjects().Allocate(id);
+        if (data == nullptr) {
+            return false;
+        }
+
+        data->handle = instance;
+        data->generation = generation;
+        data->state = AllocationState::Allocated;
+
+        // The instance is externally owned so it shouldn't be destroyed when we receive a destroy
+        // message from the client. Add a reference to counterbalance the eventual release.
+        mProcs.instanceReference(instance);
+
+        return true;
+    }
+
     WGPUDevice Server::GetDevice(uint32_t id, uint32_t generation) {
         ObjectData<WGPUDevice>* data = DeviceObjects().Get(id);
         if (data == nullptr || data->generation != generation) {
diff --git a/src/dawn_wire/server/Server.h b/src/dawn_wire/server/Server.h
index b442987..33b0e04 100644
--- a/src/dawn_wire/server/Server.h
+++ b/src/dawn_wire/server/Server.h
@@ -171,6 +171,8 @@
 
         bool InjectDevice(WGPUDevice device, uint32_t id, uint32_t generation);
 
+        bool InjectInstance(WGPUInstance instance, uint32_t id, uint32_t generation);
+
         WGPUDevice GetDevice(uint32_t id, uint32_t generation);
 
         template <typename T,
diff --git a/src/include/dawn_wire/WireClient.h b/src/include/dawn_wire/WireClient.h
index bbcaaee..1c10e07 100644
--- a/src/include/dawn_wire/WireClient.h
+++ b/src/include/dawn_wire/WireClient.h
@@ -52,6 +52,12 @@
         uint32_t generation;
     };
 
+    struct ReservedInstance {
+        WGPUInstance instance;
+        uint32_t id;
+        uint32_t generation;
+    };
+
     struct DAWN_WIRE_EXPORT WireClientDescriptor {
         CommandSerializer* serializer;
         client::MemoryTransferService* memoryTransferService = nullptr;
@@ -68,10 +74,12 @@
         ReservedTexture ReserveTexture(WGPUDevice device);
         ReservedSwapChain ReserveSwapChain(WGPUDevice device);
         ReservedDevice ReserveDevice();
+        ReservedInstance ReserveInstance();
 
         void ReclaimTextureReservation(const ReservedTexture& reservation);
         void ReclaimSwapChainReservation(const ReservedSwapChain& reservation);
         void ReclaimDeviceReservation(const ReservedDevice& reservation);
+        void ReclaimInstanceReservation(const ReservedInstance& reservation);
 
         // Disconnects the client.
         // Commands allocated after this point will not be sent.
diff --git a/src/include/dawn_wire/WireServer.h b/src/include/dawn_wire/WireServer.h
index 98a2e69..a720874 100644
--- a/src/include/dawn_wire/WireServer.h
+++ b/src/include/dawn_wire/WireServer.h
@@ -55,6 +55,8 @@
 
         bool InjectDevice(WGPUDevice device, uint32_t id, uint32_t generation);
 
+        bool InjectInstance(WGPUInstance instance, uint32_t id, uint32_t generation);
+
         // Look up a device by (id, generation) pair. Returns nullptr if the generation
         // has expired or the id is not found.
         // The Wire does not have destroy hooks to allow an embedder to observe when an object
diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn
index 04ff218..59544b3 100644
--- a/src/tests/BUILD.gn
+++ b/src/tests/BUILD.gn
@@ -281,6 +281,7 @@
     "unittests/wire/WireErrorCallbackTests.cpp",
     "unittests/wire/WireExtensionTests.cpp",
     "unittests/wire/WireInjectDeviceTests.cpp",
+    "unittests/wire/WireInjectInstanceTests.cpp",
     "unittests/wire/WireInjectSwapChainTests.cpp",
     "unittests/wire/WireInjectTextureTests.cpp",
     "unittests/wire/WireMemoryTransferServiceTests.cpp",
diff --git a/src/tests/unittests/wire/WireInjectInstanceTests.cpp b/src/tests/unittests/wire/WireInjectInstanceTests.cpp
new file mode 100644
index 0000000..7bdf170
--- /dev/null
+++ b/src/tests/unittests/wire/WireInjectInstanceTests.cpp
@@ -0,0 +1,119 @@
+// Copyright 2021 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/wire/WireTest.h"
+
+#include "dawn_wire/WireClient.h"
+#include "dawn_wire/WireServer.h"
+
+using namespace testing;
+using namespace dawn_wire;
+
+namespace {
+
+    class WireInjectInstanceTests : public WireTest {
+    public:
+        WireInjectInstanceTests() {
+        }
+        ~WireInjectInstanceTests() override = default;
+    };
+
+    // Test that reserving and injecting an instance makes calls on the client object forward to the
+    // server object correctly.
+    TEST_F(WireInjectInstanceTests, CallAfterReserveInject) {
+        ReservedInstance reservation = GetWireClient()->ReserveInstance();
+
+        WGPUInstance serverInstance = api.GetNewInstance();
+        EXPECT_CALL(api, InstanceReference(serverInstance));
+        ASSERT_TRUE(
+            GetWireServer()->InjectInstance(serverInstance, reservation.id, reservation.generation));
+
+        WGPUSurfaceDescriptor surfaceDesc = {};
+        wgpuInstanceCreateSurface(reservation.instance, &surfaceDesc);
+        WGPUSurface serverSurface = api.GetNewSurface();
+        EXPECT_CALL(api, InstanceCreateSurface(serverInstance, NotNull())).WillOnce(Return(serverSurface));
+        FlushClient();
+    }
+
+    // Test that reserve correctly returns different IDs each time.
+    TEST_F(WireInjectInstanceTests, ReserveDifferentIDs) {
+        ReservedInstance reservation1 = GetWireClient()->ReserveInstance();
+        ReservedInstance reservation2 = GetWireClient()->ReserveInstance();
+
+        ASSERT_NE(reservation1.id, reservation2.id);
+        ASSERT_NE(reservation1.instance, reservation2.instance);
+    }
+
+
+    // Test that injecting the same id fails.
+    TEST_F(WireInjectInstanceTests, InjectExistingID) {
+        ReservedInstance reservation = GetWireClient()->ReserveInstance();
+
+        WGPUInstance serverInstance = api.GetNewInstance();
+        EXPECT_CALL(api, InstanceReference(serverInstance));
+        ASSERT_TRUE(
+            GetWireServer()->InjectInstance(serverInstance, reservation.id, reservation.generation));
+
+        // ID already in use, call fails.
+        ASSERT_FALSE(
+            GetWireServer()->InjectInstance(serverInstance, reservation.id, reservation.generation));
+    }
+
+    // Test that the server only borrows the instance and does a single reference-release
+    TEST_F(WireInjectInstanceTests, InjectedInstanceLifetime) {
+        ReservedInstance reservation = GetWireClient()->ReserveInstance();
+
+        // Injecting the instance adds a reference
+        WGPUInstance serverInstance = api.GetNewInstance();
+        EXPECT_CALL(api, InstanceReference(serverInstance));
+        ASSERT_TRUE(
+            GetWireServer()->InjectInstance(serverInstance, reservation.id, reservation.generation));
+
+        // Releasing the instance removes a single reference.
+        wgpuInstanceRelease(reservation.instance);
+        EXPECT_CALL(api, InstanceRelease(serverInstance));
+        FlushClient();
+
+        // Deleting the server doesn't release a second reference.
+        DeleteServer();
+        Mock::VerifyAndClearExpectations(&api);
+    }
+
+    // Test that a device reservation can be reclaimed. This is necessary to
+    // avoid leaking ObjectIDs for reservations that are never injected.
+    TEST_F(WireInjectInstanceTests, ReclaimInstanceReservation) {
+        // Test that doing a reservation and full release is an error.
+        {
+            ReservedInstance reservation = GetWireClient()->ReserveInstance();
+            wgpuInstanceRelease(reservation.instance);
+            FlushClient(false);
+        }
+
+        // Test that doing a reservation and then reclaiming it recycles the ID.
+        {
+            ReservedInstance reservation1 = GetWireClient()->ReserveInstance();
+            GetWireClient()->ReclaimInstanceReservation(reservation1);
+
+            ReservedInstance reservation2 = GetWireClient()->ReserveInstance();
+
+            // The ID is the same, but the generation is still different.
+            ASSERT_EQ(reservation1.id, reservation2.id);
+            ASSERT_NE(reservation1.generation, reservation2.generation);
+
+            // No errors should occur.
+            FlushClient();
+        }
+    }
+
+}  // anonymous namespace