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 <jiawei.shao@intel.com>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/dawn.json b/dawn.json
index 5e5fa34..6a3bb9a 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": [
@@ -594,6 +603,15 @@
]
},
{
+ "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",
"args": [
diff --git a/dawn_wire.json b/dawn_wire.json
index 8870932..505af37 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 77f73be..10d3716 100644
--- a/generator/templates/mock_webgpu.cpp
+++ b/generator/templates/mock_webgpu.cpp
@@ -112,6 +112,18 @@
OnDeviceCreateReadyComputePipelineCallback(self, descriptor, callback, userdata);
}
+void ProcTableAsClass::DeviceCreateReadyRenderPipeline(
+ WGPUDevice self,
+ WGPURenderPipelineDescriptor const * descriptor,
+ WGPUCreateReadyRenderPipelineCallback callback,
+ void* userdata) {
+ auto object = reinterpret_cast<ProcTableAsClass::Object*>(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 @@
object->createReadyComputePipelineCallback(status, pipeline, message, object->userdata);
}
+void ProcTableAsClass::CallDeviceCreateReadyRenderPipelineCallback(WGPUDevice device,
+ WGPUCreateReadyPipelineStatus status,
+ WGPURenderPipeline pipeline,
+ const char* message) {
+ auto object = reinterpret_cast<ProcTableAsClass::Object*>(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 813f4bd..e3dfdbb 100644
--- a/generator/templates/mock_webgpu.h
+++ b/generator/templates/mock_webgpu.h
@@ -56,6 +56,10 @@
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 @@
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 @@
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 @@
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 @@
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 9d4ccc3..cfc19cc 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<WGPUComputePipeline>(mPipeline), "", mUserData);
+ ASSERT(mPipeline != nullptr);
+ ASSERT(mCreateReadyComputePipelineCallback != nullptr);
+
+ mCreateReadyComputePipelineCallback(WGPUCreateReadyPipelineStatus_Success,
+ reinterpret_cast<WGPUComputePipeline>(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<WGPURenderPipeline>(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<CreateReadyComputePipelineTask> task,
+ void CreateReadyPipelineTracker::TrackTask(std::unique_ptr<CreateReadyPipelineTaskBase> 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 b85c8c2..0c6b1dc 100644
--- a/src/dawn_native/CreateReadyPipelineTracker.h
+++ b/src/dawn_native/CreateReadyPipelineTracker.h
@@ -25,19 +25,41 @@
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 @@
CreateReadyPipelineTracker(DeviceBase* device);
~CreateReadyPipelineTracker();
- void TrackTask(std::unique_ptr<CreateReadyComputePipelineTask> task,
- ExecutionSerial serial);
+ void TrackTask(std::unique_ptr<CreateReadyPipelineTaskBase> task, ExecutionSerial serial);
void Tick(ExecutionSerial finishedSerial);
private:
DeviceBase* mDevice;
- SerialQueue<ExecutionSerial, std::unique_ptr<CreateReadyComputePipelineTask>>
- mCreateReadyComputePipelineTasksInFlight;
+ SerialQueue<ExecutionSerial, std::unique_ptr<CreateReadyPipelineTaskBase>>
+ mCreateReadyPipelineTasksInFlight;
};
} // namespace dawn_native
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index 5ae6e46..2c5b3aa 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -674,6 +674,22 @@
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<ErrorData> error = maybeError.AcquireError();
+ callback(WGPUCreateReadyPipelineStatus_Error, nullptr, error->GetMessage().c_str(),
+ userdata);
+ return;
+ }
+
+ std::unique_ptr<CreateReadyRenderPipelineTask> request =
+ std::make_unique<CreateReadyRenderPipelineTask>(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 eab0763..494c431 100644
--- a/src/dawn_native/Device.h
+++ b/src/dawn_native/Device.h
@@ -149,6 +149,9 @@
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 338863e..cd9b5ab 100644
--- a/src/dawn_wire/client/ClientDoers.cpp
+++ b/src/dawn_wire/client/ClientDoers.cpp
@@ -85,8 +85,13 @@
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 fd175fe..b49f8a4 100644
--- a/src/dawn_wire/client/Device.cpp
+++ b/src/dawn_wire/client/Device.cpp
@@ -43,10 +43,18 @@
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 @@
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 @@
// 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<WGPUComputePipeline>(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<WGPURenderPipeline>(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 087c72c..7d14c6f 100644
--- a/src/dawn_wire/client/Device.h
+++ b/src/dawn_wire/client/Device.h
@@ -43,6 +43,9 @@
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 @@
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 @@
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<uint64_t, CreateReadyComputePipelineRequest> mCreateReadyComputePipelineRequests;
- uint64_t mCreateReadyComputePipelineRequestSerial = 0;
+ std::map<uint64_t, CreateReadyPipelineRequest> 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 acf321a..4e725a8 100644
--- a/src/dawn_wire/server/Server.h
+++ b/src/dawn_wire/server/Server.h
@@ -99,6 +99,10 @@
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 @@
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 9975562..b3accf8 100644
--- a/src/dawn_wire/server/ServerDevice.cpp
+++ b/src/dawn_wire/server/ServerDevice.cpp
@@ -36,6 +36,16 @@
status, pipeline, message, createReadyPipelineUserData);
}
+ void Server::ForwardCreateReadyRenderPipeline(WGPUCreateReadyPipelineStatus status,
+ WGPURenderPipeline pipeline,
+ const char* message,
+ void* userdata) {
+ CreateReadyPipelineUserData* createReadyPipelineUserData =
+ static_cast<CreateReadyPipelineUserData*>(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 @@
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<CreateReadyPipelineUserData> userdata =
+ std::make_unique<CreateReadyPipelineUserData>();
+ 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<CreateReadyPipelineUserData> 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<ErrorScopeUserdata*>(userdata);
diff --git a/src/tests/end2end/CreateReadyPipelineTests.cpp b/src/tests/end2end/CreateReadyPipelineTests.cpp
index 89845fb..60c4b06 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 @@
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<CreateReadyPipelineTask*>(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<CreateReadyPipelineTask*>(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 6d699c0..98fb1f8 100644
--- a/src/tests/unittests/wire/WireCreateReadyPipelineTests.cpp
+++ b/src/tests/unittests/wire/WireCreateReadyPipelineTests.cpp
@@ -39,6 +39,25 @@
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<StrictMock<MockCreateReadyRenderPipelineCallback>>
+ 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 @@
mockCreateReadyComputePipelineCallback =
std::make_unique<StrictMock<MockCreateReadyComputePipelineCallback>>();
+ mockCreateReadyRenderPipelineCallback =
+ std::make_unique<StrictMock<MockCreateReadyRenderPipelineCallback>>();
}
void TearDown() override {
@@ -55,6 +76,7 @@
// Delete mock so that expectations are checked
mockCreateReadyComputePipelineCallback = nullptr;
+ mockCreateReadyRenderPipelineCallback = nullptr;
}
void FlushClient() {
@@ -125,3 +147,69 @@
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();
+}