blob: d1ececfcb15a72c9792a63c1daeb7aadb20c3696 [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_METHOD(void,
Call,
(WGPUBufferMapAsyncStatus status,
const uint32_t* ptr,
uint64_t dataLength,
void* userdata));
};
static std::unique_ptr<MockBufferMapReadCallback> mockBufferMapReadCallback;
static void ToMockBufferMapReadCallback(WGPUBufferMapAsyncStatus status,
const void* ptr,
uint64_t dataLength,
void* userdata) {
// Assume the data is uint32_t to make writing matchers easier
mockBufferMapReadCallback->Call(status, reinterpret_cast<const uint32_t*>(ptr), dataLength,
userdata);
}
class MockBufferMapWriteCallback {
public:
MOCK_METHOD(
void,
Call,
(WGPUBufferMapAsyncStatus status, uint32_t* ptr, uint64_t dataLength, void* userdata));
};
static std::unique_ptr<MockBufferMapWriteCallback> mockBufferMapWriteCallback;
static void ToMockBufferMapWriteCallback(WGPUBufferMapAsyncStatus status,
void* ptr,
uint64_t dataLength,
void* userdata) {
// Assume the data is uint32_t to make writing matchers easier
mockBufferMapWriteCallback->Call(status, reinterpret_cast<uint32_t*>(ptr), dataLength,
userdata);
}
class BufferValidationTest : public ValidationTest {
protected:
wgpu::Buffer CreateMapReadBuffer(uint64_t size) {
wgpu::BufferDescriptor descriptor;
descriptor.size = size;
descriptor.usage = wgpu::BufferUsage::MapRead;
return device.CreateBuffer(&descriptor);
}
wgpu::Buffer CreateMapWriteBuffer(uint64_t size) {
wgpu::BufferDescriptor descriptor;
descriptor.size = size;
descriptor.usage = wgpu::BufferUsage::MapWrite;
return device.CreateBuffer(&descriptor);
}
wgpu::CreateBufferMappedResult CreateBufferMapped(uint64_t size, wgpu::BufferUsage usage) {
wgpu::BufferDescriptor descriptor;
descriptor.size = size;
descriptor.usage = usage;
return device.CreateBufferMapped(&descriptor);
}
wgpu::Queue queue;
private:
void SetUp() override {
ValidationTest::SetUp();
mockBufferMapReadCallback = std::make_unique<MockBufferMapReadCallback>();
mockBufferMapWriteCallback = std::make_unique<MockBufferMapWriteCallback>();
queue = device.GetDefaultQueue();
}
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
{
wgpu::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = wgpu::BufferUsage::Uniform;
device.CreateBuffer(&descriptor);
}
}
// Test restriction on usages allowed with MapRead and MapWrite
TEST_F(BufferValidationTest, CreationMapUsageRestrictions) {
// MapRead with CopyDst is ok
{
wgpu::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
device.CreateBuffer(&descriptor);
}
// MapRead with something else is an error
{
wgpu::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::Uniform;
ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
}
// MapWrite with CopySrc is ok
{
wgpu::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
device.CreateBuffer(&descriptor);
}
// MapWrite with something else is an error
{
wgpu::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::Uniform;
ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
}
}
// Test the success case for mapping buffer for reading
TEST_F(BufferValidationTest, MapReadSuccess) {
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr);
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _))
.Times(1);
queue.Submit(0, nullptr);
buf.Unmap();
}
// Test the success case for mapping buffer for writing
TEST_F(BufferValidationTest, MapWriteSuccess) {
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr);
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _))
.Times(1);
queue.Submit(0, nullptr);
buf.Unmap();
}
// Test the success case for CreateBufferMapped
TEST_F(BufferValidationTest, CreateBufferMappedSuccess) {
wgpu::CreateBufferMappedResult result = CreateBufferMapped(4, wgpu::BufferUsage::MapWrite);
ASSERT_NE(result.data, nullptr);
ASSERT_EQ(result.dataLength, 4u);
result.buffer.Unmap();
}
// Test the success case for non-mappable CreateBufferMapped
TEST_F(BufferValidationTest, NonMappableCreateBufferMappedSuccess) {
wgpu::CreateBufferMappedResult result = CreateBufferMapped(4, wgpu::BufferUsage::CopySrc);
ASSERT_NE(result.data, nullptr);
ASSERT_EQ(result.dataLength, 4u);
result.buffer.Unmap();
}
// Test map reading a buffer with wrong current usage
TEST_F(BufferValidationTest, MapReadWrongUsage) {
wgpu::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = wgpu::BufferUsage::CopyDst;
wgpu::Buffer buf = device.CreateBuffer(&descriptor);
EXPECT_CALL(*mockBufferMapReadCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0u, _))
.Times(1);
ASSERT_DEVICE_ERROR(buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr));
}
// Test map writing a buffer with wrong current usage
TEST_F(BufferValidationTest, MapWriteWrongUsage) {
wgpu::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = wgpu::BufferUsage::CopySrc;
wgpu::Buffer buf = device.CreateBuffer(&descriptor);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0u, _))
.Times(1);
ASSERT_DEVICE_ERROR(buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr));
}
// Test map reading a buffer that is already mapped
TEST_F(BufferValidationTest, MapReadAlreadyMapped) {
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.MapReadAsync(ToMockBufferMapReadCallback, this + 0);
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, this + 0))
.Times(1);
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0u, this + 1))
.Times(1);
ASSERT_DEVICE_ERROR(buf.MapReadAsync(ToMockBufferMapReadCallback, this + 1));
queue.Submit(0, nullptr);
}
// Test map writing a buffer that is already mapped
TEST_F(BufferValidationTest, MapWriteAlreadyMapped) {
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.MapWriteAsync(ToMockBufferMapWriteCallback, this + 0);
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, this + 0))
.Times(1);
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0u, this + 1))
.Times(1);
ASSERT_DEVICE_ERROR(buf.MapWriteAsync(ToMockBufferMapWriteCallback, this + 1));
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) {
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr);
EXPECT_CALL(*mockBufferMapReadCallback, Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u, _))
.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) {
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u, _))
.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) {
{
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr);
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u, _))
.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) {
{
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr);
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u, _))
.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) {
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.MapReadAsync(ToMockBufferMapReadCallback, this + 0);
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u, this + 0))
.Times(1);
buf.Unmap();
buf.MapReadAsync(ToMockBufferMapReadCallback, this + 1);
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, this + 1))
.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) {
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.MapWriteAsync(ToMockBufferMapWriteCallback, this + 0);
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0u, this + 0))
.Times(1);
buf.Unmap();
buf.MapWriteAsync(ToMockBufferMapWriteCallback, this + 1);
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, this + 1))
.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) {
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr);
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _))
.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) {
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr);
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _))
.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) {
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr);
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _))
.WillOnce(InvokeWithoutArgs([&]() { buf = wgpu::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) {
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr);
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _))
.WillOnce(InvokeWithoutArgs([&]() { buf = wgpu::Buffer(); }));
queue.Submit(0, nullptr);
}
// Test that it is valid to destroy an unmapped buffer
TEST_F(BufferValidationTest, DestroyUnmappedBuffer) {
{
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.Destroy();
}
{
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.Destroy();
}
}
// Test that it is valid to destroy a mapped buffer
TEST_F(BufferValidationTest, DestroyMappedBuffer) {
{
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr);
buf.Destroy();
}
{
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr);
buf.Destroy();
}
}
// Test that destroying a buffer implicitly unmaps it
TEST_F(BufferValidationTest, DestroyMappedBufferCausesImplicitUnmap) {
{
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.MapReadAsync(ToMockBufferMapReadCallback, this + 0);
// Buffer is destroyed. Callback should be called with UNKNOWN status
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0, this + 0))
.Times(1);
buf.Destroy();
queue.Submit(0, nullptr);
}
{
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.MapWriteAsync(ToMockBufferMapWriteCallback, this + 1);
// Buffer is destroyed. Callback should be called with UNKNOWN status
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0, this + 1))
.Times(1);
buf.Destroy();
queue.Submit(0, nullptr);
}
}
// Test that it is valid to Destroy a destroyed buffer
TEST_F(BufferValidationTest, DestroyDestroyedBuffer) {
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.Destroy();
buf.Destroy();
}
// Test that it is invalid to Unmap a destroyed buffer
TEST_F(BufferValidationTest, UnmapDestroyedBuffer) {
{
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.Destroy();
ASSERT_DEVICE_ERROR(buf.Unmap());
}
{
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.Destroy();
ASSERT_DEVICE_ERROR(buf.Unmap());
}
}
// Test that it is invalid to map a destroyed buffer
TEST_F(BufferValidationTest, MapDestroyedBuffer) {
{
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.Destroy();
ASSERT_DEVICE_ERROR(buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr));
}
{
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.Destroy();
ASSERT_DEVICE_ERROR(buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr));
}
}
// Test that is is invalid to Map a mapped buffer
TEST_F(BufferValidationTest, MapMappedBuffer) {
{
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr);
ASSERT_DEVICE_ERROR(buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr));
queue.Submit(0, nullptr);
}
{
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr);
ASSERT_DEVICE_ERROR(buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr));
queue.Submit(0, nullptr);
}
}
// Test that is is invalid to Map a CreateBufferMapped buffer
TEST_F(BufferValidationTest, MapCreateBufferMappedBuffer) {
{
wgpu::Buffer buf = CreateBufferMapped(4, wgpu::BufferUsage::MapRead).buffer;
ASSERT_DEVICE_ERROR(buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr));
queue.Submit(0, nullptr);
}
{
wgpu::Buffer buf = CreateBufferMapped(4, wgpu::BufferUsage::MapWrite).buffer;
ASSERT_DEVICE_ERROR(buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr));
queue.Submit(0, nullptr);
}
}
// Test that it is valid to submit a buffer in a queue with a map usage if it is unmapped
TEST_F(BufferValidationTest, SubmitBufferWithMapUsage) {
wgpu::BufferDescriptor descriptorA;
descriptorA.size = 4;
descriptorA.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite;
wgpu::BufferDescriptor descriptorB;
descriptorB.size = 4;
descriptorB.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead;
wgpu::Buffer bufA = device.CreateBuffer(&descriptorA);
wgpu::Buffer bufB = device.CreateBuffer(&descriptorB);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4);
wgpu::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
}
// Test that it is invalid to submit a mapped buffer in a queue
TEST_F(BufferValidationTest, SubmitMappedBuffer) {
wgpu::BufferDescriptor descriptorA;
descriptorA.size = 4;
descriptorA.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite;
wgpu::BufferDescriptor descriptorB;
descriptorB.size = 4;
descriptorB.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead;
{
wgpu::Buffer bufA = device.CreateBuffer(&descriptorA);
wgpu::Buffer bufB = device.CreateBuffer(&descriptorB);
bufA.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4);
wgpu::CommandBuffer commands = encoder.Finish();
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
queue.Submit(0, nullptr);
}
{
wgpu::Buffer bufA = device.CreateBuffer(&descriptorA);
wgpu::Buffer bufB = device.CreateBuffer(&descriptorB);
bufB.MapReadAsync(ToMockBufferMapReadCallback, nullptr);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4);
wgpu::CommandBuffer commands = encoder.Finish();
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
queue.Submit(0, nullptr);
}
{
wgpu::Buffer bufA = device.CreateBufferMapped(&descriptorA).buffer;
wgpu::Buffer bufB = device.CreateBuffer(&descriptorB);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4);
wgpu::CommandBuffer commands = encoder.Finish();
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
queue.Submit(0, nullptr);
}
{
wgpu::Buffer bufA = device.CreateBuffer(&descriptorA);
wgpu::Buffer bufB = device.CreateBufferMapped(&descriptorB).buffer;
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4);
wgpu::CommandBuffer commands = encoder.Finish();
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
queue.Submit(0, nullptr);
}
}
// Test that it is invalid to submit a destroyed buffer in a queue
TEST_F(BufferValidationTest, SubmitDestroyedBuffer) {
wgpu::BufferDescriptor descriptorA;
descriptorA.size = 4;
descriptorA.usage = wgpu::BufferUsage::CopySrc;
wgpu::BufferDescriptor descriptorB;
descriptorB.size = 4;
descriptorB.usage = wgpu::BufferUsage::CopyDst;
wgpu::Buffer bufA = device.CreateBuffer(&descriptorA);
wgpu::Buffer bufB = device.CreateBuffer(&descriptorB);
bufA.Destroy();
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4);
wgpu::CommandBuffer commands = encoder.Finish();
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
}
// Test that a map usage is required to call Unmap
TEST_F(BufferValidationTest, UnmapWithoutMapUsage) {
wgpu::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = wgpu::BufferUsage::CopyDst;
wgpu::Buffer buf = device.CreateBuffer(&descriptor);
ASSERT_DEVICE_ERROR(buf.Unmap());
}
// Test that it is valid to call Unmap on a buffer that is not mapped
TEST_F(BufferValidationTest, UnmapUnmappedBuffer) {
{
wgpu::Buffer buf = CreateMapReadBuffer(4);
// Buffer starts unmapped. Unmap should succeed.
buf.Unmap();
buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr);
buf.Unmap();
// Unmapping twice should succeed
buf.Unmap();
}
{
wgpu::Buffer buf = CreateMapWriteBuffer(4);
// Buffer starts unmapped. Unmap should succeed.
buf.Unmap();
buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr);
// Unmapping twice should succeed
buf.Unmap();
buf.Unmap();
}
}
// Test that it is invalid to call GetMappedRange on an unmapped buffer.
TEST_F(BufferValidationTest, GetMappedRangeOnUnmappedBuffer) {
// Unmapped at creation case.
{
wgpu::BufferDescriptor desc;
desc.size = 4;
desc.usage = wgpu::BufferUsage::CopySrc;
wgpu::Buffer buf = device.CreateBuffer(&desc);
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetMappedRange()));
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetConstMappedRange()));
}
// Unmapped after CreateBufferMapped case.
{
wgpu::Buffer buf = CreateBufferMapped(4, wgpu::BufferUsage::CopySrc).buffer;
buf.Unmap();
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetMappedRange()));
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetConstMappedRange()));
}
// Unmapped after MapReadAsync case.
{
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr);
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _))
.Times(1);
queue.Submit(0, nullptr);
buf.Unmap();
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetMappedRange()));
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetConstMappedRange()));
}
// Unmapped after MapWriteAsync case.
{
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr);
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _))
.Times(1);
queue.Submit(0, nullptr);
buf.Unmap();
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetMappedRange()));
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetConstMappedRange()));
}
}
// Test that it is invalid to call GetMappedRange on a destroyed buffer.
TEST_F(BufferValidationTest, GetMappedRangeOnDestroyedBuffer) {
// Destroyed after creation case.
{
wgpu::BufferDescriptor desc;
desc.size = 4;
desc.usage = wgpu::BufferUsage::CopySrc;
wgpu::Buffer buf = device.CreateBuffer(&desc);
buf.Destroy();
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetMappedRange()));
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetConstMappedRange()));
}
// Destroyed after CreateBufferMapped case.
{
wgpu::Buffer buf = CreateBufferMapped(4, wgpu::BufferUsage::CopySrc).buffer;
buf.Destroy();
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetMappedRange()));
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetConstMappedRange()));
}
// Destroyed after MapReadAsync case.
{
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr);
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _))
.Times(1);
queue.Submit(0, nullptr);
buf.Destroy();
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetMappedRange()));
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetConstMappedRange()));
}
// Destroyed after MapWriteAsync case.
{
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr);
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _))
.Times(1);
queue.Submit(0, nullptr);
buf.Destroy();
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetMappedRange()));
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetConstMappedRange()));
}
}
// Test that it is invalid to call GetMappedRange on a buffer afterMapReadAsync
TEST_F(BufferValidationTest, GetMappedRangeOnMappedForReading) {
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr);
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _))
.Times(1);
queue.Submit(0, nullptr);
ASSERT_DEVICE_ERROR(ASSERT_EQ(nullptr, buf.GetMappedRange()));
}
// Test valid cases to call GetMappedRange on a buffer.
TEST_F(BufferValidationTest, GetMappedRangeValidCases) {
// GetMappedRange after CreateBufferMapped case.
{
wgpu::CreateBufferMappedResult result = CreateBufferMapped(4, wgpu::BufferUsage::CopySrc);
ASSERT_NE(result.buffer.GetConstMappedRange(), nullptr);
ASSERT_EQ(result.buffer.GetConstMappedRange(), result.buffer.GetMappedRange());
ASSERT_EQ(result.buffer.GetConstMappedRange(), result.data);
}
// GetMappedRange after MapReadAsync case.
{
wgpu::Buffer buf = CreateMapReadBuffer(4);
buf.MapReadAsync(ToMockBufferMapReadCallback, nullptr);
const void* mappedPointer = nullptr;
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _))
.WillOnce(SaveArg<1>(&mappedPointer));
queue.Submit(0, nullptr);
ASSERT_NE(buf.GetConstMappedRange(), nullptr);
ASSERT_EQ(buf.GetConstMappedRange(), mappedPointer);
}
// GetMappedRange after MapWriteAsync case.
{
wgpu::Buffer buf = CreateMapWriteBuffer(4);
buf.MapWriteAsync(ToMockBufferMapWriteCallback, nullptr);
const void* mappedPointer = nullptr;
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_Success, Ne(nullptr), 4u, _))
.WillOnce(SaveArg<1>(&mappedPointer));
queue.Submit(0, nullptr);
ASSERT_NE(buf.GetConstMappedRange(), nullptr);
ASSERT_EQ(buf.GetConstMappedRange(), buf.GetMappedRange());
ASSERT_EQ(buf.GetConstMappedRange(), mappedPointer);
}
}