Add the entry point of CreateReadyComputePipeline

This patch adds the entry point of CreateReadyComputePipeline in both
dawn_native and dawn_wire.

TODOs:
1. Add more tests in dawn_unittests and dawn_end2end_tests.
2. Put the main logic of creating a pipeline into a separate thread.

BUG=dawn:529
TEST=dawn_end2end_tests

Change-Id: I7edd269a5422a8b85320a7f9173df925decba633
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/30060
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
Jiawei Shao 2020-10-19 01:56:08 +00:00 committed by Commit Bot service account
parent 47a6a94e15
commit ae5f950444
18 changed files with 638 additions and 1 deletions

View File

@ -501,6 +501,24 @@
{"name": "compute stage", "type": "programmable stage descriptor"} {"name": "compute stage", "type": "programmable stage descriptor"}
] ]
}, },
"create ready compute pipeline callback": {
"category": "callback",
"args": [
{"name": "status", "type": "create ready pipeline status"},
{"name": "pipeline", "type": "compute pipeline"},
{"name": "message", "type": "char", "annotation": "const*", "length": "strlen"},
{"name": "userdata", "type": "void", "annotation": "*"}
]
},
"create ready pipeline status": {
"category": "enum",
"values": [
{"value": 0, "name": "success"},
{"value": 1, "name": "error"},
{"value": 2, "name": "device lost"},
{"value": 3, "name": "unknown"}
]
},
"cull mode": { "cull mode": {
"category": "enum", "category": "enum",
"values": [ "values": [
@ -552,6 +570,15 @@
{"name": "descriptor", "type": "compute pipeline descriptor", "annotation": "const*"} {"name": "descriptor", "type": "compute pipeline descriptor", "annotation": "const*"}
] ]
}, },
{
"name": "create ready compute pipeline",
"returns": "void",
"args": [
{"name": "descriptor", "type": "compute pipeline descriptor", "annotation": "const*"},
{"name": "callback", "type": "create ready compute pipeline callback"},
{"name": "userdata", "type": "void", "annotation": "*"}
]
},
{ {
"name": "create pipeline layout", "name": "create pipeline layout",
"returns": "pipeline layout", "returns": "pipeline layout",

View File

@ -39,6 +39,12 @@
{ "name": "handle create info length", "type": "uint64_t" }, { "name": "handle create info length", "type": "uint64_t" },
{ "name": "handle create info", "type": "uint8_t", "annotation": "const*", "length": "handle create info length", "skip_serialize": true} { "name": "handle create info", "type": "uint8_t", "annotation": "const*", "length": "handle create info length", "skip_serialize": true}
], ],
"device create ready compute pipeline": [
{ "name": "device", "type": "device" },
{ "name": "request serial", "type": "uint64_t" },
{ "name": "pipeline object handle", "type": "ObjectHandle", "handle_type": "compute pipeline"},
{ "name": "descriptor", "type": "compute pipeline descriptor", "annotation": "const*"}
],
"device pop error scope": [ "device pop error scope": [
{ "name": "device", "type": "device" }, { "name": "device", "type": "device" },
{ "name": "request serial", "type": "uint64_t" } { "name": "request serial", "type": "uint64_t" }
@ -76,6 +82,11 @@
{ "name": "read initial data info length", "type": "uint64_t" }, { "name": "read initial data info length", "type": "uint64_t" },
{ "name": "read initial data info", "type": "uint8_t", "annotation": "const*", "length": "read initial data info length", "skip_serialize": true } { "name": "read initial data info", "type": "uint8_t", "annotation": "const*", "length": "read initial data info length", "skip_serialize": true }
], ],
"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"}
],
"device uncaptured error callback": [ "device uncaptured error callback": [
{ "name": "type", "type": "error type"}, { "name": "type", "type": "error type"},
{ "name": "message", "type": "char", "annotation": "const*", "length": "strlen" } { "name": "message", "type": "char", "annotation": "const*", "length": "strlen" }
@ -109,6 +120,7 @@
"BufferGetConstMappedRange", "BufferGetConstMappedRange",
"BufferGetMappedRange", "BufferGetMappedRange",
"DeviceCreateBuffer", "DeviceCreateBuffer",
"DeviceCreateReadyComputePipeline",
"DevicePopErrorScope", "DevicePopErrorScope",
"DeviceSetDeviceLostCallback", "DeviceSetDeviceLostCallback",
"DeviceSetUncapturedErrorCallback", "DeviceSetUncapturedErrorCallback",

View File

@ -100,6 +100,18 @@ void ProcTableAsClass::FenceOnCompletion(WGPUFence self,
OnFenceOnCompletionCallback(self, value, callback, userdata); OnFenceOnCompletionCallback(self, value, callback, userdata);
} }
void ProcTableAsClass::DeviceCreateReadyComputePipeline(
WGPUDevice self,
WGPUComputePipelineDescriptor const * descriptor,
WGPUCreateReadyComputePipelineCallback callback,
void* userdata) {
auto object = reinterpret_cast<ProcTableAsClass::Object*>(self);
object->createReadyComputePipelineCallback = callback;
object->userdata = userdata;
OnDeviceCreateReadyComputePipelineCallback(self, descriptor, callback, userdata);
}
void ProcTableAsClass::CallDeviceErrorCallback(WGPUDevice device, void ProcTableAsClass::CallDeviceErrorCallback(WGPUDevice device,
WGPUErrorType type, WGPUErrorType type,
const char* message) { const char* message) {
@ -123,6 +135,14 @@ void ProcTableAsClass::CallFenceOnCompletionCallback(WGPUFence fence,
object->fenceOnCompletionCallback(status, object->userdata); object->fenceOnCompletionCallback(status, object->userdata);
} }
void ProcTableAsClass::CallDeviceCreateReadyComputePipelineCallback(WGPUDevice device,
WGPUCreateReadyPipelineStatus status,
WGPUComputePipeline pipeline,
const char* message) {
auto object = reinterpret_cast<ProcTableAsClass::Object*>(device);
object->createReadyComputePipelineCallback(status, pipeline, message, object->userdata);
}
{% for type in by_category["object"] %} {% for type in by_category["object"] %}
{{as_cType(type.name)}} ProcTableAsClass::GetNew{{type.name.CamelCase()}}() { {{as_cType(type.name)}} ProcTableAsClass::GetNew{{type.name.CamelCase()}}() {
mObjects.emplace_back(new Object); mObjects.emplace_back(new Object);

View File

@ -52,6 +52,10 @@ class ProcTableAsClass {
{% endfor %} {% endfor %}
// Stores callback and userdata and calls the On* methods // Stores callback and userdata and calls the On* methods
void DeviceCreateReadyComputePipeline(WGPUDevice self,
WGPUComputePipelineDescriptor const * descriptor,
WGPUCreateReadyComputePipelineCallback callback,
void* userdata);
void DeviceSetUncapturedErrorCallback(WGPUDevice self, void DeviceSetUncapturedErrorCallback(WGPUDevice self,
WGPUErrorCallback callback, WGPUErrorCallback callback,
void* userdata); void* userdata);
@ -71,6 +75,11 @@ class ProcTableAsClass {
void* userdata); void* userdata);
// Special cased mockable methods // Special cased mockable methods
virtual void OnDeviceCreateReadyComputePipelineCallback(
WGPUDevice device,
WGPUComputePipelineDescriptor const * descriptor,
WGPUCreateReadyComputePipelineCallback callback,
void* userdata) = 0;
virtual void OnDeviceSetUncapturedErrorCallback(WGPUDevice device, virtual void OnDeviceSetUncapturedErrorCallback(WGPUDevice device,
WGPUErrorCallback callback, WGPUErrorCallback callback,
void* userdata) = 0; void* userdata) = 0;
@ -89,6 +98,10 @@ class ProcTableAsClass {
void* userdata) = 0; void* userdata) = 0;
// Calls the stored callbacks // Calls the stored callbacks
void CallDeviceCreateReadyComputePipelineCallback(WGPUDevice device,
WGPUCreateReadyPipelineStatus status,
WGPUComputePipeline pipeline,
const char* message);
void CallDeviceErrorCallback(WGPUDevice device, WGPUErrorType type, const char* message); void CallDeviceErrorCallback(WGPUDevice device, WGPUErrorType type, const char* message);
void CallDeviceLostCallback(WGPUDevice device, const char* message); void CallDeviceLostCallback(WGPUDevice device, const char* message);
void CallMapAsyncCallback(WGPUBuffer buffer, WGPUBufferMapAsyncStatus status); void CallMapAsyncCallback(WGPUBuffer buffer, WGPUBufferMapAsyncStatus status);
@ -97,6 +110,7 @@ class ProcTableAsClass {
struct Object { struct Object {
ProcTableAsClass* procs = nullptr; ProcTableAsClass* procs = nullptr;
WGPUErrorCallback deviceErrorCallback = nullptr; WGPUErrorCallback deviceErrorCallback = nullptr;
WGPUCreateReadyComputePipelineCallback createReadyComputePipelineCallback = nullptr;
WGPUDeviceLostCallback deviceLostCallback = nullptr; WGPUDeviceLostCallback deviceLostCallback = nullptr;
WGPUBufferMapCallback mapAsyncCallback = nullptr; WGPUBufferMapCallback mapAsyncCallback = nullptr;
WGPUFenceOnCompletionCallback fenceOnCompletionCallback = nullptr; WGPUFenceOnCompletionCallback fenceOnCompletionCallback = nullptr;
@ -130,6 +144,12 @@ class MockProcTable : public ProcTableAsClass {
MOCK_METHOD(void, {{as_MethodSuffix(type.name, Name("release"))}}, ({{as_cType(type.name)}} self), (override)); MOCK_METHOD(void, {{as_MethodSuffix(type.name, Name("release"))}}, ({{as_cType(type.name)}} self), (override));
{% endfor %} {% endfor %}
MOCK_METHOD(void,
OnDeviceCreateReadyComputePipelineCallback,
(WGPUDevice device, WGPUComputePipelineDescriptor const * descriptor,
WGPUCreateReadyComputePipelineCallback callback,
void* userdata),
(override));
MOCK_METHOD(void, OnDeviceSetUncapturedErrorCallback, (WGPUDevice device, WGPUErrorCallback 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(void, OnDeviceSetDeviceLostCallback, (WGPUDevice device, WGPUDeviceLostCallback callback, void* userdata), (override));
MOCK_METHOD(bool, OnDevicePopErrorScopeCallback, (WGPUDevice device, WGPUErrorCallback callback, void* userdata), (override)); MOCK_METHOD(bool, OnDevicePopErrorScopeCallback, (WGPUDevice device, WGPUErrorCallback callback, void* userdata), (override));

View File

@ -183,6 +183,8 @@ source_set("dawn_native_sources") {
"ComputePassEncoder.h", "ComputePassEncoder.h",
"ComputePipeline.cpp", "ComputePipeline.cpp",
"ComputePipeline.h", "ComputePipeline.h",
"CreateReadyPipelineTracker.cpp",
"CreateReadyPipelineTracker.h",
"Device.cpp", "Device.cpp",
"Device.h", "Device.h",
"DynamicUploader.cpp", "DynamicUploader.cpp",

View File

@ -66,6 +66,8 @@ target_sources(dawn_native PRIVATE
"ComputePassEncoder.h" "ComputePassEncoder.h"
"ComputePipeline.cpp" "ComputePipeline.cpp"
"ComputePipeline.h" "ComputePipeline.h"
"CreateReadyPipelineTracker.cpp",
"CreateReadyPipelineTracker.h",
"Device.cpp" "Device.cpp"
"Device.h" "Device.h"
"DynamicUploader.cpp" "DynamicUploader.cpp"

View File

@ -0,0 +1,56 @@
// Copyright 2020 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "dawn_native/CreateReadyPipelineTracker.h"
#include "dawn_native/Device.h"
namespace dawn_native {
CreateReadyComputePipelineTask::CreateReadyComputePipelineTask(
ComputePipelineBase* pipeline,
WGPUCreateReadyComputePipelineCallback callback,
void* userdata)
: mPipeline(pipeline), mCallback(callback), mUserData(userdata) {
}
CreateReadyComputePipelineTask::~CreateReadyComputePipelineTask() {
}
void CreateReadyComputePipelineTask::Finish() {
mCallback(WGPUCreateReadyPipelineStatus_Success,
reinterpret_cast<WGPUComputePipeline>(mPipeline), "", mUserData);
}
CreateReadyPipelineTracker::CreateReadyPipelineTracker(DeviceBase* device) : mDevice(device) {
}
CreateReadyPipelineTracker::~CreateReadyPipelineTracker() {
ASSERT(mCreateReadyComputePipelineTasksInFlight.Empty());
}
void CreateReadyPipelineTracker::TrackTask(std::unique_ptr<CreateReadyComputePipelineTask> task,
ExecutionSerial serial) {
mCreateReadyComputePipelineTasksInFlight.Enqueue(std::move(task), serial);
mDevice->AddFutureSerial(serial);
}
void CreateReadyPipelineTracker::Tick(ExecutionSerial finishedSerial) {
for (auto& task : mCreateReadyComputePipelineTasksInFlight.IterateUpTo(finishedSerial)) {
task->Finish();
}
mCreateReadyComputePipelineTasksInFlight.ClearUpTo(finishedSerial);
}
} // namespace dawn_native

View File

@ -0,0 +1,60 @@
// Copyright 2020 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.
#ifndef DAWNNATIVE_CREATEREADYPIPELINETRACKER_H_
#define DAWNNATIVE_CREATEREADYPIPELINETRACKER_H_
#include "common/SerialQueue.h"
#include "dawn/webgpu.h"
#include "dawn_native/IntegerTypes.h"
#include <memory>
namespace dawn_native {
class ComputePipelineBase;
class DeviceBase;
struct CreateReadyComputePipelineTask {
CreateReadyComputePipelineTask(ComputePipelineBase* pipeline,
WGPUCreateReadyComputePipelineCallback callback,
void* userdata);
~CreateReadyComputePipelineTask();
void Finish();
private:
ComputePipelineBase* mPipeline;
WGPUCreateReadyComputePipelineCallback mCallback;
void* mUserData;
};
class CreateReadyPipelineTracker {
public:
CreateReadyPipelineTracker(DeviceBase* device);
~CreateReadyPipelineTracker();
void TrackTask(std::unique_ptr<CreateReadyComputePipelineTask> task,
ExecutionSerial serial);
void Tick(ExecutionSerial finishedSerial);
private:
DeviceBase* mDevice;
SerialQueue<ExecutionSerial, std::unique_ptr<CreateReadyComputePipelineTask>>
mCreateReadyComputePipelineTasksInFlight;
};
} // namespace dawn_native
#endif

View File

@ -23,6 +23,7 @@
#include "dawn_native/CommandBuffer.h" #include "dawn_native/CommandBuffer.h"
#include "dawn_native/CommandEncoder.h" #include "dawn_native/CommandEncoder.h"
#include "dawn_native/ComputePipeline.h" #include "dawn_native/ComputePipeline.h"
#include "dawn_native/CreateReadyPipelineTracker.h"
#include "dawn_native/DynamicUploader.h" #include "dawn_native/DynamicUploader.h"
#include "dawn_native/ErrorData.h" #include "dawn_native/ErrorData.h"
#include "dawn_native/ErrorScope.h" #include "dawn_native/ErrorScope.h"
@ -102,6 +103,7 @@ namespace dawn_native {
mCaches = std::make_unique<DeviceBase::Caches>(); mCaches = std::make_unique<DeviceBase::Caches>();
mErrorScopeTracker = std::make_unique<ErrorScopeTracker>(this); mErrorScopeTracker = std::make_unique<ErrorScopeTracker>(this);
mDynamicUploader = std::make_unique<DynamicUploader>(this); mDynamicUploader = std::make_unique<DynamicUploader>(this);
mCreateReadyPipelineTracker = std::make_unique<CreateReadyPipelineTracker>(this);
mDeprecationWarnings = std::make_unique<DeprecationWarnings>(); mDeprecationWarnings = std::make_unique<DeprecationWarnings>();
// Starting from now the backend can start doing reentrant calls so the device is marked as // Starting from now the backend can start doing reentrant calls so the device is marked as
@ -149,6 +151,8 @@ namespace dawn_native {
// pending callbacks. // pending callbacks.
mErrorScopeTracker->Tick(GetCompletedCommandSerial()); mErrorScopeTracker->Tick(GetCompletedCommandSerial());
GetDefaultQueue()->Tick(GetCompletedCommandSerial()); GetDefaultQueue()->Tick(GetCompletedCommandSerial());
mCreateReadyPipelineTracker->Tick(GetCompletedCommandSerial());
// call TickImpl once last time to clean up resources // call TickImpl once last time to clean up resources
// Ignore errors so that we can continue with destruction // Ignore errors so that we can continue with destruction
IgnoreErrors(TickImpl()); IgnoreErrors(TickImpl());
@ -163,6 +167,7 @@ namespace dawn_native {
} }
mErrorScopeTracker = nullptr; mErrorScopeTracker = nullptr;
mDynamicUploader = nullptr; mDynamicUploader = nullptr;
mCreateReadyPipelineTracker = nullptr;
mEmptyBindGroupLayout = nullptr; mEmptyBindGroupLayout = nullptr;
@ -619,6 +624,22 @@ namespace dawn_native {
return result; return result;
} }
void DeviceBase::CreateReadyComputePipeline(const ComputePipelineDescriptor* descriptor,
WGPUCreateReadyComputePipelineCallback callback,
void* userdata) {
ComputePipelineBase* result = nullptr;
MaybeError maybeError = CreateComputePipelineInternal(&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<CreateReadyComputePipelineTask> request =
std::make_unique<CreateReadyComputePipelineTask>(result, callback, userdata);
mCreateReadyPipelineTracker->TrackTask(std::move(request), GetPendingCommandSerial());
}
PipelineLayoutBase* DeviceBase::CreatePipelineLayout( PipelineLayoutBase* DeviceBase::CreatePipelineLayout(
const PipelineLayoutDescriptor* descriptor) { const PipelineLayoutDescriptor* descriptor) {
PipelineLayoutBase* result = nullptr; PipelineLayoutBase* result = nullptr;
@ -750,6 +771,7 @@ namespace dawn_native {
mDynamicUploader->Deallocate(mCompletedSerial); mDynamicUploader->Deallocate(mCompletedSerial);
mErrorScopeTracker->Tick(mCompletedSerial); mErrorScopeTracker->Tick(mCompletedSerial);
GetDefaultQueue()->Tick(mCompletedSerial); GetDefaultQueue()->Tick(mCompletedSerial);
mCreateReadyPipelineTracker->Tick(mCompletedSerial);
} }
return !IsDeviceIdle(); return !IsDeviceIdle();

View File

@ -33,6 +33,7 @@ namespace dawn_native {
class AttachmentState; class AttachmentState;
class AttachmentStateBlueprint; class AttachmentStateBlueprint;
class BindGroupLayoutBase; class BindGroupLayoutBase;
class CreateReadyPipelineTracker;
class DynamicUploader; class DynamicUploader;
class ErrorScope; class ErrorScope;
class ErrorScopeTracker; class ErrorScopeTracker;
@ -145,6 +146,9 @@ namespace dawn_native {
PipelineLayoutBase* CreatePipelineLayout(const PipelineLayoutDescriptor* descriptor); PipelineLayoutBase* CreatePipelineLayout(const PipelineLayoutDescriptor* descriptor);
QuerySetBase* CreateQuerySet(const QuerySetDescriptor* descriptor); QuerySetBase* CreateQuerySet(const QuerySetDescriptor* descriptor);
QueueBase* CreateQueue(); QueueBase* CreateQueue();
void CreateReadyComputePipeline(const ComputePipelineDescriptor* descriptor,
WGPUCreateReadyComputePipelineCallback callback,
void* userdata);
RenderBundleEncoder* CreateRenderBundleEncoder( RenderBundleEncoder* CreateRenderBundleEncoder(
const RenderBundleEncoderDescriptor* descriptor); const RenderBundleEncoderDescriptor* descriptor);
RenderPipelineBase* CreateRenderPipeline(const RenderPipelineDescriptor* descriptor); RenderPipelineBase* CreateRenderPipeline(const RenderPipelineDescriptor* descriptor);
@ -362,6 +366,7 @@ namespace dawn_native {
std::unique_ptr<DynamicUploader> mDynamicUploader; std::unique_ptr<DynamicUploader> mDynamicUploader;
std::unique_ptr<ErrorScopeTracker> mErrorScopeTracker; std::unique_ptr<ErrorScopeTracker> mErrorScopeTracker;
std::unique_ptr<CreateReadyPipelineTracker> mCreateReadyPipelineTracker;
Ref<QueueBase> mDefaultQueue; Ref<QueueBase> mDefaultQueue;
struct DeprecationWarnings; struct DeprecationWarnings;

View File

@ -82,4 +82,11 @@ namespace dawn_wire { namespace client {
return true; return true;
} }
bool Client::DoDeviceCreateReadyComputePipelineCallback(uint64_t requestSerial,
WGPUCreateReadyPipelineStatus status,
const char* message) {
mDevice->OnCreateReadyComputePipelineCallback(requestSerial, status, message);
return true;
}
}} // namespace dawn_wire::client }} // namespace dawn_wire::client

View File

@ -15,7 +15,6 @@
#include "dawn_wire/client/Device.h" #include "dawn_wire/client/Device.h"
#include "common/Assert.h" #include "common/Assert.h"
#include "dawn_wire/WireCmd_autogen.h"
#include "dawn_wire/client/ApiObjects_autogen.h" #include "dawn_wire/client/ApiObjects_autogen.h"
#include "dawn_wire/client/Client.h" #include "dawn_wire/client/Client.h"
#include "dawn_wire/client/ObjectAllocator.h" #include "dawn_wire/client/ObjectAllocator.h"
@ -44,6 +43,12 @@ namespace dawn_wire { namespace client {
it.second.callback(WGPUErrorType_Unknown, "Device destroyed", it.second.userdata); 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);
}
// Destroy the default queue // Destroy the default queue
DestroyObjectCmd cmd; DestroyObjectCmd cmd;
cmd.objectType = ObjectType::Queue; cmd.objectType = ObjectType::Queue;
@ -158,4 +163,56 @@ namespace dawn_wire { namespace client {
return ToAPI(mDefaultQueue); return ToAPI(mDefaultQueue);
} }
void Device::CreateReadyComputePipeline(WGPUComputePipelineDescriptor const* descriptor,
WGPUCreateReadyComputePipelineCallback callback,
void* userdata) {
DeviceCreateReadyComputePipelineCmd cmd;
cmd.device = ToAPI(this);
cmd.descriptor = descriptor;
uint64_t serial = mCreateReadyComputePipelineRequestSerial++;
ASSERT(mCreateReadyComputePipelineRequests.find(serial) ==
mCreateReadyComputePipelineRequests.end());
cmd.requestSerial = serial;
auto* allocation = GetClient()->ComputePipelineAllocator().New(this);
CreateReadyComputePipelineRequest request = {};
request.callback = 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);
}
bool Device::OnCreateReadyComputePipelineCallback(uint64_t requestSerial,
WGPUCreateReadyPipelineStatus status,
const char* message) {
const auto& requestIt = mCreateReadyComputePipelineRequests.find(requestSerial);
if (requestIt == mCreateReadyComputePipelineRequests.end()) {
return false;
}
CreateReadyComputePipelineRequest request = std::move(requestIt->second);
mCreateReadyComputePipelineRequests.erase(requestIt);
auto pipelineAllocation =
GetClient()->ComputePipelineAllocator().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()->ComputePipelineAllocator().Free(pipelineAllocation);
request.callback(status, nullptr, message, request.userdata);
return true;
}
WGPUComputePipeline pipeline = reinterpret_cast<WGPUComputePipeline>(pipelineAllocation);
request.callback(status, pipeline, message, request.userdata);
return true;
}
}} // namespace dawn_wire::client }} // namespace dawn_wire::client

View File

@ -17,6 +17,7 @@
#include <dawn/webgpu.h> #include <dawn/webgpu.h>
#include "dawn_wire/WireCmd_autogen.h"
#include "dawn_wire/client/ObjectBase.h" #include "dawn_wire/client/ObjectBase.h"
#include <map> #include <map>
@ -39,12 +40,18 @@ namespace dawn_wire { namespace client {
bool PopErrorScope(WGPUErrorCallback callback, void* userdata); bool PopErrorScope(WGPUErrorCallback callback, void* userdata);
WGPUBuffer CreateBuffer(const WGPUBufferDescriptor* descriptor); WGPUBuffer CreateBuffer(const WGPUBufferDescriptor* descriptor);
WGPUBuffer CreateErrorBuffer(); WGPUBuffer CreateErrorBuffer();
void CreateReadyComputePipeline(WGPUComputePipelineDescriptor const* descriptor,
WGPUCreateReadyComputePipelineCallback callback,
void* userdata);
void HandleError(WGPUErrorType errorType, const char* message); void HandleError(WGPUErrorType errorType, const char* message);
void HandleDeviceLost(const char* message); void HandleDeviceLost(const char* message);
bool OnPopErrorScopeCallback(uint64_t requestSerial, bool OnPopErrorScopeCallback(uint64_t requestSerial,
WGPUErrorType type, WGPUErrorType type,
const char* message); const char* message);
bool OnCreateReadyComputePipelineCallback(uint64_t requestSerial,
WGPUCreateReadyPipelineStatus status,
const char* message);
WGPUQueue GetDefaultQueue(); WGPUQueue GetDefaultQueue();
@ -57,6 +64,14 @@ namespace dawn_wire { namespace client {
uint64_t mErrorScopeRequestSerial = 0; uint64_t mErrorScopeRequestSerial = 0;
uint64_t mErrorScopeStackSize = 0; uint64_t mErrorScopeStackSize = 0;
struct CreateReadyComputePipelineRequest {
WGPUCreateReadyComputePipelineCallback callback = nullptr;
void* userdata = nullptr;
ObjectId pipelineObjectID;
};
std::map<uint64_t, CreateReadyComputePipelineRequest> mCreateReadyComputePipelineRequests;
uint64_t mCreateReadyComputePipelineRequestSerial = 0;
Client* mClient = nullptr; Client* mClient = nullptr;
WGPUErrorCallback mErrorCallback = nullptr; WGPUErrorCallback mErrorCallback = nullptr;
WGPUDeviceLostCallback mDeviceLostCallback = nullptr; WGPUDeviceLostCallback mDeviceLostCallback = nullptr;

View File

@ -55,6 +55,12 @@ namespace dawn_wire { namespace server {
uint64_t requestSerial; uint64_t requestSerial;
}; };
struct CreateReadyPipelineUserData {
Server* server;
uint64_t requestSerial;
ObjectId pipelineObjectID;
};
class Server : public ServerBase { class Server : public ServerBase {
public: public:
Server(WGPUDevice device, Server(WGPUDevice device,
@ -89,6 +95,10 @@ namespace dawn_wire { namespace server {
static void ForwardBufferMapAsync(WGPUBufferMapAsyncStatus status, void* userdata); static void ForwardBufferMapAsync(WGPUBufferMapAsyncStatus status, void* userdata);
static void ForwardFenceCompletedValue(WGPUFenceCompletionStatus status, void* userdata); static void ForwardFenceCompletedValue(WGPUFenceCompletionStatus status, void* userdata);
static void ForwardFenceOnCompletion(WGPUFenceCompletionStatus status, void* userdata); static void ForwardFenceOnCompletion(WGPUFenceCompletionStatus status, void* userdata);
static void ForwardCreateReadyComputePipeline(WGPUCreateReadyPipelineStatus status,
WGPUComputePipeline pipeline,
const char* message,
void* userdata);
// Error callbacks // Error callbacks
void OnUncapturedError(WGPUErrorType type, const char* message); void OnUncapturedError(WGPUErrorType type, const char* message);
@ -101,6 +111,10 @@ namespace dawn_wire { namespace server {
FenceCompletionUserdata* userdata); FenceCompletionUserdata* userdata);
void OnFenceOnCompletion(WGPUFenceCompletionStatus status, void OnFenceOnCompletion(WGPUFenceCompletionStatus status,
FenceOnCompletionUserdata* userdata); FenceOnCompletionUserdata* userdata);
void OnCreateReadyComputePipelineCallback(WGPUCreateReadyPipelineStatus status,
WGPUComputePipeline pipeline,
const char* message,
CreateReadyPipelineUserData* userdata);
#include "dawn_wire/server/ServerPrototypes_autogen.inc" #include "dawn_wire/server/ServerPrototypes_autogen.inc"

View File

@ -26,6 +26,16 @@ namespace dawn_wire { namespace server {
server->OnDeviceLost(message); server->OnDeviceLost(message);
} }
void Server::ForwardCreateReadyComputePipeline(WGPUCreateReadyPipelineStatus status,
WGPUComputePipeline pipeline,
const char* message,
void* userdata) {
CreateReadyPipelineUserData* createReadyPipelineUserData =
static_cast<CreateReadyPipelineUserData*>(userdata);
createReadyPipelineUserData->server->OnCreateReadyComputePipelineCallback(
status, pipeline, message, createReadyPipelineUserData);
}
void Server::OnUncapturedError(WGPUErrorType type, const char* message) { void Server::OnUncapturedError(WGPUErrorType type, const char* message) {
ReturnDeviceUncapturedErrorCallbackCmd cmd; ReturnDeviceUncapturedErrorCallbackCmd cmd;
cmd.type = type; cmd.type = type;
@ -53,6 +63,48 @@ namespace dawn_wire { namespace server {
return success; return success;
} }
bool Server::DoDeviceCreateReadyComputePipeline(
WGPUDevice cDevice,
uint64_t requestSerial,
ObjectHandle pipelineObjectHandle,
const WGPUComputePipelineDescriptor* descriptor) {
auto* resultData = ComputePipelineObjects().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.deviceCreateReadyComputePipeline(
cDevice, descriptor, ForwardCreateReadyComputePipeline, userdata.release());
return true;
}
void Server::OnCreateReadyComputePipelineCallback(WGPUCreateReadyPipelineStatus status,
WGPUComputePipeline pipeline,
const char* message,
CreateReadyPipelineUserData* userdata) {
std::unique_ptr<CreateReadyPipelineUserData> data(userdata);
if (status != WGPUCreateReadyPipelineStatus_Success) {
ComputePipelineObjects().Free(data->pipelineObjectID);
} else {
ComputePipelineObjects().Get(data->pipelineObjectID)->handle = pipeline;
}
ReturnDeviceCreateReadyComputePipelineCallbackCmd cmd;
cmd.status = status;
cmd.requestSerial = data->requestSerial;
cmd.message = message;
SerializeCommand(cmd);
}
// static // static
void Server::ForwardPopErrorScope(WGPUErrorType type, const char* message, void* userdata) { void Server::ForwardPopErrorScope(WGPUErrorType type, const char* message, void* userdata) {
auto* data = reinterpret_cast<ErrorScopeUserdata*>(userdata); auto* data = reinterpret_cast<ErrorScopeUserdata*>(userdata);

View File

@ -215,6 +215,7 @@ test("dawn_unittests") {
"unittests/wire/WireArgumentTests.cpp", "unittests/wire/WireArgumentTests.cpp",
"unittests/wire/WireBasicTests.cpp", "unittests/wire/WireBasicTests.cpp",
"unittests/wire/WireBufferMappingTests.cpp", "unittests/wire/WireBufferMappingTests.cpp",
"unittests/wire/WireCreateReadyPipelineTests.cpp",
"unittests/wire/WireDisconnectTests.cpp", "unittests/wire/WireDisconnectTests.cpp",
"unittests/wire/WireErrorCallbackTests.cpp", "unittests/wire/WireErrorCallbackTests.cpp",
"unittests/wire/WireExtensionTests.cpp", "unittests/wire/WireExtensionTests.cpp",
@ -273,6 +274,7 @@ source_set("dawn_end2end_tests_sources") {
"end2end/ComputeSharedMemoryTests.cpp", "end2end/ComputeSharedMemoryTests.cpp",
"end2end/ComputeStorageBufferBarrierTests.cpp", "end2end/ComputeStorageBufferBarrierTests.cpp",
"end2end/CopyTests.cpp", "end2end/CopyTests.cpp",
"end2end/CreateReadyPipelineTests.cpp",
"end2end/CullingTests.cpp", "end2end/CullingTests.cpp",
"end2end/DebugMarkerTests.cpp", "end2end/DebugMarkerTests.cpp",
"end2end/DeprecatedAPITests.cpp", "end2end/DeprecatedAPITests.cpp",

View File

@ -0,0 +1,137 @@
// Copyright 2020 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 "tests/DawnTest.h"
#include "utils/WGPUHelpers.h"
namespace {
struct CreateReadyPipelineTask {
wgpu::ComputePipeline computePipeline;
bool isCompleted = false;
std::string message;
};
} // anonymous namespace
class CreateReadyPipelineTest : public DawnTest {};
// Verify the basic use of CreateReadyComputePipeline works on all backends.
TEST_P(CreateReadyPipelineTest, BasicUseOfCreateReadyComputePipeline) {
const char* computeShader = R"(
#version 450
layout(std140, set = 0, binding = 0) buffer SSBO { uint value; } ssbo;
void main() {
ssbo.value = 1u;
})";
wgpu::ComputePipelineDescriptor csDesc;
csDesc.computeStage.module =
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, computeShader);
csDesc.computeStage.entryPoint = "main";
CreateReadyPipelineTask task;
device.CreateReadyComputePipeline(
&csDesc,
[](WGPUCreateReadyPipelineStatus status, WGPUComputePipeline returnPipeline,
const char* message, void* userdata) {
ASSERT_EQ(WGPUCreateReadyPipelineStatus::WGPUCreateReadyPipelineStatus_Success, status);
CreateReadyPipelineTask* task = static_cast<CreateReadyPipelineTask*>(userdata);
task->computePipeline = wgpu::ComputePipeline::Acquire(returnPipeline);
task->isCompleted = true;
task->message = message;
},
&task);
wgpu::BufferDescriptor bufferDesc;
bufferDesc.size = sizeof(uint32_t);
bufferDesc.usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopySrc;
wgpu::Buffer ssbo = device.CreateBuffer(&bufferDesc);
wgpu::CommandBuffer commands;
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
while (!task.isCompleted) {
WaitABit();
}
ASSERT_TRUE(task.message.empty());
ASSERT_NE(nullptr, task.computePipeline.Get());
wgpu::BindGroup bindGroup =
utils::MakeBindGroup(device, task.computePipeline.GetBindGroupLayout(0),
{
{0, ssbo, 0, sizeof(uint32_t)},
});
pass.SetBindGroup(0, bindGroup);
pass.SetPipeline(task.computePipeline);
pass.Dispatch(1);
pass.EndPass();
commands = encoder.Finish();
}
queue.Submit(1, &commands);
constexpr uint32_t kExpected = 1u;
EXPECT_BUFFER_U32_EQ(kExpected, ssbo, 0);
}
// Verify CreateReadyComputePipeline() works as expected when there is any error that happens during
// the creation of the compute pipeline. The SPEC requires that during the call of
// CreateReadyComputePipeline() any error won't be forwarded to the error scope / unhandled error
// callback.
TEST_P(CreateReadyPipelineTest, CreateComputePipelineFailed) {
DAWN_SKIP_TEST_IF(IsDawnValidationSkipped());
const char* computeShader = R"(
#version 450
layout(std140, set = 0, binding = 0) buffer SSBO { uint value; } ssbo;
void main() {
ssbo.value = 1u;
})";
wgpu::ComputePipelineDescriptor csDesc;
csDesc.computeStage.module =
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, computeShader);
csDesc.computeStage.entryPoint = "main0";
CreateReadyPipelineTask task;
device.CreateReadyComputePipeline(
&csDesc,
[](WGPUCreateReadyPipelineStatus status, WGPUComputePipeline returnPipeline,
const char* message, void* userdata) {
ASSERT_EQ(WGPUCreateReadyPipelineStatus::WGPUCreateReadyPipelineStatus_Error, status);
CreateReadyPipelineTask* task = static_cast<CreateReadyPipelineTask*>(userdata);
task->computePipeline = wgpu::ComputePipeline::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(),
OpenGLBackend(),
VulkanBackend());

View File

@ -0,0 +1,127 @@
// Copyright 2020 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 "tests/unittests/wire/WireTest.h"
using namespace testing;
using namespace dawn_wire;
namespace {
// Mock class to add expectations on the wire calling callbacks
class MockCreateReadyComputePipelineCallback {
public:
MOCK_METHOD(void,
Call,
(WGPUCreateReadyPipelineStatus status,
WGPUComputePipeline pipeline,
const char* message,
void* userdata));
};
std::unique_ptr<StrictMock<MockCreateReadyComputePipelineCallback>>
mockCreateReadyComputePipelineCallback;
void ToMockCreateReadyComputePipelineCallback(WGPUCreateReadyPipelineStatus status,
WGPUComputePipeline pipeline,
const char* message,
void* userdata) {
mockCreateReadyComputePipelineCallback->Call(status, pipeline, message, userdata);
}
} // anonymous namespace
class WireCreateReadyPipelineTest : public WireTest {
public:
void SetUp() override {
WireTest::SetUp();
mockCreateReadyComputePipelineCallback =
std::make_unique<StrictMock<MockCreateReadyComputePipelineCallback>>();
}
void TearDown() override {
WireTest::TearDown();
// Delete mock so that expectations are checked
mockCreateReadyComputePipelineCallback = nullptr;
}
void FlushClient() {
WireTest::FlushClient();
Mock::VerifyAndClearExpectations(&mockCreateReadyComputePipelineCallback);
}
void FlushServer() {
WireTest::FlushServer();
Mock::VerifyAndClearExpectations(&mockCreateReadyComputePipelineCallback);
}
};
// Test when creating a compute pipeline with CreateReadyComputePipeline() successfully.
TEST_F(WireCreateReadyPipelineTest, CreateReadyComputePipelineSuccess) {
WGPUShaderModuleDescriptor csDescriptor{};
WGPUShaderModule csModule = wgpuDeviceCreateShaderModule(device, &csDescriptor);
WGPUShaderModule apiCsModule = api.GetNewShaderModule();
EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiCsModule));
WGPUComputePipelineDescriptor descriptor{};
descriptor.computeStage.module = csModule;
descriptor.computeStage.entryPoint = "main";
wgpuDeviceCreateReadyComputePipeline(device, &descriptor,
ToMockCreateReadyComputePipelineCallback, this);
EXPECT_CALL(api, OnDeviceCreateReadyComputePipelineCallback(apiDevice, _, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallDeviceCreateReadyComputePipelineCallback(
apiDevice, WGPUCreateReadyPipelineStatus_Success, nullptr, "");
}));
FlushClient();
EXPECT_CALL(*mockCreateReadyComputePipelineCallback,
Call(WGPUCreateReadyPipelineStatus_Success, _, StrEq(""), this))
.Times(1);
FlushServer();
}
// Test when creating a compute pipeline with CreateReadyComputePipeline() results in an error.
TEST_F(WireCreateReadyPipelineTest, CreateReadyComputePipelineError) {
WGPUShaderModuleDescriptor csDescriptor{};
WGPUShaderModule csModule = wgpuDeviceCreateShaderModule(device, &csDescriptor);
WGPUShaderModule apiCsModule = api.GetNewShaderModule();
EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiCsModule));
WGPUComputePipelineDescriptor descriptor{};
descriptor.computeStage.module = csModule;
descriptor.computeStage.entryPoint = "main";
wgpuDeviceCreateReadyComputePipeline(device, &descriptor,
ToMockCreateReadyComputePipelineCallback, this);
EXPECT_CALL(api, OnDeviceCreateReadyComputePipelineCallback(apiDevice, _, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallDeviceCreateReadyComputePipelineCallback(
apiDevice, WGPUCreateReadyPipelineStatus_Error, nullptr, "Some error message");
}));
FlushClient();
EXPECT_CALL(*mockCreateReadyComputePipelineCallback,
Call(WGPUCreateReadyPipelineStatus_Error, _, StrEq("Some error message"), this))
.Times(1);
FlushServer();
}