blob: 53f95b7992bc64d7f8d0cd314241e45db5e83a1c [file] [log] [blame]
// Copyright 2017 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>
#include <memory>
using namespace testing;
class MockBufferMapReadCallback {
public:
MOCK_METHOD3(Call, void(dawnBufferMapAsyncStatus status, const uint32_t* ptr, dawnCallbackUserdata userdata));
};
static std::unique_ptr<MockBufferMapReadCallback> mockBufferMapReadCallback;
static void ToMockBufferMapReadCallback(dawnBufferMapAsyncStatus status, const void* ptr, dawnCallbackUserdata userdata) {
// Assume the data is uint32_t to make writing matchers easier
mockBufferMapReadCallback->Call(status, reinterpret_cast<const uint32_t*>(ptr), userdata);
}
class MockBufferMapWriteCallback {
public:
MOCK_METHOD3(Call, void(dawnBufferMapAsyncStatus status, uint32_t* ptr, dawnCallbackUserdata userdata));
};
static std::unique_ptr<MockBufferMapWriteCallback> mockBufferMapWriteCallback;
static void ToMockBufferMapWriteCallback(dawnBufferMapAsyncStatus status, void* ptr, dawnCallbackUserdata userdata) {
// Assume the data is uint32_t to make writing matchers easier
mockBufferMapWriteCallback->Call(status, reinterpret_cast<uint32_t*>(ptr), userdata);
}
class BufferValidationTest : public ValidationTest {
protected:
dawn::Buffer CreateMapReadBuffer(uint32_t size) {
dawn::BufferDescriptor descriptor;
descriptor.size = size;
descriptor.usage = dawn::BufferUsageBit::MapRead;
return device.CreateBuffer(&descriptor);
}
dawn::Buffer CreateMapWriteBuffer(uint32_t size) {
dawn::BufferDescriptor descriptor;
descriptor.size = size;
descriptor.usage = dawn::BufferUsageBit::MapWrite;
return device.CreateBuffer(&descriptor);
}
dawn::Buffer CreateSetSubDataBuffer(uint32_t size) {
dawn::BufferDescriptor descriptor;
descriptor.size = size;
descriptor.usage = dawn::BufferUsageBit::TransferDst;
return device.CreateBuffer(&descriptor);
}
dawn::Queue queue;
private:
void SetUp() override {
ValidationTest::SetUp();
mockBufferMapReadCallback = std::make_unique<MockBufferMapReadCallback>();
mockBufferMapWriteCallback = std::make_unique<MockBufferMapWriteCallback>();
queue = device.CreateQueue();
}
void TearDown() override {
// Delete mocks so that expectations are checked
mockBufferMapReadCallback = nullptr;
mockBufferMapWriteCallback = nullptr;
ValidationTest::TearDown();
}
};
// Test case where creation should succeed
TEST_F(BufferValidationTest, CreationSuccess) {
// Success
{
dawn::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = dawn::BufferUsageBit::Uniform;
device.CreateBuffer(&descriptor);
}
}
// Test restriction on usages allowed with MapRead and MapWrite
TEST_F(BufferValidationTest, CreationMapUsageRestrictions) {
// MapRead with TransferDst is ok
{
dawn::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::TransferDst;
device.CreateBuffer(&descriptor);
}
// MapRead with something else is an error
{
dawn::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::Uniform;
ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
}
// MapWrite with TransferSrc is ok
{
dawn::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc;
device.CreateBuffer(&descriptor);
}
// MapWrite with something else is an error
{
dawn::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::Uniform;
ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
}
}
// Test the success case for mapping buffer for reading
TEST_F(BufferValidationTest, MapReadSuccess) {
dawn::Buffer buf = CreateMapReadBuffer(4);
dawn::CallbackUserdata userdata = 40598;
buf.MapReadAsync(0, 4, ToMockBufferMapReadCallback, userdata);
EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), userdata))
.Times(1);
queue.Submit(0, nullptr);
buf.Unmap();
}
// Test the success case for mapping buffer for writing
TEST_F(BufferValidationTest, MapWriteSuccess) {
dawn::Buffer buf = CreateMapWriteBuffer(4);
dawn::CallbackUserdata userdata = 40598;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), userdata))
.Times(1);
queue.Submit(0, nullptr);
buf.Unmap();
}
// Test map reading out of range causes an error
TEST_F(BufferValidationTest, MapReadOutOfRange) {
dawn::Buffer buf = CreateMapReadBuffer(4);
dawn::CallbackUserdata userdata = 40599;
EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
.Times(1);
ASSERT_DEVICE_ERROR(buf.MapReadAsync(0, 5, ToMockBufferMapReadCallback, userdata));
}
// Test map writing out of range causes an error
TEST_F(BufferValidationTest, MapWriteOutOfRange) {
dawn::Buffer buf = CreateMapWriteBuffer(4);
dawn::CallbackUserdata userdata = 40599;
EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
.Times(1);
ASSERT_DEVICE_ERROR(buf.MapWriteAsync(0, 5, ToMockBufferMapWriteCallback, userdata));
}
// Test map reading a buffer with wrong current usage
TEST_F(BufferValidationTest, MapReadWrongUsage) {
dawn::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = dawn::BufferUsageBit::TransferDst;
dawn::Buffer buf = device.CreateBuffer(&descriptor);
dawn::CallbackUserdata userdata = 40600;
EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
.Times(1);
ASSERT_DEVICE_ERROR(buf.MapReadAsync(0, 4, ToMockBufferMapReadCallback, userdata));
}
// Test map writing a buffer with wrong current usage
TEST_F(BufferValidationTest, MapWriteWrongUsage) {
dawn::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = dawn::BufferUsageBit::TransferSrc;
dawn::Buffer buf = device.CreateBuffer(&descriptor);
dawn::CallbackUserdata userdata = 40600;
EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
.Times(1);
ASSERT_DEVICE_ERROR(buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata));
}
// Test map reading a buffer that is already mapped
TEST_F(BufferValidationTest, MapReadAlreadyMapped) {
dawn::Buffer buf = CreateMapReadBuffer(4);
dawn::CallbackUserdata userdata1 = 40601;
buf.MapReadAsync(0, 4, ToMockBufferMapReadCallback, userdata1);
EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), userdata1))
.Times(1);
dawn::CallbackUserdata userdata2 = 40602;
EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata2))
.Times(1);
ASSERT_DEVICE_ERROR(buf.MapReadAsync(0, 4, ToMockBufferMapReadCallback, userdata2));
queue.Submit(0, nullptr);
}
// Test map writing a buffer that is already mapped
TEST_F(BufferValidationTest, MapWriteAlreadyMapped) {
dawn::Buffer buf = CreateMapWriteBuffer(4);
dawn::CallbackUserdata userdata1 = 40601;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata1);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), userdata1))
.Times(1);
dawn::CallbackUserdata userdata2 = 40602;
EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata2))
.Times(1);
ASSERT_DEVICE_ERROR(buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata2));
queue.Submit(0, nullptr);
}
// TODO(cwallez@chromium.org) Test a MapWrite and already MapRead and vice-versa
// Test unmapping before having the result gives UNKNOWN - for reading
TEST_F(BufferValidationTest, MapReadUnmapBeforeResult) {
dawn::Buffer buf = CreateMapReadBuffer(4);
dawn::CallbackUserdata userdata = 40603;
buf.MapReadAsync(0, 4, ToMockBufferMapReadCallback, userdata);
EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
.Times(1);
buf.Unmap();
// Submitting the queue makes the null backend process map request, but the callback shouldn't
// be called again
queue.Submit(0, nullptr);
}
// Test unmapping before having the result gives UNKNOWN - for writing
TEST_F(BufferValidationTest, MapWriteUnmapBeforeResult) {
dawn::Buffer buf = CreateMapWriteBuffer(4);
dawn::CallbackUserdata userdata = 40603;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
.Times(1);
buf.Unmap();
// Submitting the queue makes the null backend process map request, but the callback shouldn't
// be called again
queue.Submit(0, nullptr);
}
// Test destroying the buffer before having the result gives UNKNOWN - for reading
// TODO(cwallez@chromium.org) currently this doesn't work because the buffer doesn't know
// when its external ref count reaches 0.
TEST_F(BufferValidationTest, DISABLED_MapReadDestroyBeforeResult) {
{
dawn::Buffer buf = CreateMapReadBuffer(4);
dawn::CallbackUserdata userdata = 40604;
buf.MapReadAsync(0, 4, ToMockBufferMapReadCallback, userdata);
EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
.Times(1);
}
// Submitting the queue makes the null backend process map request, but the callback shouldn't
// be called again
queue.Submit(0, nullptr);
}
// Test destroying the buffer before having the result gives UNKNOWN - for writing
// TODO(cwallez@chromium.org) currently this doesn't work because the buffer doesn't know
// when its external ref count reaches 0.
TEST_F(BufferValidationTest, DISABLED_MapWriteDestroyBeforeResult) {
{
dawn::Buffer buf = CreateMapWriteBuffer(4);
dawn::CallbackUserdata userdata = 40604;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
.Times(1);
}
// Submitting the queue makes the null backend process map request, but the callback shouldn't
// be called again
queue.Submit(0, nullptr);
}
// When a MapRead is cancelled with Unmap it might still be in flight, test doing a new request
// works as expected and we don't get the cancelled request's data.
TEST_F(BufferValidationTest, MapReadUnmapBeforeResultThenMapAgain) {
dawn::Buffer buf = CreateMapReadBuffer(4);
dawn::CallbackUserdata userdata = 40605;
buf.MapReadAsync(0, 4, ToMockBufferMapReadCallback, userdata);
EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
.Times(1);
buf.Unmap();
userdata ++;
buf.MapReadAsync(0, 4, ToMockBufferMapReadCallback, userdata);
EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), userdata))
.Times(1);
queue.Submit(0, nullptr);
}
// TODO(cwallez@chromium.org) Test a MapWrite and already MapRead and vice-versa
// When a MapWrite is cancelled with Unmap it might still be in flight, test doing a new request
// works as expected and we don't get the cancelled request's data.
TEST_F(BufferValidationTest, MapWriteUnmapBeforeResultThenMapAgain) {
dawn::Buffer buf = CreateMapWriteBuffer(4);
dawn::CallbackUserdata userdata = 40605;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
.Times(1);
buf.Unmap();
userdata ++;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), userdata))
.Times(1);
queue.Submit(0, nullptr);
}
// Test that the MapReadCallback isn't fired twice when unmap() is called inside the callback
TEST_F(BufferValidationTest, UnmapInsideMapReadCallback) {
dawn::Buffer buf = CreateMapReadBuffer(4);
dawn::CallbackUserdata userdata = 40678;
buf.MapReadAsync(0, 4, ToMockBufferMapReadCallback, userdata);
EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), userdata))
.WillOnce(InvokeWithoutArgs([&]() {
buf.Unmap();
}));
queue.Submit(0, nullptr);
}
// Test that the MapWriteCallback isn't fired twice when unmap() is called inside the callback
TEST_F(BufferValidationTest, UnmapInsideMapWriteCallback) {
dawn::Buffer buf = CreateMapWriteBuffer(4);
dawn::CallbackUserdata userdata = 40678;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), userdata))
.WillOnce(InvokeWithoutArgs([&]() {
buf.Unmap();
}));
queue.Submit(0, nullptr);
}
// Test that the MapReadCallback isn't fired twice the buffer external refcount reaches 0 in the callback
TEST_F(BufferValidationTest, DestroyInsideMapReadCallback) {
dawn::Buffer buf = CreateMapReadBuffer(4);
dawn::CallbackUserdata userdata = 40679;
buf.MapReadAsync(0, 4, ToMockBufferMapReadCallback, userdata);
EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), userdata))
.WillOnce(InvokeWithoutArgs([&]() {
buf = dawn::Buffer();
}));
queue.Submit(0, nullptr);
}
// Test that the MapWriteCallback isn't fired twice the buffer external refcount reaches 0 in the callback
TEST_F(BufferValidationTest, DestroyInsideMapWriteCallback) {
dawn::Buffer buf = CreateMapWriteBuffer(4);
dawn::CallbackUserdata userdata = 40679;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), userdata))
.WillOnce(InvokeWithoutArgs([&]() {
buf = dawn::Buffer();
}));
queue.Submit(0, nullptr);
}
// Test the success case for Buffer::SetSubData
TEST_F(BufferValidationTest, SetSubDataSuccess) {
dawn::Buffer buf = CreateSetSubDataBuffer(1);
uint8_t foo = 0;
buf.SetSubData(0, sizeof(foo), &foo);
}
// Test error case for SetSubData out of bounds
TEST_F(BufferValidationTest, SetSubDataOutOfBounds) {
dawn::Buffer buf = CreateSetSubDataBuffer(1);
uint8_t foo = 0;
ASSERT_DEVICE_ERROR(buf.SetSubData(0, 2, &foo));
}
// Test error case for SetSubData with the wrong usage
TEST_F(BufferValidationTest, SetSubDataWrongUsage) {
dawn::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = dawn::BufferUsageBit::Vertex;
dawn::Buffer buf = device.CreateBuffer(&descriptor);
uint8_t foo = 0;
ASSERT_DEVICE_ERROR(buf.SetSubData(0, sizeof(foo), &foo));
}