From 03e1400fced48ec090f95860b3c96e0e0e80bc58 Mon Sep 17 00:00:00 2001 From: Jiawei Shao Date: Wed, 21 Oct 2020 04:37:41 +0000 Subject: [PATCH] Add the entry point of CreateReadyRenderPipeline BUG=dawn:529 TEST=dawn_end2end_tests Change-Id: I42ac0edc77e5b6119eb374da72698fca14596f7b Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/30540 Commit-Queue: Jiawei Shao Reviewed-by: Austin Eng --- dawn.json | 18 +++ dawn_wire.json | 14 +- generator/templates/mock_webgpu.cpp | 20 +++ generator/templates/mock_webgpu.h | 20 +++ .../CreateReadyPipelineTracker.cpp | 55 ++++++-- src/dawn_native/CreateReadyPipelineTracker.h | 39 ++++-- src/dawn_native/Device.cpp | 16 +++ src/dawn_native/Device.h | 3 + src/dawn_wire/client/ClientDoers.cpp | 9 +- src/dawn_wire/client/Device.cpp | 89 +++++++++--- src/dawn_wire/client/Device.h | 15 +- src/dawn_wire/server/Server.h | 8 ++ src/dawn_wire/server/ServerDevice.cpp | 51 +++++++ .../end2end/CreateReadyPipelineTests.cpp | 131 +++++++++++++++++- .../wire/WireCreateReadyPipelineTests.cpp | 88 ++++++++++++ 15 files changed, 532 insertions(+), 44 deletions(-) diff --git a/dawn.json b/dawn.json index 5e5fa34a66..6a3bb9a83b 100644 --- a/dawn.json +++ b/dawn.json @@ -519,6 +519,15 @@ {"value": 3, "name": "unknown"} ] }, + "create ready render pipeline callback": { + "category": "callback", + "args": [ + {"name": "status", "type": "create ready pipeline status"}, + {"name": "pipeline", "type": "render pipeline"}, + {"name": "message", "type": "char", "annotation": "const*", "length": "strlen"}, + {"name": "userdata", "type": "void", "annotation": "*"} + ] + }, "cull mode": { "category": "enum", "values": [ @@ -593,6 +602,15 @@ {"name": "descriptor", "type": "query set descriptor", "annotation": "const*"} ] }, + { + "name": "create ready render pipeline", + "returns": "void", + "args": [ + {"name": "descriptor", "type": "render pipeline descriptor", "annotation": "const*"}, + {"name": "callback", "type": "create ready render pipeline callback"}, + {"name": "userdata", "type": "void", "annotation": "*"} + ] + }, { "name": "create render bundle encoder", "returns": "render bundle encoder", diff --git a/dawn_wire.json b/dawn_wire.json index 8870932876..505af37093 100644 --- a/dawn_wire.json +++ b/dawn_wire.json @@ -45,6 +45,12 @@ { "name": "pipeline object handle", "type": "ObjectHandle", "handle_type": "compute pipeline"}, { "name": "descriptor", "type": "compute pipeline descriptor", "annotation": "const*"} ], + "device create ready render pipeline": [ + { "name": "device", "type": "device" }, + { "name": "request serial", "type": "uint64_t" }, + { "name": "pipeline object handle", "type": "ObjectHandle", "handle_type": "render pipeline"}, + { "name": "descriptor", "type": "render pipeline descriptor", "annotation": "const*"} + ], "device pop error scope": [ { "name": "device", "type": "device" }, { "name": "request serial", "type": "uint64_t" } @@ -85,7 +91,12 @@ "device create ready compute pipeline callback": [ { "name": "request serial", "type": "uint64_t" }, { "name": "status", "type": "create ready pipeline status" }, - { "name": "message", "type": "char", "annotation": "const*", "length": "strlen"} + { "name": "message", "type": "char", "annotation": "const*", "length": "strlen" } + ], + "device create ready render pipeline callback": [ + { "name": "request serial", "type": "uint64_t" }, + { "name": "status", "type": "create ready pipeline status" }, + { "name": "message", "type": "char", "annotation": "const*", "length": "strlen" } ], "device uncaptured error callback": [ { "name": "type", "type": "error type"}, @@ -121,6 +132,7 @@ "BufferGetMappedRange", "DeviceCreateBuffer", "DeviceCreateReadyComputePipeline", + "DeviceCreateReadyRenderPipeline", "DevicePopErrorScope", "DeviceSetDeviceLostCallback", "DeviceSetUncapturedErrorCallback", diff --git a/generator/templates/mock_webgpu.cpp b/generator/templates/mock_webgpu.cpp index 77f73be61c..10d3716643 100644 --- a/generator/templates/mock_webgpu.cpp +++ b/generator/templates/mock_webgpu.cpp @@ -112,6 +112,18 @@ void ProcTableAsClass::DeviceCreateReadyComputePipeline( OnDeviceCreateReadyComputePipelineCallback(self, descriptor, callback, userdata); } +void ProcTableAsClass::DeviceCreateReadyRenderPipeline( + WGPUDevice self, + WGPURenderPipelineDescriptor const * descriptor, + WGPUCreateReadyRenderPipelineCallback callback, + void* userdata) { + auto object = reinterpret_cast(self); + object->createReadyRenderPipelineCallback = callback; + object->userdata = userdata; + + OnDeviceCreateReadyRenderPipelineCallback(self, descriptor, callback, userdata); +} + void ProcTableAsClass::CallDeviceErrorCallback(WGPUDevice device, WGPUErrorType type, const char* message) { @@ -143,6 +155,14 @@ void ProcTableAsClass::CallDeviceCreateReadyComputePipelineCallback(WGPUDevice d object->createReadyComputePipelineCallback(status, pipeline, message, object->userdata); } +void ProcTableAsClass::CallDeviceCreateReadyRenderPipelineCallback(WGPUDevice device, + WGPUCreateReadyPipelineStatus status, + WGPURenderPipeline pipeline, + const char* message) { + auto object = reinterpret_cast(device); + object->createReadyRenderPipelineCallback(status, pipeline, message, object->userdata); +} + {% for type in by_category["object"] %} {{as_cType(type.name)}} ProcTableAsClass::GetNew{{type.name.CamelCase()}}() { mObjects.emplace_back(new Object); diff --git a/generator/templates/mock_webgpu.h b/generator/templates/mock_webgpu.h index 813f4bdd93..e3dfdbb6ed 100644 --- a/generator/templates/mock_webgpu.h +++ b/generator/templates/mock_webgpu.h @@ -56,6 +56,10 @@ class ProcTableAsClass { WGPUComputePipelineDescriptor const * descriptor, WGPUCreateReadyComputePipelineCallback callback, void* userdata); + void DeviceCreateReadyRenderPipeline(WGPUDevice self, + WGPURenderPipelineDescriptor const * descriptor, + WGPUCreateReadyRenderPipelineCallback callback, + void* userdata); void DeviceSetUncapturedErrorCallback(WGPUDevice self, WGPUErrorCallback callback, void* userdata); @@ -80,6 +84,11 @@ class ProcTableAsClass { WGPUComputePipelineDescriptor const * descriptor, WGPUCreateReadyComputePipelineCallback callback, void* userdata) = 0; + virtual void OnDeviceCreateReadyRenderPipelineCallback( + WGPUDevice device, + WGPURenderPipelineDescriptor const * descriptor, + WGPUCreateReadyRenderPipelineCallback callback, + void* userdata) = 0; virtual void OnDeviceSetUncapturedErrorCallback(WGPUDevice device, WGPUErrorCallback callback, void* userdata) = 0; @@ -102,6 +111,10 @@ class ProcTableAsClass { WGPUCreateReadyPipelineStatus status, WGPUComputePipeline pipeline, const char* message); + void CallDeviceCreateReadyRenderPipelineCallback(WGPUDevice device, + WGPUCreateReadyPipelineStatus status, + WGPURenderPipeline pipeline, + const char* message); void CallDeviceErrorCallback(WGPUDevice device, WGPUErrorType type, const char* message); void CallDeviceLostCallback(WGPUDevice device, const char* message); void CallMapAsyncCallback(WGPUBuffer buffer, WGPUBufferMapAsyncStatus status); @@ -111,6 +124,7 @@ class ProcTableAsClass { ProcTableAsClass* procs = nullptr; WGPUErrorCallback deviceErrorCallback = nullptr; WGPUCreateReadyComputePipelineCallback createReadyComputePipelineCallback = nullptr; + WGPUCreateReadyRenderPipelineCallback createReadyRenderPipelineCallback = nullptr; WGPUDeviceLostCallback deviceLostCallback = nullptr; WGPUBufferMapCallback mapAsyncCallback = nullptr; WGPUFenceOnCompletionCallback fenceOnCompletionCallback = nullptr; @@ -150,6 +164,12 @@ class MockProcTable : public ProcTableAsClass { WGPUCreateReadyComputePipelineCallback callback, void* userdata), (override)); + MOCK_METHOD(void, + OnDeviceCreateReadyRenderPipelineCallback, + (WGPUDevice device, WGPURenderPipelineDescriptor const * descriptor, + WGPUCreateReadyRenderPipelineCallback callback, + void* userdata), + (override)); MOCK_METHOD(void, OnDeviceSetUncapturedErrorCallback, (WGPUDevice device, WGPUErrorCallback callback, void* userdata), (override)); MOCK_METHOD(void, OnDeviceSetDeviceLostCallback, (WGPUDevice device, WGPUDeviceLostCallback callback, void* userdata), (override)); MOCK_METHOD(bool, OnDevicePopErrorScopeCallback, (WGPUDevice device, WGPUErrorCallback callback, void* userdata), (override)); diff --git a/src/dawn_native/CreateReadyPipelineTracker.cpp b/src/dawn_native/CreateReadyPipelineTracker.cpp index 9d4ccc37d2..cfc19cc21b 100644 --- a/src/dawn_native/CreateReadyPipelineTracker.cpp +++ b/src/dawn_native/CreateReadyPipelineTracker.cpp @@ -18,39 +18,72 @@ namespace dawn_native { + CreateReadyPipelineTaskBase::CreateReadyPipelineTaskBase(void* userdata) : mUserData(userdata) { + } + + CreateReadyPipelineTaskBase::~CreateReadyPipelineTaskBase() { + } + CreateReadyComputePipelineTask::CreateReadyComputePipelineTask( ComputePipelineBase* pipeline, WGPUCreateReadyComputePipelineCallback callback, void* userdata) - : mPipeline(pipeline), mCallback(callback), mUserData(userdata) { - } - - CreateReadyComputePipelineTask::~CreateReadyComputePipelineTask() { + : CreateReadyPipelineTaskBase(userdata), + mPipeline(pipeline), + mCreateReadyComputePipelineCallback(callback) { } void CreateReadyComputePipelineTask::Finish() { - mCallback(WGPUCreateReadyPipelineStatus_Success, - reinterpret_cast(mPipeline), "", mUserData); + ASSERT(mPipeline != nullptr); + ASSERT(mCreateReadyComputePipelineCallback != nullptr); + + mCreateReadyComputePipelineCallback(WGPUCreateReadyPipelineStatus_Success, + reinterpret_cast(mPipeline), "", + mUserData); + + // Set mCreateReadyComputePipelineCallback to nullptr in case it is called more than once. + mCreateReadyComputePipelineCallback = nullptr; + } + + CreateReadyRenderPipelineTask::CreateReadyRenderPipelineTask( + RenderPipelineBase* pipeline, + WGPUCreateReadyRenderPipelineCallback callback, + void* userdata) + : CreateReadyPipelineTaskBase(userdata), + mPipeline(pipeline), + mCreateReadyRenderPipelineCallback(callback) { + } + + void CreateReadyRenderPipelineTask::Finish() { + ASSERT(mPipeline != nullptr); + ASSERT(mCreateReadyRenderPipelineCallback != nullptr); + + mCreateReadyRenderPipelineCallback(WGPUCreateReadyPipelineStatus_Success, + reinterpret_cast(mPipeline), "", + mUserData); + + // Set mCreateReadyPipelineCallback to nullptr in case it is called more than once. + mCreateReadyRenderPipelineCallback = nullptr; } CreateReadyPipelineTracker::CreateReadyPipelineTracker(DeviceBase* device) : mDevice(device) { } CreateReadyPipelineTracker::~CreateReadyPipelineTracker() { - ASSERT(mCreateReadyComputePipelineTasksInFlight.Empty()); + ASSERT(mCreateReadyPipelineTasksInFlight.Empty()); } - void CreateReadyPipelineTracker::TrackTask(std::unique_ptr task, + void CreateReadyPipelineTracker::TrackTask(std::unique_ptr task, ExecutionSerial serial) { - mCreateReadyComputePipelineTasksInFlight.Enqueue(std::move(task), serial); + mCreateReadyPipelineTasksInFlight.Enqueue(std::move(task), serial); mDevice->AddFutureSerial(serial); } void CreateReadyPipelineTracker::Tick(ExecutionSerial finishedSerial) { - for (auto& task : mCreateReadyComputePipelineTasksInFlight.IterateUpTo(finishedSerial)) { + for (auto& task : mCreateReadyPipelineTasksInFlight.IterateUpTo(finishedSerial)) { task->Finish(); } - mCreateReadyComputePipelineTasksInFlight.ClearUpTo(finishedSerial); + mCreateReadyPipelineTasksInFlight.ClearUpTo(finishedSerial); } } // namespace dawn_native diff --git a/src/dawn_native/CreateReadyPipelineTracker.h b/src/dawn_native/CreateReadyPipelineTracker.h index b85c8c29fd..0c6b1dc4c3 100644 --- a/src/dawn_native/CreateReadyPipelineTracker.h +++ b/src/dawn_native/CreateReadyPipelineTracker.h @@ -25,19 +25,41 @@ namespace dawn_native { class ComputePipelineBase; class DeviceBase; + class PipelineBase; + class RenderPipelineBase; - struct CreateReadyComputePipelineTask { + struct CreateReadyPipelineTaskBase { + CreateReadyPipelineTaskBase(void* userData); + virtual ~CreateReadyPipelineTaskBase(); + + virtual void Finish() = 0; + + protected: + void* mUserData; + }; + + struct CreateReadyComputePipelineTask final : public CreateReadyPipelineTaskBase { CreateReadyComputePipelineTask(ComputePipelineBase* pipeline, WGPUCreateReadyComputePipelineCallback callback, void* userdata); - ~CreateReadyComputePipelineTask(); - void Finish(); + void Finish() final; private: ComputePipelineBase* mPipeline; - WGPUCreateReadyComputePipelineCallback mCallback; - void* mUserData; + WGPUCreateReadyComputePipelineCallback mCreateReadyComputePipelineCallback; + }; + + struct CreateReadyRenderPipelineTask final : public CreateReadyPipelineTaskBase { + CreateReadyRenderPipelineTask(RenderPipelineBase* pipeline, + WGPUCreateReadyRenderPipelineCallback callback, + void* userdata); + + void Finish() final; + + private: + RenderPipelineBase* mPipeline; + WGPUCreateReadyRenderPipelineCallback mCreateReadyRenderPipelineCallback; }; class CreateReadyPipelineTracker { @@ -45,14 +67,13 @@ namespace dawn_native { CreateReadyPipelineTracker(DeviceBase* device); ~CreateReadyPipelineTracker(); - void TrackTask(std::unique_ptr task, - ExecutionSerial serial); + void TrackTask(std::unique_ptr task, ExecutionSerial serial); void Tick(ExecutionSerial finishedSerial); private: DeviceBase* mDevice; - SerialQueue> - mCreateReadyComputePipelineTasksInFlight; + SerialQueue> + mCreateReadyPipelineTasksInFlight; }; } // namespace dawn_native diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp index 5ae6e4673e..2c5b3aa185 100644 --- a/src/dawn_native/Device.cpp +++ b/src/dawn_native/Device.cpp @@ -674,6 +674,22 @@ namespace dawn_native { return result; } + void DeviceBase::CreateReadyRenderPipeline(const RenderPipelineDescriptor* descriptor, + WGPUCreateReadyRenderPipelineCallback callback, + void* userdata) { + RenderPipelineBase* result = nullptr; + MaybeError maybeError = CreateRenderPipelineInternal(&result, descriptor); + if (maybeError.IsError()) { + std::unique_ptr error = maybeError.AcquireError(); + callback(WGPUCreateReadyPipelineStatus_Error, nullptr, error->GetMessage().c_str(), + userdata); + return; + } + + std::unique_ptr request = + std::make_unique(result, callback, userdata); + mCreateReadyPipelineTracker->TrackTask(std::move(request), GetPendingCommandSerial()); + } RenderBundleEncoder* DeviceBase::CreateRenderBundleEncoder( const RenderBundleEncoderDescriptor* descriptor) { RenderBundleEncoder* result = nullptr; diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h index eab0763894..494c4311f3 100644 --- a/src/dawn_native/Device.h +++ b/src/dawn_native/Device.h @@ -149,6 +149,9 @@ namespace dawn_native { void CreateReadyComputePipeline(const ComputePipelineDescriptor* descriptor, WGPUCreateReadyComputePipelineCallback callback, void* userdata); + void CreateReadyRenderPipeline(const RenderPipelineDescriptor* descriptor, + WGPUCreateReadyRenderPipelineCallback callback, + void* userdata); RenderBundleEncoder* CreateRenderBundleEncoder( const RenderBundleEncoderDescriptor* descriptor); RenderPipelineBase* CreateRenderPipeline(const RenderPipelineDescriptor* descriptor); diff --git a/src/dawn_wire/client/ClientDoers.cpp b/src/dawn_wire/client/ClientDoers.cpp index 338863eef5..cd9b5aba83 100644 --- a/src/dawn_wire/client/ClientDoers.cpp +++ b/src/dawn_wire/client/ClientDoers.cpp @@ -85,8 +85,13 @@ namespace dawn_wire { namespace client { bool Client::DoDeviceCreateReadyComputePipelineCallback(uint64_t requestSerial, WGPUCreateReadyPipelineStatus status, const char* message) { - mDevice->OnCreateReadyComputePipelineCallback(requestSerial, status, message); - return true; + return mDevice->OnCreateReadyComputePipelineCallback(requestSerial, status, message); + } + + bool Client::DoDeviceCreateReadyRenderPipelineCallback(uint64_t requestSerial, + WGPUCreateReadyPipelineStatus status, + const char* message) { + return mDevice->OnCreateReadyRenderPipelineCallback(requestSerial, status, message); } }} // namespace dawn_wire::client diff --git a/src/dawn_wire/client/Device.cpp b/src/dawn_wire/client/Device.cpp index fd175fe0fc..b49f8a4226 100644 --- a/src/dawn_wire/client/Device.cpp +++ b/src/dawn_wire/client/Device.cpp @@ -43,10 +43,18 @@ namespace dawn_wire { namespace client { it.second.callback(WGPUErrorType_Unknown, "Device destroyed", it.second.userdata); } - auto createReadyComputePipelineRequests = std::move(mCreateReadyComputePipelineRequests); - for (const auto& it : createReadyComputePipelineRequests) { - it.second.callback(WGPUCreateReadyPipelineStatus_Unknown, nullptr, "Device destroyed", - it.second.userdata); + auto createReadyPipelineRequests = std::move(mCreateReadyPipelineRequests); + for (const auto& it : createReadyPipelineRequests) { + if (it.second.createReadyComputePipelineCallback != nullptr) { + it.second.createReadyComputePipelineCallback(WGPUCreateReadyPipelineStatus_Unknown, + nullptr, "Device destroyed", + it.second.userdata); + } else { + ASSERT(it.second.createReadyRenderPipelineCallback != nullptr); + it.second.createReadyRenderPipelineCallback(WGPUCreateReadyPipelineStatus_Unknown, + nullptr, "Device destroyed", + it.second.userdata); + } } // Destroy the default queue @@ -170,33 +178,32 @@ namespace dawn_wire { namespace client { cmd.device = ToAPI(this); cmd.descriptor = descriptor; - uint64_t serial = mCreateReadyComputePipelineRequestSerial++; - ASSERT(mCreateReadyComputePipelineRequests.find(serial) == - mCreateReadyComputePipelineRequests.end()); + uint64_t serial = mCreateReadyPipelineRequestSerial++; + ASSERT(mCreateReadyPipelineRequests.find(serial) == mCreateReadyPipelineRequests.end()); cmd.requestSerial = serial; auto* allocation = GetClient()->ComputePipelineAllocator().New(this); - CreateReadyComputePipelineRequest request = {}; - request.callback = callback; + CreateReadyPipelineRequest request = {}; + request.createReadyComputePipelineCallback = callback; request.userdata = userdata; request.pipelineObjectID = allocation->object->id; cmd.pipelineObjectHandle = ObjectHandle{allocation->object->id, allocation->generation}; GetClient()->SerializeCommand(cmd); - mCreateReadyComputePipelineRequests[serial] = std::move(request); + mCreateReadyPipelineRequests[serial] = std::move(request); } bool Device::OnCreateReadyComputePipelineCallback(uint64_t requestSerial, WGPUCreateReadyPipelineStatus status, const char* message) { - const auto& requestIt = mCreateReadyComputePipelineRequests.find(requestSerial); - if (requestIt == mCreateReadyComputePipelineRequests.end()) { + const auto& requestIt = mCreateReadyPipelineRequests.find(requestSerial); + if (requestIt == mCreateReadyPipelineRequests.end()) { return false; } - CreateReadyComputePipelineRequest request = std::move(requestIt->second); - mCreateReadyComputePipelineRequests.erase(requestIt); + CreateReadyPipelineRequest request = std::move(requestIt->second); + mCreateReadyPipelineRequests.erase(requestIt); auto pipelineAllocation = GetClient()->ComputePipelineAllocator().GetObject(request.pipelineObjectID); @@ -205,14 +212,64 @@ namespace dawn_wire { namespace client { // free the allocation both on the client side and the server side. if (status != WGPUCreateReadyPipelineStatus_Success) { GetClient()->ComputePipelineAllocator().Free(pipelineAllocation); - request.callback(status, nullptr, message, request.userdata); + request.createReadyComputePipelineCallback(status, nullptr, message, request.userdata); return true; } WGPUComputePipeline pipeline = reinterpret_cast(pipelineAllocation); - request.callback(status, pipeline, message, request.userdata); + request.createReadyComputePipelineCallback(status, pipeline, message, request.userdata); return true; } + void Device::CreateReadyRenderPipeline(WGPURenderPipelineDescriptor const* descriptor, + WGPUCreateReadyRenderPipelineCallback callback, + void* userdata) { + DeviceCreateReadyRenderPipelineCmd cmd; + cmd.device = ToAPI(this); + cmd.descriptor = descriptor; + + uint64_t serial = mCreateReadyPipelineRequestSerial++; + ASSERT(mCreateReadyPipelineRequests.find(serial) == mCreateReadyPipelineRequests.end()); + cmd.requestSerial = serial; + + auto* allocation = GetClient()->RenderPipelineAllocator().New(this); + CreateReadyPipelineRequest request = {}; + request.createReadyRenderPipelineCallback = callback; + request.userdata = userdata; + request.pipelineObjectID = allocation->object->id; + + cmd.pipelineObjectHandle = ObjectHandle(allocation->object->id, allocation->generation); + GetClient()->SerializeCommand(cmd); + + mCreateReadyPipelineRequests[serial] = std::move(request); + } + + bool Device::OnCreateReadyRenderPipelineCallback(uint64_t requestSerial, + WGPUCreateReadyPipelineStatus status, + const char* message) { + const auto& requestIt = mCreateReadyPipelineRequests.find(requestSerial); + if (requestIt == mCreateReadyPipelineRequests.end()) { + return false; + } + + CreateReadyPipelineRequest request = std::move(requestIt->second); + mCreateReadyPipelineRequests.erase(requestIt); + + auto pipelineAllocation = + GetClient()->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); + request.createReadyRenderPipelineCallback(status, nullptr, message, request.userdata); + return true; + } + + WGPURenderPipeline pipeline = reinterpret_cast(pipelineAllocation); + request.createReadyRenderPipelineCallback(status, pipeline, message, request.userdata); + + return true; + } }} // namespace dawn_wire::client diff --git a/src/dawn_wire/client/Device.h b/src/dawn_wire/client/Device.h index 087c72cba3..7d14c6fe7f 100644 --- a/src/dawn_wire/client/Device.h +++ b/src/dawn_wire/client/Device.h @@ -43,6 +43,9 @@ namespace dawn_wire { namespace client { void CreateReadyComputePipeline(WGPUComputePipelineDescriptor const* descriptor, WGPUCreateReadyComputePipelineCallback callback, void* userdata); + void CreateReadyRenderPipeline(WGPURenderPipelineDescriptor const* descriptor, + WGPUCreateReadyRenderPipelineCallback callback, + void* userdata); void HandleError(WGPUErrorType errorType, const char* message); void HandleDeviceLost(const char* message); @@ -52,6 +55,9 @@ namespace dawn_wire { namespace client { bool OnCreateReadyComputePipelineCallback(uint64_t requestSerial, WGPUCreateReadyPipelineStatus status, const char* message); + bool OnCreateReadyRenderPipelineCallback(uint64_t requestSerial, + WGPUCreateReadyPipelineStatus status, + const char* message); WGPUQueue GetDefaultQueue(); @@ -64,13 +70,14 @@ namespace dawn_wire { namespace client { uint64_t mErrorScopeRequestSerial = 0; uint64_t mErrorScopeStackSize = 0; - struct CreateReadyComputePipelineRequest { - WGPUCreateReadyComputePipelineCallback callback = nullptr; + struct CreateReadyPipelineRequest { + WGPUCreateReadyComputePipelineCallback createReadyComputePipelineCallback = nullptr; + WGPUCreateReadyRenderPipelineCallback createReadyRenderPipelineCallback = nullptr; void* userdata = nullptr; ObjectId pipelineObjectID; }; - std::map mCreateReadyComputePipelineRequests; - uint64_t mCreateReadyComputePipelineRequestSerial = 0; + std::map mCreateReadyPipelineRequests; + uint64_t mCreateReadyPipelineRequestSerial = 0; Client* mClient = nullptr; WGPUErrorCallback mErrorCallback = nullptr; diff --git a/src/dawn_wire/server/Server.h b/src/dawn_wire/server/Server.h index acf321ad19..4e725a88a7 100644 --- a/src/dawn_wire/server/Server.h +++ b/src/dawn_wire/server/Server.h @@ -99,6 +99,10 @@ namespace dawn_wire { namespace server { WGPUComputePipeline pipeline, const char* message, void* userdata); + static void ForwardCreateReadyRenderPipeline(WGPUCreateReadyPipelineStatus status, + WGPURenderPipeline pipeline, + const char* message, + void* userdata); // Error callbacks void OnUncapturedError(WGPUErrorType type, const char* message); @@ -115,6 +119,10 @@ namespace dawn_wire { namespace server { WGPUComputePipeline pipeline, const char* message, CreateReadyPipelineUserData* userdata); + void OnCreateReadyRenderPipelineCallback(WGPUCreateReadyPipelineStatus status, + WGPURenderPipeline pipeline, + const char* message, + CreateReadyPipelineUserData* userdata); #include "dawn_wire/server/ServerPrototypes_autogen.inc" diff --git a/src/dawn_wire/server/ServerDevice.cpp b/src/dawn_wire/server/ServerDevice.cpp index 997556200b..b3accf88d2 100644 --- a/src/dawn_wire/server/ServerDevice.cpp +++ b/src/dawn_wire/server/ServerDevice.cpp @@ -36,6 +36,16 @@ namespace dawn_wire { namespace server { status, pipeline, message, createReadyPipelineUserData); } + void Server::ForwardCreateReadyRenderPipeline(WGPUCreateReadyPipelineStatus status, + WGPURenderPipeline pipeline, + const char* message, + void* userdata) { + CreateReadyPipelineUserData* createReadyPipelineUserData = + static_cast(userdata); + createReadyPipelineUserData->server->OnCreateReadyRenderPipelineCallback( + status, pipeline, message, createReadyPipelineUserData); + } + void Server::OnUncapturedError(WGPUErrorType type, const char* message) { ReturnDeviceUncapturedErrorCallbackCmd cmd; cmd.type = type; @@ -105,6 +115,47 @@ namespace dawn_wire { namespace server { SerializeCommand(cmd); } + bool Server::DoDeviceCreateReadyRenderPipeline(WGPUDevice cDevice, + uint64_t requestSerial, + ObjectHandle pipelineObjectHandle, + const WGPURenderPipelineDescriptor* descriptor) { + auto* resultData = RenderPipelineObjects().Allocate(pipelineObjectHandle.id); + if (resultData == nullptr) { + return false; + } + + resultData->generation = pipelineObjectHandle.generation; + + std::unique_ptr userdata = + std::make_unique(); + userdata->server = this; + userdata->requestSerial = requestSerial; + userdata->pipelineObjectID = pipelineObjectHandle.id; + + mProcs.deviceCreateReadyRenderPipeline( + cDevice, descriptor, ForwardCreateReadyRenderPipeline, userdata.release()); + return true; + } + + void Server::OnCreateReadyRenderPipelineCallback(WGPUCreateReadyPipelineStatus status, + WGPURenderPipeline pipeline, + const char* message, + CreateReadyPipelineUserData* userdata) { + std::unique_ptr data(userdata); + if (status != WGPUCreateReadyPipelineStatus_Success) { + RenderPipelineObjects().Free(data->pipelineObjectID); + } else { + RenderPipelineObjects().Get(data->pipelineObjectID)->handle = pipeline; + } + + ReturnDeviceCreateReadyRenderPipelineCallbackCmd cmd; + cmd.status = status; + cmd.requestSerial = data->requestSerial; + cmd.message = message; + + SerializeCommand(cmd); + } + // static void Server::ForwardPopErrorScope(WGPUErrorType type, const char* message, void* userdata) { auto* data = reinterpret_cast(userdata); diff --git a/src/tests/end2end/CreateReadyPipelineTests.cpp b/src/tests/end2end/CreateReadyPipelineTests.cpp index 89845fb0e2..60c4b0678d 100644 --- a/src/tests/end2end/CreateReadyPipelineTests.cpp +++ b/src/tests/end2end/CreateReadyPipelineTests.cpp @@ -14,11 +14,13 @@ #include "tests/DawnTest.h" +#include "utils/ComboRenderPipelineDescriptor.h" #include "utils/WGPUHelpers.h" namespace { struct CreateReadyPipelineTask { - wgpu::ComputePipeline computePipeline; + wgpu::ComputePipeline computePipeline = nullptr; + wgpu::RenderPipeline renderPipeline = nullptr; bool isCompleted = false; std::string message; }; @@ -130,6 +132,133 @@ TEST_P(CreateReadyPipelineTest, CreateComputePipelineFailed) { ASSERT_EQ(nullptr, task.computePipeline.Get()); } +// Verify the basic use of CreateReadyRenderPipeline() works on all backends. +TEST_P(CreateReadyPipelineTest, BasicUseOfCreateReadyRenderPipeline) { + constexpr wgpu::TextureFormat kOutputAttachmentFormat = wgpu::TextureFormat::RGBA8Unorm; + + const char* vertexShader = R"( + #version 450 + void main() { + gl_Position = vec4(0.f, 0.f, 0.f, 1.f); + gl_PointSize = 1.0f; + })"; + const char* fragmentShader = R"( + #version 450 + layout(location = 0) out vec4 o_color; + void main() { + o_color = vec4(0.f, 1.f, 0.f, 1.f); + })"; + + utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device); + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vertexShader); + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fragmentShader); + renderPipelineDescriptor.vertexStage.module = vsModule; + renderPipelineDescriptor.cFragmentStage.module = fsModule; + renderPipelineDescriptor.cColorStates[0].format = kOutputAttachmentFormat; + renderPipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList; + + CreateReadyPipelineTask task; + device.CreateReadyRenderPipeline( + &renderPipelineDescriptor, + [](WGPUCreateReadyPipelineStatus status, WGPURenderPipeline returnPipeline, + const char* message, void* userdata) { + ASSERT_EQ(WGPUCreateReadyPipelineStatus::WGPUCreateReadyPipelineStatus_Success, status); + + CreateReadyPipelineTask* task = static_cast(userdata); + task->renderPipeline = wgpu::RenderPipeline::Acquire(returnPipeline); + task->isCompleted = true; + task->message = message; + }, + &task); + + wgpu::TextureDescriptor textureDescriptor; + textureDescriptor.size = {1, 1, 1}; + textureDescriptor.format = kOutputAttachmentFormat; + textureDescriptor.usage = wgpu::TextureUsage::OutputAttachment | wgpu::TextureUsage::CopySrc; + wgpu::Texture outputTexture = device.CreateTexture(&textureDescriptor); + + utils::ComboRenderPassDescriptor renderPassDescriptor({outputTexture.CreateView()}); + renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear; + renderPassDescriptor.cColorAttachments[0].clearColor = {1.f, 0.f, 0.f, 1.f}; + + wgpu::CommandBuffer commands; + { + wgpu::CommandEncoder encoder = device.CreateCommandEncoder(); + wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor); + + while (!task.isCompleted) { + WaitABit(); + } + ASSERT_TRUE(task.message.empty()); + ASSERT_NE(nullptr, task.renderPipeline.Get()); + + renderPassEncoder.SetPipeline(task.renderPipeline); + renderPassEncoder.Draw(1); + renderPassEncoder.EndPass(); + commands = encoder.Finish(); + } + + queue.Submit(1, &commands); + + EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), outputTexture, 0, 0); +} + +// Verify CreateReadyRenderPipeline() works as expected when there is any error that happens during +// the creation of the render pipeline. The SPEC requires that during the call of +// CreateReadyRenderPipeline() any error won't be forwarded to the error scope / unhandled error +// callback. +TEST_P(CreateReadyPipelineTest, CreateRenderPipelineFailed) { + DAWN_SKIP_TEST_IF(IsDawnValidationSkipped()); + + constexpr wgpu::TextureFormat kOutputAttachmentFormat = wgpu::TextureFormat::Depth32Float; + + const char* vertexShader = R"( + #version 450 + void main() { + gl_Position = vec4(0.f, 0.f, 0.f, 1.f); + gl_PointSize = 1.0f; + })"; + const char* fragmentShader = R"( + #version 450 + layout(location = 0) out vec4 o_color; + void main() { + o_color = vec4(0.f, 1.f, 0.f, 1.f); + })"; + + utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device); + wgpu::ShaderModule vsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vertexShader); + wgpu::ShaderModule fsModule = + utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fragmentShader); + renderPipelineDescriptor.vertexStage.module = vsModule; + renderPipelineDescriptor.cFragmentStage.module = fsModule; + renderPipelineDescriptor.cColorStates[0].format = kOutputAttachmentFormat; + renderPipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList; + + CreateReadyPipelineTask task; + device.CreateReadyRenderPipeline( + &renderPipelineDescriptor, + [](WGPUCreateReadyPipelineStatus status, WGPURenderPipeline returnPipeline, + const char* message, void* userdata) { + ASSERT_EQ(WGPUCreateReadyPipelineStatus::WGPUCreateReadyPipelineStatus_Error, status); + + CreateReadyPipelineTask* task = static_cast(userdata); + task->renderPipeline = wgpu::RenderPipeline::Acquire(returnPipeline); + task->isCompleted = true; + task->message = message; + }, + &task); + + while (!task.isCompleted) { + WaitABit(); + } + + ASSERT_FALSE(task.message.empty()); + ASSERT_EQ(nullptr, task.computePipeline.Get()); +} + DAWN_INSTANTIATE_TEST(CreateReadyPipelineTest, D3D12Backend(), MetalBackend(), diff --git a/src/tests/unittests/wire/WireCreateReadyPipelineTests.cpp b/src/tests/unittests/wire/WireCreateReadyPipelineTests.cpp index 6d699c0a06..98fb1f8d05 100644 --- a/src/tests/unittests/wire/WireCreateReadyPipelineTests.cpp +++ b/src/tests/unittests/wire/WireCreateReadyPipelineTests.cpp @@ -39,6 +39,25 @@ namespace { mockCreateReadyComputePipelineCallback->Call(status, pipeline, message, userdata); } + class MockCreateReadyRenderPipelineCallback { + public: + MOCK_METHOD(void, + Call, + (WGPUCreateReadyPipelineStatus status, + WGPURenderPipeline pipeline, + const char* message, + void* userdata)); + }; + + std::unique_ptr> + mockCreateReadyRenderPipelineCallback; + void ToMockCreateReadyRenderPipelineCallback(WGPUCreateReadyPipelineStatus status, + WGPURenderPipeline pipeline, + const char* message, + void* userdata) { + mockCreateReadyRenderPipelineCallback->Call(status, pipeline, message, userdata); + } + } // anonymous namespace class WireCreateReadyPipelineTest : public WireTest { @@ -48,6 +67,8 @@ class WireCreateReadyPipelineTest : public WireTest { mockCreateReadyComputePipelineCallback = std::make_unique>(); + mockCreateReadyRenderPipelineCallback = + std::make_unique>(); } void TearDown() override { @@ -55,6 +76,7 @@ class WireCreateReadyPipelineTest : public WireTest { // Delete mock so that expectations are checked mockCreateReadyComputePipelineCallback = nullptr; + mockCreateReadyRenderPipelineCallback = nullptr; } void FlushClient() { @@ -125,3 +147,69 @@ TEST_F(WireCreateReadyPipelineTest, CreateReadyComputePipelineError) { FlushServer(); } + +// Test when creating a render pipeline with CreateReadyRenderPipeline() successfully. +TEST_F(WireCreateReadyPipelineTest, CreateReadyRenderPipelineSuccess) { + WGPUShaderModuleDescriptor vertexDescriptor = {}; + WGPUShaderModule vsModule = wgpuDeviceCreateShaderModule(device, &vertexDescriptor); + WGPUShaderModule apiVsModule = api.GetNewShaderModule(); + EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiVsModule)); + + WGPURenderPipelineDescriptor pipelineDescriptor{}; + pipelineDescriptor.vertexStage.module = vsModule; + pipelineDescriptor.vertexStage.entryPoint = "main"; + + WGPUProgrammableStageDescriptor fragmentStage = {}; + fragmentStage.module = vsModule; + fragmentStage.entryPoint = "main"; + pipelineDescriptor.fragmentStage = &fragmentStage; + + wgpuDeviceCreateReadyRenderPipeline(device, &pipelineDescriptor, + ToMockCreateReadyRenderPipelineCallback, this); + EXPECT_CALL(api, OnDeviceCreateReadyRenderPipelineCallback(apiDevice, _, _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallDeviceCreateReadyRenderPipelineCallback( + apiDevice, WGPUCreateReadyPipelineStatus_Success, nullptr, ""); + })); + + FlushClient(); + + EXPECT_CALL(*mockCreateReadyRenderPipelineCallback, + Call(WGPUCreateReadyPipelineStatus_Success, _, StrEq(""), this)) + .Times(1); + + FlushServer(); +} + +// Test when creating a render pipeline with CreateReadyRenderPipeline() results in an error. +TEST_F(WireCreateReadyPipelineTest, CreateReadyRenderPipelineError) { + WGPUShaderModuleDescriptor vertexDescriptor = {}; + WGPUShaderModule vsModule = wgpuDeviceCreateShaderModule(device, &vertexDescriptor); + WGPUShaderModule apiVsModule = api.GetNewShaderModule(); + EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiVsModule)); + + WGPURenderPipelineDescriptor pipelineDescriptor{}; + pipelineDescriptor.vertexStage.module = vsModule; + pipelineDescriptor.vertexStage.entryPoint = "main"; + + WGPUProgrammableStageDescriptor fragmentStage = {}; + fragmentStage.module = vsModule; + fragmentStage.entryPoint = "main"; + pipelineDescriptor.fragmentStage = &fragmentStage; + + wgpuDeviceCreateReadyRenderPipeline(device, &pipelineDescriptor, + ToMockCreateReadyRenderPipelineCallback, this); + EXPECT_CALL(api, OnDeviceCreateReadyRenderPipelineCallback(apiDevice, _, _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallDeviceCreateReadyRenderPipelineCallback( + apiDevice, WGPUCreateReadyPipelineStatus_Error, nullptr, "Some error message"); + })); + + FlushClient(); + + EXPECT_CALL(*mockCreateReadyRenderPipelineCallback, + Call(WGPUCreateReadyPipelineStatus_Error, _, StrEq("Some error message"), this)) + .Times(1); + + FlushServer(); +}