Implement Queue::OnSubmittedWorkDone
This is the replacement for Fence in the single-queue WebGPU world. To keep this CL focused, it doesn't deprecate the fences yet. Bug: chromium:1177476 Change-Id: I09d60732ec67bc1deb49f7a9d57699c049475acf Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/41723 Auto-Submit: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
parent
0a295c027d
commit
c093db250e
24
dawn.json
24
dawn.json
|
@ -1126,6 +1126,14 @@
|
||||||
{"name": "descriptor", "type": "fence descriptor", "annotation": "const*", "optional": true}
|
{"name": "descriptor", "type": "fence descriptor", "annotation": "const*", "optional": true}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "on submitted work done",
|
||||||
|
"args": [
|
||||||
|
{"name": "signal value", "type": "uint64_t"},
|
||||||
|
{"name": "callback", "type": "queue work done callback"},
|
||||||
|
{"name": "userdata", "type": "void", "annotation": "*"}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "write buffer",
|
"name": "write buffer",
|
||||||
"args": [
|
"args": [
|
||||||
|
@ -1157,6 +1165,22 @@
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"queue work done callback": {
|
||||||
|
"category": "callback",
|
||||||
|
"args": [
|
||||||
|
{"name": "status", "type": "queue work done status"},
|
||||||
|
{"name": "userdata", "type": "void", "annotation": "*"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"queue work done status": {
|
||||||
|
"category": "enum",
|
||||||
|
"values": [
|
||||||
|
{"value": 0, "name": "success"},
|
||||||
|
{"value": 1, "name": "error"},
|
||||||
|
{"value": 2, "name": "unknown"},
|
||||||
|
{"value": 3, "name": "device lost"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
"rasterization state descriptor": {
|
"rasterization state descriptor": {
|
||||||
"category": "structure",
|
"category": "structure",
|
||||||
|
|
|
@ -61,6 +61,11 @@
|
||||||
{ "name": "value", "type": "uint64_t" },
|
{ "name": "value", "type": "uint64_t" },
|
||||||
{ "name": "request serial", "type": "uint64_t" }
|
{ "name": "request serial", "type": "uint64_t" }
|
||||||
],
|
],
|
||||||
|
"queue on submitted work done": [
|
||||||
|
{ "name": "queue id", "type": "ObjectId" },
|
||||||
|
{ "name": "signal value", "type": "uint64_t" },
|
||||||
|
{ "name": "request serial", "type": "uint64_t" }
|
||||||
|
],
|
||||||
"queue write buffer internal": [
|
"queue write buffer internal": [
|
||||||
{"name": "queue id", "type": "ObjectId" },
|
{"name": "queue id", "type": "ObjectId" },
|
||||||
{"name": "buffer id", "type": "ObjectId" },
|
{"name": "buffer id", "type": "ObjectId" },
|
||||||
|
@ -120,6 +125,11 @@
|
||||||
"fence update completed value": [
|
"fence update completed value": [
|
||||||
{ "name": "fence", "type": "ObjectHandle", "handle_type": "fence" },
|
{ "name": "fence", "type": "ObjectHandle", "handle_type": "fence" },
|
||||||
{ "name": "value", "type": "uint64_t" }
|
{ "name": "value", "type": "uint64_t" }
|
||||||
|
],
|
||||||
|
"queue work done callback": [
|
||||||
|
{ "name": "queue", "type": "ObjectHandle", "handle_type": "queue" },
|
||||||
|
{ "name": "request serial", "type": "uint64_t" },
|
||||||
|
{ "name": "status", "type": "queue work done status" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"special items": {
|
"special items": {
|
||||||
|
@ -140,6 +150,7 @@
|
||||||
"DeviceSetUncapturedErrorCallback",
|
"DeviceSetUncapturedErrorCallback",
|
||||||
"FenceGetCompletedValue",
|
"FenceGetCompletedValue",
|
||||||
"FenceOnCompletion",
|
"FenceOnCompletion",
|
||||||
|
"QueueOnSubmittedWorkDone",
|
||||||
"QueueWriteBuffer",
|
"QueueWriteBuffer",
|
||||||
"QueueWriteTexture"
|
"QueueWriteTexture"
|
||||||
],
|
],
|
||||||
|
|
|
@ -122,6 +122,27 @@ namespace dawn_native {
|
||||||
return uploadHandle;
|
return uploadHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SubmittedWorkDone : QueueBase::TaskInFlight {
|
||||||
|
SubmittedWorkDone(WGPUQueueWorkDoneCallback callback, void* userdata)
|
||||||
|
: mCallback(callback), mUserdata(userdata) {
|
||||||
|
}
|
||||||
|
void Finish() override {
|
||||||
|
ASSERT(mCallback != nullptr);
|
||||||
|
mCallback(WGPUQueueWorkDoneStatus_Success, mUserdata);
|
||||||
|
mCallback = nullptr;
|
||||||
|
}
|
||||||
|
void HandleDeviceLoss() override {
|
||||||
|
ASSERT(mCallback != nullptr);
|
||||||
|
mCallback(WGPUQueueWorkDoneStatus_DeviceLost, mUserdata);
|
||||||
|
mCallback = nullptr;
|
||||||
|
}
|
||||||
|
~SubmittedWorkDone() override = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
WGPUQueueWorkDoneCallback mCallback = nullptr;
|
||||||
|
void* mUserdata;
|
||||||
|
};
|
||||||
|
|
||||||
class ErrorQueue : public QueueBase {
|
class ErrorQueue : public QueueBase {
|
||||||
public:
|
public:
|
||||||
ErrorQueue(DeviceBase* device) : QueueBase(device, ObjectBase::kError) {
|
ErrorQueue(DeviceBase* device) : QueueBase(device, ObjectBase::kError) {
|
||||||
|
@ -176,6 +197,26 @@ namespace dawn_native {
|
||||||
fence->UpdateFenceOnComplete(fence, signalValue);
|
fence->UpdateFenceOnComplete(fence, signalValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QueueBase::OnSubmittedWorkDone(uint64_t signalValue,
|
||||||
|
WGPUQueueWorkDoneCallback callback,
|
||||||
|
void* userdata) {
|
||||||
|
// The error status depends on the type of error so we let the validation function choose it
|
||||||
|
WGPUQueueWorkDoneStatus status;
|
||||||
|
if (GetDevice()->ConsumedError(ValidateOnSubmittedWorkDone(signalValue, &status))) {
|
||||||
|
callback(status, userdata);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<SubmittedWorkDone> task =
|
||||||
|
std::make_unique<SubmittedWorkDone>(callback, userdata);
|
||||||
|
|
||||||
|
// Technically we only need to wait for previously submitted work but OnSubmittedWorkDone is
|
||||||
|
// also used to make sure ALL queue work is finished in tests, so we also wait for pending
|
||||||
|
// commands (this is non-observable outside of tests so it's ok to do deviate a bit from the
|
||||||
|
// spec).
|
||||||
|
TrackTask(std::move(task), GetDevice()->GetPendingCommandSerial());
|
||||||
|
}
|
||||||
|
|
||||||
void QueueBase::TrackTask(std::unique_ptr<TaskInFlight> task, ExecutionSerial serial) {
|
void QueueBase::TrackTask(std::unique_ptr<TaskInFlight> task, ExecutionSerial serial) {
|
||||||
mTasksInFlight.Enqueue(std::move(task), serial);
|
mTasksInFlight.Enqueue(std::move(task), serial);
|
||||||
GetDevice()->AddFutureSerial(serial);
|
GetDevice()->AddFutureSerial(serial);
|
||||||
|
@ -387,6 +428,21 @@ namespace dawn_native {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MaybeError QueueBase::ValidateOnSubmittedWorkDone(uint64_t signalValue,
|
||||||
|
WGPUQueueWorkDoneStatus* status) const {
|
||||||
|
*status = WGPUQueueWorkDoneStatus_DeviceLost;
|
||||||
|
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
||||||
|
|
||||||
|
*status = WGPUQueueWorkDoneStatus_Error;
|
||||||
|
DAWN_TRY(GetDevice()->ValidateObject(this));
|
||||||
|
|
||||||
|
if (signalValue != 0) {
|
||||||
|
return DAWN_VALIDATION_ERROR("SignalValue must currently be 0.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
MaybeError QueueBase::ValidateCreateFence(const FenceDescriptor* descriptor) const {
|
MaybeError QueueBase::ValidateCreateFence(const FenceDescriptor* descriptor) const {
|
||||||
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
||||||
DAWN_TRY(GetDevice()->ValidateObject(this));
|
DAWN_TRY(GetDevice()->ValidateObject(this));
|
||||||
|
|
|
@ -40,6 +40,9 @@ namespace dawn_native {
|
||||||
void Submit(uint32_t commandCount, CommandBufferBase* const* commands);
|
void Submit(uint32_t commandCount, CommandBufferBase* const* commands);
|
||||||
void Signal(Fence* fence, uint64_t signalValue);
|
void Signal(Fence* fence, uint64_t signalValue);
|
||||||
Fence* CreateFence(const FenceDescriptor* descriptor);
|
Fence* CreateFence(const FenceDescriptor* descriptor);
|
||||||
|
void OnSubmittedWorkDone(uint64_t signalValue,
|
||||||
|
WGPUQueueWorkDoneCallback callback,
|
||||||
|
void* userdata);
|
||||||
void WriteBuffer(BufferBase* buffer, uint64_t bufferOffset, const void* data, size_t size);
|
void WriteBuffer(BufferBase* buffer, uint64_t bufferOffset, const void* data, size_t size);
|
||||||
void WriteTexture(const TextureCopyView* destination,
|
void WriteTexture(const TextureCopyView* destination,
|
||||||
const void* data,
|
const void* data,
|
||||||
|
@ -87,6 +90,8 @@ namespace dawn_native {
|
||||||
|
|
||||||
MaybeError ValidateSubmit(uint32_t commandCount, CommandBufferBase* const* commands) const;
|
MaybeError ValidateSubmit(uint32_t commandCount, CommandBufferBase* const* commands) const;
|
||||||
MaybeError ValidateSignal(const Fence* fence, FenceAPISerial signalValue) const;
|
MaybeError ValidateSignal(const Fence* fence, FenceAPISerial signalValue) const;
|
||||||
|
MaybeError ValidateOnSubmittedWorkDone(uint64_t signalValue,
|
||||||
|
WGPUQueueWorkDoneStatus* status) const;
|
||||||
MaybeError ValidateCreateFence(const FenceDescriptor* descriptor) const;
|
MaybeError ValidateCreateFence(const FenceDescriptor* descriptor) const;
|
||||||
MaybeError ValidateWriteBuffer(const BufferBase* buffer,
|
MaybeError ValidateWriteBuffer(const BufferBase* buffer,
|
||||||
uint64_t bufferOffset,
|
uint64_t bufferOffset,
|
||||||
|
|
|
@ -66,7 +66,6 @@ namespace dawn_wire { namespace client {
|
||||||
if (buffer == nullptr) {
|
if (buffer == nullptr) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer->OnMapAsyncCallback(requestSerial, status, readInitialDataInfoLength,
|
return buffer->OnMapAsyncCallback(requestSerial, status, readInitialDataInfoLength,
|
||||||
readInitialDataInfo);
|
readInitialDataInfo);
|
||||||
}
|
}
|
||||||
|
@ -88,10 +87,18 @@ namespace dawn_wire { namespace client {
|
||||||
if (fence == nullptr) {
|
if (fence == nullptr) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return fence->OnCompletionCallback(requestSerial, status);
|
||||||
|
}
|
||||||
|
|
||||||
fence->OnCompletionCallback(requestSerial, status);
|
bool Client::DoQueueWorkDoneCallback(Queue* queue,
|
||||||
|
uint64_t requestSerial,
|
||||||
|
WGPUQueueWorkDoneStatus status) {
|
||||||
|
// The queue might have been deleted or recreated so this isn't an error.
|
||||||
|
if (queue == nullptr) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return queue->OnWorkDoneCallback(requestSerial, status);
|
||||||
|
}
|
||||||
|
|
||||||
bool Client::DoDeviceCreateComputePipelineAsyncCallback(Device* device,
|
bool Client::DoDeviceCreateComputePipelineAsyncCallback(Device* device,
|
||||||
uint64_t requestSerial,
|
uint64_t requestSerial,
|
||||||
|
|
|
@ -46,7 +46,8 @@ namespace dawn_wire { namespace client {
|
||||||
WGPUFenceOnCompletionCallback callback,
|
WGPUFenceOnCompletionCallback callback,
|
||||||
void* userdata) {
|
void* userdata) {
|
||||||
if (client->IsDisconnected()) {
|
if (client->IsDisconnected()) {
|
||||||
return callback(WGPUFenceCompletionStatus_DeviceLost, userdata);
|
callback(WGPUFenceCompletionStatus_DeviceLost, userdata);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t serial = mOnCompletionRequestSerial++;
|
uint32_t serial = mOnCompletionRequestSerial++;
|
||||||
|
|
|
@ -19,6 +19,47 @@
|
||||||
|
|
||||||
namespace dawn_wire { namespace client {
|
namespace dawn_wire { namespace client {
|
||||||
|
|
||||||
|
Queue::~Queue() {
|
||||||
|
ClearAllCallbacks(WGPUQueueWorkDoneStatus_Unknown);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Queue::OnWorkDoneCallback(uint64_t requestSerial, WGPUQueueWorkDoneStatus status) {
|
||||||
|
auto requestIt = mOnWorkDoneRequests.find(requestSerial);
|
||||||
|
if (requestIt == mOnWorkDoneRequests.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the request data so that the callback cannot be called again.
|
||||||
|
// ex.) inside the callback: if the queue is deleted (when there are multiple queues),
|
||||||
|
// all callbacks reject.
|
||||||
|
OnWorkDoneData request = std::move(requestIt->second);
|
||||||
|
mOnWorkDoneRequests.erase(requestIt);
|
||||||
|
|
||||||
|
request.callback(status, request.userdata);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Queue::OnSubmittedWorkDone(uint64_t signalValue,
|
||||||
|
WGPUQueueWorkDoneCallback callback,
|
||||||
|
void* userdata) {
|
||||||
|
if (client->IsDisconnected()) {
|
||||||
|
callback(WGPUQueueWorkDoneStatus_DeviceLost, userdata);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t serial = mOnWorkDoneSerial++;
|
||||||
|
ASSERT(mOnWorkDoneRequests.find(serial) == mOnWorkDoneRequests.end());
|
||||||
|
|
||||||
|
QueueOnSubmittedWorkDoneCmd cmd;
|
||||||
|
cmd.queueId = this->id;
|
||||||
|
cmd.signalValue = signalValue;
|
||||||
|
cmd.requestSerial = serial;
|
||||||
|
|
||||||
|
mOnWorkDoneRequests[serial] = {callback, userdata};
|
||||||
|
|
||||||
|
client->SerializeCommand(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
WGPUFence Queue::CreateFence(WGPUFenceDescriptor const* descriptor) {
|
WGPUFence Queue::CreateFence(WGPUFenceDescriptor const* descriptor) {
|
||||||
auto* allocation = client->FenceAllocator().New(client);
|
auto* allocation = client->FenceAllocator().New(client);
|
||||||
|
|
||||||
|
@ -65,4 +106,17 @@ namespace dawn_wire { namespace client {
|
||||||
client->SerializeCommand(cmd);
|
client->SerializeCommand(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Queue::CancelCallbacksForDisconnect() {
|
||||||
|
ClearAllCallbacks(WGPUQueueWorkDoneStatus_DeviceLost);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Queue::ClearAllCallbacks(WGPUQueueWorkDoneStatus status) {
|
||||||
|
for (auto& it : mOnWorkDoneRequests) {
|
||||||
|
if (it.second.callback) {
|
||||||
|
it.second.callback(status, it.second.userdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mOnWorkDoneRequests.clear();
|
||||||
|
}
|
||||||
|
|
||||||
}} // namespace dawn_wire::client
|
}} // namespace dawn_wire::client
|
||||||
|
|
|
@ -27,7 +27,14 @@ namespace dawn_wire { namespace client {
|
||||||
class Queue final : public ObjectBase {
|
class Queue final : public ObjectBase {
|
||||||
public:
|
public:
|
||||||
using ObjectBase::ObjectBase;
|
using ObjectBase::ObjectBase;
|
||||||
|
~Queue();
|
||||||
|
|
||||||
|
bool OnWorkDoneCallback(uint64_t requestSerial, WGPUQueueWorkDoneStatus status);
|
||||||
|
|
||||||
|
// Dawn API
|
||||||
|
void OnSubmittedWorkDone(uint64_t signalValue,
|
||||||
|
WGPUQueueWorkDoneCallback callback,
|
||||||
|
void* userdata);
|
||||||
WGPUFence CreateFence(const WGPUFenceDescriptor* descriptor);
|
WGPUFence CreateFence(const WGPUFenceDescriptor* descriptor);
|
||||||
void WriteBuffer(WGPUBuffer cBuffer, uint64_t bufferOffset, const void* data, size_t size);
|
void WriteBuffer(WGPUBuffer cBuffer, uint64_t bufferOffset, const void* data, size_t size);
|
||||||
void WriteTexture(const WGPUTextureCopyView* destination,
|
void WriteTexture(const WGPUTextureCopyView* destination,
|
||||||
|
@ -35,6 +42,18 @@ namespace dawn_wire { namespace client {
|
||||||
size_t dataSize,
|
size_t dataSize,
|
||||||
const WGPUTextureDataLayout* dataLayout,
|
const WGPUTextureDataLayout* dataLayout,
|
||||||
const WGPUExtent3D* writeSize);
|
const WGPUExtent3D* writeSize);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void CancelCallbacksForDisconnect() override;
|
||||||
|
|
||||||
|
void ClearAllCallbacks(WGPUQueueWorkDoneStatus status);
|
||||||
|
|
||||||
|
struct OnWorkDoneData {
|
||||||
|
WGPUQueueWorkDoneCallback callback = nullptr;
|
||||||
|
void* userdata = nullptr;
|
||||||
|
};
|
||||||
|
uint64_t mOnWorkDoneSerial = 0;
|
||||||
|
std::map<uint64_t, OnWorkDoneData> mOnWorkDoneRequests;
|
||||||
};
|
};
|
||||||
|
|
||||||
}} // namespace dawn_wire::client
|
}} // namespace dawn_wire::client
|
||||||
|
|
|
@ -141,6 +141,13 @@ namespace dawn_wire { namespace server {
|
||||||
uint64_t requestSerial;
|
uint64_t requestSerial;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct QueueWorkDoneUserdata : CallbackUserdata {
|
||||||
|
using CallbackUserdata::CallbackUserdata;
|
||||||
|
|
||||||
|
ObjectHandle queue;
|
||||||
|
uint64_t requestSerial;
|
||||||
|
};
|
||||||
|
|
||||||
struct CreatePipelineAsyncUserData : CallbackUserdata {
|
struct CreatePipelineAsyncUserData : CallbackUserdata {
|
||||||
using CallbackUserdata::CallbackUserdata;
|
using CallbackUserdata::CallbackUserdata;
|
||||||
|
|
||||||
|
@ -202,6 +209,7 @@ namespace dawn_wire { namespace server {
|
||||||
FenceCompletionUserdata* userdata);
|
FenceCompletionUserdata* userdata);
|
||||||
void OnFenceOnCompletion(WGPUFenceCompletionStatus status,
|
void OnFenceOnCompletion(WGPUFenceCompletionStatus status,
|
||||||
FenceOnCompletionUserdata* userdata);
|
FenceOnCompletionUserdata* userdata);
|
||||||
|
void OnQueueWorkDone(WGPUQueueWorkDoneStatus status, QueueWorkDoneUserdata* userdata);
|
||||||
void OnCreateComputePipelineAsyncCallback(WGPUCreatePipelineAsyncStatus status,
|
void OnCreateComputePipelineAsyncCallback(WGPUCreatePipelineAsyncStatus status,
|
||||||
WGPUComputePipeline pipeline,
|
WGPUComputePipeline pipeline,
|
||||||
const char* message,
|
const char* message,
|
||||||
|
|
|
@ -40,6 +40,34 @@ namespace dawn_wire { namespace server {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Server::OnQueueWorkDone(WGPUQueueWorkDoneStatus status, QueueWorkDoneUserdata* data) {
|
||||||
|
ReturnQueueWorkDoneCallbackCmd cmd;
|
||||||
|
cmd.queue = data->queue;
|
||||||
|
cmd.requestSerial = data->requestSerial;
|
||||||
|
cmd.status = status;
|
||||||
|
|
||||||
|
SerializeCommand(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Server::DoQueueOnSubmittedWorkDone(ObjectId queueId,
|
||||||
|
uint64_t signalValue,
|
||||||
|
uint64_t requestSerial) {
|
||||||
|
auto* queue = QueueObjects().Get(queueId);
|
||||||
|
if (queue == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto userdata = MakeUserdata<QueueWorkDoneUserdata>();
|
||||||
|
userdata->queue = ObjectHandle{queueId, queue->generation};
|
||||||
|
userdata->requestSerial = requestSerial;
|
||||||
|
|
||||||
|
mProcs.queueOnSubmittedWorkDone(
|
||||||
|
queue->handle, signalValue,
|
||||||
|
ForwardToServer<decltype(&Server::OnQueueWorkDone)>::Func<&Server::OnQueueWorkDone>(),
|
||||||
|
userdata.release());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Server::DoQueueWriteBufferInternal(ObjectId queueId,
|
bool Server::DoQueueWriteBufferInternal(ObjectId queueId,
|
||||||
ObjectId bufferId,
|
ObjectId bufferId,
|
||||||
uint64_t bufferOffset,
|
uint64_t bufferOffset,
|
||||||
|
|
|
@ -198,6 +198,7 @@ test("dawn_unittests") {
|
||||||
"unittests/validation/MinimumBufferSizeValidationTests.cpp",
|
"unittests/validation/MinimumBufferSizeValidationTests.cpp",
|
||||||
"unittests/validation/MultipleDeviceTests.cpp",
|
"unittests/validation/MultipleDeviceTests.cpp",
|
||||||
"unittests/validation/QueryValidationTests.cpp",
|
"unittests/validation/QueryValidationTests.cpp",
|
||||||
|
"unittests/validation/QueueOnSubmittedWorkDoneValidationTests.cpp",
|
||||||
"unittests/validation/QueueSubmitValidationTests.cpp",
|
"unittests/validation/QueueSubmitValidationTests.cpp",
|
||||||
"unittests/validation/QueueWriteBufferValidationTests.cpp",
|
"unittests/validation/QueueWriteBufferValidationTests.cpp",
|
||||||
"unittests/validation/QueueWriteTextureValidationTests.cpp",
|
"unittests/validation/QueueWriteTextureValidationTests.cpp",
|
||||||
|
@ -231,6 +232,7 @@ test("dawn_unittests") {
|
||||||
"unittests/wire/WireInjectTextureTests.cpp",
|
"unittests/wire/WireInjectTextureTests.cpp",
|
||||||
"unittests/wire/WireMemoryTransferServiceTests.cpp",
|
"unittests/wire/WireMemoryTransferServiceTests.cpp",
|
||||||
"unittests/wire/WireOptionalTests.cpp",
|
"unittests/wire/WireOptionalTests.cpp",
|
||||||
|
"unittests/wire/WireQueueTests.cpp",
|
||||||
"unittests/wire/WireTest.cpp",
|
"unittests/wire/WireTest.cpp",
|
||||||
"unittests/wire/WireTest.h",
|
"unittests/wire/WireTest.h",
|
||||||
"unittests/wire/WireWGPUDevicePropertiesTests.cpp",
|
"unittests/wire/WireWGPUDevicePropertiesTests.cpp",
|
||||||
|
|
|
@ -52,6 +52,16 @@ static void ToMockFenceOnCompletionSucceeds(WGPUFenceCompletionStatus status, vo
|
||||||
mockFenceOnCompletionCallback = nullptr;
|
mockFenceOnCompletionCallback = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MockQueueWorkDoneCallback {
|
||||||
|
public:
|
||||||
|
MOCK_METHOD(void, Call, (WGPUQueueWorkDoneStatus status, void* userdata));
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unique_ptr<MockQueueWorkDoneCallback> mockQueueWorkDoneCallback;
|
||||||
|
static void ToMockQueueWorkDone(WGPUQueueWorkDoneStatus status, void* userdata) {
|
||||||
|
mockQueueWorkDoneCallback->Call(status, userdata);
|
||||||
|
}
|
||||||
|
|
||||||
static const int fakeUserData = 0;
|
static const int fakeUserData = 0;
|
||||||
|
|
||||||
class DeviceLostTest : public DawnTest {
|
class DeviceLostTest : public DawnTest {
|
||||||
|
@ -61,11 +71,13 @@ class DeviceLostTest : public DawnTest {
|
||||||
DAWN_SKIP_TEST_IF(UsesWire());
|
DAWN_SKIP_TEST_IF(UsesWire());
|
||||||
mockDeviceLostCallback = std::make_unique<MockDeviceLostCallback>();
|
mockDeviceLostCallback = std::make_unique<MockDeviceLostCallback>();
|
||||||
mockFenceOnCompletionCallback = std::make_unique<MockFenceOnCompletionCallback>();
|
mockFenceOnCompletionCallback = std::make_unique<MockFenceOnCompletionCallback>();
|
||||||
|
mockQueueWorkDoneCallback = std::make_unique<MockQueueWorkDoneCallback>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
mockDeviceLostCallback = nullptr;
|
mockDeviceLostCallback = nullptr;
|
||||||
mockFenceOnCompletionCallback = nullptr;
|
mockFenceOnCompletionCallback = nullptr;
|
||||||
|
mockQueueWorkDoneCallback = nullptr;
|
||||||
DawnTest::TearDown();
|
DawnTest::TearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -473,6 +485,28 @@ TEST_P(DeviceLostTest, FenceOnCompletionBeforeLossFails) {
|
||||||
EXPECT_EQ(fence.GetCompletedValue(), 2u);
|
EXPECT_EQ(fence.GetCompletedValue(), 2u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that QueueOnSubmittedWorkDone fails after device is lost.
|
||||||
|
TEST_P(DeviceLostTest, QueueOnSubmittedWorkDoneFails) {
|
||||||
|
SetCallbackAndLoseForTesting();
|
||||||
|
|
||||||
|
// callback should have device lost status
|
||||||
|
EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_DeviceLost, nullptr))
|
||||||
|
.Times(1);
|
||||||
|
ASSERT_DEVICE_ERROR(queue.OnSubmittedWorkDone(0, ToMockQueueWorkDone, nullptr));
|
||||||
|
ASSERT_DEVICE_ERROR(device.Tick());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that QueueOnSubmittedWorkDone when the device is lost after calling OnSubmittedWorkDone
|
||||||
|
TEST_P(DeviceLostTest, QueueOnSubmittedWorkDoneBeforeLossFails) {
|
||||||
|
// callback should have device lost status
|
||||||
|
EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_DeviceLost, nullptr))
|
||||||
|
.Times(1);
|
||||||
|
queue.OnSubmittedWorkDone(0, ToMockQueueWorkDone, nullptr);
|
||||||
|
|
||||||
|
SetCallbackAndLoseForTesting();
|
||||||
|
ASSERT_DEVICE_ERROR(device.Tick());
|
||||||
|
}
|
||||||
|
|
||||||
// Regression test for the Null backend not properly setting the completedSerial when
|
// Regression test for the Null backend not properly setting the completedSerial when
|
||||||
// WaitForIdleForDestruction is called, causing the fence signaling to not be retired and an
|
// WaitForIdleForDestruction is called, causing the fence signaling to not be retired and an
|
||||||
// ASSERT to fire.
|
// ASSERT to fire.
|
||||||
|
|
|
@ -39,6 +39,16 @@ static void ToMockFenceOnCompletion(WGPUFenceCompletionStatus status, void* user
|
||||||
mockFenceOnCompletionCallback->Call(status, userdata);
|
mockFenceOnCompletionCallback->Call(status, userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class MockQueueWorkDoneCallback {
|
||||||
|
public:
|
||||||
|
MOCK_METHOD(void, Call, (WGPUQueueWorkDoneStatus status, void* userdata));
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unique_ptr<MockQueueWorkDoneCallback> mockQueueWorkDoneCallback;
|
||||||
|
static void ToMockQueueWorkDone(WGPUQueueWorkDoneStatus status, void* userdata) {
|
||||||
|
mockQueueWorkDoneCallback->Call(status, userdata);
|
||||||
|
}
|
||||||
|
|
||||||
class QueueTimelineTests : public DawnTest {
|
class QueueTimelineTests : public DawnTest {
|
||||||
protected:
|
protected:
|
||||||
void SetUp() override {
|
void SetUp() override {
|
||||||
|
@ -46,6 +56,7 @@ class QueueTimelineTests : public DawnTest {
|
||||||
|
|
||||||
mockMapCallback = std::make_unique<MockMapCallback>();
|
mockMapCallback = std::make_unique<MockMapCallback>();
|
||||||
mockFenceOnCompletionCallback = std::make_unique<MockFenceOnCompletionCallback>();
|
mockFenceOnCompletionCallback = std::make_unique<MockFenceOnCompletionCallback>();
|
||||||
|
mockQueueWorkDoneCallback = std::make_unique<MockQueueWorkDoneCallback>();
|
||||||
|
|
||||||
wgpu::BufferDescriptor descriptor;
|
wgpu::BufferDescriptor descriptor;
|
||||||
descriptor.size = 4;
|
descriptor.size = 4;
|
||||||
|
@ -54,8 +65,9 @@ class QueueTimelineTests : public DawnTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
void TearDown() override {
|
void TearDown() override {
|
||||||
mockFenceOnCompletionCallback = nullptr;
|
|
||||||
mockMapCallback = nullptr;
|
mockMapCallback = nullptr;
|
||||||
|
mockFenceOnCompletionCallback = nullptr;
|
||||||
|
mockQueueWorkDoneCallback = nullptr;
|
||||||
DawnTest::TearDown();
|
DawnTest::TearDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,8 +94,24 @@ TEST_P(QueueTimelineTests, MapReadSignalOnComplete) {
|
||||||
mMapReadBuffer.Unmap();
|
mMapReadBuffer.Unmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that mMapReadBuffer.MapAsync callback happens before queue.OnWorkDone callback
|
||||||
|
// when queue.OnSubmittedWorkDone is called after mMapReadBuffer.MapAsync. The callback order should
|
||||||
|
// happen in the order the functions are called.
|
||||||
|
TEST_P(QueueTimelineTests, MapRead_OnWorkDone) {
|
||||||
|
testing::InSequence sequence;
|
||||||
|
EXPECT_CALL(*mockMapCallback, Call(WGPUBufferMapAsyncStatus_Success, this)).Times(1);
|
||||||
|
EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_Success, this)).Times(1);
|
||||||
|
|
||||||
|
mMapReadBuffer.MapAsync(wgpu::MapMode::Read, 0, 0, ToMockMapCallback, this);
|
||||||
|
|
||||||
|
queue.OnSubmittedWorkDone(0u, ToMockQueueWorkDone, this);
|
||||||
|
|
||||||
|
WaitForAllOperations();
|
||||||
|
mMapReadBuffer.Unmap();
|
||||||
|
}
|
||||||
|
|
||||||
// Test that fence.OnCompletion callback happens before mMapReadBuffer.MapAsync callback when
|
// Test that fence.OnCompletion callback happens before mMapReadBuffer.MapAsync callback when
|
||||||
// queue.Signal is called before mMapReadBuffer.MapAsync. The callback order should
|
// queue.OnSubmittedWorkDone is called before mMapReadBuffer.MapAsync. The callback order should
|
||||||
// happen in the order the functions are called.
|
// happen in the order the functions are called.
|
||||||
TEST_P(QueueTimelineTests, SignalMapReadOnComplete) {
|
TEST_P(QueueTimelineTests, SignalMapReadOnComplete) {
|
||||||
testing::InSequence sequence;
|
testing::InSequence sequence;
|
||||||
|
@ -101,6 +129,22 @@ TEST_P(QueueTimelineTests, SignalMapReadOnComplete) {
|
||||||
mMapReadBuffer.Unmap();
|
mMapReadBuffer.Unmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that queue.OnWorkDone callback happens before mMapReadBuffer.MapAsync callback when
|
||||||
|
// queue.Signal is called before mMapReadBuffer.MapAsync. The callback order should
|
||||||
|
// happen in the order the functions are called.
|
||||||
|
TEST_P(QueueTimelineTests, OnWorkDone_MapRead) {
|
||||||
|
testing::InSequence sequence;
|
||||||
|
EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_Success, this)).Times(1);
|
||||||
|
EXPECT_CALL(*mockMapCallback, Call(WGPUBufferMapAsyncStatus_Success, this)).Times(1);
|
||||||
|
|
||||||
|
queue.OnSubmittedWorkDone(0u, ToMockQueueWorkDone, this);
|
||||||
|
|
||||||
|
mMapReadBuffer.MapAsync(wgpu::MapMode::Read, 0, 0, ToMockMapCallback, this);
|
||||||
|
|
||||||
|
WaitForAllOperations();
|
||||||
|
mMapReadBuffer.Unmap();
|
||||||
|
}
|
||||||
|
|
||||||
// Test that fence.OnCompletion callback happens before mMapReadBuffer.MapAsync callback when
|
// Test that fence.OnCompletion callback happens before mMapReadBuffer.MapAsync callback when
|
||||||
// queue.Signal is called before mMapReadBuffer.MapAsync. The callback order should
|
// queue.Signal is called before mMapReadBuffer.MapAsync. The callback order should
|
||||||
// happen in the order the functions are called
|
// happen in the order the functions are called
|
||||||
|
@ -121,6 +165,7 @@ TEST_P(QueueTimelineTests, SignalOnCompleteMapRead) {
|
||||||
mMapReadBuffer.Unmap();
|
mMapReadBuffer.Unmap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test a complicated case with many signals surrounding a buffer mapping.
|
||||||
TEST_P(QueueTimelineTests, SurroundWithFenceSignals) {
|
TEST_P(QueueTimelineTests, SurroundWithFenceSignals) {
|
||||||
testing::InSequence sequence;
|
testing::InSequence sequence;
|
||||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 0))
|
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 0))
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright 2021 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/validation/ValidationTest.h"
|
||||||
|
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
|
||||||
|
using namespace testing;
|
||||||
|
|
||||||
|
class MockQueueWorkDoneCallback {
|
||||||
|
public:
|
||||||
|
MOCK_METHOD(void, Call, (WGPUQueueWorkDoneStatus status, void* userdata));
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unique_ptr<MockQueueWorkDoneCallback> mockQueueWorkDoneCallback;
|
||||||
|
static void ToMockQueueWorkDone(WGPUQueueWorkDoneStatus status, void* userdata) {
|
||||||
|
mockQueueWorkDoneCallback->Call(status, userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
class QueueOnSubmittedWorkDoneValidationTests : public ValidationTest {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
ValidationTest::SetUp();
|
||||||
|
mockQueueWorkDoneCallback = std::make_unique<MockQueueWorkDoneCallback>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
mockQueueWorkDoneCallback = nullptr;
|
||||||
|
ValidationTest::TearDown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test that OnSubmittedWorkDone can be called as soon as the queue is created.
|
||||||
|
TEST_F(QueueOnSubmittedWorkDoneValidationTests, CallBeforeSubmits) {
|
||||||
|
EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_Success, this)).Times(1);
|
||||||
|
device.GetQueue().OnSubmittedWorkDone(0u, ToMockQueueWorkDone, this);
|
||||||
|
|
||||||
|
WaitForAllOperations(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that OnSubmittedWorkDone is an error if signalValue isn't 0.
|
||||||
|
TEST_F(QueueOnSubmittedWorkDoneValidationTests, SignaledValueNotZeroIsInvalid) {
|
||||||
|
EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_Error, this)).Times(1);
|
||||||
|
ASSERT_DEVICE_ERROR(device.GetQueue().OnSubmittedWorkDone(1u, ToMockQueueWorkDone, this));
|
||||||
|
|
||||||
|
WaitForAllOperations(device);
|
||||||
|
}
|
|
@ -0,0 +1,99 @@
|
||||||
|
// Copyright 2021 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"
|
||||||
|
|
||||||
|
#include "dawn_wire/WireClient.h"
|
||||||
|
|
||||||
|
using namespace testing;
|
||||||
|
using namespace dawn_wire;
|
||||||
|
|
||||||
|
class MockQueueWorkDoneCallback {
|
||||||
|
public:
|
||||||
|
MOCK_METHOD(void, Call, (WGPUQueueWorkDoneStatus status, void* userdata));
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::unique_ptr<MockQueueWorkDoneCallback> mockQueueWorkDoneCallback;
|
||||||
|
static void ToMockQueueWorkDone(WGPUQueueWorkDoneStatus status, void* userdata) {
|
||||||
|
mockQueueWorkDoneCallback->Call(status, userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
class WireQueueTests : public WireTest {
|
||||||
|
protected:
|
||||||
|
void SetUp() override {
|
||||||
|
WireTest::SetUp();
|
||||||
|
mockQueueWorkDoneCallback = std::make_unique<MockQueueWorkDoneCallback>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
mockQueueWorkDoneCallback = nullptr;
|
||||||
|
WireTest::TearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FlushServer() {
|
||||||
|
WireTest::FlushServer();
|
||||||
|
Mock::VerifyAndClearExpectations(&mockQueueWorkDoneCallback);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test that a successful OnSubmittedWorkDone call is forwarded to the client.
|
||||||
|
TEST_F(WireQueueTests, OnSubmittedWorkDoneSuccess) {
|
||||||
|
wgpuQueueOnSubmittedWorkDone(queue, 0u, ToMockQueueWorkDone, this);
|
||||||
|
EXPECT_CALL(api, OnQueueOnSubmittedWorkDone(apiQueue, 0u, _, _))
|
||||||
|
.WillOnce(InvokeWithoutArgs([&]() {
|
||||||
|
api.CallQueueOnSubmittedWorkDoneCallback(apiQueue, WGPUQueueWorkDoneStatus_Success);
|
||||||
|
}));
|
||||||
|
FlushClient();
|
||||||
|
|
||||||
|
EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_Success, this)).Times(1);
|
||||||
|
FlushServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that an error OnSubmittedWorkDone call is forwarded as an error to the client.
|
||||||
|
TEST_F(WireQueueTests, OnSubmittedWorkDoneError) {
|
||||||
|
wgpuQueueOnSubmittedWorkDone(queue, 0u, ToMockQueueWorkDone, this);
|
||||||
|
EXPECT_CALL(api, OnQueueOnSubmittedWorkDone(apiQueue, 0u, _, _))
|
||||||
|
.WillOnce(InvokeWithoutArgs([&]() {
|
||||||
|
api.CallQueueOnSubmittedWorkDoneCallback(apiQueue, WGPUQueueWorkDoneStatus_Error);
|
||||||
|
}));
|
||||||
|
FlushClient();
|
||||||
|
|
||||||
|
EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_Error, this)).Times(1);
|
||||||
|
FlushServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test registering an OnSubmittedWorkDone then disconnecting the wire calls the callback with
|
||||||
|
// device loss
|
||||||
|
TEST_F(WireQueueTests, OnSubmittedWorkDoneBeforeDisconnect) {
|
||||||
|
wgpuQueueOnSubmittedWorkDone(queue, 0u, ToMockQueueWorkDone, this);
|
||||||
|
EXPECT_CALL(api, OnQueueOnSubmittedWorkDone(apiQueue, 0u, _, _))
|
||||||
|
.WillOnce(InvokeWithoutArgs([&]() {
|
||||||
|
api.CallQueueOnSubmittedWorkDoneCallback(apiQueue, WGPUQueueWorkDoneStatus_Error);
|
||||||
|
}));
|
||||||
|
FlushClient();
|
||||||
|
|
||||||
|
EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_DeviceLost, this))
|
||||||
|
.Times(1);
|
||||||
|
GetWireClient()->Disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test registering an OnSubmittedWorkDone after disconnecting the wire calls the callback with
|
||||||
|
// device loss
|
||||||
|
TEST_F(WireQueueTests, OnSubmittedWorkDoneAfterDisconnect) {
|
||||||
|
GetWireClient()->Disconnect();
|
||||||
|
|
||||||
|
EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_DeviceLost, this))
|
||||||
|
.Times(1);
|
||||||
|
wgpuQueueOnSubmittedWorkDone(queue, 0u, ToMockQueueWorkDone, this);
|
||||||
|
}
|
Loading…
Reference in New Issue