diff --git a/BUILD.gn b/BUILD.gn index 60bda328fe..8d0d8ed7c1 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -779,6 +779,7 @@ dawn_generator("libdawn_wire_gen") { "dawn_wire/client/ClientPrototypes_autogen.inl", "dawn_wire/server/ServerBase_autogen.h", "dawn_wire/server/ServerCallbacks_autogen.cpp", + "dawn_wire/server/ServerDoers_autogen.cpp", "dawn_wire/server/ServerHandlers_autogen.cpp", "dawn_wire/server/ServerPrototypes_autogen.inl", ] diff --git a/dawn_wire.json b/dawn_wire.json index cb55415a22..f19bb2a710 100644 --- a/dawn_wire.json +++ b/dawn_wire.json @@ -71,7 +71,7 @@ "server_custom_pre_handler_commands": [ "BufferUnmap" ], - "server_custom_post_handler_commands": [ + "server_handwritten_commands": [ "QueueSignal" ], "server_reverse_lookup_objects": [ diff --git a/generator/main.py b/generator/main.py index d6953536cf..5c5ce05eb4 100644 --- a/generator/main.py +++ b/generator/main.py @@ -311,12 +311,17 @@ def js_native_methods(types, typ): def debug(text): print(text) +def do_assert(expr): + assert expr + return '' + def get_renders_for_targets(api_params, wire_json, targets): base_params = { 'enumerate': enumerate, 'format': format, 'len': len, 'debug': debug, + 'assert': do_assert, 'Name': lambda name: Name(name), @@ -393,6 +398,7 @@ def get_renders_for_targets(api_params, wire_json, targets): renders.append(FileRender('dawn_wire/client/ClientPrototypes.inl', 'dawn_wire/client/ClientPrototypes_autogen.inl', wire_params)) renders.append(FileRender('dawn_wire/server/ServerBase.h', 'dawn_wire/server/ServerBase_autogen.h', wire_params)) renders.append(FileRender('dawn_wire/server/ServerCallbacks.cpp', 'dawn_wire/server/ServerCallbacks_autogen.cpp', wire_params)) + renders.append(FileRender('dawn_wire/server/ServerDoers.cpp', 'dawn_wire/server/ServerDoers_autogen.cpp', wire_params)) renders.append(FileRender('dawn_wire/server/ServerHandlers.cpp', 'dawn_wire/server/ServerHandlers_autogen.cpp', wire_params)) renders.append(FileRender('dawn_wire/server/ServerPrototypes.inl', 'dawn_wire/server/ServerPrototypes_autogen.inl', wire_params)) diff --git a/generator/templates/dawn_wire/server/ServerCallbacks.cpp b/generator/templates/dawn_wire/server/ServerCallbacks.cpp index ecb52ccfb4..6f69dce30d 100644 --- a/generator/templates/dawn_wire/server/ServerCallbacks.cpp +++ b/generator/templates/dawn_wire/server/ServerCallbacks.cpp @@ -17,7 +17,7 @@ namespace dawn_wire { namespace server { {% for type in by_category["object"] if type.is_builder%} - void Server::Forward{{type.name.CamelCase()}}ToClient(dawnBuilderErrorStatus status, const char* message, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2) { + void Server::Forward{{type.name.CamelCase()}}(dawnBuilderErrorStatus status, const char* message, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2) { auto server = reinterpret_cast(static_cast(userdata1)); uint32_t id = userdata2 & 0xFFFFFFFFu; uint32_t serial = userdata2 >> uint64_t(32); diff --git a/generator/templates/dawn_wire/server/ServerDoers.cpp b/generator/templates/dawn_wire/server/ServerDoers.cpp new file mode 100644 index 0000000000..5b7d8c37ea --- /dev/null +++ b/generator/templates/dawn_wire/server/ServerDoers.cpp @@ -0,0 +1,97 @@ +//* 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. + +#include "common/Assert.h" +#include "dawn_wire/server/Server.h" + +namespace dawn_wire { namespace server { + //* Implementation of the command doers + {% for command in cmd_records["command"] %} + {% set type = command.derived_object %} + {% set method = command.derived_method %} + {% set is_method = method is not none %} + + {% set Suffix = command.name.CamelCase() %} + {% if Suffix not in client_side_commands %} + {% if is_method and Suffix not in server_handwritten_commands %} + bool Server::Do{{Suffix}}( + {%- for member in command.members -%} + {%- if member.is_return_value -%} + {%- if member.handle_type -%} + {{as_cType(member.handle_type.name)}}* {{as_varName(member.name)}} + {%- else -%} + {{as_cType(member.type.name)}}* {{as_varName(member.name)}} + {%- endif -%} + {%- else -%} + {{as_annotated_cType(member)}} + {%- endif -%} + {%- if not loop.last -%}, {% endif %} + {%- endfor -%} + ) { + {% set ret = command.members|selectattr("is_return_value")|list %} + //* If there is a return value, assign it. + {% if ret|length == 1 %} + *{{as_varName(ret[0].name)}} = + {% else %} + //* Only one member should be a return value. + {{ assert(ret|length == 0) }} + {% endif %} + mProcs.{{as_varName(type.name, method.name)}}( + {%- for member in command.members if not member.is_return_value -%} + {{as_varName(member.name)}} + {%- if not loop.last -%}, {% endif %} + {%- endfor -%} + ); + return true; + } + {% endif %} + {% endif %} + {% endfor %} + + bool Server::DoDestroyObject(ObjectType objectType, ObjectId objectId) { + //* ID 0 are reserved for nullptr and cannot be destroyed. + if (objectId == 0) { + return false; + } + + switch(objectType) { + {% for type in by_category["object"] %} + case ObjectType::{{type.name.CamelCase()}}: { + {% if type.name.CamelCase() == "Device" %} + //* Freeing the device has to be done out of band. + return false; + {% else %} + auto* data = {{type.name.CamelCase()}}Objects().Get(objectId); + if (data == nullptr) { + return false; + } + {% if type.name.CamelCase() in server_reverse_lookup_objects %} + {{type.name.CamelCase()}}ObjectIdTable().Remove(data->handle); + {% endif %} + if (data->handle != nullptr) { + mProcs.{{as_varName(type.name, Name("release"))}}(data->handle); + } + {{type.name.CamelCase()}}Objects().Free(objectId); + return true; + {% endif %} + } + {% endfor %} + default: + return false; + } + + return true; + } + +}} // namespace dawn_wire::server diff --git a/generator/templates/dawn_wire/server/ServerHandlers.cpp b/generator/templates/dawn_wire/server/ServerHandlers.cpp index 096d9a29bf..6a1d48bbbf 100644 --- a/generator/templates/dawn_wire/server/ServerHandlers.cpp +++ b/generator/templates/dawn_wire/server/ServerHandlers.cpp @@ -16,144 +16,120 @@ #include "dawn_wire/server/Server.h" namespace dawn_wire { namespace server { - {% 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 + {% for command in cmd_records["command"] %} + {% set type = command.derived_object %} + {% set method = command.derived_method %} + {% set is_method = method != None %} + {% set returns = is_method and method.return_type.name.canonical_case() != "void" %} - bool Server::Handle{{Suffix}}(const char** commands, size_t* size) { - {{Suffix}}Cmd cmd; - DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator, *this); + {% set Suffix = command.name.CamelCase() %} + {% if Suffix not in client_side_commands %} + //* The generic command handlers + bool Server::Handle{{Suffix}}(const char** commands, size_t* size) { + {{Suffix}}Cmd cmd; + DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator + {%- if command.has_dawn_object -%} + , *this + {%- endif -%} + ); - if (deserializeResult == DeserializeResult::FatalError) { + if (deserializeResult == DeserializeResult::FatalError) { + return false; + } + + {% if Suffix in server_custom_pre_handler_commands %} + if (!PreHandle{{Suffix}}(cmd)) { return false; } + {% endif %} - {% if Suffix in server_custom_pre_handler_commands %} - if (!PreHandle{{Suffix}}(cmd)) { - return false; - } - {% endif %} - + {% if is_method %} //* Unpack 'self' auto* selfData = {{type.name.CamelCase()}}Objects().Get(cmd.selfId); ASSERT(selfData != nullptr); + {% endif %} - //* 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 = {{Type}}Objects().Allocate(cmd.result.id); - if (resultData == nullptr) { - return false; - } - resultData->serial = cmd.result.serial; + //* Allocate any result objects + {%- for member in command.members if member.is_return_value -%} + {{ assert(member.handle_type) }} + {% set Type = member.handle_type.name.CamelCase() %} + {% set name = as_varName(member.name) %} - {% 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; + auto* {{name}}Data = {{Type}}Objects().Allocate(cmd.{{name}}.id); + if ({{name}}Data == nullptr) { + return false; } + {{name}}Data->serial = cmd.{{name}}.serial; - {% 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; - } + {% if type.is_builder %} + selfData->builtObject = cmd.{{name}}; {% endif %} + {% endfor %} - {% 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) { - {{return_type.name.CamelCase()}}ObjectIdTable().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(reinterpret_cast(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); - } + //* 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; } - {% endif %} - {% endfor %} - {% endfor %} - bool Server::HandleDestroyObject(const char** commands, size_t* size) { - DestroyObjectCmd cmd; - DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator); + //* Do command + bool success = Do{{Suffix}}( + {%- for member in command.members -%} + {%- if member.is_return_value -%} + {%- if member.handle_type -%} + &{{as_varName(member.name)}}Data->handle //* Pass the handle of the output object to be written by the doer + {%- else -%} + &cmd.{{as_varName(member.name)}} + {%- endif -%} + {%- else -%} + cmd.{{as_varName(member.name)}} + {%- endif -%} + {%- if not loop.last -%}, {% endif %} + {%- endfor -%} + ); - if (deserializeResult == DeserializeResult::FatalError) { - return false; - } + //* Mark output object handles as valid/invalid + {%- for member in command.members if member.is_return_value and member.handle_type -%} + {% set name = as_varName(member.name) %} + {{name}}Data->valid = {{name}}Data->handle != nullptr; + {% endfor %} - 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 = {{type.name.CamelCase()}}Objects().Get(objectId); - if (data == nullptr) { - return false; - } - {% if type.name.CamelCase() in server_reverse_lookup_objects %} - {{type.name.CamelCase()}}ObjectIdTable().Remove(data->handle); - {% endif %} - - if (data->handle != nullptr) { - mProcs.{{as_varName(type.name, Name("release"))}}(data->handle); - } - - {{type.name.CamelCase()}}Objects().Free(objectId); - return true; - {% endif %} + if (!success) { + return false; } - {% endfor %} - default: - UNREACHABLE(); - } - } + + {%- for member in command.members if member.is_return_value and member.handle_type -%} + {% set Type = member.handle_type.name.CamelCase() %} + {% set name = as_varName(member.name) %} + + {% if Type in server_reverse_lookup_objects %} + //* For created objects, store a mapping from them back to their client IDs + if ({{name}}Data->valid) { + {{Type}}ObjectIdTable().Store({{name}}Data->handle, cmd.{{name}}.id); + } + {% endif %} + + //* builders remember the ID of the object they built so that they can send it + //* in the callback to the client. + {% if member.handle_type.is_builder %} + if ({{name}}Data->valid) { + uint64_t userdata1 = static_cast(reinterpret_cast(this)); + uint64_t userdata2 = (uint64_t({{name}}Data->serial) << uint64_t(32)) + cmd.{{name}}.id; + mProcs.{{as_varName(member.handle_type.name, Name("set error callback"))}}({{name}}Data->handle, Forward{{Type}}, userdata1, userdata2); + } + {% endif %} + {% endfor %} + + return true; + } + {% endif %} + {% endfor %} const char* Server::HandleCommands(const char* commands, size_t size) { mProcs.deviceTick(DeviceObjects().Get(1)->handle); diff --git a/generator/templates/dawn_wire/server/ServerPrototypes.inl b/generator/templates/dawn_wire/server/ServerPrototypes.inl index e01f63f188..881835cf1b 100644 --- a/generator/templates/dawn_wire/server/ServerPrototypes.inl +++ b/generator/templates/dawn_wire/server/ServerPrototypes.inl @@ -14,7 +14,7 @@ // Forwarding callbacks {% for type in by_category["object"] if type.is_builder%} - static void Forward{{type.name.CamelCase()}}ToClient(dawnBuilderErrorStatus status, const char* message, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2); + static void Forward{{type.name.CamelCase()}}(dawnBuilderErrorStatus status, const char* message, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2); {% endfor %} // Error callbacks @@ -23,12 +23,27 @@ void On{{Type}}Error(dawnBuilderErrorStatus status, const char* message, uint32_t id, uint32_t serial); {% endfor %} -// 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 %} - bool Handle{{Suffix}}(const char** commands, size_t* size); - {% endif %} - {% endfor %} +// Command handlers & doers +{% for command in cmd_records["command"] if command.name.CamelCase() not in client_side_commands %} + {% set Suffix = command.name.CamelCase() %} + bool Handle{{Suffix}}(const char** commands, size_t* size); + + bool Do{{Suffix}}( + {%- for member in command.members -%} + {%- if member.is_return_value -%} + {%- if member.handle_type -%} + {{as_cType(member.handle_type.name)}}* {{as_varName(member.name)}} + {%- else -%} + {{as_cType(member.type.name)}}* {{as_varName(member.name)}} + {%- endif -%} + {%- else -%} + {{as_annotated_cType(member)}} + {%- endif -%} + {%- if not loop.last -%}, {% endif %} + {%- endfor -%} + ); +{% endfor %} + +{% for CommandName in server_custom_pre_handler_commands %} + bool PreHandle{{CommandName}}(const {{CommandName}}Cmd& cmd); {% endfor %} diff --git a/src/dawn_wire/server/Server.cpp b/src/dawn_wire/server/Server.cpp index 352e5d5885..bd1ec9254d 100644 --- a/src/dawn_wire/server/Server.cpp +++ b/src/dawn_wire/server/Server.cpp @@ -24,7 +24,7 @@ namespace dawn_wire { namespace server { deviceData->valid = true; auto userdata = static_cast(reinterpret_cast(this)); - procs.deviceSetErrorCallback(device, ForwardDeviceErrorToServer, userdata); + procs.deviceSetErrorCallback(device, ForwardDeviceError, userdata); } Server::~Server() { diff --git a/src/dawn_wire/server/Server.h b/src/dawn_wire/server/Server.h index 0e0cb778c4..bc7b99d20f 100644 --- a/src/dawn_wire/server/Server.h +++ b/src/dawn_wire/server/Server.h @@ -46,7 +46,7 @@ namespace dawn_wire { namespace server { void* GetCmdSpace(size_t size); // Forwarding callbacks - static void ForwardDeviceErrorToServer(const char* message, dawnCallbackUserdata userdata); + static void ForwardDeviceError(const char* message, dawnCallbackUserdata userdata); static void ForwardBufferMapReadAsync(dawnBufferMapAsyncStatus status, const void* ptr, dawnCallbackUserdata userdata); @@ -66,13 +66,6 @@ namespace dawn_wire { namespace server { MapUserdata* userdata); void OnFenceCompletedValueUpdated(FenceCompletionUserdata* userdata); - // Command handlers - bool PreHandleBufferUnmap(const BufferUnmapCmd& cmd); - bool PostHandleQueueSignal(const QueueSignalCmd& cmd); - bool HandleBufferMapAsync(const char** commands, size_t* size); - bool HandleBufferUpdateMappedData(const char** commands, size_t* size); - bool HandleDestroyObject(const char** commands, size_t* size); - #include "dawn_wire/server/ServerPrototypes_autogen.inl" CommandSerializer* mSerializer = nullptr; diff --git a/src/dawn_wire/server/ServerBuffer.cpp b/src/dawn_wire/server/ServerBuffer.cpp index 75769930ee..67ab8ea04d 100644 --- a/src/dawn_wire/server/ServerBuffer.cpp +++ b/src/dawn_wire/server/ServerBuffer.cpp @@ -21,28 +21,20 @@ namespace dawn_wire { namespace server { bool Server::PreHandleBufferUnmap(const BufferUnmapCmd& cmd) { auto* selfData = BufferObjects().Get(cmd.selfId); - ASSERT(selfData != nullptr); + DAWN_ASSERT(selfData != nullptr); selfData->mappedData = nullptr; return true; } - bool Server::HandleBufferMapAsync(const char** commands, size_t* size) { + bool Server::DoBufferMapAsync(ObjectId bufferId, + uint32_t requestSerial, + uint32_t start, + uint32_t size, + bool isWrite) { // 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) { @@ -58,7 +50,7 @@ namespace dawn_wire { namespace server { data->server = this; data->buffer = ObjectHandle{bufferId, buffer->serial}; data->requestSerial = requestSerial; - data->size = requestSize; + data->size = size; data->isWrite = isWrite; auto userdata = static_cast(reinterpret_cast(data)); @@ -74,27 +66,17 @@ namespace dawn_wire { namespace server { } if (isWrite) { - mProcs.bufferMapWriteAsync(buffer->handle, requestStart, requestSize, - ForwardBufferMapWriteAsync, userdata); + mProcs.bufferMapWriteAsync(buffer->handle, start, size, ForwardBufferMapWriteAsync, + userdata); } else { - mProcs.bufferMapReadAsync(buffer->handle, requestStart, requestSize, - ForwardBufferMapReadAsync, userdata); + mProcs.bufferMapReadAsync(buffer->handle, start, size, ForwardBufferMapReadAsync, + userdata); } return true; } - bool Server::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; - + bool Server::DoBufferUpdateMappedData(ObjectId bufferId, uint32_t count, const uint8_t* data) { // The null object isn't valid as `self` if (bufferId == 0) { return false; @@ -102,13 +84,15 @@ namespace dawn_wire { namespace server { auto* buffer = BufferObjects().Get(bufferId); if (buffer == nullptr || !buffer->valid || buffer->mappedData == nullptr || - buffer->mappedDataSize != dataLength) { + buffer->mappedDataSize != count) { return false; } - DAWN_ASSERT(cmd.data != nullptr); + if (data == nullptr) { + return false; + } - memcpy(buffer->mappedData, cmd.data, dataLength); + memcpy(buffer->mappedData, data, count); return true; } diff --git a/src/dawn_wire/server/ServerDevice.cpp b/src/dawn_wire/server/ServerDevice.cpp index aaf6b29f94..93390e1e4b 100644 --- a/src/dawn_wire/server/ServerDevice.cpp +++ b/src/dawn_wire/server/ServerDevice.cpp @@ -16,7 +16,7 @@ namespace dawn_wire { namespace server { - void Server::ForwardDeviceErrorToServer(const char* message, dawnCallbackUserdata userdata) { + void Server::ForwardDeviceError(const char* message, dawnCallbackUserdata userdata) { auto server = reinterpret_cast(static_cast(userdata)); server->OnDeviceError(message); } diff --git a/src/dawn_wire/server/ServerQueue.cpp b/src/dawn_wire/server/ServerQueue.cpp index b347f09526..e993fba32e 100644 --- a/src/dawn_wire/server/ServerQueue.cpp +++ b/src/dawn_wire/server/ServerQueue.cpp @@ -17,11 +17,14 @@ namespace dawn_wire { namespace server { - bool Server::PostHandleQueueSignal(const QueueSignalCmd& cmd) { - if (cmd.fence == nullptr) { + bool Server::DoQueueSignal(dawnQueue cSelf, dawnFence cFence, uint64_t signalValue) { + if (cFence == nullptr) { return false; } - ObjectId fenceId = FenceObjectIdTable().Get(cmd.fence); + + mProcs.queueSignal(cSelf, cFence, signalValue); + + ObjectId fenceId = FenceObjectIdTable().Get(cFence); ASSERT(fenceId != 0); auto* fence = FenceObjects().Get(fenceId); ASSERT(fence != nullptr); @@ -29,10 +32,10 @@ namespace dawn_wire { namespace server { auto* data = new FenceCompletionUserdata; data->server = this; data->fence = ObjectHandle{fenceId, fence->serial}; - data->value = cmd.signalValue; + data->value = signalValue; auto userdata = static_cast(reinterpret_cast(data)); - mProcs.fenceOnCompletion(cmd.fence, cmd.signalValue, ForwardFenceCompletedValue, userdata); + mProcs.fenceOnCompletion(cFence, signalValue, ForwardFenceCompletedValue, userdata); return true; }