| // Copyright 2019 The Dawn & Tint Authors | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are met: | |
| // | |
| // 1. Redistributions of source code must retain the above copyright notice, this | |
| // list of conditions and the following disclaimer. | |
| // | |
| // 2. Redistributions in binary form must reproduce the above copyright notice, | |
| // this list of conditions and the following disclaimer in the documentation | |
| // and/or other materials provided with the distribution. | |
| // | |
| // 3. Neither the name of the copyright holder nor the names of its | |
| // contributors may be used to endorse or promote products derived from | |
| // this software without specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| #ifndef SRC_DAWN_WIRE_SERVER_SERVER_H_ | |
| #define SRC_DAWN_WIRE_SERVER_SERVER_H_ | |
| #include <memory> | |
| #include <utility> | |
| #include "dawn/common/MutexProtected.h" | |
| #include "dawn/wire/ChunkedCommandSerializer.h" | |
| #include "dawn/wire/server/ServerBase_autogen.h" | |
| #include "partition_alloc/pointers/raw_ptr.h" | |
| namespace dawn::wire::server { | |
| class Server; | |
| class MemoryTransferService; | |
| // CallbackUserdata and its derived classes are intended to be created by | |
| // Server::MakeUserdata<T> and then passed as the userdata argument for Dawn | |
| // callbacks. | |
| // It contains a pointer back to the Server so that the callback can call the | |
| // Server to perform operations like serialization, and it contains a weak pointer | |
| // |serverIsAlive|. If the weak pointer has expired, it means the server has | |
| // been destroyed and the callback must not use the Server pointer. | |
| // To assist with checking |serverIsAlive| and lifetime management of the userdata, | |
| // |ForwardToServer| (defined later in this file) can be used to acquire the userdata, | |
| // return early if |serverIsAlive| has expired, and then forward the arguments | |
| // to userdata->server->MyCallbackHandler. | |
| // | |
| // Example Usage: | |
| // | |
| // struct MyUserdata : CallbackUserdata { uint32_t foo; }; | |
| // | |
| // auto userdata = MakeUserdata<MyUserdata>(); | |
| // userdata->foo = 2; | |
| // | |
| // callMyCallbackHandler( | |
| // ForwardToServer<&Server::MyCallbackHandler>::Callback, | |
| // userdata.release()); | |
| // | |
| // void Server::MyCallbackHandler(MyUserdata* userdata, Other args) { } | |
| struct CallbackUserdata { | |
| const std::weak_ptr<Server> server; | |
| const std::shared_ptr<const DawnProcTable> procs; | |
| CallbackUserdata() = delete; | |
| CallbackUserdata(const std::weak_ptr<Server>& server, | |
| std::shared_ptr<const DawnProcTable>& procs); | |
| }; | |
| template <auto F, typename _ = decltype(F)> | |
| struct ForwardToServerHelper; | |
| template <auto F, typename UserdataT, typename... Args> | |
| struct ForwardToServerHelper<F, void (Server::*)(UserdataT*, Args...)> { | |
| using Userdata = UserdataT; | |
| static void Callback(Args... args, void* userdata, void*) { | |
| // Acquire the userdata, and cast it to UserdataT. | |
| std::unique_ptr<Userdata> data(static_cast<Userdata*>(userdata)); | |
| auto server = data->server.lock(); | |
| if (!server) { | |
| // If the server is destroyed, release any callback owned results and return. | |
| ( | |
| []<typename T>(const DawnProcTable& procs, T arg) { | |
| if constexpr (WGPUTraits<T>::Release != nullptr) { | |
| if (arg) { | |
| (procs.*WGPUTraits<T>::Release)(arg); | |
| } | |
| } | |
| }(*(data->procs), std::forward<Args>(args)), | |
| ...); | |
| return; | |
| } | |
| // Forward the arguments and the typed userdata to the Server:: member function. | |
| { | |
| auto serverGuard = server.get()->GetGuard(); | |
| (server.get()->*F)(data.get(), std::forward<Args>(args)...); | |
| } | |
| server.get()->Flush(); | |
| } | |
| }; | |
| struct MapUserdata : CallbackUserdata { | |
| using CallbackUserdata::CallbackUserdata; | |
| ObjectHandle buffer; | |
| WGPUBuffer bufferObj; | |
| ObjectHandle eventManager; | |
| WGPUFuture future; | |
| uint64_t offset; | |
| uint64_t size; | |
| WGPUMapMode mode; | |
| }; | |
| struct ErrorScopeUserdata : CallbackUserdata { | |
| using CallbackUserdata::CallbackUserdata; | |
| ObjectHandle device; | |
| ObjectHandle eventManager; | |
| WGPUFuture future; | |
| }; | |
| struct ShaderModuleGetCompilationInfoUserdata : CallbackUserdata { | |
| using CallbackUserdata::CallbackUserdata; | |
| ObjectHandle eventManager; | |
| WGPUFuture future; | |
| }; | |
| struct QueueWorkDoneUserdata : CallbackUserdata { | |
| using CallbackUserdata::CallbackUserdata; | |
| ObjectHandle queue; | |
| ObjectHandle eventManager; | |
| WGPUFuture future; | |
| }; | |
| struct CreatePipelineAsyncUserData : CallbackUserdata { | |
| using CallbackUserdata::CallbackUserdata; | |
| ObjectHandle device; | |
| ObjectHandle eventManager; | |
| WGPUFuture future; | |
| ObjectId pipelineObjectID; | |
| }; | |
| struct RequestAdapterUserdata : CallbackUserdata { | |
| using CallbackUserdata::CallbackUserdata; | |
| ObjectHandle eventManager; | |
| WGPUFuture future; | |
| ObjectId adapterObjectId; | |
| }; | |
| struct RequestDeviceUserdata : CallbackUserdata { | |
| using CallbackUserdata::CallbackUserdata; | |
| ObjectHandle eventManager; | |
| WGPUFuture future; | |
| ObjectId deviceObjectId; | |
| WGPUFuture deviceLostFuture; | |
| }; | |
| struct DeviceLostUserdata : CallbackUserdata { | |
| using CallbackUserdata::CallbackUserdata; | |
| ObjectHandle eventManager; | |
| WGPUFuture future; | |
| }; | |
| class Server : public ServerBase { | |
| public: | |
| static std::shared_ptr<Server> Create(const DawnProcTable& procs, | |
| CommandSerializer* serializer, | |
| MemoryTransferService* memoryTransferService, | |
| bool useSpontaneousCallbacks); | |
| ~Server() override; | |
| // ChunkedCommandHandler implementation | |
| const volatile char* HandleCommands(const volatile char* commands, size_t size) override; | |
| WireResult InjectBuffer(WGPUBuffer buffer, const Handle& handle, const Handle& deviceHandle); | |
| WireResult InjectTexture(WGPUTexture texture, const Handle& handle, const Handle& deviceHandle); | |
| WireResult InjectSurface(WGPUSurface surface, | |
| const Handle& handle, | |
| const Handle& instanceHandle); | |
| WireResult InjectInstance(WGPUInstance instance, const Handle& handle); | |
| WGPUDevice GetDevice(uint32_t id, uint32_t generation); | |
| using ServerBase::IsDeviceKnown; | |
| // Flushes the command serialized from server->client if spontaneous callbacks are enabled. | |
| void Flush(); | |
| template <typename T, | |
| typename Enable = std::enable_if<std::is_base_of<CallbackUserdata, T>::value>> | |
| std::unique_ptr<T> MakeUserdata() { | |
| return std::unique_ptr<T>(new T(mSelf, mProcs)); | |
| } | |
| template <typename CallbackInfo, | |
| auto F, | |
| WGPUCallbackMode DefaultMode = WGPUCallbackMode_AllowProcessEvents> | |
| CallbackInfo MakeCallbackInfo(ForwardToServerHelper<F>::Userdata* userdata) { | |
| return {nullptr, mUseSpontaneousCallbacks ? WGPUCallbackMode_AllowSpontaneous : DefaultMode, | |
| &ForwardToServerHelper<F>::Callback, userdata, nullptr}; | |
| } | |
| private: | |
| Server(const DawnProcTable& procs, | |
| CommandSerializer* serializer, | |
| MemoryTransferService* memoryTransferService, | |
| bool useSpontaneousCallbacks); | |
| template <typename Cmd> | |
| void SerializeCommand(const Cmd& cmd) { | |
| mSerializer->SerializeCommand(cmd); | |
| } | |
| template <typename Cmd, typename... Extensions> | |
| void SerializeCommand(const Cmd& cmd, Extensions&&... es) { | |
| mSerializer->SerializeCommand(cmd, std::forward<Extensions>(es)...); | |
| } | |
| // Wrapper RAII helper for structs with FreeMember calls. | |
| template <typename Struct> | |
| class FreeMembers : public Struct { | |
| public: | |
| explicit FreeMembers(std::shared_ptr<const DawnProcTable>& procs) | |
| : Struct({}), mProcs(procs) {} | |
| ~FreeMembers() { ((*mProcs).*WGPUTraits<Struct>::FreeMembers)(*this); } | |
| private: | |
| std::shared_ptr<const DawnProcTable> mProcs; | |
| }; | |
| void SetForwardingDeviceCallbacks(Known<WGPUDevice> device); | |
| // Async event callbacks: | |
| // These callbacks are expected to be called while holding the server object lock via | |
| // |GetGuard|, and should almost always be followed by a call to |Flush|. Note that | |
| // pretty much all of these functions are called as a part of | |
| // ForwardToServerHelper::Callback unless specified otherwise in the comments. | |
| void OnDeviceLost(DeviceLostUserdata* userdata, | |
| WGPUDevice const* device, | |
| WGPUDeviceLostReason reason, | |
| WGPUStringView message); | |
| void OnDevicePopErrorScope(ErrorScopeUserdata* userdata, | |
| WGPUPopErrorScopeStatus status, | |
| WGPUErrorType type, | |
| WGPUStringView message); | |
| void OnBufferMapAsyncCallback(MapUserdata* userdata, | |
| WGPUMapAsyncStatus status, | |
| WGPUStringView message); | |
| void OnQueueWorkDone(QueueWorkDoneUserdata* userdata, | |
| WGPUQueueWorkDoneStatus status, | |
| WGPUStringView message); | |
| void OnCreateComputePipelineAsyncCallback(CreatePipelineAsyncUserData* userdata, | |
| WGPUCreatePipelineAsyncStatus status, | |
| WGPUComputePipeline pipeline, | |
| WGPUStringView message); | |
| void OnCreateRenderPipelineAsyncCallback(CreatePipelineAsyncUserData* userdata, | |
| WGPUCreatePipelineAsyncStatus status, | |
| WGPURenderPipeline pipeline, | |
| WGPUStringView message); | |
| void OnShaderModuleGetCompilationInfo(ShaderModuleGetCompilationInfoUserdata* userdata, | |
| WGPUCompilationInfoRequestStatus status, | |
| const WGPUCompilationInfo* info); | |
| void OnRequestAdapterCallback(RequestAdapterUserdata* userdata, | |
| WGPURequestAdapterStatus status, | |
| WGPUAdapter adapter, | |
| WGPUStringView message); | |
| void OnRequestDeviceCallback(RequestDeviceUserdata* userdata, | |
| WGPURequestDeviceStatus status, | |
| WGPUDevice device, | |
| WGPUStringView message); | |
| // The |OnUncapturedError| callback is special in that: | |
| // 1) It is a repeating callback, so it can't be used with ForwardToServerHelper::Callback. | |
| void OnUncapturedError(ObjectHandle device, WGPUErrorType type, WGPUStringView message); | |
| // The |OnLogging| callback is special in that: | |
| // 1) It is a repeating callback, so it can't be used with ForwardToServerHelper::Callback. | |
| // 2) It does not require holding the server object storage lock, i.e. |GetGuard| before | |
| // being called because it never interacts with the object store. | |
| void OnLogging(ObjectHandle device, WGPULoggingType type, WGPUStringView message); | |
| #include "dawn/wire/server/ServerPrototypes_autogen.inc" | |
| MutexProtected<ChunkedCommandSerializer> mSerializer; | |
| std::unique_ptr<MemoryTransferService> mOwnedMemoryTransferService = nullptr; | |
| raw_ptr<MemoryTransferService> mMemoryTransferService = nullptr; | |
| bool mUseSpontaneousCallbacks = false; | |
| // Weak pointer to self to facilitate creation of userdata. | |
| std::weak_ptr<Server> mSelf; | |
| }; | |
| std::unique_ptr<MemoryTransferService> CreateInlineMemoryTransferService(); | |
| } // namespace dawn::wire::server | |
| #endif // SRC_DAWN_WIRE_SERVER_SERVER_H_ |