dawn_wire: Make all objects owned by the client

This removes the logic where the Client owns the Device and the
Device owns all other objects. Ownership should be tracked in
dawn_native either with refcounting or validation to disallow
operations after an object's parent has been destroyed.

This simplifies the wire client code in that the client only
tracks allocated handles and does not manage parent/child lifetimes.
This is an important simplification so we can support multiple WebGPU
instances, adapters, and devices on a single wire.

Bug: dawn:384
Change-Id: I8ecc7c368130b8917202150c467b5f0e7d4b753e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/37000
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/generator/templates/dawn_wire/client/ApiProcs.cpp b/generator/templates/dawn_wire/client/ApiProcs.cpp
index 0ad9a77..1b98d02 100644
--- a/generator/templates/dawn_wire/client/ApiProcs.cpp
+++ b/generator/templates/dawn_wire/client/ApiProcs.cpp
@@ -34,14 +34,14 @@
         {%- endmacro %}
 
         {% for type in by_category["object"] %}
-            DAWN_DECLARE_UNUSED bool DeviceMatches(const Device* device, const {{as_cType(type.name)}} obj) {
-                return device == reinterpret_cast<const {{as_wireType(type)}}>(obj)->device;
+            DAWN_DECLARE_UNUSED bool ClientMatches(const Client* client, const {{as_cType(type.name)}} obj) {
+                return client == reinterpret_cast<const {{as_wireType(type)}}>(obj)->client;
             }
 
-            DAWN_DECLARE_UNUSED bool DeviceMatches(const Device* device, const {{as_cType(type.name)}} *const obj, uint32_t count = 1) {
+            DAWN_DECLARE_UNUSED bool ClientMatches(const Client* client, const {{as_cType(type.name)}} *const obj, uint32_t count = 1) {
                 ASSERT(count == 0 || obj != nullptr);
                 for (uint32_t i = 0; i < count; ++i) {
-                    if (!DeviceMatches(device, obj[i])) {
+                    if (!ClientMatches(client, obj[i])) {
                         return false;
                     }
                 }
@@ -49,12 +49,12 @@
             }
         {% endfor %}
 
-        bool DeviceMatches(const Device* device, WGPUChainedStruct const* chainedStruct);
+        bool ClientMatches(const Client* client, WGPUChainedStruct const* chainedStruct);
 
         {% for type in by_category["structure"] if type.may_have_dawn_object %}
-            DAWN_DECLARE_UNUSED bool DeviceMatches(const Device* device, const {{as_cType(type.name)}}& obj) {
+            DAWN_DECLARE_UNUSED bool ClientMatches(const Client* client, const {{as_cType(type.name)}}& obj) {
                 {% if type.extensible %}
-                    if (!DeviceMatches(device, obj.nextInChain)) {
+                    if (!ClientMatches(client, obj.nextInChain)) {
                         return false;
                     }
                 {% endif %}
@@ -63,7 +63,7 @@
                         if (obj.{{as_varName(member.name)}} != nullptr)
                     {% endif %}
                     {
-                        if (!DeviceMatches(device, obj.{{as_varName(member.name)}}
+                        if (!ClientMatches(client, obj.{{as_varName(member.name)}}
                             {%- if member.annotation != "value" and member.length != "strlen" -%}
                                 , {{member_length(member, "obj.")}}
                             {%- endif -%})) {
@@ -74,9 +74,9 @@
                 return true;
             }
 
-            DAWN_DECLARE_UNUSED bool DeviceMatches(const Device* device, const {{as_cType(type.name)}} *const obj, uint32_t count = 1) {
+            DAWN_DECLARE_UNUSED bool ClientMatches(const Client* client, const {{as_cType(type.name)}} *const obj, uint32_t count = 1) {
                 for (uint32_t i = 0; i < count; ++i) {
-                    if (!DeviceMatches(device, obj[i])) {
+                    if (!ClientMatches(client, obj[i])) {
                         return false;
                     }
                 }
@@ -84,14 +84,14 @@
             }
         {% endfor %}
 
-        bool DeviceMatches(const Device* device, WGPUChainedStruct const* chainedStruct) {
+        bool ClientMatches(const Client* client, WGPUChainedStruct const* chainedStruct) {
             while (chainedStruct != nullptr) {
                 switch (chainedStruct->sType) {
                     {% for sType in types["s type"].values if sType.valid %}
                         {% set CType = as_cType(sType.name) %}
                         case {{as_cEnum(types["s type"].name, sType.name)}}: {
                             {% if types[sType.name.get()].may_have_dawn_object %}
-                                if (!DeviceMatches(device, reinterpret_cast<const {{CType}}*>(chainedStruct))) {
+                                if (!ClientMatches(client, reinterpret_cast<const {{CType}}*>(chainedStruct))) {
                                     return false;
                                 }
                             {% endif %}
@@ -103,7 +103,7 @@
                     default:
                         UNREACHABLE();
                         dawn::WarningLog()
-                            << "All objects may not be from the same device. "
+                            << "All objects may not be from the same client. "
                             << "Unknown sType " << chainedStruct->sType << " discarded.";
                         return false;
                 }
@@ -133,10 +133,10 @@
             ) {
                 {% if len(method.arguments) > 0 %}
                     {
-                        bool sameDevice = true;
+                        bool sameClient = true;
                         auto self = reinterpret_cast<{{as_wireType(type)}}>(cSelf);
-                        Device* device = self->device;
-                        DAWN_UNUSED(device);
+                        Client* client = self->client;
+                        DAWN_UNUSED(client);
 
                         do {
                             {% for arg in method.arguments if arg.type.may_have_dawn_object or arg.type.category == "object" %}
@@ -144,26 +144,26 @@
                                     if ({{as_varName(arg.name)}} != nullptr)
                                 {% endif %}
                                 {
-                                    if (!DeviceMatches(device, {{as_varName(arg.name)}}
+                                    if (!ClientMatches(client, {{as_varName(arg.name)}}
                                         {%- if arg.annotation != "value" and arg.length != "strlen" -%}
                                             , {{member_length(arg, "")}}
                                         {%- endif -%})) {
-                                        sameDevice = false;
+                                        sameClient = false;
                                         break;
                                     }
                                 }
                             {% endfor %}
                         } while (false);
 
-                        if (DAWN_UNLIKELY(!sameDevice)) {
-                            device->InjectError(WGPUErrorType_Validation,
+                        if (DAWN_UNLIKELY(!sameClient)) {
+                            reinterpret_cast<Device*>(client->GetDevice())->InjectError(WGPUErrorType_Validation,
                                                 "All objects must be from the same device.");
                             {% if method.return_type.category == "object" %}
                                 // Allocate an object without registering it on the server. This is backed by a real allocation on
                                 // the client so commands can be sent with it. But because it's not allocated on the server, it will
                                 // be a fatal error to use it.
                                 auto self = reinterpret_cast<{{as_wireType(type)}}>(cSelf);
-                                auto* allocation = self->device->GetClient()->{{method.return_type.name.CamelCase()}}Allocator().New(self->device);
+                                auto* allocation = self->client->{{method.return_type.name.CamelCase()}}Allocator().New(self->client);
                                 return reinterpret_cast<{{as_cType(method.return_type.name)}}>(allocation->object.get());
                             {% elif method.return_type.name.canonical_case() == "void" %}
                                 return;
@@ -176,7 +176,6 @@
 
                 auto self = reinterpret_cast<{{as_wireType(type)}}>(cSelf);
                 {% if Suffix not in client_handwritten_commands %}
-                    Device* device = self->device;
                     {{Suffix}}Cmd cmd;
 
                     //* Create the structure going on the wire on the stack and fill it with the value
@@ -185,7 +184,7 @@
 
                     //* For object creation, store the object ID the client will use for the result.
                     {% if method.return_type.category == "object" %}
-                        auto* allocation = self->device->GetClient()->{{method.return_type.name.CamelCase()}}Allocator().New(self->device);
+                        auto* allocation = self->client->{{method.return_type.name.CamelCase()}}Allocator().New(self->client);
                         cmd.result = ObjectHandle{allocation->object->id, allocation->generation};
                     {% endif %}
 
@@ -194,7 +193,7 @@
                     {% endfor %}
 
                     //* Allocate space to send the command and copy the value args over.
-                    device->GetClient()->SerializeCommand(cmd);
+                    self->client->SerializeCommand(cmd);
 
                     {% if method.return_type.category == "object" %}
                         return reinterpret_cast<{{as_cType(method.return_type.name)}}>(allocation->object.get());
@@ -222,8 +221,8 @@
                 cmd.objectType = ObjectType::{{type.name.CamelCase()}};
                 cmd.objectId = obj->id;
 
-                obj->device->GetClient()->SerializeCommand(cmd);
-                obj->device->GetClient()->{{type.name.CamelCase()}}Allocator().Free(obj);
+                obj->client->SerializeCommand(cmd);
+                obj->client->{{type.name.CamelCase()}}Allocator().Free(obj);
             }
 
             void Client{{as_MethodSuffix(type.name, Name("reference"))}}({{cType}} cObj) {
diff --git a/src/dawn_wire/client/Buffer.cpp b/src/dawn_wire/client/Buffer.cpp
index 23ca0ab..2244ace 100644
--- a/src/dawn_wire/client/Buffer.cpp
+++ b/src/dawn_wire/client/Buffer.cpp
@@ -20,15 +20,15 @@
 namespace dawn_wire { namespace client {
 
     // static
-    WGPUBuffer Buffer::Create(Device* device_, const WGPUBufferDescriptor* descriptor) {
-        Client* wireClient = device_->GetClient();
+    WGPUBuffer Buffer::Create(Device* device, const WGPUBufferDescriptor* descriptor) {
+        Client* wireClient = device->client;
 
         bool mappable =
             (descriptor->usage & (WGPUBufferUsage_MapRead | WGPUBufferUsage_MapWrite)) != 0 ||
             descriptor->mappedAtCreation;
         if (mappable && descriptor->size >= std::numeric_limits<size_t>::max()) {
-            device_->InjectError(WGPUErrorType_OutOfMemory, "Buffer is too large for map usage");
-            return device_->CreateErrorBuffer();
+            device->InjectError(WGPUErrorType_OutOfMemory, "Buffer is too large for map usage");
+            return device->CreateErrorBuffer();
         }
 
         std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = nullptr;
@@ -42,16 +42,16 @@
             writeHandle.reset(
                 wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size));
             if (writeHandle == nullptr) {
-                device_->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
-                return device_->CreateErrorBuffer();
+                device->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
+                return device->CreateErrorBuffer();
             }
 
             // Open the handle, it may fail by returning a nullptr in writeData.
             size_t writeDataLength = 0;
             std::tie(writeData, writeDataLength) = writeHandle->Open();
             if (writeData == nullptr) {
-                device_->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
-                return device_->CreateErrorBuffer();
+                device->InjectError(WGPUErrorType_OutOfMemory, "Buffer mapping allocation failed");
+                return device->CreateErrorBuffer();
             }
             ASSERT(writeDataLength == descriptor->size);
 
@@ -60,12 +60,13 @@
         }
 
         // Create the buffer and send the creation command.
-        auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device_);
+        auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(wireClient);
         Buffer* buffer = bufferObjectAndSerial->object.get();
+        buffer->mDevice = device;
         buffer->mSize = descriptor->size;
 
         DeviceCreateBufferCmd cmd;
-        cmd.device = ToAPI(device_);
+        cmd.device = ToAPI(device);
         cmd.descriptor = descriptor;
         cmd.result = ObjectHandle{buffer->id, bufferObjectAndSerial->generation};
         cmd.handleCreateInfoLength = writeHandleCreateInfoLength;
@@ -88,13 +89,14 @@
     }
 
     // static
-    WGPUBuffer Buffer::CreateError(Device* device_) {
-        auto* allocation = device_->GetClient()->BufferAllocator().New(device_);
+    WGPUBuffer Buffer::CreateError(Device* device) {
+        auto* allocation = device->client->BufferAllocator().New(device->client);
+        allocation->object->mDevice = device;
 
         DeviceCreateErrorBufferCmd cmd;
-        cmd.self = ToAPI(device_);
+        cmd.self = ToAPI(device);
         cmd.result = ObjectHandle{allocation->object->id, allocation->generation};
-        device_->GetClient()->SerializeCommand(cmd);
+        device->client->SerializeCommand(cmd);
 
         return ToAPI(allocation->object.get());
     }
@@ -124,7 +126,7 @@
                           size_t size,
                           WGPUBufferMapCallback callback,
                           void* userdata) {
-        if (device->GetClient()->IsDisconnected()) {
+        if (client->IsDisconnected()) {
             return callback(WGPUBufferMapAsyncStatus_DeviceLost, userdata);
         }
 
@@ -138,7 +140,8 @@
 
         // Step 1. Do early validation of READ ^ WRITE because the server rejects mode = 0.
         if (!(isReadMode ^ isWriteMode)) {
-            device->InjectError(WGPUErrorType_Validation, "MapAsync error (you figure out :P)");
+            mDevice->InjectError(WGPUErrorType_Validation,
+                                 "MapAsync mode must be exactly one of Read or Write");
             if (callback != nullptr) {
                 callback(WGPUBufferMapAsyncStatus_Error, userdata);
             }
@@ -158,19 +161,17 @@
 
         // Step 2a: Create the read / write handles for this request.
         if (isReadMode) {
-            request.readHandle.reset(
-                device->GetClient()->GetMemoryTransferService()->CreateReadHandle(size));
+            request.readHandle.reset(client->GetMemoryTransferService()->CreateReadHandle(size));
             if (request.readHandle == nullptr) {
-                device->InjectError(WGPUErrorType_OutOfMemory, "Failed to create buffer mapping");
+                mDevice->InjectError(WGPUErrorType_OutOfMemory, "Failed to create buffer mapping");
                 callback(WGPUBufferMapAsyncStatus_Error, userdata);
                 return;
             }
         } else {
             ASSERT(isWriteMode);
-            request.writeHandle.reset(
-                device->GetClient()->GetMemoryTransferService()->CreateWriteHandle(size));
+            request.writeHandle.reset(client->GetMemoryTransferService()->CreateWriteHandle(size));
             if (request.writeHandle == nullptr) {
-                device->InjectError(WGPUErrorType_OutOfMemory, "Failed to create buffer mapping");
+                mDevice->InjectError(WGPUErrorType_OutOfMemory, "Failed to create buffer mapping");
                 callback(WGPUBufferMapAsyncStatus_Error, userdata);
                 return;
             }
@@ -188,15 +189,15 @@
         // Step 3a. Fill the handle create info in the command.
         if (isReadMode) {
             cmd.handleCreateInfoLength = request.readHandle->SerializeCreateSize();
-            device->GetClient()->SerializeCommand(
-                cmd, cmd.handleCreateInfoLength,
-                [&](char* cmdSpace) { request.readHandle->SerializeCreate(cmdSpace); });
+            client->SerializeCommand(cmd, cmd.handleCreateInfoLength, [&](char* cmdSpace) {
+                request.readHandle->SerializeCreate(cmdSpace);
+            });
         } else {
             ASSERT(isWriteMode);
             cmd.handleCreateInfoLength = request.writeHandle->SerializeCreateSize();
-            device->GetClient()->SerializeCommand(
-                cmd, cmd.handleCreateInfoLength,
-                [&](char* cmdSpace) { request.writeHandle->SerializeCreate(cmdSpace); });
+            client->SerializeCommand(cmd, cmd.handleCreateInfoLength, [&](char* cmdSpace) {
+                request.writeHandle->SerializeCreate(cmdSpace);
+            });
         }
 
         // Step 4. Register this request so that we can retrieve it from its serial when the server
@@ -323,7 +324,7 @@
             cmd.writeFlushInfoLength = writeFlushInfoLength;
             cmd.writeFlushInfo = nullptr;
 
-            device->GetClient()->SerializeCommand(cmd, writeFlushInfoLength, [&](char* cmdSpace) {
+            client->SerializeCommand(cmd, writeFlushInfoLength, [&](char* cmdSpace) {
                 // Serialize flush metadata into the space after the command.
                 // This closes the handle for writing.
                 mWriteHandle->SerializeFlush(cmdSpace);
@@ -347,7 +348,7 @@
 
         BufferUnmapCmd cmd;
         cmd.self = ToAPI(this);
-        device->GetClient()->SerializeCommand(cmd);
+        client->SerializeCommand(cmd);
     }
 
     void Buffer::Destroy() {
@@ -365,7 +366,7 @@
 
         BufferDestroyCmd cmd;
         cmd.self = ToAPI(this);
-        device->GetClient()->SerializeCommand(cmd);
+        client->SerializeCommand(cmd);
     }
 
     bool Buffer::IsMappedForReading() const {
diff --git a/src/dawn_wire/client/Buffer.h b/src/dawn_wire/client/Buffer.h
index cacfd48..d6a275c 100644
--- a/src/dawn_wire/client/Buffer.h
+++ b/src/dawn_wire/client/Buffer.h
@@ -24,6 +24,8 @@
 
 namespace dawn_wire { namespace client {
 
+    class Device;
+
     class Buffer final : public ObjectBase {
       public:
         using ObjectBase::ObjectBase;
@@ -55,6 +57,8 @@
         bool IsMappedForWriting() const;
         bool CheckGetMappedRangeOffsetSize(size_t offset, size_t size) const;
 
+        Device* mDevice;
+
         // We want to defer all the validation to the server, which means we could have multiple
         // map request in flight at a single time and need to track them separately.
         // On well-behaved applications, only one request should exist at a single time.
diff --git a/src/dawn_wire/client/Client.cpp b/src/dawn_wire/client/Client.cpp
index 0b53bc4..d12bc57 100644
--- a/src/dawn_wire/client/Client.cpp
+++ b/src/dawn_wire/client/Client.cpp
@@ -57,10 +57,23 @@
     }
 
     void Client::DestroyAllObjects() {
-        while (!mDevices.empty()) {
-            // Note: We don't send a DestroyObject command for the device
-            // since freeing a device object is done out of band.
-            DeviceAllocator().Free(static_cast<Device*>(mDevices.head()->value()));
+        for (auto& objectList : mObjects) {
+            ObjectType objectType = static_cast<ObjectType>(&objectList - mObjects.data());
+            while (!objectList.empty()) {
+                ObjectBase* object = objectList.head()->value();
+                if (object == mDevice) {
+                    // Note: We don't send a DestroyObject command for the device
+                    // since freeing a device object is done out of band.
+                    DeviceAllocator().Free(mDevice);
+                    continue;
+                }
+
+                DestroyObjectCmd cmd;
+                cmd.objectType = objectType;
+                cmd.objectId = object->id;
+                SerializeCommand(cmd);
+                FreeObject(objectType, object);
+            }
         }
     }
 
@@ -71,9 +84,8 @@
         return reinterpret_cast<WGPUDeviceImpl*>(mDevice);
     }
 
-    ReservedTexture Client::ReserveTexture(WGPUDevice cDevice) {
-        Device* device = FromAPI(cDevice);
-        auto* allocation = TextureAllocator().New(device);
+    ReservedTexture Client::ReserveTexture(WGPUDevice device) {
+        auto* allocation = TextureAllocator().New(this);
 
         ReservedTexture result;
         result.texture = ToAPI(allocation->object.get());
@@ -87,12 +99,14 @@
         mSerializer = ChunkedCommandSerializer(NoopCommandSerializer::GetInstance());
         if (mDevice != nullptr) {
             mDevice->HandleDeviceLost("GPU connection lost");
-            mDevice->CancelCallbacksForDisconnect();
         }
-    }
-
-    void Client::TrackObject(Device* device) {
-        mDevices.Append(device);
+        for (auto& objectList : mObjects) {
+            LinkNode<ObjectBase>* object = objectList.head();
+            while (object != objectList.end()) {
+                object->value()->CancelCallbacksForDisconnect();
+                object = object->next();
+            }
+        }
     }
 
     bool Client::IsDisconnected() const {
diff --git a/src/dawn_wire/client/Client.h b/src/dawn_wire/client/Client.h
index 9a44cc3..4902df8 100644
--- a/src/dawn_wire/client/Client.h
+++ b/src/dawn_wire/client/Client.h
@@ -62,7 +62,10 @@
         void Disconnect();
         bool IsDisconnected() const;
 
-        void TrackObject(Device* device);
+        template <typename T>
+        void TrackObject(T* object) {
+            mObjects[ObjectTypeToTypeEnum<T>::value].Append(object);
+        }
 
       private:
         void DestroyAllObjects();
@@ -75,7 +78,7 @@
         MemoryTransferService* mMemoryTransferService = nullptr;
         std::unique_ptr<MemoryTransferService> mOwnedMemoryTransferService = nullptr;
 
-        LinkedList<ObjectBase> mDevices;
+        PerObjectType<LinkedList<ObjectBase>> mObjects;
         bool mDisconnected = false;
     };
 
diff --git a/src/dawn_wire/client/Device.cpp b/src/dawn_wire/client/Device.cpp
index 767e05b..af4cbfb 100644
--- a/src/dawn_wire/client/Device.cpp
+++ b/src/dawn_wire/client/Device.cpp
@@ -22,8 +22,8 @@
 
 namespace dawn_wire { namespace client {
 
-    Device::Device(Client* client, uint32_t initialRefcount, uint32_t initialId)
-        : ObjectBase(this, initialRefcount, initialId), mClient(client) {
+    Device::Device(Client* clientIn, uint32_t initialRefcount, uint32_t initialId)
+        : ObjectBase(clientIn, initialRefcount, initialId) {
 #if defined(DAWN_ENABLE_ASSERTS)
         mErrorCallback = [](WGPUErrorType, char const*, void*) {
             static bool calledOnce = false;
@@ -46,14 +46,14 @@
         };
 #endif  // DAWN_ENABLE_ASSERTS
         // Get the default queue for this device.
-        auto* allocation = mClient->QueueAllocator().New(this);
+        auto* allocation = client->QueueAllocator().New(client);
         mDefaultQueue = allocation->object.get();
 
         DeviceGetDefaultQueueCmd cmd;
         cmd.self = ToAPI(this);
         cmd.result = ObjectHandle{allocation->object->id, allocation->generation};
 
-        mClient->SerializeCommand(cmd);
+        client->SerializeCommand(cmd);
     }
 
     Device::~Device() {
@@ -77,27 +77,6 @@
                     "Device destroyed before callback", it.second.userdata);
             }
         }
-
-        DestroyAllObjects();
-    }
-
-    void Device::DestroyAllObjects() {
-        for (auto& objectList : mObjects) {
-            ObjectType objectType = static_cast<ObjectType>(&objectList - mObjects.data());
-            while (!objectList.empty()) {
-                ObjectBase* object = objectList.head()->value();
-
-                DestroyObjectCmd cmd;
-                cmd.objectType = objectType;
-                cmd.objectId = object->id;
-                mClient->SerializeCommand(cmd);
-                mClient->FreeObject(objectType, object);
-            }
-        }
-    }
-
-    Client* Device::GetClient() {
-        return mClient;
     }
 
     void Device::HandleError(WGPUErrorType errorType, const char* message) {
@@ -133,14 +112,6 @@
             it.second.callback(WGPUErrorType_DeviceLost, "Device lost", it.second.userdata);
         }
         mErrorScopes.clear();
-
-        for (auto& objectList : mObjects) {
-            LinkNode<ObjectBase>* object = objectList.head();
-            while (object != objectList.end()) {
-                object->value()->CancelCallbacksForDisconnect();
-                object = object->next();
-            }
-        }
     }
 
     void Device::SetUncapturedErrorCallback(WGPUErrorCallback errorCallback, void* errorUserdata) {
@@ -160,7 +131,7 @@
         cmd.self = ToAPI(this);
         cmd.filter = filter;
 
-        mClient->SerializeCommand(cmd);
+        client->SerializeCommand(cmd);
     }
 
     bool Device::PopErrorScope(WGPUErrorCallback callback, void* userdata) {
@@ -169,7 +140,7 @@
         }
         mErrorScopeStackSize--;
 
-        if (GetClient()->IsDisconnected()) {
+        if (client->IsDisconnected()) {
             callback(WGPUErrorType_DeviceLost, "GPU device disconnected", userdata);
             return true;
         }
@@ -183,7 +154,7 @@
         cmd.device = ToAPI(this);
         cmd.requestSerial = serial;
 
-        mClient->SerializeCommand(cmd);
+        client->SerializeCommand(cmd);
 
         return true;
     }
@@ -219,7 +190,7 @@
         cmd.self = ToAPI(this);
         cmd.type = type;
         cmd.message = message;
-        mClient->SerializeCommand(cmd);
+        client->SerializeCommand(cmd);
     }
 
     WGPUBuffer Device::CreateBuffer(const WGPUBufferDescriptor* descriptor) {
@@ -238,7 +209,7 @@
     void Device::CreateReadyComputePipeline(WGPUComputePipelineDescriptor const* descriptor,
                                             WGPUCreateReadyComputePipelineCallback callback,
                                             void* userdata) {
-        if (device->GetClient()->IsDisconnected()) {
+        if (client->IsDisconnected()) {
             return callback(WGPUCreateReadyPipelineStatus_DeviceLost, nullptr,
                             "GPU device disconnected", userdata);
         }
@@ -251,14 +222,14 @@
         ASSERT(mCreateReadyPipelineRequests.find(serial) == mCreateReadyPipelineRequests.end());
         cmd.requestSerial = serial;
 
-        auto* allocation = GetClient()->ComputePipelineAllocator().New(this);
+        auto* allocation = client->ComputePipelineAllocator().New(client);
         CreateReadyPipelineRequest request = {};
         request.createReadyComputePipelineCallback = callback;
         request.userdata = userdata;
         request.pipelineObjectID = allocation->object->id;
 
         cmd.pipelineObjectHandle = ObjectHandle{allocation->object->id, allocation->generation};
-        GetClient()->SerializeCommand(cmd);
+        client->SerializeCommand(cmd);
 
         mCreateReadyPipelineRequests[serial] = std::move(request);
     }
@@ -275,12 +246,12 @@
         mCreateReadyPipelineRequests.erase(requestIt);
 
         auto pipelineAllocation =
-            GetClient()->ComputePipelineAllocator().GetObject(request.pipelineObjectID);
+            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.
         if (status != WGPUCreateReadyPipelineStatus_Success) {
-            GetClient()->ComputePipelineAllocator().Free(pipelineAllocation);
+            client->ComputePipelineAllocator().Free(pipelineAllocation);
             request.createReadyComputePipelineCallback(status, nullptr, message, request.userdata);
             return true;
         }
@@ -294,7 +265,7 @@
     void Device::CreateReadyRenderPipeline(WGPURenderPipelineDescriptor const* descriptor,
                                            WGPUCreateReadyRenderPipelineCallback callback,
                                            void* userdata) {
-        if (GetClient()->IsDisconnected()) {
+        if (client->IsDisconnected()) {
             return callback(WGPUCreateReadyPipelineStatus_DeviceLost, nullptr,
                             "GPU device disconnected", userdata);
         }
@@ -306,14 +277,14 @@
         ASSERT(mCreateReadyPipelineRequests.find(serial) == mCreateReadyPipelineRequests.end());
         cmd.requestSerial = serial;
 
-        auto* allocation = GetClient()->RenderPipelineAllocator().New(this);
+        auto* allocation = client->RenderPipelineAllocator().New(client);
         CreateReadyPipelineRequest request = {};
         request.createReadyRenderPipelineCallback = callback;
         request.userdata = userdata;
         request.pipelineObjectID = allocation->object->id;
 
         cmd.pipelineObjectHandle = ObjectHandle(allocation->object->id, allocation->generation);
-        GetClient()->SerializeCommand(cmd);
+        client->SerializeCommand(cmd);
 
         mCreateReadyPipelineRequests[serial] = std::move(request);
     }
@@ -330,12 +301,12 @@
         mCreateReadyPipelineRequests.erase(requestIt);
 
         auto pipelineAllocation =
-            GetClient()->RenderPipelineAllocator().GetObject(request.pipelineObjectID);
+            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.
         if (status != WGPUCreateReadyPipelineStatus_Success) {
-            GetClient()->RenderPipelineAllocator().Free(pipelineAllocation);
+            client->RenderPipelineAllocator().Free(pipelineAllocation);
             request.createReadyRenderPipelineCallback(status, nullptr, message, request.userdata);
             return true;
         }
diff --git a/src/dawn_wire/client/Device.h b/src/dawn_wire/client/Device.h
index a0036a4..6862bb3 100644
--- a/src/dawn_wire/client/Device.h
+++ b/src/dawn_wire/client/Device.h
@@ -34,7 +34,6 @@
         Device(Client* client, uint32_t refcount, uint32_t id);
         ~Device();
 
-        Client* GetClient();
         void SetUncapturedErrorCallback(WGPUErrorCallback errorCallback, void* errorUserdata);
         void SetDeviceLostCallback(WGPUDeviceLostCallback errorCallback, void* errorUserdata);
         void InjectError(WGPUErrorType type, const char* message);
@@ -63,16 +62,9 @@
 
         WGPUQueue GetDefaultQueue();
 
-        template <typename T>
-        void TrackObject(T* object) {
-            mObjects[ObjectTypeToTypeEnum<T>::value].Append(object);
-        }
-
         void CancelCallbacksForDisconnect() override;
 
       private:
-        void DestroyAllObjects();
-
         struct ErrorScopeData {
             WGPUErrorCallback callback = nullptr;
             void* userdata = nullptr;
@@ -90,7 +82,6 @@
         std::map<uint64_t, CreateReadyPipelineRequest> mCreateReadyPipelineRequests;
         uint64_t mCreateReadyPipelineRequestSerial = 0;
 
-        Client* mClient = nullptr;
         WGPUErrorCallback mErrorCallback = nullptr;
         WGPUDeviceLostCallback mDeviceLostCallback = nullptr;
         bool mDidRunLostCallback = false;
@@ -98,8 +89,6 @@
         void* mDeviceLostUserdata = nullptr;
 
         Queue* mDefaultQueue = nullptr;
-
-        PerObjectType<LinkedList<ObjectBase>> mObjects;
     };
 
 }}  // namespace dawn_wire::client
diff --git a/src/dawn_wire/client/Fence.cpp b/src/dawn_wire/client/Fence.cpp
index a11d4e9..9bb40b3 100644
--- a/src/dawn_wire/client/Fence.cpp
+++ b/src/dawn_wire/client/Fence.cpp
@@ -15,7 +15,6 @@
 #include "dawn_wire/client/Fence.h"
 
 #include "dawn_wire/client/Client.h"
-#include "dawn_wire/client/Device.h"
 
 namespace dawn_wire { namespace client {
 
@@ -48,7 +47,7 @@
     void Fence::OnCompletion(uint64_t value,
                              WGPUFenceOnCompletionCallback callback,
                              void* userdata) {
-        if (device->GetClient()->IsDisconnected()) {
+        if (client->IsDisconnected()) {
             return callback(WGPUFenceCompletionStatus_DeviceLost, userdata);
         }
 
@@ -62,7 +61,7 @@
 
         mOnCompletionRequests[serial] = {callback, userdata};
 
-        this->device->GetClient()->SerializeCommand(cmd);
+        client->SerializeCommand(cmd);
     }
 
     void Fence::OnUpdateCompletedValueCallback(uint64_t value) {
diff --git a/src/dawn_wire/client/ObjectAllocator.h b/src/dawn_wire/client/ObjectAllocator.h
index bb0c4e4..4572752 100644
--- a/src/dawn_wire/client/ObjectAllocator.h
+++ b/src/dawn_wire/client/ObjectAllocator.h
@@ -25,14 +25,8 @@
 
 namespace dawn_wire { namespace client {
 
-    class Client;
-    class Device;
-
     template <typename T>
     class ObjectAllocator {
-        using ObjectOwner =
-            typename std::conditional<std::is_same<T, Device>::value, Client, Device>::type;
-
       public:
         struct ObjectAndSerial {
             ObjectAndSerial(std::unique_ptr<T> object, uint32_t generation)
@@ -47,10 +41,11 @@
             mObjects.emplace_back(nullptr, 0);
         }
 
-        ObjectAndSerial* New(ObjectOwner* owner) {
+        template <typename Client>
+        ObjectAndSerial* New(Client* client) {
             uint32_t id = GetNewId();
-            auto object = std::make_unique<T>(owner, 1, id);
-            owner->TrackObject(object.get());
+            auto object = std::make_unique<T>(client, 1, id);
+            client->TrackObject(object.get());
 
             if (id >= mObjects.size()) {
                 ASSERT(id == mObjects.size());
@@ -109,7 +104,6 @@
         uint32_t mCurrentId = 1;
         std::vector<uint32_t> mFreeIds;
         std::vector<ObjectAndSerial> mObjects;
-        Device* mDevice;
     };
 }}  // namespace dawn_wire::client
 
diff --git a/src/dawn_wire/client/ObjectBase.h b/src/dawn_wire/client/ObjectBase.h
index e317611..ac257c8 100644
--- a/src/dawn_wire/client/ObjectBase.h
+++ b/src/dawn_wire/client/ObjectBase.h
@@ -22,16 +22,16 @@
 
 namespace dawn_wire { namespace client {
 
-    class Device;
+    class Client;
 
-    // All non-Device objects of the client side have:
-    //  - A pointer to the device to get where to serialize commands
+    // All objects on the client side have:
+    //  - A pointer to the Client to get where to serialize commands
     //  - The external reference count
     //  - An ID that is used to refer to this object when talking with the server side
     //  - A next/prev pointer. They are part of a linked list of objects of the same type.
     struct ObjectBase : public LinkNode<ObjectBase> {
-        ObjectBase(Device* device, uint32_t refcount, uint32_t id)
-            : device(device), refcount(refcount), id(id) {
+        ObjectBase(Client* client, uint32_t refcount, uint32_t id)
+            : client(client), refcount(refcount), id(id) {
         }
 
         ~ObjectBase() {
@@ -41,7 +41,7 @@
         virtual void CancelCallbacksForDisconnect() {
         }
 
-        Device* const device;
+        Client* const client;
         uint32_t refcount;
         const uint32_t id;
     };
diff --git a/src/dawn_wire/client/Queue.cpp b/src/dawn_wire/client/Queue.cpp
index 52de14c..37ae3fa 100644
--- a/src/dawn_wire/client/Queue.cpp
+++ b/src/dawn_wire/client/Queue.cpp
@@ -20,13 +20,13 @@
 namespace dawn_wire { namespace client {
 
     WGPUFence Queue::CreateFence(WGPUFenceDescriptor const* descriptor) {
-        auto* allocation = device->GetClient()->FenceAllocator().New(device);
+        auto* allocation = client->FenceAllocator().New(client);
 
         QueueCreateFenceCmd cmd;
         cmd.self = ToAPI(this);
         cmd.result = ObjectHandle{allocation->object->id, allocation->generation};
         cmd.descriptor = descriptor;
-        device->GetClient()->SerializeCommand(cmd);
+        client->SerializeCommand(cmd);
 
         Fence* fence = allocation->object.get();
         fence->Initialize(this, descriptor);
@@ -46,7 +46,7 @@
         cmd.data = static_cast<const uint8_t*>(data);
         cmd.size = size;
 
-        device->GetClient()->SerializeCommand(cmd);
+        client->SerializeCommand(cmd);
     }
 
     void Queue::WriteTexture(const WGPUTextureCopyView* destination,
@@ -62,7 +62,7 @@
         cmd.dataLayout = dataLayout;
         cmd.writeSize = writeSize;
 
-        device->GetClient()->SerializeCommand(cmd);
+        client->SerializeCommand(cmd);
     }
 
 }}  // namespace dawn_wire::client