| //* Copyright 2017 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/TypeTraits_autogen.h" |
| #include "dawn_wire/Wire.h" |
| #include "dawn_wire/WireCmd_autogen.h" |
| #include "dawn_wire/WireDeserializeAllocator.h" |
| |
| #include "common/Assert.h" |
| |
| #include <algorithm> |
| #include <cstdlib> |
| #include <cstring> |
| #include <map> |
| #include <memory> |
| #include <vector> |
| |
| namespace dawn_wire { |
| |
| namespace server { |
| class Server; |
| |
| struct MapUserdata { |
| Server* server; |
| ObjectHandle buffer; |
| uint32_t requestSerial; |
| uint32_t size; |
| bool isWrite; |
| }; |
| |
| struct FenceCompletionUserdata { |
| Server* server; |
| ObjectHandle fence; |
| uint64_t value; |
| }; |
| |
| template <typename T> |
| struct ObjectDataBase { |
| //* The backend-provided handle and serial to this object. |
| T handle; |
| uint32_t serial = 0; |
| |
| //* Used by the error-propagation mechanism to know if this object is an error. |
| //* TODO(cwallez@chromium.org): this is doubling the memory usage of |
| //* std::vector<ObjectDataBase> consider making it a special marker value in handle instead. |
| bool valid; |
| //* Whether this object has been allocated, used by the KnownObjects queries |
| //* TODO(cwallez@chromium.org): make this an internal bit vector in KnownObjects. |
| bool allocated; |
| }; |
| |
| //* Stores what the backend knows about the type. |
| template<typename T, bool IsBuilder = IsBuilderType<T>::value> |
| struct ObjectData : public ObjectDataBase<T> { |
| }; |
| |
| |
| template <typename T> |
| struct ObjectData<T, true> : public ObjectDataBase<T> { |
| ObjectHandle builtObject = ObjectHandle{0, 0}; |
| }; |
| |
| template <> |
| struct ObjectData<dawnBuffer, false> : public ObjectDataBase<dawnBuffer> { |
| void* mappedData = nullptr; |
| size_t mappedDataSize = 0; |
| }; |
| |
| //* Keeps track of the mapping between client IDs and backend objects. |
| template<typename T> |
| class KnownObjects { |
| public: |
| using Data = ObjectData<T>; |
| |
| KnownObjects() { |
| //* Pre-allocate ID 0 to refer to the null handle. |
| Data nullObject; |
| nullObject.handle = nullptr; |
| nullObject.valid = true; |
| nullObject.allocated = true; |
| mKnown.push_back(nullObject); |
| } |
| |
| //* Get a backend objects for a given client ID. |
| //* Returns nullptr if the ID hasn't previously been allocated. |
| const Data* Get(uint32_t id) const { |
| if (id >= mKnown.size()) { |
| return nullptr; |
| } |
| |
| const Data* data = &mKnown[id]; |
| |
| if (!data->allocated) { |
| return nullptr; |
| } |
| |
| return data; |
| } |
| Data* Get(uint32_t id) { |
| if (id >= mKnown.size()) { |
| return nullptr; |
| } |
| |
| Data* data = &mKnown[id]; |
| |
| if (!data->allocated) { |
| return nullptr; |
| } |
| |
| return data; |
| } |
| |
| //* Allocates the data for a given ID and returns it. |
| //* Returns nullptr if the ID is already allocated, or too far ahead. |
| //* Invalidates all the Data* |
| Data* Allocate(uint32_t id) { |
| if (id > mKnown.size()) { |
| return nullptr; |
| } |
| |
| Data data; |
| data.allocated = true; |
| data.valid = false; |
| data.handle = nullptr; |
| |
| if (id >= mKnown.size()) { |
| mKnown.push_back(data); |
| return &mKnown.back(); |
| } |
| |
| if (mKnown[id].allocated) { |
| return nullptr; |
| } |
| |
| mKnown[id] = data; |
| return &mKnown[id]; |
| } |
| |
| //* Marks an ID as deallocated |
| void Free(uint32_t id) { |
| ASSERT(id < mKnown.size()); |
| mKnown[id].allocated = false; |
| } |
| |
| std::vector<T> AcquireAllHandles() { |
| std::vector<T> objects; |
| for (Data& data : mKnown) { |
| if (data.allocated && data.handle != nullptr) { |
| objects.push_back(data.handle); |
| data.valid = false; |
| data.allocated = false; |
| data.handle = nullptr; |
| } |
| } |
| |
| return objects; |
| } |
| |
| private: |
| std::vector<Data> mKnown; |
| }; |
| |
| // ObjectIds are lost in deserialization. Store the ids of deserialized |
| // objects here so they can be used in command handlers. This is useful |
| // for creating ReturnWireCmds which contain client ids |
| template <typename T> |
| class ObjectIdLookupTable { |
| public: |
| void Store(T key, ObjectId id) { |
| mTable[key] = id; |
| } |
| |
| // Return the cached ObjectId, or 0 (null handle) |
| ObjectId Get(T key) const { |
| const auto it = mTable.find(key); |
| if (it != mTable.end()) { |
| return it->second; |
| } |
| return 0; |
| } |
| |
| void Remove(T key) { |
| auto it = mTable.find(key); |
| if (it != mTable.end()) { |
| mTable.erase(it); |
| } |
| } |
| |
| private: |
| std::map<T, ObjectId> mTable; |
| }; |
| |
| void ForwardDeviceErrorToServer(const char* message, dawnCallbackUserdata userdata); |
| |
| {% for type in by_category["object"] if type.is_builder%} |
| void Forward{{type.name.CamelCase()}}ToClient(dawnBuilderErrorStatus status, const char* message, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2); |
| {% endfor %} |
| |
| void ForwardBufferMapReadAsync(dawnBufferMapAsyncStatus status, const void* ptr, dawnCallbackUserdata userdata); |
| void ForwardBufferMapWriteAsync(dawnBufferMapAsyncStatus status, void* ptr, dawnCallbackUserdata userdata); |
| void ForwardFenceCompletedValue(dawnFenceCompletionStatus status, |
| dawnCallbackUserdata userdata); |
| |
| class Server : public CommandHandler, public ObjectIdResolver { |
| public: |
| Server(dawnDevice device, const dawnProcTable& procs, CommandSerializer* serializer) |
| : mProcs(procs), mSerializer(serializer) { |
| //* The client-server knowledge is bootstrapped with device 1. |
| auto* deviceData = mKnownDevice.Allocate(1); |
| deviceData->handle = device; |
| deviceData->valid = true; |
| |
| auto userdata = static_cast<dawnCallbackUserdata>(reinterpret_cast<intptr_t>(this)); |
| procs.deviceSetErrorCallback(device, ForwardDeviceErrorToServer, userdata); |
| } |
| |
| ~Server() override { |
| //* Free all objects when the server is destroyed |
| {% for type in by_category["object"] if type.name.canonical_case() != "device" %} |
| { |
| std::vector<{{as_cType(type.name)}}> handles = mKnown{{type.name.CamelCase()}}.AcquireAllHandles(); |
| for ({{as_cType(type.name)}} handle : handles) { |
| mProcs.{{as_varName(type.name, Name("release"))}}(handle); |
| } |
| } |
| {% endfor %} |
| } |
| |
| void OnDeviceError(const char* message) { |
| ReturnDeviceErrorCallbackCmd cmd; |
| cmd.message = message; |
| |
| size_t requiredSize = cmd.GetRequiredSize(); |
| char* allocatedBuffer = static_cast<char*>(GetCmdSpace(requiredSize)); |
| cmd.Serialize(allocatedBuffer); |
| } |
| |
| {% for type in by_category["object"] if type.is_builder%} |
| {% set Type = type.name.CamelCase() %} |
| void On{{Type}}Error(dawnBuilderErrorStatus status, const char* message, uint32_t id, uint32_t serial) { |
| auto* builder = mKnown{{Type}}.Get(id); |
| |
| if (builder == nullptr || builder->serial != serial) { |
| return; |
| } |
| |
| if (status != DAWN_BUILDER_ERROR_STATUS_SUCCESS) { |
| builder->valid = false; |
| } |
| |
| if (status != DAWN_BUILDER_ERROR_STATUS_UNKNOWN) { |
| //* Unknown is the only status that can be returned without a call to GetResult |
| //* so we are guaranteed to have created an object. |
| ASSERT(builder->builtObject.id != 0); |
| |
| Return{{Type}}ErrorCallbackCmd cmd; |
| cmd.builtObject = builder->builtObject; |
| cmd.status = status; |
| cmd.message = message; |
| |
| size_t requiredSize = cmd.GetRequiredSize(); |
| char* allocatedBuffer = static_cast<char*>(GetCmdSpace(requiredSize)); |
| cmd.Serialize(allocatedBuffer); |
| } |
| } |
| {% endfor %} |
| |
| void OnMapReadAsyncCallback(dawnBufferMapAsyncStatus status, const void* ptr, MapUserdata* userdata) { |
| std::unique_ptr<MapUserdata> data(userdata); |
| |
| // Skip sending the callback if the buffer has already been destroyed. |
| auto* bufferData = mKnownBuffer.Get(data->buffer.id); |
| if (bufferData == nullptr || bufferData->serial != data->buffer.serial) { |
| return; |
| } |
| |
| ReturnBufferMapReadAsyncCallbackCmd cmd; |
| cmd.buffer = data->buffer; |
| cmd.requestSerial = data->requestSerial; |
| cmd.status = status; |
| cmd.dataLength = 0; |
| cmd.data = reinterpret_cast<const uint8_t*>(ptr); |
| |
| if (status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) { |
| cmd.dataLength = data->size; |
| } |
| |
| size_t requiredSize = cmd.GetRequiredSize(); |
| char* allocatedBuffer = static_cast<char*>(GetCmdSpace(requiredSize)); |
| cmd.Serialize(allocatedBuffer); |
| } |
| |
| void OnMapWriteAsyncCallback(dawnBufferMapAsyncStatus status, void* ptr, MapUserdata* userdata) { |
| std::unique_ptr<MapUserdata> data(userdata); |
| |
| // Skip sending the callback if the buffer has already been destroyed. |
| auto* bufferData = mKnownBuffer.Get(data->buffer.id); |
| if (bufferData == nullptr || bufferData->serial != data->buffer.serial) { |
| return; |
| } |
| |
| ReturnBufferMapWriteAsyncCallbackCmd cmd; |
| cmd.buffer = data->buffer; |
| cmd.requestSerial = data->requestSerial; |
| cmd.status = status; |
| |
| size_t requiredSize = cmd.GetRequiredSize(); |
| char* allocatedBuffer = static_cast<char*>(GetCmdSpace(requiredSize)); |
| cmd.Serialize(allocatedBuffer); |
| |
| if (status == DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS) { |
| bufferData->mappedData = ptr; |
| bufferData->mappedDataSize = data->size; |
| } |
| } |
| |
| void OnFenceCompletedValueUpdated(FenceCompletionUserdata* userdata) { |
| std::unique_ptr<FenceCompletionUserdata> data(userdata); |
| |
| ReturnFenceUpdateCompletedValueCmd cmd; |
| cmd.fence = data->fence; |
| cmd.value = data->value; |
| |
| size_t requiredSize = cmd.GetRequiredSize(); |
| char* allocatedBuffer = static_cast<char*>(GetCmdSpace(requiredSize)); |
| cmd.Serialize(allocatedBuffer); |
| } |
| |
| const char* HandleCommands(const char* commands, size_t size) override { |
| mProcs.deviceTick(mKnownDevice.Get(1)->handle); |
| |
| while (size >= sizeof(WireCmd)) { |
| WireCmd cmdId = *reinterpret_cast<const WireCmd*>(commands); |
| |
| bool success = false; |
| switch (cmdId) { |
| {% for command in cmd_records["command"] %} |
| case WireCmd::{{command.name.CamelCase()}}: |
| success = Handle{{command.name.CamelCase()}}(&commands, &size); |
| break; |
| {% endfor %} |
| default: |
| success = false; |
| } |
| |
| if (!success) { |
| return nullptr; |
| } |
| mAllocator.Reset(); |
| } |
| |
| if (size != 0) { |
| return nullptr; |
| } |
| |
| return commands; |
| } |
| |
| private: |
| dawnProcTable mProcs; |
| CommandSerializer* mSerializer = nullptr; |
| |
| WireDeserializeAllocator mAllocator; |
| |
| void* GetCmdSpace(size_t size) { |
| return mSerializer->GetCmdSpace(size); |
| } |
| |
| // Implementation of the ObjectIdResolver interface |
| {% for type in by_category["object"] %} |
| DeserializeResult GetFromId(ObjectId id, {{as_cType(type.name)}}* out) const final { |
| auto data = mKnown{{type.name.CamelCase()}}.Get(id); |
| if (data == nullptr) { |
| return DeserializeResult::FatalError; |
| } |
| |
| *out = data->handle; |
| if (data->valid) { |
| return DeserializeResult::Success; |
| } else { |
| return DeserializeResult::ErrorObject; |
| } |
| } |
| |
| DeserializeResult GetOptionalFromId(ObjectId id, {{as_cType(type.name)}}* out) const final { |
| if (id == 0) { |
| *out = nullptr; |
| return DeserializeResult::Success; |
| } |
| |
| return GetFromId(id, out); |
| } |
| {% endfor %} |
| |
| //* The list of known IDs for each object type. |
| {% for type in by_category["object"] %} |
| KnownObjects<{{as_cType(type.name)}}> mKnown{{type.name.CamelCase()}}; |
| {% endfor %} |
| |
| {% for type in by_category["object"] if type.name.CamelCase() in server_reverse_lookup_objects %} |
| ObjectIdLookupTable<{{as_cType(type.name)}}> m{{type.name.CamelCase()}}IdTable; |
| {% endfor %} |
| |
| bool PreHandleBufferUnmap(const BufferUnmapCmd& cmd) { |
| auto* selfData = mKnownBuffer.Get(cmd.selfId); |
| ASSERT(selfData != nullptr); |
| |
| selfData->mappedData = nullptr; |
| |
| return true; |
| } |
| |
| bool PostHandleQueueSignal(const QueueSignalCmd& cmd) { |
| if (cmd.fence == nullptr) { |
| return false; |
| } |
| ObjectId fenceId = mFenceIdTable.Get(cmd.fence); |
| ASSERT(fenceId != 0); |
| auto* fence = mKnownFence.Get(fenceId); |
| ASSERT(fence != nullptr); |
| |
| auto* data = new FenceCompletionUserdata; |
| data->server = this; |
| data->fence = ObjectHandle{fenceId, fence->serial}; |
| data->value = cmd.signalValue; |
| |
| auto userdata = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(data)); |
| mProcs.fenceOnCompletion(cmd.fence, cmd.signalValue, ForwardFenceCompletedValue, userdata); |
| return true; |
| } |
| |
| //* Implementation of the command handlers |
| {% for type in by_category["object"] %} |
| {% for method in type.methods %} |
| {% set Suffix = as_MethodSuffix(type.name, method.name) %} |
| {% if Suffix not in client_side_commands %} |
| //* The generic command handlers |
| |
| bool Handle{{Suffix}}(const char** commands, size_t* size) { |
| {{Suffix}}Cmd cmd; |
| DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator, *this); |
| |
| if (deserializeResult == DeserializeResult::FatalError) { |
| return false; |
| } |
| |
| {% if Suffix in server_custom_pre_handler_commands %} |
| if (!PreHandle{{Suffix}}(cmd)) { |
| return false; |
| } |
| {% endif %} |
| |
| //* Unpack 'self' |
| auto* selfData = mKnown{{type.name.CamelCase()}}.Get(cmd.selfId); |
| ASSERT(selfData != nullptr); |
| |
| //* In all cases allocate the object data as it will be refered-to by the client. |
| {% set return_type = method.return_type %} |
| {% set returns = return_type.name.canonical_case() != "void" %} |
| {% if returns %} |
| {% set Type = method.return_type.name.CamelCase() %} |
| auto* resultData = mKnown{{Type}}.Allocate(cmd.result.id); |
| if (resultData == nullptr) { |
| return false; |
| } |
| resultData->serial = cmd.result.serial; |
| |
| {% if type.is_builder %} |
| selfData->builtObject = cmd.result; |
| {% endif %} |
| {% endif %} |
| |
| //* After the data is allocated, apply the argument error propagation mechanism |
| if (deserializeResult == DeserializeResult::ErrorObject) { |
| {% if type.is_builder %} |
| selfData->valid = false; |
| //* If we are in GetResult, fake an error callback |
| {% if returns %} |
| On{{type.name.CamelCase()}}Error(DAWN_BUILDER_ERROR_STATUS_ERROR, "Maybe monad", cmd.selfId, selfData->serial); |
| {% endif %} |
| {% endif %} |
| return true; |
| } |
| |
| {% if returns %} |
| auto result ={{" "}} |
| {%- endif %} |
| mProcs.{{as_varName(type.name, method.name)}}(cmd.self |
| {%- for arg in method.arguments -%} |
| , cmd.{{as_varName(arg.name)}} |
| {%- endfor -%} |
| ); |
| |
| {% if Suffix in server_custom_post_handler_commands %} |
| if (!PostHandle{{Suffix}}(cmd)) { |
| return false; |
| } |
| {% endif %} |
| |
| {% if returns %} |
| resultData->handle = result; |
| resultData->valid = result != nullptr; |
| |
| {% if return_type.name.CamelCase() in server_reverse_lookup_objects %} |
| //* For created objects, store a mapping from them back to their client IDs |
| if (result) { |
| m{{return_type.name.CamelCase()}}IdTable.Store(result, cmd.result.id); |
| } |
| {% endif %} |
| |
| //* builders remember the ID of the object they built so that they can send it |
| //* in the callback to the client. |
| {% if return_type.is_builder %} |
| if (result != nullptr) { |
| uint64_t userdata1 = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this)); |
| uint64_t userdata2 = (uint64_t(resultData->serial) << uint64_t(32)) + cmd.result.id; |
| mProcs.{{as_varName(return_type.name, Name("set error callback"))}}(result, Forward{{return_type.name.CamelCase()}}ToClient, userdata1, userdata2); |
| } |
| {% endif %} |
| {% endif %} |
| |
| return true; |
| } |
| {% endif %} |
| {% endfor %} |
| {% endfor %} |
| |
| bool HandleBufferMapAsync(const char** commands, size_t* size) { |
| //* These requests are just forwarded to the buffer, with userdata containing what the client |
| //* will require in the return command. |
| BufferMapAsyncCmd cmd; |
| DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator); |
| |
| if (deserializeResult == DeserializeResult::FatalError) { |
| return false; |
| } |
| |
| ObjectId bufferId = cmd.bufferId; |
| uint32_t requestSerial = cmd.requestSerial; |
| uint32_t requestSize = cmd.size; |
| uint32_t requestStart = cmd.start; |
| bool isWrite = cmd.isWrite; |
| |
| //* The null object isn't valid as `self` |
| if (bufferId == 0) { |
| return false; |
| } |
| |
| auto* buffer = mKnownBuffer.Get(bufferId); |
| if (buffer == nullptr) { |
| return false; |
| } |
| |
| auto* data = new MapUserdata; |
| data->server = this; |
| data->buffer = ObjectHandle{bufferId, buffer->serial}; |
| data->requestSerial = requestSerial; |
| data->size = requestSize; |
| data->isWrite = isWrite; |
| |
| auto userdata = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(data)); |
| |
| if (!buffer->valid) { |
| //* Fake the buffer returning a failure, data will be freed in this call. |
| if (isWrite) { |
| ForwardBufferMapWriteAsync(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata); |
| } else { |
| ForwardBufferMapReadAsync(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata); |
| } |
| return true; |
| } |
| |
| if (isWrite) { |
| mProcs.bufferMapWriteAsync(buffer->handle, requestStart, requestSize, ForwardBufferMapWriteAsync, userdata); |
| } else { |
| mProcs.bufferMapReadAsync(buffer->handle, requestStart, requestSize, ForwardBufferMapReadAsync, userdata); |
| } |
| |
| return true; |
| } |
| |
| bool HandleBufferUpdateMappedData(const char** commands, size_t* size) { |
| BufferUpdateMappedDataCmd cmd; |
| DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator); |
| |
| if (deserializeResult == DeserializeResult::FatalError) { |
| return false; |
| } |
| |
| ObjectId bufferId = cmd.bufferId; |
| size_t dataLength = cmd.dataLength; |
| |
| //* The null object isn't valid as `self` |
| if (bufferId == 0) { |
| return false; |
| } |
| |
| auto* buffer = mKnownBuffer.Get(bufferId); |
| if (buffer == nullptr || !buffer->valid || buffer->mappedData == nullptr || |
| buffer->mappedDataSize != dataLength) { |
| return false; |
| } |
| |
| DAWN_ASSERT(cmd.data != nullptr); |
| |
| memcpy(buffer->mappedData, cmd.data, dataLength); |
| |
| return true; |
| } |
| |
| bool HandleDestroyObject(const char** commands, size_t* size) { |
| DestroyObjectCmd cmd; |
| DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator); |
| |
| if (deserializeResult == DeserializeResult::FatalError) { |
| return false; |
| } |
| |
| ObjectId objectId = cmd.objectId; |
| //* ID 0 are reserved for nullptr and cannot be destroyed. |
| if (objectId == 0) { |
| return false; |
| } |
| |
| switch (cmd.objectType) { |
| {% for type in by_category["object"] %} |
| {% set ObjectType = type.name.CamelCase() %} |
| case ObjectType::{{ObjectType}}: { |
| {% if ObjectType == "Device" %} |
| //* Freeing the device has to be done out of band. |
| return false; |
| {% else %} |
| auto* data = mKnown{{type.name.CamelCase()}}.Get(objectId); |
| if (data == nullptr) { |
| return false; |
| } |
| {% if type.name.CamelCase() in server_reverse_lookup_objects %} |
| m{{type.name.CamelCase()}}IdTable.Remove(data->handle); |
| {% endif %} |
| |
| if (data->handle != nullptr) { |
| mProcs.{{as_varName(type.name, Name("release"))}}(data->handle); |
| } |
| |
| mKnown{{type.name.CamelCase()}}.Free(objectId); |
| return true; |
| {% endif %} |
| } |
| {% endfor %} |
| default: |
| UNREACHABLE(); |
| } |
| } |
| }; |
| |
| void ForwardDeviceErrorToServer(const char* message, dawnCallbackUserdata userdata) { |
| auto server = reinterpret_cast<Server*>(static_cast<intptr_t>(userdata)); |
| server->OnDeviceError(message); |
| } |
| |
| {% for type in by_category["object"] if type.is_builder%} |
| void Forward{{type.name.CamelCase()}}ToClient(dawnBuilderErrorStatus status, const char* message, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2) { |
| auto server = reinterpret_cast<Server*>(static_cast<uintptr_t>(userdata1)); |
| uint32_t id = userdata2 & 0xFFFFFFFFu; |
| uint32_t serial = userdata2 >> uint64_t(32); |
| server->On{{type.name.CamelCase()}}Error(status, message, id, serial); |
| } |
| {% endfor %} |
| |
| void ForwardBufferMapReadAsync(dawnBufferMapAsyncStatus status, const void* ptr, dawnCallbackUserdata userdata) { |
| auto data = reinterpret_cast<MapUserdata*>(static_cast<uintptr_t>(userdata)); |
| data->server->OnMapReadAsyncCallback(status, ptr, data); |
| } |
| |
| void ForwardBufferMapWriteAsync(dawnBufferMapAsyncStatus status, void* ptr, dawnCallbackUserdata userdata) { |
| auto data = reinterpret_cast<MapUserdata*>(static_cast<uintptr_t>(userdata)); |
| data->server->OnMapWriteAsyncCallback(status, ptr, data); |
| } |
| |
| void ForwardFenceCompletedValue(dawnFenceCompletionStatus status, dawnCallbackUserdata userdata) { |
| auto data = reinterpret_cast<FenceCompletionUserdata*>(static_cast<uintptr_t>(userdata)); |
| if (status == DAWN_FENCE_COMPLETION_STATUS_SUCCESS) { |
| data->server->OnFenceCompletedValueUpdated(data); |
| } |
| } |
| } |
| |
| CommandHandler* NewServerCommandHandler(dawnDevice device, const dawnProcTable& procs, CommandSerializer* serializer) { |
| return new server::Server(device, procs, serializer); |
| } |
| |
| } // namespace dawn_wire |