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": "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",
|
||||
"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": {
|
||||
"category": "structure",
|
||||
|
|
|
@ -61,6 +61,11 @@
|
|||
{ "name": "value", "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": [
|
||||
{"name": "queue id", "type": "ObjectId" },
|
||||
{"name": "buffer id", "type": "ObjectId" },
|
||||
|
@ -120,6 +125,11 @@
|
|||
"fence update completed value": [
|
||||
{ "name": "fence", "type": "ObjectHandle", "handle_type": "fence" },
|
||||
{ "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": {
|
||||
|
@ -140,6 +150,7 @@
|
|||
"DeviceSetUncapturedErrorCallback",
|
||||
"FenceGetCompletedValue",
|
||||
"FenceOnCompletion",
|
||||
"QueueOnSubmittedWorkDone",
|
||||
"QueueWriteBuffer",
|
||||
"QueueWriteTexture"
|
||||
],
|
||||
|
|
|
@ -122,6 +122,27 @@ namespace dawn_native {
|
|||
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 {
|
||||
public:
|
||||
ErrorQueue(DeviceBase* device) : QueueBase(device, ObjectBase::kError) {
|
||||
|
@ -176,6 +197,26 @@ namespace dawn_native {
|
|||
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) {
|
||||
mTasksInFlight.Enqueue(std::move(task), serial);
|
||||
GetDevice()->AddFutureSerial(serial);
|
||||
|
@ -387,6 +428,21 @@ namespace dawn_native {
|
|||
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 {
|
||||
DAWN_TRY(GetDevice()->ValidateIsAlive());
|
||||
DAWN_TRY(GetDevice()->ValidateObject(this));
|
||||
|
|
|
@ -40,6 +40,9 @@ namespace dawn_native {
|
|||
void Submit(uint32_t commandCount, CommandBufferBase* const* commands);
|
||||
void Signal(Fence* fence, uint64_t signalValue);
|
||||
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 WriteTexture(const TextureCopyView* destination,
|
||||
const void* data,
|
||||
|
@ -87,6 +90,8 @@ namespace dawn_native {
|
|||
|
||||
MaybeError ValidateSubmit(uint32_t commandCount, CommandBufferBase* const* commands) const;
|
||||
MaybeError ValidateSignal(const Fence* fence, FenceAPISerial signalValue) const;
|
||||
MaybeError ValidateOnSubmittedWorkDone(uint64_t signalValue,
|
||||
WGPUQueueWorkDoneStatus* status) const;
|
||||
MaybeError ValidateCreateFence(const FenceDescriptor* descriptor) const;
|
||||
MaybeError ValidateWriteBuffer(const BufferBase* buffer,
|
||||
uint64_t bufferOffset,
|
||||
|
|
|
@ -66,7 +66,6 @@ namespace dawn_wire { namespace client {
|
|||
if (buffer == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return buffer->OnMapAsyncCallback(requestSerial, status, readInitialDataInfoLength,
|
||||
readInitialDataInfo);
|
||||
}
|
||||
|
@ -88,10 +87,18 @@ namespace dawn_wire { namespace client {
|
|||
if (fence == nullptr) {
|
||||
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 queue->OnWorkDoneCallback(requestSerial, status);
|
||||
}
|
||||
|
||||
bool Client::DoDeviceCreateComputePipelineAsyncCallback(Device* device,
|
||||
uint64_t requestSerial,
|
||||
|
|
|
@ -46,7 +46,8 @@ namespace dawn_wire { namespace client {
|
|||
WGPUFenceOnCompletionCallback callback,
|
||||
void* userdata) {
|
||||
if (client->IsDisconnected()) {
|
||||
return callback(WGPUFenceCompletionStatus_DeviceLost, userdata);
|
||||
callback(WGPUFenceCompletionStatus_DeviceLost, userdata);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t serial = mOnCompletionRequestSerial++;
|
||||
|
|
|
@ -19,6 +19,47 @@
|
|||
|
||||
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) {
|
||||
auto* allocation = client->FenceAllocator().New(client);
|
||||
|
||||
|
@ -65,4 +106,17 @@ namespace dawn_wire { namespace client {
|
|||
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
|
||||
|
|
|
@ -27,7 +27,14 @@ namespace dawn_wire { namespace client {
|
|||
class Queue final : public ObjectBase {
|
||||
public:
|
||||
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);
|
||||
void WriteBuffer(WGPUBuffer cBuffer, uint64_t bufferOffset, const void* data, size_t size);
|
||||
void WriteTexture(const WGPUTextureCopyView* destination,
|
||||
|
@ -35,6 +42,18 @@ namespace dawn_wire { namespace client {
|
|||
size_t dataSize,
|
||||
const WGPUTextureDataLayout* dataLayout,
|
||||
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
|
||||
|
|
|
@ -141,6 +141,13 @@ namespace dawn_wire { namespace server {
|
|||
uint64_t requestSerial;
|
||||
};
|
||||
|
||||
struct QueueWorkDoneUserdata : CallbackUserdata {
|
||||
using CallbackUserdata::CallbackUserdata;
|
||||
|
||||
ObjectHandle queue;
|
||||
uint64_t requestSerial;
|
||||
};
|
||||
|
||||
struct CreatePipelineAsyncUserData : CallbackUserdata {
|
||||
using CallbackUserdata::CallbackUserdata;
|
||||
|
||||
|
@ -202,6 +209,7 @@ namespace dawn_wire { namespace server {
|
|||
FenceCompletionUserdata* userdata);
|
||||
void OnFenceOnCompletion(WGPUFenceCompletionStatus status,
|
||||
FenceOnCompletionUserdata* userdata);
|
||||
void OnQueueWorkDone(WGPUQueueWorkDoneStatus status, QueueWorkDoneUserdata* userdata);
|
||||
void OnCreateComputePipelineAsyncCallback(WGPUCreatePipelineAsyncStatus status,
|
||||
WGPUComputePipeline pipeline,
|
||||
const char* message,
|
||||
|
|
|
@ -40,6 +40,34 @@ namespace dawn_wire { namespace server {
|
|||
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,
|
||||
ObjectId bufferId,
|
||||
uint64_t bufferOffset,
|
||||
|
|
|
@ -198,6 +198,7 @@ test("dawn_unittests") {
|
|||
"unittests/validation/MinimumBufferSizeValidationTests.cpp",
|
||||
"unittests/validation/MultipleDeviceTests.cpp",
|
||||
"unittests/validation/QueryValidationTests.cpp",
|
||||
"unittests/validation/QueueOnSubmittedWorkDoneValidationTests.cpp",
|
||||
"unittests/validation/QueueSubmitValidationTests.cpp",
|
||||
"unittests/validation/QueueWriteBufferValidationTests.cpp",
|
||||
"unittests/validation/QueueWriteTextureValidationTests.cpp",
|
||||
|
@ -231,6 +232,7 @@ test("dawn_unittests") {
|
|||
"unittests/wire/WireInjectTextureTests.cpp",
|
||||
"unittests/wire/WireMemoryTransferServiceTests.cpp",
|
||||
"unittests/wire/WireOptionalTests.cpp",
|
||||
"unittests/wire/WireQueueTests.cpp",
|
||||
"unittests/wire/WireTest.cpp",
|
||||
"unittests/wire/WireTest.h",
|
||||
"unittests/wire/WireWGPUDevicePropertiesTests.cpp",
|
||||
|
|
|
@ -52,6 +52,16 @@ static void ToMockFenceOnCompletionSucceeds(WGPUFenceCompletionStatus status, vo
|
|||
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;
|
||||
|
||||
class DeviceLostTest : public DawnTest {
|
||||
|
@ -61,11 +71,13 @@ class DeviceLostTest : public DawnTest {
|
|||
DAWN_SKIP_TEST_IF(UsesWire());
|
||||
mockDeviceLostCallback = std::make_unique<MockDeviceLostCallback>();
|
||||
mockFenceOnCompletionCallback = std::make_unique<MockFenceOnCompletionCallback>();
|
||||
mockQueueWorkDoneCallback = std::make_unique<MockQueueWorkDoneCallback>();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
mockDeviceLostCallback = nullptr;
|
||||
mockFenceOnCompletionCallback = nullptr;
|
||||
mockQueueWorkDoneCallback = nullptr;
|
||||
DawnTest::TearDown();
|
||||
}
|
||||
|
||||
|
@ -473,6 +485,28 @@ TEST_P(DeviceLostTest, FenceOnCompletionBeforeLossFails) {
|
|||
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
|
||||
// WaitForIdleForDestruction is called, causing the fence signaling to not be retired and an
|
||||
// ASSERT to fire.
|
||||
|
|
|
@ -39,6 +39,16 @@ static void ToMockFenceOnCompletion(WGPUFenceCompletionStatus status, void* user
|
|||
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 {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
|
@ -46,6 +56,7 @@ class QueueTimelineTests : public DawnTest {
|
|||
|
||||
mockMapCallback = std::make_unique<MockMapCallback>();
|
||||
mockFenceOnCompletionCallback = std::make_unique<MockFenceOnCompletionCallback>();
|
||||
mockQueueWorkDoneCallback = std::make_unique<MockQueueWorkDoneCallback>();
|
||||
|
||||
wgpu::BufferDescriptor descriptor;
|
||||
descriptor.size = 4;
|
||||
|
@ -54,8 +65,9 @@ class QueueTimelineTests : public DawnTest {
|
|||
}
|
||||
|
||||
void TearDown() override {
|
||||
mockFenceOnCompletionCallback = nullptr;
|
||||
mockMapCallback = nullptr;
|
||||
mockFenceOnCompletionCallback = nullptr;
|
||||
mockQueueWorkDoneCallback = nullptr;
|
||||
DawnTest::TearDown();
|
||||
}
|
||||
|
||||
|
@ -82,8 +94,24 @@ TEST_P(QueueTimelineTests, MapReadSignalOnComplete) {
|
|||
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
|
||||
// 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.
|
||||
TEST_P(QueueTimelineTests, SignalMapReadOnComplete) {
|
||||
testing::InSequence sequence;
|
||||
|
@ -101,6 +129,22 @@ TEST_P(QueueTimelineTests, SignalMapReadOnComplete) {
|
|||
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
|
||||
// queue.Signal is called before mMapReadBuffer.MapAsync. The callback order should
|
||||
// happen in the order the functions are called
|
||||
|
@ -121,6 +165,7 @@ TEST_P(QueueTimelineTests, SignalOnCompleteMapRead) {
|
|||
mMapReadBuffer.Unmap();
|
||||
}
|
||||
|
||||
// Test a complicated case with many signals surrounding a buffer mapping.
|
||||
TEST_P(QueueTimelineTests, SurroundWithFenceSignals) {
|
||||
testing::InSequence sequence;
|
||||
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