dawn::wire::client: Merge object allocators, add variadic Make.

This commit changes all the [Object]Allocators from the Client into a
PerType<ObjectStore> member that contains a bunch of ObjectStore acting
on ObjectBase.

Adds a new (template) member functions to the client, Make/Get/Free that
act on any object type, and update all the uses of previous
[Object]Allocator to use these new methods.

Also removes generated code that was generated per object type in favor
of using the type-generic ObjectAllocator.

Bug: dawn:1451
Change-Id: I6463b2fc4a827e3000c2a666abf08aa1a71c3b3b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/93141
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Corentin Wallez 2022-06-17 21:21:39 +00:00 committed by Dawn LUCI CQ
parent e57c296035
commit 0ebe86d1ca
16 changed files with 195 additions and 169 deletions

View File

@ -58,7 +58,7 @@ namespace dawn::wire::client {
//* For object creation, store the object ID the client will use for the result. //* For object creation, store the object ID the client will use for the result.
{% if method.return_type.category == "object" %} {% if method.return_type.category == "object" %}
auto* returnObject = self->GetClient()->{{method.return_type.name.CamelCase()}}Allocator().New(self->GetClient()); auto* returnObject = self->GetClient()->Make<{{method.return_type.name.CamelCase()}}>();
cmd.result = returnObject->GetWireHandle(); cmd.result = returnObject->GetWireHandle();
{% endif %} {% endif %}
@ -97,7 +97,7 @@ namespace dawn::wire::client {
Client* client = obj->GetClient(); Client* client = obj->GetClient();
client->SerializeCommand(cmd); client->SerializeCommand(cmd);
client->{{type.name.CamelCase()}}Allocator().Free(obj); client->Free(obj);
} }
void Client{{as_MethodSuffix(type.name, Name("reference"))}}({{cType}} cObj) { void Client{{as_MethodSuffix(type.name, Name("reference"))}}({{cType}} cObj) {

View File

@ -18,7 +18,6 @@
#include "dawn/wire/ChunkedCommandHandler.h" #include "dawn/wire/ChunkedCommandHandler.h"
#include "dawn/wire/WireCmd_autogen.h" #include "dawn/wire/WireCmd_autogen.h"
#include "dawn/wire/client/ApiObjects.h" #include "dawn/wire/client/ApiObjects.h"
#include "dawn/wire/client/ObjectAllocator.h"
namespace dawn::wire::client { namespace dawn::wire::client {
@ -27,25 +26,6 @@ namespace dawn::wire::client {
ClientBase() = default; ClientBase() = default;
~ClientBase() override = default; ~ClientBase() override = default;
{% for type in by_category["object"] %}
const ObjectAllocator<{{type.name.CamelCase()}}>& {{type.name.CamelCase()}}Allocator() const {
return m{{type.name.CamelCase()}}Allocator;
}
ObjectAllocator<{{type.name.CamelCase()}}>& {{type.name.CamelCase()}}Allocator() {
return m{{type.name.CamelCase()}}Allocator;
}
{% endfor %}
void FreeObject(ObjectType objectType, ObjectBase* obj) {
switch (objectType) {
{% for type in by_category["object"] %}
case ObjectType::{{type.name.CamelCase()}}:
m{{type.name.CamelCase()}}Allocator.Free(static_cast<{{type.name.CamelCase()}}*>(obj));
break;
{% endfor %}
}
}
private: private:
// Implementation of the ObjectIdProvider interface // Implementation of the ObjectIdProvider interface
{% for type in by_category["object"] %} {% for type in by_category["object"] %}
@ -63,10 +43,6 @@ namespace dawn::wire::client {
return WireResult::Success; return WireResult::Success;
} }
{% endfor %} {% endfor %}
{% for type in by_category["object"] %}
ObjectAllocator<{{type.name.CamelCase()}}> m{{type.name.CamelCase()}}Allocator;
{% endfor %}
}; };
} // namespace dawn::wire::client } // namespace dawn::wire::client

View File

@ -21,7 +21,7 @@ namespace dawn::wire::client {
{% for command in cmd_records["return command"] %} {% for command in cmd_records["return command"] %}
bool Client::Handle{{command.name.CamelCase()}}(DeserializeBuffer* deserializeBuffer) { bool Client::Handle{{command.name.CamelCase()}}(DeserializeBuffer* deserializeBuffer) {
Return{{command.name.CamelCase()}}Cmd cmd; Return{{command.name.CamelCase()}}Cmd cmd;
WireResult deserializeResult = cmd.Deserialize(deserializeBuffer, &mAllocator); WireResult deserializeResult = cmd.Deserialize(deserializeBuffer, &mWireCommandAllocator);
if (deserializeResult == WireResult::FatalError) { if (deserializeResult == WireResult::FatalError) {
return false; return false;
@ -32,7 +32,7 @@ namespace dawn::wire::client {
{% set name = as_varName(member.name) %} {% set name = as_varName(member.name) %}
{% if member.type.dict_name == "ObjectHandle" %} {% if member.type.dict_name == "ObjectHandle" %}
{{Type}}* {{name}} = {{Type}}Allocator().GetObject(cmd.{{name}}.id); {{Type}}* {{name}} = Get<{{Type}}>(cmd.{{name}}.id);
if ({{name}} != nullptr && {{name}}->GetWireGeneration() != cmd.{{name}}.generation) { if ({{name}} != nullptr && {{name}}->GetWireGeneration() != cmd.{{name}}.generation) {
{{name}} = nullptr; {{name}} = nullptr;
} }
@ -84,7 +84,7 @@ namespace dawn::wire::client {
if (!success) { if (!success) {
return nullptr; return nullptr;
} }
mAllocator.Reset(); mWireCommandAllocator.Reset();
} }
if (deserializeBuffer.AvailableSize() != 0) { if (deserializeBuffer.AvailableSize() != 0) {

View File

@ -90,9 +90,10 @@ dawn_component("wire") {
"client/Instance.h", "client/Instance.h",
"client/LimitsAndFeatures.cpp", "client/LimitsAndFeatures.cpp",
"client/LimitsAndFeatures.h", "client/LimitsAndFeatures.h",
"client/ObjectAllocator.h",
"client/ObjectBase.cpp", "client/ObjectBase.cpp",
"client/ObjectBase.h", "client/ObjectBase.h",
"client/ObjectStore.cpp",
"client/ObjectStore.h",
"client/QuerySet.cpp", "client/QuerySet.cpp",
"client/QuerySet.h", "client/QuerySet.h",
"client/Queue.cpp", "client/Queue.cpp",

View File

@ -63,7 +63,8 @@ target_sources(dawn_wire PRIVATE
"client/Instance.h" "client/Instance.h"
"client/LimitsAndFeatures.cpp" "client/LimitsAndFeatures.cpp"
"client/LimitsAndFeatures.h" "client/LimitsAndFeatures.h"
"client/ObjectAllocator.h" "client/ObjectStore.cpp"
"client/ObjectStore.h"
"client/ObjectBase.cpp" "client/ObjectBase.cpp"
"client/ObjectBase.h" "client/ObjectBase.h"
"client/QuerySet.cpp" "client/QuerySet.cpp"

View File

@ -71,7 +71,7 @@ void Adapter::RequestDevice(const WGPUDeviceDescriptor* descriptor,
return; return;
} }
Device* device = client->DeviceAllocator().New(client); Device* device = client->Make<Device>();
uint64_t serial = mRequestDeviceRequests.Add({callback, device->GetWireId(), userdata}); uint64_t serial = mRequestDeviceRequests.Add({callback, device->GetWireId(), userdata});
AdapterRequestDeviceCmd cmd; AdapterRequestDeviceCmd cmd;
@ -110,12 +110,12 @@ bool Adapter::OnRequestDeviceCallback(uint64_t requestSerial,
} }
Client* client = GetClient(); Client* client = GetClient();
Device* device = client->DeviceAllocator().GetObject(request.deviceObjectId); Device* device = client->Get<Device>(request.deviceObjectId);
// If the return status is a failure we should give a null device to the callback and // If the return status is a failure we should give a null device to the callback and
// free the allocation. // free the allocation.
if (status != WGPURequestDeviceStatus_Success) { if (status != WGPURequestDeviceStatus_Success) {
client->DeviceAllocator().Free(device); client->Free(device);
request.callback(status, nullptr, message, request.userdata); request.callback(status, nullptr, message, request.userdata);
return true; return true;
} }

View File

@ -74,7 +74,7 @@ WGPUBuffer Buffer::Create(Device* device, const WGPUBufferDescriptor* descriptor
// Create the buffer and send the creation command. // Create the buffer and send the creation command.
// This must happen after any potential device->CreateErrorBuffer() // This must happen after any potential device->CreateErrorBuffer()
// as server expects allocating ids to be monotonically increasing // as server expects allocating ids to be monotonically increasing
Buffer* buffer = wireClient->BufferAllocator().New(wireClient); Buffer* buffer = wireClient->Make<Buffer>();
buffer->mDevice = device; buffer->mDevice = device;
buffer->mDeviceIsAlive = device->GetAliveWeakPtr(); buffer->mDeviceIsAlive = device->GetAliveWeakPtr();
buffer->mSize = descriptor->size; buffer->mSize = descriptor->size;
@ -127,7 +127,7 @@ WGPUBuffer Buffer::Create(Device* device, const WGPUBufferDescriptor* descriptor
WGPUBuffer Buffer::CreateError(Device* device, const WGPUBufferDescriptor* descriptor) { WGPUBuffer Buffer::CreateError(Device* device, const WGPUBufferDescriptor* descriptor) {
Client* client = device->GetClient(); Client* client = device->GetClient();
Buffer* buffer = client->BufferAllocator().New(client); Buffer* buffer = client->Make<Buffer>();
buffer->mDevice = device; buffer->mDevice = device;
buffer->mDeviceIsAlive = device->GetAliveWeakPtr(); buffer->mDeviceIsAlive = device->GetAliveWeakPtr();
buffer->mSize = descriptor->size; buffer->mSize = descriptor->size;

View File

@ -61,7 +61,7 @@ void Client::DestroyAllObjects() {
cmd.objectType = ObjectType::Device; cmd.objectType = ObjectType::Device;
cmd.objectId = object->GetWireId(); cmd.objectId = object->GetWireId();
SerializeCommand(cmd); SerializeCommand(cmd);
FreeObject(ObjectType::Device, object); mObjectStores[ObjectType::Device].Free(object);
} }
for (auto& objectList : mObjects) { for (auto& objectList : mObjects) {
@ -76,13 +76,13 @@ void Client::DestroyAllObjects() {
cmd.objectType = objectType; cmd.objectType = objectType;
cmd.objectId = object->GetWireId(); cmd.objectId = object->GetWireId();
SerializeCommand(cmd); SerializeCommand(cmd);
FreeObject(objectType, object); mObjectStores[objectType].Free(object);
} }
} }
} }
ReservedTexture Client::ReserveTexture(WGPUDevice device) { ReservedTexture Client::ReserveTexture(WGPUDevice device) {
Texture* texture = TextureAllocator().New(this); Texture* texture = Make<Texture>();
ReservedTexture result; ReservedTexture result;
result.texture = ToAPI(texture); result.texture = ToAPI(texture);
@ -94,7 +94,7 @@ ReservedTexture Client::ReserveTexture(WGPUDevice device) {
} }
ReservedSwapChain Client::ReserveSwapChain(WGPUDevice device) { ReservedSwapChain Client::ReserveSwapChain(WGPUDevice device) {
SwapChain* swapChain = SwapChainAllocator().New(this); SwapChain* swapChain = Make<SwapChain>();
ReservedSwapChain result; ReservedSwapChain result;
result.swapchain = ToAPI(swapChain); result.swapchain = ToAPI(swapChain);
@ -106,7 +106,7 @@ ReservedSwapChain Client::ReserveSwapChain(WGPUDevice device) {
} }
ReservedDevice Client::ReserveDevice() { ReservedDevice Client::ReserveDevice() {
Device* device = DeviceAllocator().New(this); Device* device = Make<Device>();
ReservedDevice result; ReservedDevice result;
result.device = ToAPI(device); result.device = ToAPI(device);
@ -116,7 +116,7 @@ ReservedDevice Client::ReserveDevice() {
} }
ReservedInstance Client::ReserveInstance() { ReservedInstance Client::ReserveInstance() {
Instance* instance = InstanceAllocator().New(this); Instance* instance = Make<Instance>();
ReservedInstance result; ReservedInstance result;
result.instance = ToAPI(instance); result.instance = ToAPI(instance);
@ -126,19 +126,19 @@ ReservedInstance Client::ReserveInstance() {
} }
void Client::ReclaimTextureReservation(const ReservedTexture& reservation) { void Client::ReclaimTextureReservation(const ReservedTexture& reservation) {
TextureAllocator().Free(FromAPI(reservation.texture)); Free(FromAPI(reservation.texture));
} }
void Client::ReclaimSwapChainReservation(const ReservedSwapChain& reservation) { void Client::ReclaimSwapChainReservation(const ReservedSwapChain& reservation) {
SwapChainAllocator().Free(FromAPI(reservation.swapchain)); Free(FromAPI(reservation.swapchain));
} }
void Client::ReclaimDeviceReservation(const ReservedDevice& reservation) { void Client::ReclaimDeviceReservation(const ReservedDevice& reservation) {
DeviceAllocator().Free(FromAPI(reservation.device)); Free(FromAPI(reservation.device));
} }
void Client::ReclaimInstanceReservation(const ReservedInstance& reservation) { void Client::ReclaimInstanceReservation(const ReservedInstance& reservation) {
InstanceAllocator().Free(FromAPI(reservation.instance)); Free(FromAPI(reservation.instance));
} }
void Client::Disconnect() { void Client::Disconnect() {
@ -165,4 +165,8 @@ bool Client::IsDisconnected() const {
return mDisconnected; return mDisconnected;
} }
void Client::Free(ObjectBase* obj, ObjectType type) {
mObjectStores[type].Free(obj);
}
} // namespace dawn::wire::client } // namespace dawn::wire::client

View File

@ -16,6 +16,7 @@
#define SRC_DAWN_WIRE_CLIENT_CLIENT_H_ #define SRC_DAWN_WIRE_CLIENT_CLIENT_H_
#include <memory> #include <memory>
#include <utility>
#include "dawn/common/LinkedList.h" #include "dawn/common/LinkedList.h"
#include "dawn/common/NonCopyable.h" #include "dawn/common/NonCopyable.h"
@ -26,6 +27,7 @@
#include "dawn/wire/WireCmd_autogen.h" #include "dawn/wire/WireCmd_autogen.h"
#include "dawn/wire/WireDeserializeAllocator.h" #include "dawn/wire/WireDeserializeAllocator.h"
#include "dawn/wire/client/ClientBase_autogen.h" #include "dawn/wire/client/ClientBase_autogen.h"
#include "dawn/wire/client/ObjectStore.h"
namespace dawn::wire::client { namespace dawn::wire::client {
@ -37,6 +39,32 @@ class Client : public ClientBase {
Client(CommandSerializer* serializer, MemoryTransferService* memoryTransferService); Client(CommandSerializer* serializer, MemoryTransferService* memoryTransferService);
~Client() override; ~Client() override;
// Make<T>(arg1, arg2, arg3) creates a new T, calling a constructor of the form:
//
// T::T(ObjectBaseParams, arg1, arg2, arg3)
template <typename T, typename... Args>
T* Make(Args&&... args) {
constexpr ObjectType type = ObjectTypeToTypeEnum<T>::value;
ObjectBaseParams params = {this, mObjectStores[type].ReserveHandle()};
T* object = new T(params, std::forward<Args>(args)...);
mObjects[type].Append(object);
mObjectStores[type].Insert(std::unique_ptr<T>(object));
return object;
}
template <typename T>
void Free(T* obj) {
Free(obj, ObjectTypeToTypeEnum<T>::value);
}
void Free(ObjectBase* obj, ObjectType type);
template <typename T>
T* Get(ObjectId id) {
return static_cast<T*>(mObjectStores[ObjectTypeToTypeEnum<T>::value].Get(id));
}
// ChunkedCommandHandler implementation // ChunkedCommandHandler implementation
const volatile char* HandleCommandsImpl(const volatile char* commands, size_t size) override; const volatile char* HandleCommandsImpl(const volatile char* commands, size_t size) override;
@ -67,21 +95,16 @@ class Client : public ClientBase {
void Disconnect(); void Disconnect();
bool IsDisconnected() const; bool IsDisconnected() const;
template <typename T>
void TrackObject(T* object) {
mObjects[ObjectTypeToTypeEnum<T>::value].Append(object);
}
private: private:
void DestroyAllObjects(); void DestroyAllObjects();
#include "dawn/wire/client/ClientPrototypes_autogen.inc" #include "dawn/wire/client/ClientPrototypes_autogen.inc"
ChunkedCommandSerializer mSerializer; ChunkedCommandSerializer mSerializer;
WireDeserializeAllocator mAllocator; WireDeserializeAllocator mWireCommandAllocator;
PerObjectType<ObjectStore> mObjectStores;
MemoryTransferService* mMemoryTransferService = nullptr; MemoryTransferService* mMemoryTransferService = nullptr;
std::unique_ptr<MemoryTransferService> mOwnedMemoryTransferService = nullptr; std::unique_ptr<MemoryTransferService> mOwnedMemoryTransferService = nullptr;
PerObjectType<LinkedList<ObjectBase>> mObjects; PerObjectType<LinkedList<ObjectBase>> mObjects;
bool mDisconnected = false; bool mDisconnected = false;
}; };

View File

@ -20,7 +20,6 @@
#include "dawn/common/Log.h" #include "dawn/common/Log.h"
#include "dawn/wire/client/ApiObjects_autogen.h" #include "dawn/wire/client/ApiObjects_autogen.h"
#include "dawn/wire/client/Client.h" #include "dawn/wire/client/Client.h"
#include "dawn/wire/client/ObjectAllocator.h"
namespace dawn::wire::client { namespace dawn::wire::client {
@ -221,7 +220,7 @@ WGPUQueue Device::GetQueue() {
if (mQueue == nullptr) { if (mQueue == nullptr) {
// Get the primary queue for this device. // Get the primary queue for this device.
Client* client = GetClient(); Client* client = GetClient();
mQueue = client->QueueAllocator().New(client); mQueue = client->Make<Queue>();
DeviceGetQueueCmd cmd; DeviceGetQueueCmd cmd;
cmd.self = ToAPI(this); cmd.self = ToAPI(this);
@ -243,7 +242,7 @@ void Device::CreateComputePipelineAsync(WGPUComputePipelineDescriptor const* des
"GPU device disconnected", userdata); "GPU device disconnected", userdata);
} }
ComputePipeline* pipeline = client->ComputePipelineAllocator().New(client); ComputePipeline* pipeline = client->Make<ComputePipeline>();
CreatePipelineAsyncRequest request = {}; CreatePipelineAsyncRequest request = {};
request.createComputePipelineAsyncCallback = callback; request.createComputePipelineAsyncCallback = callback;
@ -270,20 +269,17 @@ bool Device::OnCreateComputePipelineAsyncCallback(uint64_t requestSerial,
} }
Client* client = GetClient(); Client* client = GetClient();
auto* pipelineAllocation = ComputePipeline* pipeline = client->Get<ComputePipeline>(request.pipelineObjectID);
client->ComputePipelineAllocator().GetObject(request.pipelineObjectID);
// If the return status is a failure we should give a null pipeline to the callback and // If the return status is a failure we should give a null pipeline to the callback and
// free the allocation. // free the allocation.
if (status != WGPUCreatePipelineAsyncStatus_Success) { if (status != WGPUCreatePipelineAsyncStatus_Success) {
client->ComputePipelineAllocator().Free(pipelineAllocation); client->Free(pipeline);
request.createComputePipelineAsyncCallback(status, nullptr, message, request.userdata); request.createComputePipelineAsyncCallback(status, nullptr, message, request.userdata);
return true; return true;
} }
WGPUComputePipeline pipeline = reinterpret_cast<WGPUComputePipeline>(pipelineAllocation); request.createComputePipelineAsyncCallback(status, ToAPI(pipeline), message, request.userdata);
request.createComputePipelineAsyncCallback(status, pipeline, message, request.userdata);
return true; return true;
} }
@ -296,7 +292,7 @@ void Device::CreateRenderPipelineAsync(WGPURenderPipelineDescriptor const* descr
"GPU device disconnected", userdata); "GPU device disconnected", userdata);
} }
RenderPipeline* pipeline = client->RenderPipelineAllocator().New(client); RenderPipeline* pipeline = client->Make<RenderPipeline>();
CreatePipelineAsyncRequest request = {}; CreatePipelineAsyncRequest request = {};
request.createRenderPipelineAsyncCallback = callback; request.createRenderPipelineAsyncCallback = callback;
@ -323,20 +319,17 @@ bool Device::OnCreateRenderPipelineAsyncCallback(uint64_t requestSerial,
} }
Client* client = GetClient(); Client* client = GetClient();
auto* pipelineAllocation = RenderPipeline* pipeline = client->Get<RenderPipeline>(request.pipelineObjectID);
client->RenderPipelineAllocator().GetObject(request.pipelineObjectID);
// If the return status is a failure we should give a null pipeline to the callback and // If the return status is a failure we should give a null pipeline to the callback and
// free the allocation. // free the allocation.
if (status != WGPUCreatePipelineAsyncStatus_Success) { if (status != WGPUCreatePipelineAsyncStatus_Success) {
client->RenderPipelineAllocator().Free(pipelineAllocation); client->Free(pipeline);
request.createRenderPipelineAsyncCallback(status, nullptr, message, request.userdata); request.createRenderPipelineAsyncCallback(status, nullptr, message, request.userdata);
return true; return true;
} }
WGPURenderPipeline pipeline = reinterpret_cast<WGPURenderPipeline>(pipelineAllocation); request.createRenderPipelineAsyncCallback(status, ToAPI(pipeline), message, request.userdata);
request.createRenderPipelineAsyncCallback(status, pipeline, message, request.userdata);
return true; return true;
} }

View File

@ -41,7 +41,7 @@ void Instance::RequestAdapter(const WGPURequestAdapterOptions* options,
return; return;
} }
Adapter* adapter = client->AdapterAllocator().New(client); Adapter* adapter = client->Make<Adapter>();
uint64_t serial = mRequestAdapterRequests.Add({callback, adapter->GetWireId(), userdata}); uint64_t serial = mRequestAdapterRequests.Add({callback, adapter->GetWireId(), userdata});
InstanceRequestAdapterCmd cmd; InstanceRequestAdapterCmd cmd;
@ -82,12 +82,12 @@ bool Instance::OnRequestAdapterCallback(uint64_t requestSerial,
} }
Client* client = GetClient(); Client* client = GetClient();
Adapter* adapter = client->AdapterAllocator().GetObject(request.adapterObjectId); Adapter* adapter = client->Get<Adapter>(request.adapterObjectId);
// If the return status is a failure we should give a null adapter to the callback and // If the return status is a failure we should give a null adapter to the callback and
// free the allocation. // free the allocation.
if (status != WGPURequestAdapterStatus_Success) { if (status != WGPURequestAdapterStatus_Success) {
client->AdapterAllocator().Free(adapter); client->Free(adapter);
request.callback(status, nullptr, message, request.userdata); request.callback(status, nullptr, message, request.userdata);
return true; return true;
} }

View File

@ -1,95 +0,0 @@
// Copyright 2019 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 SRC_DAWN_WIRE_CLIENT_OBJECTALLOCATOR_H_
#define SRC_DAWN_WIRE_CLIENT_OBJECTALLOCATOR_H_
#include <limits>
#include <memory>
#include <utility>
#include <vector>
#include "dawn/common/Assert.h"
#include "dawn/common/Compiler.h"
#include "dawn/wire/WireCmd_autogen.h"
#include "dawn/wire/client/ObjectBase.h"
namespace dawn::wire::client {
template <typename T>
class ObjectAllocator {
public:
ObjectAllocator() {
// ID 0 is nullptr
mObjects.emplace_back(nullptr);
}
template <typename Client>
T* New(Client* client) {
ObjectHandle handle = GetFreeHandle();
ObjectBaseParams params = {client, handle};
auto object = std::make_unique<T>(params);
client->TrackObject(object.get());
if (handle.id >= mObjects.size()) {
ASSERT(handle.id == mObjects.size());
mObjects.emplace_back(std::move(object));
} else {
// The generation should never overflow. We don't recycle ObjectIds that would
// overflow their next generation.
ASSERT(handle.generation != 0);
ASSERT(mObjects[handle.id] == nullptr);
mObjects[handle.id] = std::move(object);
}
return mObjects[handle.id].get();
}
void Free(T* obj) {
ASSERT(obj->IsInList());
// The wire reuses ID for objects to keep them in a packed array starting from 0.
// To avoid issues with asynchronous server->client communication referring to an ID that's
// already reused, each handle also has a generation that's increment by one on each reuse.
// Avoid overflows by only reusing the ID if the increment of the generation won't overflow.
ObjectHandle currentHandle = obj->GetWireHandle();
if (DAWN_LIKELY(currentHandle.generation != std::numeric_limits<ObjectGeneration>::max())) {
mFreeHandles.push_back({currentHandle.id, currentHandle.generation + 1});
}
mObjects[currentHandle.id] = nullptr;
}
T* GetObject(uint32_t id) {
if (id >= mObjects.size()) {
return nullptr;
}
return mObjects[id].get();
}
private:
ObjectHandle GetFreeHandle() {
if (mFreeHandles.empty()) {
return {mCurrentId++, 0};
}
ObjectHandle handle = mFreeHandles.back();
mFreeHandles.pop_back();
return handle;
}
// 0 is an ID reserved to represent nullptr
uint32_t mCurrentId = 1;
std::vector<ObjectHandle> mFreeHandles;
std::vector<std::unique_ptr<T>> mObjects;
};
} // namespace dawn::wire::client
#endif // SRC_DAWN_WIRE_CLIENT_OBJECTALLOCATOR_H_

View File

@ -0,0 +1,72 @@
// Copyright 2022 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/ObjectStore.h"
#include <limits>
#include <utility>
namespace dawn::wire::client {
ObjectStore::ObjectStore() {
// ID 0 is nullptr
mObjects.emplace_back(nullptr);
mCurrentId = 1;
}
ObjectHandle ObjectStore::ReserveHandle() {
if (mFreeHandles.empty()) {
return {mCurrentId++, 0};
}
ObjectHandle handle = mFreeHandles.back();
mFreeHandles.pop_back();
return handle;
}
void ObjectStore::Insert(std::unique_ptr<ObjectBase> obj) {
ObjectId id = obj->GetWireId();
if (id >= mObjects.size()) {
ASSERT(id == mObjects.size());
mObjects.emplace_back(std::move(obj));
} else {
// The generation should never overflow. We don't recycle ObjectIds that would
// overflow their next generation.
ASSERT(obj->GetWireGeneration() != 0);
ASSERT(mObjects[id] == nullptr);
mObjects[id] = std::move(obj);
}
}
void ObjectStore::Free(ObjectBase* obj) {
ASSERT(obj->IsInList());
// The wire reuses ID for objects to keep them in a packed array starting from 0.
// To avoid issues with asynchronous server->client communication referring to an ID that's
// already reused, each handle also has a generation that's increment by one on each reuse.
// Avoid overflows by only reusing the ID if the increment of the generation won't overflow.
const ObjectHandle& currentHandle = obj->GetWireHandle();
if (DAWN_LIKELY(currentHandle.generation != std::numeric_limits<ObjectGeneration>::max())) {
mFreeHandles.push_back({currentHandle.id, currentHandle.generation + 1});
}
mObjects[currentHandle.id] = nullptr;
}
ObjectBase* ObjectStore::Get(ObjectId id) const {
if (id >= mObjects.size()) {
return nullptr;
}
return mObjects[id].get();
}
} // namespace dawn::wire::client

View File

@ -0,0 +1,51 @@
// Copyright 2019 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 SRC_DAWN_WIRE_CLIENT_OBJECTSTORE_H_
#define SRC_DAWN_WIRE_CLIENT_OBJECTSTORE_H_
#include <memory>
#include <vector>
#include "dawn/wire/client/ObjectBase.h"
namespace dawn::wire::client {
class Client;
// A helper class used in Client, ObjectStore owns the association of some ObjectBase and
// ObjectHandles. The lifetime of the ObjectBase is then owned by the ObjectStore, destruction
// happening when Free is called.
//
// Since the wire has one "ID" namespace per type of object, each ObjectStore should contain a
// single type of objects. However no templates are used because Client wraps ObjectStore and is
// type-generic, so ObjectStore is type-erased to only work on ObjectBase.
class ObjectStore {
public:
ObjectStore();
ObjectHandle ReserveHandle();
void Insert(std::unique_ptr<ObjectBase> obj);
void Free(ObjectBase* obj);
ObjectBase* Get(ObjectId id) const;
private:
uint32_t mCurrentId;
std::vector<ObjectHandle> mFreeHandles;
std::vector<std::unique_ptr<ObjectBase>> mObjects;
};
} // namespace dawn::wire::client
#endif // SRC_DAWN_WIRE_CLIENT_OBJECTSTORE_H_

View File

@ -22,7 +22,7 @@ namespace dawn::wire::client {
// static // static
WGPUQuerySet QuerySet::Create(Device* device, const WGPUQuerySetDescriptor* descriptor) { WGPUQuerySet QuerySet::Create(Device* device, const WGPUQuerySetDescriptor* descriptor) {
Client* wireClient = device->GetClient(); Client* wireClient = device->GetClient();
QuerySet* querySet = wireClient->QuerySetAllocator().New(wireClient); QuerySet* querySet = wireClient->Make<QuerySet>();
// Copy over descriptor data for reflection. // Copy over descriptor data for reflection.
querySet->mType = descriptor->type; querySet->mType = descriptor->type;

View File

@ -22,7 +22,7 @@ namespace dawn::wire::client {
// static // static
WGPUTexture Texture::Create(Device* device, const WGPUTextureDescriptor* descriptor) { WGPUTexture Texture::Create(Device* device, const WGPUTextureDescriptor* descriptor) {
Client* wireClient = device->GetClient(); Client* wireClient = device->GetClient();
Texture* texture = wireClient->TextureAllocator().New(wireClient); Texture* texture = wireClient->Make<Texture>();
// Copy over descriptor data for reflection. // Copy over descriptor data for reflection.
texture->mSize = descriptor->size; texture->mSize = descriptor->size;