dawn_wire: Implement requestAdapter and requestDevice

This implements requestAdapter and requestDevice by
forwarding commands the the server and relaying back
replies. After an adapter or device is created,
limits/properties/features are queried and also sent
back to the client.

Bug: dawn:689
Change-Id: Ie0c2984b8ebb661efb0c284a14ae8b74ae4af2ea
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/71522
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_wire.json b/dawn_wire.json
index 4bd03a1..497a0eb 100644
--- a/dawn_wire.json
+++ b/dawn_wire.json
@@ -84,6 +84,18 @@
         "shader module get compilation info": [
             { "name": "shader module id", "type": "ObjectId" },
             { "name": "request serial", "type": "uint64_t" }
+        ],
+        "instance request adapter": [
+            { "name": "instance id", "type": "ObjectId" },
+            { "name": "request serial", "type": "uint64_t" },
+            { "name": "adapter object handle", "type": "ObjectHandle", "handle_type": "adapter"},
+            { "name": "options", "type": "request adapter options", "annotation": "const*" }
+        ],
+        "adapter request device": [
+            { "name": "adapter id", "type": "ObjectId" },
+            { "name": "request serial", "type": "uint64_t" },
+            { "name": "device object handle", "type": "ObjectHandle", "handle_type": "device"},
+            { "name": "descriptor", "type": "device descriptor", "annotation": "const*" }
         ]
     },
     "return commands": {
@@ -137,6 +149,25 @@
             { "name": "request serial", "type": "uint64_t" },
             { "name": "status", "type": "compilation info request status" },
             { "name": "info", "type": "compilation info", "annotation": "const*", "optional": true }
+        ],
+        "instance request adapter callback": [
+            { "name": "instance", "type": "ObjectHandle", "handle_type": "instance" },
+            { "name": "request serial", "type": "uint64_t" },
+            { "name": "status", "type": "request adapter status" },
+            { "name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true },
+            { "name": "properties", "type": "adapter properties", "annotation": "const*", "optional": "true" },
+            { "name": "limits", "type": "supported limits", "annotation": "const*", "optional": "true" },
+            { "name": "features count", "type": "uint32_t"},
+            { "name": "features", "type": "feature name", "annotation": "const*", "length": "features count"}
+        ],
+        "adapter request device callback": [
+            { "name": "adapter", "type": "ObjectHandle", "handle_type": "adapter" },
+            { "name": "request serial", "type": "uint64_t" },
+            { "name": "status", "type": "request device status" },
+            { "name": "message", "type": "char", "annotation": "const*", "length": "strlen", "optional": true },
+            { "name": "limits", "type": "supported limits", "annotation": "const*", "optional": "true" },
+            { "name": "features count", "type": "uint32_t"},
+            { "name": "features", "type": "feature name", "annotation": "const*", "length": "features count"}
         ]
     },
     "special items": {
diff --git a/src/dawn_wire/BUILD.gn b/src/dawn_wire/BUILD.gn
index 4137652..cfebb57 100644
--- a/src/dawn_wire/BUILD.gn
+++ b/src/dawn_wire/BUILD.gn
@@ -65,6 +65,8 @@
     "ChunkedCommandHandler.h",
     "ChunkedCommandSerializer.cpp",
     "ChunkedCommandSerializer.h",
+    "SupportedFeatures.cpp",
+    "SupportedFeatures.h",
     "Wire.cpp",
     "WireClient.cpp",
     "WireDeserializeAllocator.cpp",
@@ -84,6 +86,8 @@
     "client/Device.h",
     "client/Instance.cpp",
     "client/Instance.h",
+    "client/LimitsAndFeatures.cpp",
+    "client/LimitsAndFeatures.h",
     "client/ObjectAllocator.h",
     "client/Queue.cpp",
     "client/Queue.h",
@@ -93,9 +97,11 @@
     "server/ObjectStorage.h",
     "server/Server.cpp",
     "server/Server.h",
+    "server/ServerAdapter.cpp",
     "server/ServerBuffer.cpp",
     "server/ServerDevice.cpp",
     "server/ServerInlineMemoryTransferService.cpp",
+    "server/ServerInstance.cpp",
     "server/ServerQueue.cpp",
     "server/ServerShaderModule.cpp",
   ]
diff --git a/src/dawn_wire/CMakeLists.txt b/src/dawn_wire/CMakeLists.txt
index 8ba7571..f0b6a7e 100644
--- a/src/dawn_wire/CMakeLists.txt
+++ b/src/dawn_wire/CMakeLists.txt
@@ -37,6 +37,8 @@
     "ChunkedCommandHandler.h"
     "ChunkedCommandSerializer.cpp"
     "ChunkedCommandSerializer.h"
+    "SupportedFeatures.cpp"
+    "SupportedFeatures.h"
     "Wire.cpp"
     "WireClient.cpp"
     "WireDeserializeAllocator.cpp"
@@ -56,6 +58,8 @@
     "client/Device.h"
     "client/Instance.cpp"
     "client/Instance.h"
+    "client/LimitsAndFeatures.cpp"
+    "client/LimitsAndFeatures.h"
     "client/ObjectAllocator.h"
     "client/Queue.cpp"
     "client/Queue.h"
@@ -65,9 +69,11 @@
     "server/ObjectStorage.h"
     "server/Server.cpp"
     "server/Server.h"
+    "server/ServerAdapter.cpp"
     "server/ServerBuffer.cpp"
     "server/ServerDevice.cpp"
     "server/ServerInlineMemoryTransferService.cpp"
+    "server/ServerInstance.cpp"
     "server/ServerQueue.cpp"
     "server/ServerShaderModule.cpp"
 )
diff --git a/src/dawn_wire/SupportedFeatures.cpp b/src/dawn_wire/SupportedFeatures.cpp
new file mode 100644
index 0000000..a1d4006
--- /dev/null
+++ b/src/dawn_wire/SupportedFeatures.cpp
@@ -0,0 +1,48 @@
+// 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/SupportedFeatures.h"
+
+namespace dawn_wire {
+
+    // Note: Upon updating this list, please also update serialization/deserialization
+    // of limit structs on Adapter/Device initialization.
+    bool IsFeatureSupported(WGPUFeatureName feature) {
+        switch (feature) {
+            case WGPUFeatureName_Undefined:
+            case WGPUFeatureName_Force32:
+                return false;
+            case WGPUFeatureName_Depth24UnormStencil8:
+            case WGPUFeatureName_Depth32FloatStencil8:
+            case WGPUFeatureName_TimestampQuery:
+            case WGPUFeatureName_PipelineStatisticsQuery:
+            case WGPUFeatureName_TextureCompressionBC:
+            case WGPUFeatureName_TextureCompressionETC2:
+            case WGPUFeatureName_TextureCompressionASTC:
+            case WGPUFeatureName_IndirectFirstInstance:
+            case WGPUFeatureName_DepthClamping:
+            case WGPUFeatureName_DawnShaderFloat16:
+            case WGPUFeatureName_DawnInternalUsages:
+            case WGPUFeatureName_DawnMultiPlanarFormats:
+                return true;
+        }
+
+        // Catch-all, for unsupported features.
+        // "default:" is not used so we get compiler errors for
+        // newly added, unhandled features, but still catch completely
+        // unknown enums.
+        return false;
+    }
+
+}  // namespace dawn_wire
diff --git a/src/dawn_wire/SupportedFeatures.h b/src/dawn_wire/SupportedFeatures.h
new file mode 100644
index 0000000..5405ad4
--- /dev/null
+++ b/src/dawn_wire/SupportedFeatures.h
@@ -0,0 +1,26 @@
+// 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_SUPPORTEDFEATURES_H_
+#define DAWNWIRE_SUPPORTEDFEATURES_H_
+
+#include <dawn/webgpu.h>
+
+namespace dawn_wire {
+
+    bool IsFeatureSupported(WGPUFeatureName feature);
+
+}  // namespace dawn_wire
+
+#endif  // DAWNWIRE_SUPPORTEDFEATURES_H_
diff --git a/src/dawn_wire/client/Adapter.cpp b/src/dawn_wire/client/Adapter.cpp
index f3e46ef..fd5daf7 100644
--- a/src/dawn_wire/client/Adapter.cpp
+++ b/src/dawn_wire/client/Adapter.cpp
@@ -14,28 +14,114 @@
 
 #include "dawn_wire/client/Adapter.h"
 
+#include "dawn_wire/client/Client.h"
+
 namespace dawn_wire { namespace client {
 
-    bool Adapter::GetLimits(WGPUSupportedLimits* limits) const {
-        UNREACHABLE();
+    Adapter::~Adapter() {
+        mRequestDeviceRequests.CloseAll([](RequestDeviceData* request) {
+            request->callback(WGPURequestDeviceStatus_Unknown, nullptr,
+                              "Adapter destroyed before callback", request->userdata);
+        });
     }
 
-    void Adapter::GetProperties(WGPUAdapterProperties* properties) const {
-        UNREACHABLE();
+    void Adapter::CancelCallbacksForDisconnect() {
+        mRequestDeviceRequests.CloseAll([](RequestDeviceData* request) {
+            request->callback(WGPURequestDeviceStatus_Unknown, nullptr, "GPU connection lost",
+                              request->userdata);
+        });
+    }
+
+    bool Adapter::GetLimits(WGPUSupportedLimits* limits) const {
+        return mLimitsAndFeatures.GetLimits(limits);
     }
 
     bool Adapter::HasFeature(WGPUFeatureName feature) const {
-        UNREACHABLE();
+        return mLimitsAndFeatures.HasFeature(feature);
     }
 
     uint32_t Adapter::EnumerateFeatures(WGPUFeatureName* features) const {
-        UNREACHABLE();
+        return mLimitsAndFeatures.EnumerateFeatures(features);
+    }
+
+    void Adapter::SetLimits(const WGPUSupportedLimits* limits) {
+        return mLimitsAndFeatures.SetLimits(limits);
+    }
+
+    void Adapter::SetFeatures(const WGPUFeatureName* features, uint32_t featuresCount) {
+        return mLimitsAndFeatures.SetFeatures(features, featuresCount);
+    }
+
+    void Adapter::SetProperties(const WGPUAdapterProperties* properties) {
+        mProperties = *properties;
+        mProperties.nextInChain = nullptr;
+    }
+
+    void Adapter::GetProperties(WGPUAdapterProperties* properties) const {
+        *properties = mProperties;
     }
 
     void Adapter::RequestDevice(const WGPUDeviceDescriptor* descriptor,
                                 WGPURequestDeviceCallback callback,
                                 void* userdata) {
-        callback(WGPURequestDeviceStatus_Error, nullptr, "Not implemented", nullptr);
+        if (client->IsDisconnected()) {
+            callback(WGPURequestDeviceStatus_Error, nullptr, "GPU connection lost", userdata);
+            return;
+        }
+
+        auto* allocation = client->DeviceAllocator().New(client);
+        uint64_t serial = mRequestDeviceRequests.Add({callback, allocation->object->id, userdata});
+
+        AdapterRequestDeviceCmd cmd;
+        cmd.adapterId = this->id;
+        cmd.requestSerial = serial;
+        cmd.deviceObjectHandle = ObjectHandle(allocation->object->id, allocation->generation);
+        cmd.descriptor = descriptor;
+
+        client->SerializeCommand(cmd);
+    }
+
+    bool Client::DoAdapterRequestDeviceCallback(Adapter* adapter,
+                                                uint64_t requestSerial,
+                                                WGPURequestDeviceStatus status,
+                                                const char* message,
+                                                const WGPUSupportedLimits* limits,
+                                                uint32_t featuresCount,
+                                                const WGPUFeatureName* features) {
+        // May have been deleted or recreated so this isn't an error.
+        if (adapter == nullptr) {
+            return true;
+        }
+        return adapter->OnRequestDeviceCallback(requestSerial, status, message, limits,
+                                                featuresCount, features);
+    }
+
+    bool Adapter::OnRequestDeviceCallback(uint64_t requestSerial,
+                                          WGPURequestDeviceStatus status,
+                                          const char* message,
+                                          const WGPUSupportedLimits* limits,
+                                          uint32_t featuresCount,
+                                          const WGPUFeatureName* features) {
+        RequestDeviceData request;
+        if (!mRequestDeviceRequests.Acquire(requestSerial, &request)) {
+            return false;
+        }
+
+        Device* device = client->DeviceAllocator().GetObject(request.deviceObjectId);
+
+        // If the return status is a failure we should give a null device to the callback and
+        // free the allocation.
+        if (status != WGPURequestDeviceStatus_Success) {
+            client->DeviceAllocator().Free(device);
+            request.callback(status, nullptr, message, request.userdata);
+            return true;
+        }
+
+        device->SetLimits(limits);
+        device->SetFeatures(features, featuresCount);
+
+        request.callback(status, ToAPI(device), message, request.userdata);
+        return true;
     }
 
 }}  // namespace dawn_wire::client
diff --git a/src/dawn_wire/client/Adapter.h b/src/dawn_wire/client/Adapter.h
index 58c5d0f..e6f77dc 100644
--- a/src/dawn_wire/client/Adapter.h
+++ b/src/dawn_wire/client/Adapter.h
@@ -18,7 +18,10 @@
 #include <dawn/webgpu.h>
 
 #include "dawn_wire/WireClient.h"
+#include "dawn_wire/WireCmd_autogen.h"
+#include "dawn_wire/client/LimitsAndFeatures.h"
 #include "dawn_wire/client/ObjectBase.h"
+#include "dawn_wire/client/RequestTracker.h"
 
 namespace dawn_wire { namespace client {
 
@@ -26,13 +29,37 @@
       public:
         using ObjectBase::ObjectBase;
 
+        ~Adapter();
+        void CancelCallbacksForDisconnect() override;
+
         bool GetLimits(WGPUSupportedLimits* limits) const;
-        void GetProperties(WGPUAdapterProperties* properties) const;
         bool HasFeature(WGPUFeatureName feature) const;
         uint32_t EnumerateFeatures(WGPUFeatureName* features) const;
+        void SetLimits(const WGPUSupportedLimits* limits);
+        void SetFeatures(const WGPUFeatureName* features, uint32_t featuresCount);
+        void SetProperties(const WGPUAdapterProperties* properties);
+        void GetProperties(WGPUAdapterProperties* properties) const;
         void RequestDevice(const WGPUDeviceDescriptor* descriptor,
                            WGPURequestDeviceCallback callback,
                            void* userdata);
+
+        bool OnRequestDeviceCallback(uint64_t requestSerial,
+                                     WGPURequestDeviceStatus status,
+                                     const char* message,
+                                     const WGPUSupportedLimits* limits,
+                                     uint32_t featuresCount,
+                                     const WGPUFeatureName* features);
+
+      private:
+        LimitsAndFeatures mLimitsAndFeatures;
+        WGPUAdapterProperties mProperties;
+
+        struct RequestDeviceData {
+            WGPURequestDeviceCallback callback = nullptr;
+            ObjectId deviceObjectId;
+            void* userdata = nullptr;
+        };
+        RequestTracker<RequestDeviceData> mRequestDeviceRequests;
     };
 
 }}  // namespace dawn_wire::client
diff --git a/src/dawn_wire/client/Device.cpp b/src/dawn_wire/client/Device.cpp
index 57e6599..54d1053 100644
--- a/src/dawn_wire/client/Device.cpp
+++ b/src/dawn_wire/client/Device.cpp
@@ -67,6 +67,26 @@
         });
     }
 
+    bool Device::GetLimits(WGPUSupportedLimits* limits) const {
+        return mLimitsAndFeatures.GetLimits(limits);
+    }
+
+    bool Device::HasFeature(WGPUFeatureName feature) const {
+        return mLimitsAndFeatures.HasFeature(feature);
+    }
+
+    uint32_t Device::EnumerateFeatures(WGPUFeatureName* features) const {
+        return mLimitsAndFeatures.EnumerateFeatures(features);
+    }
+
+    void Device::SetLimits(const WGPUSupportedLimits* limits) {
+        return mLimitsAndFeatures.SetLimits(limits);
+    }
+
+    void Device::SetFeatures(const WGPUFeatureName* features, uint32_t featuresCount) {
+        return mLimitsAndFeatures.SetFeatures(features, featuresCount);
+    }
+
     void Device::HandleError(WGPUErrorType errorType, const char* message) {
         if (mErrorCallback) {
             mErrorCallback(errorType, message, mErrorUserdata);
@@ -196,20 +216,6 @@
         return Buffer::CreateError(this);
     }
 
-    bool Device::GetLimits(WGPUSupportedLimits* limits) const {
-        // Not implemented in the wire.
-        UNREACHABLE();
-        return false;
-    }
-
-    bool Device::HasFeature(WGPUFeatureName feature) const {
-        UNREACHABLE();
-    }
-
-    uint32_t Device::EnumerateFeatures(WGPUFeatureName* features) const {
-        UNREACHABLE();
-    }
-
     WGPUQueue Device::GetQueue() {
         // The queue is lazily created because if a Device is created by
         // Reserve/Inject, we cannot send the GetQueue message until
@@ -269,7 +275,7 @@
             client->ComputePipelineAllocator().GetObject(request.pipelineObjectID);
 
         // If the return status is a failure we should give a null pipeline to the callback and
-        // free the allocation both on the client side and the server side.
+        // free the allocation.
         if (status != WGPUCreatePipelineAsyncStatus_Success) {
             client->ComputePipelineAllocator().Free(pipelineAllocation);
             request.createComputePipelineAsyncCallback(status, nullptr, message, request.userdata);
@@ -320,7 +326,7 @@
             client->RenderPipelineAllocator().GetObject(request.pipelineObjectID);
 
         // If the return status is a failure we should give a null pipeline to the callback and
-        // free the allocation both on the client side and the server side.
+        // free the allocation.
         if (status != WGPUCreatePipelineAsyncStatus_Success) {
             client->RenderPipelineAllocator().Free(pipelineAllocation);
             request.createRenderPipelineAsyncCallback(status, nullptr, message, request.userdata);
diff --git a/src/dawn_wire/client/Device.h b/src/dawn_wire/client/Device.h
index fff5cf8..58be198 100644
--- a/src/dawn_wire/client/Device.h
+++ b/src/dawn_wire/client/Device.h
@@ -20,6 +20,7 @@
 #include "common/LinkedList.h"
 #include "dawn_wire/WireCmd_autogen.h"
 #include "dawn_wire/client/ApiObjects_autogen.h"
+#include "dawn_wire/client/LimitsAndFeatures.h"
 #include "dawn_wire/client/ObjectBase.h"
 #include "dawn_wire/client/RequestTracker.h"
 
@@ -67,6 +68,9 @@
         bool GetLimits(WGPUSupportedLimits* limits) const;
         bool HasFeature(WGPUFeatureName feature) const;
         uint32_t EnumerateFeatures(WGPUFeatureName* features) const;
+        void SetLimits(const WGPUSupportedLimits* limits);
+        void SetFeatures(const WGPUFeatureName* features, uint32_t featuresCount);
+
         WGPUQueue GetQueue();
 
         void CancelCallbacksForDisconnect() override;
@@ -74,6 +78,7 @@
         std::weak_ptr<bool> GetAliveWeakPtr();
 
       private:
+        LimitsAndFeatures mLimitsAndFeatures;
         struct ErrorScopeData {
             WGPUErrorCallback callback = nullptr;
             void* userdata = nullptr;
diff --git a/src/dawn_wire/client/Instance.cpp b/src/dawn_wire/client/Instance.cpp
index 74d0517..cf3976c 100644
--- a/src/dawn_wire/client/Instance.cpp
+++ b/src/dawn_wire/client/Instance.cpp
@@ -14,12 +14,88 @@
 
 #include "dawn_wire/client/Instance.h"
 
+#include "dawn_wire/client/Client.h"
+
 namespace dawn_wire { namespace client {
 
+    Instance::~Instance() {
+        mRequestAdapterRequests.CloseAll([](RequestAdapterData* request) {
+            request->callback(WGPURequestAdapterStatus_Unknown, nullptr,
+                              "Instance destroyed before callback", request->userdata);
+        });
+    }
+
+    void Instance::CancelCallbacksForDisconnect() {
+        mRequestAdapterRequests.CloseAll([](RequestAdapterData* request) {
+            request->callback(WGPURequestAdapterStatus_Unknown, nullptr, "GPU connection lost",
+                              request->userdata);
+        });
+    }
+
     void Instance::RequestAdapter(const WGPURequestAdapterOptions* options,
                                   WGPURequestAdapterCallback callback,
                                   void* userdata) {
-        callback(WGPURequestAdapterStatus_Error, nullptr, "Not implemented", nullptr);
+        if (client->IsDisconnected()) {
+            callback(WGPURequestAdapterStatus_Error, nullptr, "GPU connection lost", userdata);
+            return;
+        }
+
+        auto* allocation = client->AdapterAllocator().New(client);
+        uint64_t serial = mRequestAdapterRequests.Add({callback, allocation->object->id, userdata});
+
+        InstanceRequestAdapterCmd cmd;
+        cmd.instanceId = this->id;
+        cmd.requestSerial = serial;
+        cmd.adapterObjectHandle = ObjectHandle(allocation->object->id, allocation->generation);
+        cmd.options = options;
+
+        client->SerializeCommand(cmd);
+    }
+
+    bool Client::DoInstanceRequestAdapterCallback(Instance* instance,
+                                                  uint64_t requestSerial,
+                                                  WGPURequestAdapterStatus status,
+                                                  const char* message,
+                                                  const WGPUAdapterProperties* properties,
+                                                  const WGPUSupportedLimits* limits,
+                                                  uint32_t featuresCount,
+                                                  const WGPUFeatureName* features) {
+        // May have been deleted or recreated so this isn't an error.
+        if (instance == nullptr) {
+            return true;
+        }
+        return instance->OnRequestAdapterCallback(requestSerial, status, message, properties,
+                                                  limits, featuresCount, features);
+    }
+
+    bool Instance::OnRequestAdapterCallback(uint64_t requestSerial,
+                                            WGPURequestAdapterStatus status,
+                                            const char* message,
+                                            const WGPUAdapterProperties* properties,
+                                            const WGPUSupportedLimits* limits,
+                                            uint32_t featuresCount,
+                                            const WGPUFeatureName* features) {
+        RequestAdapterData request;
+        if (!mRequestAdapterRequests.Acquire(requestSerial, &request)) {
+            return false;
+        }
+
+        Adapter* adapter = client->AdapterAllocator().GetObject(request.adapterObjectId);
+
+        // If the return status is a failure we should give a null adapter to the callback and
+        // free the allocation.
+        if (status != WGPURequestAdapterStatus_Success) {
+            client->AdapterAllocator().Free(adapter);
+            request.callback(status, nullptr, message, request.userdata);
+            return true;
+        }
+
+        adapter->SetProperties(properties);
+        adapter->SetLimits(limits);
+        adapter->SetFeatures(features, featuresCount);
+
+        request.callback(status, ToAPI(adapter), message, request.userdata);
+        return true;
     }
 
 }}  // namespace dawn_wire::client
diff --git a/src/dawn_wire/client/Instance.h b/src/dawn_wire/client/Instance.h
index 3d55ac9..2733b9c 100644
--- a/src/dawn_wire/client/Instance.h
+++ b/src/dawn_wire/client/Instance.h
@@ -18,7 +18,9 @@
 #include <dawn/webgpu.h>
 
 #include "dawn_wire/WireClient.h"
+#include "dawn_wire/WireCmd_autogen.h"
 #include "dawn_wire/client/ObjectBase.h"
+#include "dawn_wire/client/RequestTracker.h"
 
 namespace dawn_wire { namespace client {
 
@@ -26,9 +28,27 @@
       public:
         using ObjectBase::ObjectBase;
 
+        ~Instance();
+        void CancelCallbacksForDisconnect() override;
+
         void RequestAdapter(const WGPURequestAdapterOptions* options,
                             WGPURequestAdapterCallback callback,
                             void* userdata);
+        bool OnRequestAdapterCallback(uint64_t requestSerial,
+                                      WGPURequestAdapterStatus status,
+                                      const char* message,
+                                      const WGPUAdapterProperties* properties,
+                                      const WGPUSupportedLimits* limits,
+                                      uint32_t featuresCount,
+                                      const WGPUFeatureName* features);
+
+      private:
+        struct RequestAdapterData {
+            WGPURequestAdapterCallback callback = nullptr;
+            ObjectId adapterObjectId;
+            void* userdata = nullptr;
+        };
+        RequestTracker<RequestAdapterData> mRequestAdapterRequests;
     };
 
 }}  // namespace dawn_wire::client
diff --git a/src/dawn_wire/client/LimitsAndFeatures.cpp b/src/dawn_wire/client/LimitsAndFeatures.cpp
new file mode 100644
index 0000000..c4b4656
--- /dev/null
+++ b/src/dawn_wire/client/LimitsAndFeatures.cpp
@@ -0,0 +1,63 @@
+// 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/LimitsAndFeatures.h"
+
+#include "common/Assert.h"
+#include "dawn_wire/SupportedFeatures.h"
+
+namespace dawn_wire { namespace client {
+
+    bool LimitsAndFeatures::GetLimits(WGPUSupportedLimits* limits) const {
+        ASSERT(limits != nullptr);
+        if (limits->nextInChain != nullptr) {
+            return false;
+        }
+        *limits = mLimits;
+        return true;
+    }
+
+    bool LimitsAndFeatures::HasFeature(WGPUFeatureName feature) const {
+        return mFeatures.count(feature) != 0;
+    }
+
+    uint32_t LimitsAndFeatures::EnumerateFeatures(WGPUFeatureName* features) const {
+        if (features != nullptr) {
+            for (WGPUFeatureName f : mFeatures) {
+                *features = f;
+                ++features;
+            }
+        }
+        return mFeatures.size();
+    }
+
+    void LimitsAndFeatures::SetLimits(const WGPUSupportedLimits* limits) {
+        ASSERT(limits != nullptr);
+        mLimits = *limits;
+        mLimits.nextInChain = nullptr;
+    }
+
+    void LimitsAndFeatures::SetFeatures(const WGPUFeatureName* features, uint32_t featuresCount) {
+        ASSERT(features != nullptr || featuresCount == 0);
+        for (uint32_t i = 0; i < featuresCount; ++i) {
+            // Filter out features that the server supports, but the client does not.
+            // (Could be different versions)
+            if (!IsFeatureSupported(features[i])) {
+                continue;
+            }
+            mFeatures.insert(features[i]);
+        }
+    }
+
+}}  // namespace dawn_wire::client
diff --git a/src/dawn_wire/client/LimitsAndFeatures.h b/src/dawn_wire/client/LimitsAndFeatures.h
new file mode 100644
index 0000000..996dbdb
--- /dev/null
+++ b/src/dawn_wire/client/LimitsAndFeatures.h
@@ -0,0 +1,40 @@
+// 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_LIMITSANDFEATURES_H_
+#define DAWNWIRE_CLIENT_LIMITSANDFEATURES_H_
+
+#include <dawn/webgpu.h>
+
+#include <unordered_set>
+
+namespace dawn_wire { namespace client {
+
+    class LimitsAndFeatures {
+      public:
+        bool GetLimits(WGPUSupportedLimits* limits) const;
+        bool HasFeature(WGPUFeatureName feature) const;
+        uint32_t EnumerateFeatures(WGPUFeatureName* features) const;
+
+        void SetLimits(const WGPUSupportedLimits* limits);
+        void SetFeatures(const WGPUFeatureName* features, uint32_t featuresCount);
+
+      private:
+        WGPUSupportedLimits mLimits;
+        std::unordered_set<WGPUFeatureName> mFeatures;
+    };
+
+}}  // namespace dawn_wire::client
+
+#endif  // DAWNWIRE_CLIENT_LIMITSANDFEATURES_H_
diff --git a/src/dawn_wire/server/Server.cpp b/src/dawn_wire/server/Server.cpp
index a4dbd8c..6fea9fc 100644
--- a/src/dawn_wire/server/Server.cpp
+++ b/src/dawn_wire/server/Server.cpp
@@ -122,34 +122,7 @@
         mProcs.deviceReference(device);
 
         // Set callbacks to forward errors to the client.
-        // Note: these callbacks are manually inlined here since they do not acquire and
-        // free their userdata. Also unlike other callbacks, these are cleared and unset when
-        // the server is destroyed, so we don't need to check if the server is still alive
-        // inside them.
-        mProcs.deviceSetUncapturedErrorCallback(
-            device,
-            [](WGPUErrorType type, const char* message, void* userdata) {
-                DeviceInfo* info = static_cast<DeviceInfo*>(userdata);
-                info->server->OnUncapturedError(info->self, type, message);
-            },
-            data->info.get());
-        // Set callback to post warning and other infomation to client.
-        // Almost the same with UncapturedError.
-        mProcs.deviceSetLoggingCallback(
-            device,
-            [](WGPULoggingType type, const char* message, void* userdata) {
-                DeviceInfo* info = static_cast<DeviceInfo*>(userdata);
-                info->server->OnLogging(info->self, type, message);
-            },
-            data->info.get());
-        mProcs.deviceSetDeviceLostCallback(
-            device,
-            [](WGPUDeviceLostReason reason, const char* message, void* userdata) {
-                DeviceInfo* info = static_cast<DeviceInfo*>(userdata);
-                info->server->OnDeviceLost(info->self, reason, message);
-            },
-            data->info.get());
-
+        SetForwardingDeviceCallbacks(data);
         return true;
     }
 
@@ -179,6 +152,36 @@
         return data->handle;
     }
 
+    void Server::SetForwardingDeviceCallbacks(ObjectData<WGPUDevice>* deviceObject) {
+        // Note: these callbacks are manually inlined here since they do not acquire and
+        // free their userdata. Also unlike other callbacks, these are cleared and unset when
+        // the server is destroyed, so we don't need to check if the server is still alive
+        // inside them.
+        mProcs.deviceSetUncapturedErrorCallback(
+            deviceObject->handle,
+            [](WGPUErrorType type, const char* message, void* userdata) {
+                DeviceInfo* info = static_cast<DeviceInfo*>(userdata);
+                info->server->OnUncapturedError(info->self, type, message);
+            },
+            deviceObject->info.get());
+        // Set callback to post warning and other infomation to client.
+        // Almost the same with UncapturedError.
+        mProcs.deviceSetLoggingCallback(
+            deviceObject->handle,
+            [](WGPULoggingType type, const char* message, void* userdata) {
+                DeviceInfo* info = static_cast<DeviceInfo*>(userdata);
+                info->server->OnLogging(info->self, type, message);
+            },
+            deviceObject->info.get());
+        mProcs.deviceSetDeviceLostCallback(
+            deviceObject->handle,
+            [](WGPUDeviceLostReason reason, const char* message, void* userdata) {
+                DeviceInfo* info = static_cast<DeviceInfo*>(userdata);
+                info->server->OnDeviceLost(info->self, reason, message);
+            },
+            deviceObject->info.get());
+    }
+
     void Server::ClearDeviceCallbacks(WGPUDevice device) {
         // Un-set the error and lost callbacks since we cannot forward them
         // after the server has been destroyed.
diff --git a/src/dawn_wire/server/Server.h b/src/dawn_wire/server/Server.h
index 33b0e04..1b685c7 100644
--- a/src/dawn_wire/server/Server.h
+++ b/src/dawn_wire/server/Server.h
@@ -146,6 +146,22 @@
         ObjectId pipelineObjectID;
     };
 
+    struct RequestAdapterUserdata : CallbackUserdata {
+        using CallbackUserdata::CallbackUserdata;
+
+        ObjectHandle instance;
+        uint64_t requestSerial;
+        ObjectId adapterObjectId;
+    };
+
+    struct RequestDeviceUserdata : CallbackUserdata {
+        using CallbackUserdata::CallbackUserdata;
+
+        ObjectHandle adapter;
+        uint64_t requestSerial;
+        ObjectId deviceObjectId;
+    };
+
     class Server : public ServerBase {
       public:
         Server(const DawnProcTable& procs,
@@ -194,6 +210,7 @@
             mSerializer.SerializeCommand(cmd, extraSize, SerializeExtraSize);
         }
 
+        void SetForwardingDeviceCallbacks(ObjectData<WGPUDevice>* deviceObject);
         void ClearDeviceCallbacks(WGPUDevice device);
 
         // Error callbacks
@@ -216,6 +233,14 @@
         void OnShaderModuleGetCompilationInfo(WGPUCompilationInfoRequestStatus status,
                                               const WGPUCompilationInfo* info,
                                               ShaderModuleGetCompilationInfoUserdata* userdata);
+        void OnRequestAdapterCallback(WGPURequestAdapterStatus status,
+                                      WGPUAdapter adapter,
+                                      const char* message,
+                                      RequestAdapterUserdata* userdata);
+        void OnRequestDeviceCallback(WGPURequestDeviceStatus status,
+                                     WGPUDevice device,
+                                     const char* message,
+                                     RequestDeviceUserdata* userdata);
 
 #include "dawn_wire/server/ServerPrototypes_autogen.inc"
 
diff --git a/src/dawn_wire/server/ServerAdapter.cpp b/src/dawn_wire/server/ServerAdapter.cpp
new file mode 100644
index 0000000..652d86d
--- /dev/null
+++ b/src/dawn_wire/server/ServerAdapter.cpp
@@ -0,0 +1,112 @@
+// 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/server/Server.h"
+
+#include "dawn_wire/SupportedFeatures.h"
+
+namespace dawn_wire { namespace server {
+
+    bool Server::DoAdapterRequestDevice(ObjectId adapterId,
+                                        uint64_t requestSerial,
+                                        ObjectHandle deviceHandle,
+                                        const WGPUDeviceDescriptor* descriptor) {
+        auto* adapter = AdapterObjects().Get(adapterId);
+        if (adapter == nullptr) {
+            return false;
+        }
+
+        auto* resultData = DeviceObjects().Allocate(deviceHandle.id, AllocationState::Reserved);
+        if (resultData == nullptr) {
+            return false;
+        }
+
+        resultData->generation = deviceHandle.generation;
+
+        auto userdata = MakeUserdata<RequestDeviceUserdata>();
+        userdata->adapter = ObjectHandle{adapterId, adapter->generation};
+        userdata->requestSerial = requestSerial;
+        userdata->deviceObjectId = deviceHandle.id;
+
+        mProcs.adapterRequestDevice(
+            adapter->handle, descriptor,
+            ForwardToServer<decltype(
+                &Server::OnRequestDeviceCallback)>::Func<&Server::OnRequestDeviceCallback>(),
+            userdata.release());
+        return true;
+    }
+
+    void Server::OnRequestDeviceCallback(WGPURequestDeviceStatus status,
+                                         WGPUDevice device,
+                                         const char* message,
+                                         RequestDeviceUserdata* data) {
+        auto* deviceObject = DeviceObjects().Get(data->deviceObjectId, AllocationState::Reserved);
+        // Should be impossible to fail. ObjectIds can't be freed by a destroy command until
+        // they move from Reserved to Allocated, or if they are destroyed here.
+        ASSERT(deviceObject != nullptr);
+
+        ReturnAdapterRequestDeviceCallbackCmd cmd = {};
+        cmd.adapter = data->adapter;
+        cmd.requestSerial = data->requestSerial;
+        cmd.status = status;
+        cmd.message = message;
+
+        if (status != WGPURequestDeviceStatus_Success) {
+            // Free the ObjectId which will make it unusable.
+            DeviceObjects().Free(data->deviceObjectId);
+            ASSERT(device == nullptr);
+            SerializeCommand(cmd);
+            return;
+        }
+
+        std::vector<WGPUFeatureName> features;
+
+        uint32_t featuresCount = mProcs.deviceEnumerateFeatures(device, nullptr);
+        features.resize(featuresCount);
+        mProcs.deviceEnumerateFeatures(device, features.data());
+
+        // The client should only be able to request supported features, so all enumerated
+        // features that were enabled must also be supported by the wire.
+        // Note: We fail the callback here, instead of immediately upon receiving
+        // the request to preserve callback ordering.
+        for (WGPUFeatureName f : features) {
+            if (!IsFeatureSupported(f)) {
+                // Release the device.
+                mProcs.deviceRelease(device);
+                // Free the ObjectId which will make it unusable.
+                DeviceObjects().Free(data->deviceObjectId);
+
+                cmd.status = WGPURequestDeviceStatus_Error;
+                cmd.message = "Requested feature not supported.";
+                SerializeCommand(cmd);
+                return;
+            }
+        }
+
+        cmd.featuresCount = features.size();
+        cmd.features = features.data();
+
+        WGPUSupportedLimits limits = {};
+        mProcs.deviceGetLimits(device, &limits);
+        cmd.limits = &limits;
+
+        // Assign the handle and allocated status if the device is created successfully.
+        deviceObject->state = AllocationState::Allocated;
+        deviceObject->handle = device;
+        SetForwardingDeviceCallbacks(deviceObject);
+
+        SerializeCommand(cmd);
+    }
+
+}}  // namespace dawn_wire::server
diff --git a/src/dawn_wire/server/ServerInstance.cpp b/src/dawn_wire/server/ServerInstance.cpp
new file mode 100644
index 0000000..0fb35c0
--- /dev/null
+++ b/src/dawn_wire/server/ServerInstance.cpp
@@ -0,0 +1,102 @@
+// 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/server/Server.h"
+
+#include "dawn_wire/SupportedFeatures.h"
+
+#include <algorithm>
+
+namespace dawn_wire { namespace server {
+
+    bool Server::DoInstanceRequestAdapter(ObjectId instanceId,
+                                          uint64_t requestSerial,
+                                          ObjectHandle adapterHandle,
+                                          const WGPURequestAdapterOptions* options) {
+        auto* instance = InstanceObjects().Get(instanceId);
+        if (instance == nullptr) {
+            return false;
+        }
+
+        auto* resultData = AdapterObjects().Allocate(adapterHandle.id, AllocationState::Reserved);
+        if (resultData == nullptr) {
+            return false;
+        }
+
+        resultData->generation = adapterHandle.generation;
+
+        auto userdata = MakeUserdata<RequestAdapterUserdata>();
+        userdata->instance = ObjectHandle{instanceId, instance->generation};
+        userdata->requestSerial = requestSerial;
+        userdata->adapterObjectId = adapterHandle.id;
+
+        mProcs.instanceRequestAdapter(
+            instance->handle, options,
+            ForwardToServer<decltype(
+                &Server::OnRequestAdapterCallback)>::Func<&Server::OnRequestAdapterCallback>(),
+            userdata.release());
+        return true;
+    }
+
+    void Server::OnRequestAdapterCallback(WGPURequestAdapterStatus status,
+                                          WGPUAdapter adapter,
+                                          const char* message,
+                                          RequestAdapterUserdata* data) {
+        auto* adapterObject =
+            AdapterObjects().Get(data->adapterObjectId, AllocationState::Reserved);
+        // Should be impossible to fail. ObjectIds can't be freed by a destroy command until
+        // they move from Reserved to Allocated, or if they are destroyed here.
+        ASSERT(adapterObject != nullptr);
+
+        ReturnInstanceRequestAdapterCallbackCmd cmd = {};
+        cmd.instance = data->instance;
+        cmd.requestSerial = data->requestSerial;
+        cmd.status = status;
+        cmd.message = message;
+
+        if (status != WGPURequestAdapterStatus_Success) {
+            // Free the ObjectId which will make it unusable.
+            AdapterObjects().Free(data->adapterObjectId);
+            ASSERT(adapter == nullptr);
+            SerializeCommand(cmd);
+            return;
+        }
+
+        WGPUAdapterProperties properties = {};
+        WGPUSupportedLimits limits = {};
+        std::vector<WGPUFeatureName> features;
+
+        // Assign the handle and allocated status if the adapter is created successfully.
+        adapterObject->state = AllocationState::Allocated;
+        adapterObject->handle = adapter;
+
+        uint32_t featuresCount = mProcs.adapterEnumerateFeatures(adapter, nullptr);
+        features.resize(featuresCount);
+        mProcs.adapterEnumerateFeatures(adapter, features.data());
+
+        // Hide features the wire cannot support.
+        auto it = std::partition(features.begin(), features.end(), IsFeatureSupported);
+
+        cmd.featuresCount = std::distance(features.begin(), it);
+        cmd.features = features.data();
+
+        mProcs.adapterGetProperties(adapter, &properties);
+        mProcs.adapterGetLimits(adapter, &limits);
+        cmd.properties = &properties;
+        cmd.limits = &limits;
+
+        SerializeCommand(cmd);
+    }
+
+}}  // namespace dawn_wire::server