blob: 59d2b2603943531992236656768d808d9b6e7967 [file] [log] [blame]
// Copyright 2019 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 {
class MockFenceOnCompletionCallback {
public:
MOCK_METHOD2(Call, void(WGPUFenceCompletionStatus status, void* userdata));
};
std::unique_ptr<StrictMock<MockFenceOnCompletionCallback>> mockFenceOnCompletionCallback;
void ToMockFenceOnCompletionCallback(WGPUFenceCompletionStatus status, void* userdata) {
mockFenceOnCompletionCallback->Call(status, userdata);
}
} // anonymous namespace
class WireFenceTests : public WireTest {
public:
WireFenceTests() {
}
~WireFenceTests() override = default;
void SetUp() override {
WireTest::SetUp();
mockFenceOnCompletionCallback =
std::make_unique<StrictMock<MockFenceOnCompletionCallback>>();
{
queue = wgpuDeviceCreateQueue(device);
apiQueue = api.GetNewQueue();
EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue));
FlushClient();
}
{
WGPUFenceDescriptor descriptor;
descriptor.nextInChain = nullptr;
descriptor.label = nullptr;
descriptor.initialValue = 1;
apiFence = api.GetNewFence();
fence = wgpuQueueCreateFence(queue, &descriptor);
EXPECT_CALL(api, QueueCreateFence(apiQueue, _)).WillOnce(Return(apiFence));
FlushClient();
}
}
void TearDown() override {
WireTest::TearDown();
mockFenceOnCompletionCallback = nullptr;
}
void FlushServer() {
WireTest::FlushServer();
Mock::VerifyAndClearExpectations(&mockFenceOnCompletionCallback);
}
protected:
void DoQueueSignal(uint64_t signalValue) {
wgpuQueueSignal(queue, fence, signalValue);
EXPECT_CALL(api, QueueSignal(apiQueue, apiFence, signalValue)).Times(1);
// This callback is generated to update the completedValue of the fence
// on the client
EXPECT_CALL(api, OnFenceOnCompletionCallback(apiFence, signalValue, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallFenceOnCompletionCallback(apiFence, WGPUFenceCompletionStatus_Success);
}));
}
// A successfully created fence
WGPUFence fence;
WGPUFence apiFence;
WGPUQueue queue;
WGPUQueue apiQueue;
};
// Check that signaling a fence succeeds
TEST_F(WireFenceTests, QueueSignalSuccess) {
DoQueueSignal(2u);
DoQueueSignal(3u);
FlushClient();
FlushServer();
}
// Errors should be generated when signaling a value less
// than or equal to the current signaled value
TEST_F(WireFenceTests, QueueSignalValidationError) {
wgpuQueueSignal(queue, fence, 0u); // Error
EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage()))
.Times(1);
FlushClient();
wgpuQueueSignal(queue, fence, 1u); // Error
EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage()))
.Times(1);
FlushClient();
DoQueueSignal(4u); // Success
FlushClient();
wgpuQueueSignal(queue, fence, 3u); // Error
EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage()))
.Times(1);
FlushClient();
}
// Check that callbacks are immediately called if the fence is already finished
TEST_F(WireFenceTests, OnCompletionImmediate) {
// Can call on value < (initial) signaled value happens immediately
{
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, _))
.Times(1);
wgpuFenceOnCompletion(fence, 0, ToMockFenceOnCompletionCallback, nullptr);
}
// Can call on value == (initial) signaled value happens immediately
{
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, _))
.Times(1);
wgpuFenceOnCompletion(fence, 1, ToMockFenceOnCompletionCallback, nullptr);
}
}
// Check that all passed client completion callbacks are called
TEST_F(WireFenceTests, OnCompletionMultiple) {
DoQueueSignal(3u);
DoQueueSignal(6u);
// Add callbacks in a non-monotonic order. They should still be called
// in order of increasing fence value.
// Add multiple callbacks for the same value.
wgpuFenceOnCompletion(fence, 6, ToMockFenceOnCompletionCallback, this + 0);
wgpuFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, this + 1);
wgpuFenceOnCompletion(fence, 3, ToMockFenceOnCompletionCallback, this + 2);
wgpuFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, this + 3);
Sequence s1, s2;
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 1))
.Times(1)
.InSequence(s1);
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 3))
.Times(1)
.InSequence(s2);
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 2))
.Times(1)
.InSequence(s1, s2);
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 0))
.Times(1)
.InSequence(s1, s2);
FlushClient();
FlushServer();
}
// Without any flushes, it is valid to wait on a value less than or equal to
// the last signaled value
TEST_F(WireFenceTests, OnCompletionSynchronousValidationSuccess) {
wgpuQueueSignal(queue, fence, 4u);
wgpuFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, 0);
wgpuFenceOnCompletion(fence, 3u, ToMockFenceOnCompletionCallback, 0);
wgpuFenceOnCompletion(fence, 4u, ToMockFenceOnCompletionCallback, 0);
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Unknown, _))
.Times(3);
}
// Errors should be generated when waiting on a value greater
// than the last signaled value
TEST_F(WireFenceTests, OnCompletionValidationError) {
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Error, this + 0))
.Times(1);
wgpuFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, this + 0);
EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage()))
.Times(1);
FlushClient();
}
// Check that the fence completed value is initialized
TEST_F(WireFenceTests, GetCompletedValueInitialization) {
EXPECT_EQ(wgpuFenceGetCompletedValue(fence), 1u);
}
// Check that the fence completed value updates after signaling the fence
TEST_F(WireFenceTests, GetCompletedValueUpdate) {
DoQueueSignal(3u);
FlushClient();
FlushServer();
EXPECT_EQ(wgpuFenceGetCompletedValue(fence), 3u);
}
// Check that the fence completed value does not update without a flush
TEST_F(WireFenceTests, GetCompletedValueNoUpdate) {
wgpuQueueSignal(queue, fence, 3u);
EXPECT_EQ(wgpuFenceGetCompletedValue(fence), 1u);
}
// Check that the callback is called with UNKNOWN when the fence is destroyed
// before the completed value is updated
TEST_F(WireFenceTests, DestroyBeforeOnCompletionEnd) {
wgpuQueueSignal(queue, fence, 3u);
wgpuFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, nullptr);
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Unknown, _))
.Times(1);
}
// Test that signaling a fence on a wrong queue is invalid
TEST_F(WireFenceTests, SignalWrongQueue) {
WGPUQueue queue2 = wgpuDeviceCreateQueue(device);
WGPUQueue apiQueue2 = api.GetNewQueue();
EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue2));
FlushClient();
wgpuQueueSignal(queue2, fence, 2u); // error
EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage()))
.Times(1);
FlushClient();
}
// Test that signaling a fence on a wrong queue does not update fence signaled value
TEST_F(WireFenceTests, SignalWrongQueueDoesNotUpdateValue) {
WGPUQueue queue2 = wgpuDeviceCreateQueue(device);
WGPUQueue apiQueue2 = api.GetNewQueue();
EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue2));
FlushClient();
wgpuQueueSignal(queue2, fence, 2u); // error
EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage()))
.Times(1);
FlushClient();
// Fence value should be unchanged.
FlushClient();
FlushServer();
EXPECT_EQ(wgpuFenceGetCompletedValue(fence), 1u);
// Signaling with 2 on the correct queue should succeed
DoQueueSignal(2u); // success
FlushClient();
FlushServer();
EXPECT_EQ(wgpuFenceGetCompletedValue(fence), 2u);
}