blob: 32f6562bf5a916102bf15864275a42ba388656bb [file] [log] [blame] [edit]
// Copyright 2017 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <limits>
#include <memory>
#include <vector>
#include "dawn/common/Platform.h"
#include "dawn/tests/MockCallback.h"
#include "dawn/tests/unittests/validation/ValidationTest.h"
#include "gmock/gmock.h"
using testing::_;
using testing::HasSubstr;
using testing::Invoke;
using testing::MockCppCallback;
using testing::TestParamInfo;
using testing::Values;
using testing::WithParamInterface;
using MockMapAsyncCallback = MockCppCallback<void (*)(wgpu::MapAsyncStatus, const char*)>;
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::Buffer BufferMappedAtCreation(uint64_t size, wgpu::BufferUsage usage) {
wgpu::BufferDescriptor descriptor;
descriptor.size = size;
descriptor.usage = usage;
descriptor.mappedAtCreation = true;
return device.CreateBuffer(&descriptor);
}
wgpu::Queue queue;
void SetUp() override {
ValidationTest::SetUp();
queue = device.GetQueue();
}
};
// 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 case where creation should succeed
TEST_F(BufferValidationTest, CreationMaxBufferSize) {
// Success when at limit
{
wgpu::BufferDescriptor descriptor;
descriptor.size = GetSupportedLimits().limits.maxBufferSize;
descriptor.usage = wgpu::BufferUsage::Uniform;
device.CreateBuffer(&descriptor);
}
// Error once it is pass the (default) limit on the device. (Note that MaxLimitTests tests at
// max possible limit given the adapters.)
{
wgpu::BufferDescriptor descriptor;
ASSERT_TRUE(GetSupportedLimits().limits.maxBufferSize <
std::numeric_limits<uint32_t>::max());
descriptor.size = GetSupportedLimits().limits.maxBufferSize + 1;
descriptor.usage = wgpu::BufferUsage::Uniform;
ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
}
}
// Test restriction on usages must not be None (0)
TEST_F(BufferValidationTest, CreationMapUsageNotZero) {
// Zero (None) usage is an error
{
wgpu::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = wgpu::BufferUsage::None;
ASSERT_DEVICE_ERROR(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));
}
}
class BufferMappingValidationTest : public BufferValidationTest,
public WithParamInterface<wgpu::MapMode> {
protected:
wgpu::Buffer CreateBuffer(uint64_t size) {
switch (GetParam()) {
case wgpu::MapMode::Read:
return CreateMapReadBuffer(size);
case wgpu::MapMode::Write:
return CreateMapWriteBuffer(size);
default:
DAWN_UNREACHABLE();
}
DAWN_UNREACHABLE();
}
wgpu::Buffer CreateMappedAtCreationBuffer(uint64_t size) {
switch (GetParam()) {
case wgpu::MapMode::Read:
return BufferMappedAtCreation(size, wgpu::BufferUsage::MapRead);
case wgpu::MapMode::Write:
return BufferMappedAtCreation(size, wgpu::BufferUsage::MapWrite);
default:
DAWN_UNREACHABLE();
}
DAWN_UNREACHABLE();
}
void AssertMapAsyncError(wgpu::Buffer buffer,
wgpu::MapMode mode,
size_t offset,
size_t size,
bool deviceError = true) {
// We use a new mock callback here so that the validation on the call happens as soon as the
// scope of this call ends. This is possible since we are using Spontaneous mode.
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Error, _)).Times(1);
if (deviceError) {
ASSERT_DEVICE_ERROR(buffer.MapAsync(
mode, offset, size, wgpu::CallbackMode::AllowSpontaneous, mockCb.Callback()));
} else {
buffer.MapAsync(mode, offset, size, wgpu::CallbackMode::AllowSpontaneous,
mockCb.Callback());
}
}
};
INSTANTIATE_TEST_SUITE_P(,
BufferMappingValidationTest,
testing::Values(wgpu::MapMode::Read, wgpu::MapMode::Write),
[](const TestParamInfo<BufferMappingValidationTest::ParamType>& info) {
switch (info.param) {
case wgpu::MapMode::Read:
return "Read";
case wgpu::MapMode::Write:
return "Write";
default:
DAWN_UNREACHABLE();
}
DAWN_UNREACHABLE();
});
// Test the success case for mapping buffer.
TEST_P(BufferMappingValidationTest, MapAsync_Success) {
wgpu::Buffer buffer = CreateBuffer(4);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
WaitForAllOperations();
buffer.Unmap();
}
// Test map async with a buffer that's an error
TEST_P(BufferMappingValidationTest, MapAsync_ErrorBuffer) {
wgpu::BufferDescriptor desc;
desc.size = 4;
desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite;
wgpu::Buffer buffer;
ASSERT_DEVICE_ERROR(buffer = device.CreateBuffer(&desc));
AssertMapAsyncError(buffer, GetParam(), 0, 4);
}
// Test map async with an invalid offset and size alignment.
TEST_P(BufferMappingValidationTest, MapAsync_OffsetSizeAlignment) {
// Control case, offset aligned to 8 and size to 4 is valid
{
wgpu::Buffer buffer = CreateBuffer(12);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buffer.MapAsync(GetParam(), 8, 4, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
}
// Error case, offset aligned to 4 is an error.
{
wgpu::Buffer buffer = CreateBuffer(12);
AssertMapAsyncError(buffer, GetParam(), 4, 4);
}
// Error case, size aligned to 2 is an error.
{
wgpu::Buffer buffer = CreateBuffer(8);
AssertMapAsyncError(buffer, GetParam(), 0, 6);
}
}
// Test map async with an invalid offset and size OOB checks
TEST_P(BufferMappingValidationTest, MapAsync_OffsetSizeOOB) {
// Valid case: full buffer is ok.
{
wgpu::Buffer buffer = CreateBuffer(8);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buffer.MapAsync(GetParam(), 0, 8, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
}
// Valid case: range in the middle of the buffer is ok.
{
wgpu::Buffer buffer = CreateBuffer(16);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buffer.MapAsync(GetParam(), 8, 4, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
}
// Valid case: empty range at the end of the buffer is ok.
{
wgpu::Buffer buffer = CreateBuffer(8);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buffer.MapAsync(GetParam(), 8, 0, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
}
// Error case, offset is larger than the buffer size (even if size is 0).
{
wgpu::Buffer buffer = CreateBuffer(12);
AssertMapAsyncError(buffer, GetParam(), 16, 0);
}
// Error case, offset + size is larger than the buffer
{
wgpu::Buffer buffer = CreateBuffer(12);
AssertMapAsyncError(buffer, GetParam(), 8, 8);
}
// Error case, offset + size is larger than the buffer, overflow case.
{
wgpu::Buffer buffer = CreateBuffer(12);
AssertMapAsyncError(buffer, GetParam(), 8, std::numeric_limits<size_t>::max() & ~size_t(7));
}
}
// Test map async with a buffer that has the wrong usage
TEST_P(BufferMappingValidationTest, MapAsync_WrongUsage) {
{
wgpu::BufferDescriptor desc;
desc.usage = wgpu::BufferUsage::Vertex;
desc.size = 4;
wgpu::Buffer buffer = device.CreateBuffer(&desc);
AssertMapAsyncError(buffer, GetParam(), 0, 4);
}
{
wgpu::Buffer buffer =
GetParam() == wgpu::MapMode::Read ? CreateMapWriteBuffer(4) : CreateMapReadBuffer(4);
AssertMapAsyncError(buffer, GetParam(), 0, 4);
}
}
// Test map async with a wrong mode
TEST_P(BufferMappingValidationTest, MapAsync_WrongMode) {
{
wgpu::Buffer buffer = CreateBuffer(4);
AssertMapAsyncError(buffer, wgpu::MapMode::None, 0, 4);
}
{
wgpu::Buffer buffer = CreateBuffer(4);
AssertMapAsyncError(buffer, wgpu::MapMode::Read | wgpu::MapMode::Write, 0, 4);
}
}
// Test map async with a buffer that's already mapped
TEST_P(BufferMappingValidationTest, MapAsync_AlreadyMapped) {
{
wgpu::Buffer buffer = CreateBuffer(4);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
AssertMapAsyncError(buffer, GetParam(), 0, 4);
}
{
wgpu::Buffer buffer = CreateMappedAtCreationBuffer(4);
AssertMapAsyncError(buffer, GetParam(), 0, 4);
}
}
// Test MapAsync() immediately causes a pending map error
TEST_P(BufferMappingValidationTest, MapAsync_PendingMap) {
// Note that in the wire, we currently don't generate a validation error while in native we do.
// If eventually we add a way to inject errors on the wire, we may be able to make this behavior
// more aligned.
bool validationError = !UsesWire();
// Overlapping range
{
wgpu::Buffer buffer = CreateBuffer(4);
// The first map async call should succeed while the second one should fail
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
AssertMapAsyncError(buffer, GetParam(), 0, 4, validationError);
WaitForAllOperations();
}
// Non-overlapping range
{
wgpu::Buffer buffer = CreateBuffer(16);
// The first map async call should succeed while the second one should fail
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buffer.MapAsync(GetParam(), 0, 8, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
AssertMapAsyncError(buffer, GetParam(), 8, 8, validationError);
WaitForAllOperations();
}
}
// Test map async with a buffer that's destroyed
TEST_P(BufferMappingValidationTest, MapAsync_Destroy) {
wgpu::Buffer buffer = CreateBuffer(4);
buffer.Destroy();
AssertMapAsyncError(buffer, GetParam(), 0, 4);
}
// Test map async but unmapping before the result is ready.
TEST_P(BufferMappingValidationTest, MapAsync_UnmapBeforeResult) {
wgpu::Buffer buffer = CreateBuffer(4);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Aborted, HasSubstr("unmapped"))).Times(1);
buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
buffer.Unmap();
WaitForAllOperations();
}
// When a MapAsync 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_P(BufferMappingValidationTest, MapAsync_UnmapBeforeResultAndMapAgain) {
wgpu::Buffer buffer = CreateBuffer(16);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Aborted, HasSubstr("unmapped"))).Times(1);
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buffer.MapAsync(GetParam(), 0, 8, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
buffer.Unmap();
buffer.MapAsync(GetParam(), 8, 8, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
WaitForAllOperations();
// Check that only the second MapAsync had an effect
ASSERT_EQ(nullptr, buffer.GetConstMappedRange(0));
ASSERT_NE(nullptr, buffer.GetConstMappedRange(8));
}
// Test map async but destroying before the result is ready.
TEST_P(BufferMappingValidationTest, MapAsync_DestroyBeforeResult) {
wgpu::Buffer buffer = CreateBuffer(4);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Aborted, HasSubstr("destroyed"))).Times(1);
buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
buffer.Destroy();
WaitForAllOperations();
}
// Test that the MapCallback isn't fired twice when unmap() is called inside the callback
TEST_P(BufferMappingValidationTest, MapAsync_UnmapCalledInCallback) {
wgpu::Buffer buffer = CreateBuffer(4);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).WillOnce(Invoke([&] {
buffer.Unmap();
}));
buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
WaitForAllOperations();
}
// Test that the MapCallback isn't fired twice when destroy() is called inside the callback
TEST_P(BufferMappingValidationTest, MapAsync_DestroyCalledInCallback) {
wgpu::Buffer buffer = CreateBuffer(4);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).WillOnce(Invoke([&] {
buffer.Destroy();
}));
buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
WaitForAllOperations();
}
// Test MapAsync call in MapAsync success callback
TEST_P(BufferMappingValidationTest, MapAsync_RetryInSuccessCallback) {
wgpu::Buffer buffer = CreateBuffer(4);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).WillOnce(Invoke([&] {
// MapAsync call on destroyed buffer should be invalid
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Error, HasSubstr("already mapped")))
.Times(1);
ASSERT_DEVICE_ERROR(buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowSpontaneous,
mockCb.Callback()));
}));
buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
WaitForAllOperations();
}
// Test MapAsync call in MapAsync validation error callback
TEST_P(BufferMappingValidationTest, MapAsync_RetryInErrorCallback) {
wgpu::Buffer buffer = CreateBuffer(4);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Error, _)).WillOnce(Invoke([&] {
// Retry with valid parameter and it should succeed
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
}));
// Wrong map mode on buffer is invalid and it should reject with validation error
ASSERT_DEVICE_ERROR(buffer.MapAsync(
GetParam() == wgpu::MapMode::Read ? wgpu::MapMode::Write : wgpu::MapMode::Read, 0, 4,
wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback()));
WaitForAllOperations();
}
// Test MapAsync call in MapAsync unmapped callback
TEST_P(BufferMappingValidationTest, MapAsync_RetryInUnmappedCallback) {
wgpu::Buffer buffer = CreateBuffer(4);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Aborted, _)).WillOnce(Invoke([&] {
// MapAsync call on unmapped buffer should be valid
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
}));
buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
buffer.Unmap();
WaitForAllOperations();
}
// Test MapAsync call in MapAsync destroyed callback
TEST_P(BufferMappingValidationTest, MapAsync_RetryInDestroyedCallback) {
wgpu::Buffer buffer = CreateBuffer(4);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Aborted, _)).WillOnce(Invoke([&] {
// MapAsync call on destroyed buffer should be invalid
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Error, HasSubstr("destroyed"))).Times(1);
ASSERT_DEVICE_ERROR(buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowSpontaneous,
mockCb.Callback()));
}));
buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
buffer.Destroy();
WaitForAllOperations();
}
// Test the success case for mappedAtCreation
TEST_F(BufferValidationTest, MappedAtCreationSuccess) {
BufferMappedAtCreation(4, wgpu::BufferUsage::MapWrite);
}
// Test the success case for mappedAtCreation for a non-mappable usage
TEST_F(BufferValidationTest, NonMappableMappedAtCreationSuccess) {
BufferMappedAtCreation(4, wgpu::BufferUsage::CopySrc);
}
// Test there is an error when mappedAtCreation is set but the size isn't aligned to 4.
TEST_F(BufferValidationTest, MappedAtCreationSizeAlignment) {
ASSERT_DEVICE_ERROR(BufferMappedAtCreation(2, wgpu::BufferUsage::MapWrite));
}
// Test that it is valid to destroy an error buffer
TEST_F(BufferValidationTest, DestroyErrorBuffer) {
wgpu::BufferDescriptor desc;
desc.size = 4;
desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite;
wgpu::Buffer buf;
ASSERT_DEVICE_ERROR(buf = device.CreateBuffer(&desc));
buf.Destroy();
}
// Test that it is valid to Destroy an unmapped buffer
TEST_P(BufferMappingValidationTest, DestroyUnmappedBuffer) {
wgpu::Buffer buffer = CreateBuffer(4);
buffer.Destroy();
}
// Test that it is valid to Destroy a destroyed buffer
TEST_P(BufferMappingValidationTest, DestroyDestroyedBuffer) {
wgpu::Buffer buffer = CreateBuffer(4);
buffer.Destroy();
buffer.Destroy();
}
// Test that it is valid to Unmap an error buffer
TEST_F(BufferValidationTest, UnmapErrorBuffer) {
wgpu::BufferDescriptor desc;
desc.size = 4;
desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite;
wgpu::Buffer buf;
ASSERT_DEVICE_ERROR(buf = device.CreateBuffer(&desc));
buf.Unmap();
}
// Test that it is valid to Unmap a destroyed buffer
TEST_P(BufferMappingValidationTest, UnmapDestroyedBuffer) {
wgpu::Buffer buffer = CreateBuffer(4);
buffer.Destroy();
buffer.Unmap();
}
// Test that unmap then mapping a destroyed buffer is an error.
// Regression test for crbug.com/1388920.
TEST_P(BufferMappingValidationTest, MapDestroyedBufferAfterUnmap) {
wgpu::Buffer buffer = CreateMapReadBuffer(4);
buffer.Destroy();
buffer.Unmap();
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Error, HasSubstr("destroyed"))).Times(1);
ASSERT_DEVICE_ERROR(buffer.MapAsync(GetParam(), 0, wgpu::kWholeMapSize,
wgpu::CallbackMode::AllowSpontaneous, mockCb.Callback()));
WaitForAllOperations();
}
// 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);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
bufA.MapAsync(wgpu::MapMode::Write, 0, 4, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4);
wgpu::CommandBuffer commands = encoder.Finish();
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
WaitForAllOperations();
}
{
wgpu::Buffer bufA = device.CreateBuffer(&descriptorA);
wgpu::Buffer bufB = device.CreateBuffer(&descriptorB);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
bufB.MapAsync(wgpu::MapMode::Read, 0, 4, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4);
wgpu::CommandBuffer commands = encoder.Finish();
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
WaitForAllOperations();
}
{
wgpu::BufferDescriptor mappedBufferDesc = descriptorA;
mappedBufferDesc.mappedAtCreation = true;
wgpu::Buffer bufA = device.CreateBuffer(&mappedBufferDesc);
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));
WaitForAllOperations();
}
{
wgpu::BufferDescriptor mappedBufferDesc = descriptorB;
mappedBufferDesc.mappedAtCreation = true;
wgpu::Buffer bufA = device.CreateBuffer(&descriptorA);
wgpu::Buffer bufB = device.CreateBuffer(&mappedBufferDesc);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4);
wgpu::CommandBuffer commands = encoder.Finish();
ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
WaitForAllOperations();
}
}
// 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 not 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);
buf.Unmap();
}
// Test that it is valid to call Unmap on a buffer that is not mapped
TEST_P(BufferMappingValidationTest, UnmapUnmappedBuffer) {
wgpu::Buffer buffer = CreateBuffer(4);
// Buffer starts unmapped. Unmap shouldn't fail.
buffer.Unmap();
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call).Times(1);
buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowSpontaneous, mockCb.Callback());
buffer.Unmap();
// Unmapping a second time shouldn't fail.
buffer.Unmap();
}
// Test that it is invalid to call GetMappedRange on an unmapped buffer.
TEST_F(BufferValidationTest, GetMappedRange_OnUnmappedBuffer) {
{
// Unmapped at creation case.
wgpu::BufferDescriptor desc;
desc.size = 4;
desc.usage = wgpu::BufferUsage::CopySrc;
wgpu::Buffer buf = device.CreateBuffer(&desc);
ASSERT_EQ(nullptr, buf.GetMappedRange());
ASSERT_EQ(nullptr, buf.GetConstMappedRange());
}
// Unmapped after mappedAtCreation case.
{
wgpu::Buffer buf = BufferMappedAtCreation(4, wgpu::BufferUsage::CopySrc);
buf.Unmap();
ASSERT_EQ(nullptr, buf.GetMappedRange());
ASSERT_EQ(nullptr, buf.GetConstMappedRange());
}
}
// Test that it is invalid to call GetMappedRange on an unmapped buffer.
TEST_P(BufferMappingValidationTest, GetMappedRange_OnUnmappedBuffer) {
// Unmapped after valid mapping.
wgpu::Buffer buf = CreateBuffer(4);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buf.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
WaitForAllOperations();
buf.Unmap();
ASSERT_EQ(nullptr, buf.GetMappedRange());
ASSERT_EQ(nullptr, buf.GetConstMappedRange());
}
// Test that it is invalid to call GetMappedRange on a destroyed buffer.
TEST_F(BufferValidationTest, GetMappedRange_OnDestroyedBuffer) {
// Destroyed after creation case.
{
wgpu::BufferDescriptor desc;
desc.size = 4;
desc.usage = wgpu::BufferUsage::CopySrc;
wgpu::Buffer buf = device.CreateBuffer(&desc);
buf.Destroy();
ASSERT_EQ(nullptr, buf.GetMappedRange());
ASSERT_EQ(nullptr, buf.GetConstMappedRange());
}
// Destroyed after mappedAtCreation case.
{
wgpu::Buffer buf = BufferMappedAtCreation(4, wgpu::BufferUsage::CopySrc);
buf.Destroy();
ASSERT_EQ(nullptr, buf.GetMappedRange());
ASSERT_EQ(nullptr, buf.GetConstMappedRange());
}
}
// Test that it is invalid to call GetMappedRange on a destroyed buffer.
TEST_P(BufferMappingValidationTest, GetMappedRange_OnDestroyedBuffer) {
// Destroyed after MapAsync case.
wgpu::Buffer buf = CreateBuffer(4);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buf.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
WaitForAllOperations();
buf.Destroy();
ASSERT_EQ(nullptr, buf.GetMappedRange());
ASSERT_EQ(nullptr, buf.GetConstMappedRange());
}
// Test that it is invalid to call GetMappedRange on a buffer after MapAsync for reading
TEST_F(BufferValidationTest, GetMappedRange_NonConstOnMappedForReading) {
wgpu::Buffer buf = CreateMapReadBuffer(4);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buf.MapAsync(wgpu::MapMode::Read, 0, 4, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
ASSERT_EQ(nullptr, buf.GetMappedRange());
}
// Test valid cases to call GetMappedRange on a buffer.
TEST_F(BufferValidationTest, GetMappedRange_ValidBufferStateCases) {
// GetMappedRange after mappedAtCreation case.
{
wgpu::Buffer buffer = BufferMappedAtCreation(4, wgpu::BufferUsage::CopySrc);
ASSERT_NE(buffer.GetConstMappedRange(), nullptr);
ASSERT_EQ(buffer.GetConstMappedRange(), buffer.GetMappedRange());
}
}
// Test valid cases to call GetMappedRange on a buffer.
TEST_P(BufferMappingValidationTest, GetMappedRange_ValidBufferStateCases) {
// GetMappedRange after MapAsync case.
wgpu::Buffer buf = CreateBuffer(4);
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buf.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
WaitForAllOperations();
ASSERT_NE(buf.GetConstMappedRange(), nullptr);
if (GetParam() == wgpu::MapMode::Write) {
ASSERT_EQ(buf.GetConstMappedRange(), buf.GetMappedRange());
}
}
// Test valid cases to call GetMappedRange on an error buffer.
TEST_F(BufferValidationTest, GetMappedRange_OnErrorBuffer) {
// GetMappedRange after mappedAtCreation a zero-sized buffer returns a non-nullptr.
// This is to check we don't do a malloc(0).
{
wgpu::Buffer buffer;
ASSERT_DEVICE_ERROR(buffer = BufferMappedAtCreation(
0, wgpu::BufferUsage::Storage | wgpu::BufferUsage::MapRead));
ASSERT_NE(buffer.GetConstMappedRange(), nullptr);
ASSERT_EQ(buffer.GetConstMappedRange(), buffer.GetMappedRange());
}
// GetMappedRange after mappedAtCreation non-OOM returns a non-nullptr.
{
wgpu::Buffer buffer;
ASSERT_DEVICE_ERROR(buffer = BufferMappedAtCreation(
4, wgpu::BufferUsage::Storage | wgpu::BufferUsage::MapRead));
ASSERT_NE(buffer.GetConstMappedRange(), nullptr);
ASSERT_EQ(buffer.GetConstMappedRange(), buffer.GetMappedRange());
}
}
// Test valid cases to call GetMappedRange on an error buffer that's also OOM.
TEST_F(BufferValidationTest, GetMappedRange_OnErrorBuffer_OOM) {
// TODO(crbug.com/dawn/1506): new (std::nothrow) crashes on OOM on Mac ARM64 because libunwind
// doesn't see the previous catchall try-catch.
DAWN_SKIP_TEST_IF(DAWN_PLATFORM_IS(MACOS) && DAWN_PLATFORM_IS(ARM64));
uint64_t kStupidLarge = uint64_t(1) << uint64_t(63);
if (UsesWire()) {
wgpu::Buffer buffer = BufferMappedAtCreation(
kStupidLarge, wgpu::BufferUsage::Storage | wgpu::BufferUsage::MapRead);
ASSERT_EQ(nullptr, buffer.Get());
} else {
wgpu::Buffer buffer;
ASSERT_DEVICE_ERROR(
buffer = BufferMappedAtCreation(
kStupidLarge, wgpu::BufferUsage::Storage | wgpu::BufferUsage::MapRead));
// GetMappedRange after mappedAtCreation OOM case returns nullptr.
ASSERT_EQ(buffer.GetConstMappedRange(), nullptr);
ASSERT_EQ(buffer.GetConstMappedRange(), buffer.GetMappedRange());
}
}
// Test validation of the GetMappedRange parameters
TEST_P(BufferMappingValidationTest, GetMappedRange_OffsetSizeOOB) {
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(11);
// Valid case: full range is ok
{
wgpu::Buffer buffer = CreateBuffer(8);
buffer.MapAsync(GetParam(), 0, 8, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
EXPECT_NE(buffer.GetConstMappedRange(0, 8), nullptr);
}
// Valid case: full range is ok with defaulted MapAsync size
{
wgpu::Buffer buffer = CreateBuffer(8);
buffer.MapAsync(GetParam(), 0, wgpu::kWholeMapSize, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
EXPECT_NE(buffer.GetConstMappedRange(0, 8), nullptr);
}
// Valid case: full range is ok with defaulted MapAsync size and defaulted GetMappedRangeSize
{
wgpu::Buffer buffer = CreateBuffer(8);
buffer.MapAsync(GetParam(), 0, wgpu::kWholeMapSize, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
EXPECT_NE(buffer.GetConstMappedRange(0, wgpu::kWholeMapSize), nullptr);
}
// Valid case: empty range at the end is ok
{
wgpu::Buffer buffer = CreateBuffer(8);
buffer.MapAsync(GetParam(), 0, 8, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
EXPECT_NE(buffer.GetConstMappedRange(8, 0), nullptr);
}
// Valid case: range in the middle is ok.
{
wgpu::Buffer buffer = CreateBuffer(16);
buffer.MapAsync(GetParam(), 0, 16, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
EXPECT_NE(buffer.GetConstMappedRange(8, 4), nullptr);
}
// Error case: offset is larger than the mapped range (even with size = 0)
{
wgpu::Buffer buffer = CreateBuffer(8);
buffer.MapAsync(GetParam(), 0, 8, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
EXPECT_EQ(buffer.GetConstMappedRange(9, 0), nullptr);
EXPECT_EQ(buffer.GetConstMappedRange(16, 0), nullptr);
EXPECT_EQ(buffer.GetConstMappedRange(std::numeric_limits<size_t>::max(), 0), nullptr);
}
// Error case: offset is larger than the buffer size (even with size = 0)
{
wgpu::Buffer buffer = CreateBuffer(16);
buffer.MapAsync(GetParam(), 8, 8, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
EXPECT_EQ(buffer.GetConstMappedRange(16, 4), nullptr);
EXPECT_EQ(buffer.GetConstMappedRange(24, 0), nullptr);
EXPECT_EQ(buffer.GetConstMappedRange(std::numeric_limits<size_t>::max(), 0), nullptr);
}
// Error case: offset + size is larger than the mapped range
{
wgpu::Buffer buffer = CreateBuffer(12);
buffer.MapAsync(GetParam(), 0, 12, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
EXPECT_EQ(buffer.GetConstMappedRange(8, 5), nullptr);
EXPECT_EQ(buffer.GetConstMappedRange(8, 8), nullptr);
}
// Error case: offset + size is larger than the mapped range, overflow case
{
wgpu::Buffer buffer = CreateBuffer(12);
buffer.MapAsync(GetParam(), 0, 12, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
// set size to (max - 1) to avoid being equal to kWholeMapSize
EXPECT_EQ(buffer.GetConstMappedRange(8, std::numeric_limits<size_t>::max() - 1), nullptr);
}
// Error case: size is larger than the mapped range when using default kWholeMapSize
{
wgpu::Buffer buffer = CreateBuffer(12);
buffer.MapAsync(GetParam(), 0, 8, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
EXPECT_EQ(buffer.GetConstMappedRange(0), nullptr);
}
// Error case: offset is before the start of the range (even with size = 0)
{
wgpu::Buffer buffer = CreateBuffer(12);
buffer.MapAsync(GetParam(), 8, 4, wgpu::CallbackMode::AllowProcessEvents,
mockCb.Callback());
WaitForAllOperations();
EXPECT_EQ(buffer.GetConstMappedRange(7, 4), nullptr);
EXPECT_EQ(buffer.GetConstMappedRange(0, 4), nullptr);
EXPECT_EQ(buffer.GetConstMappedRange(0, 12), nullptr);
EXPECT_EQ(buffer.GetConstMappedRange(0, 0), nullptr);
}
}
// Test that the buffer creation parameters are correctly reflected for succesfully created buffers.
TEST_F(BufferValidationTest, CreationParameterReflectionForValidBuffer) {
// Test reflection on two succesfully created but different buffers. The reflected data should
// be different!
{
wgpu::BufferDescriptor desc;
desc.size = 16;
desc.usage = wgpu::BufferUsage::Uniform;
wgpu::Buffer buf = device.CreateBuffer(&desc);
EXPECT_EQ(wgpu::BufferUsage::Uniform, buf.GetUsage());
EXPECT_EQ(16u, buf.GetSize());
}
{
wgpu::BufferDescriptor desc;
desc.size = 32;
desc.usage = wgpu::BufferUsage::Storage;
wgpu::Buffer buf = device.CreateBuffer(&desc);
EXPECT_EQ(wgpu::BufferUsage::Storage, buf.GetUsage());
EXPECT_EQ(32u, buf.GetSize());
}
}
// Test that the buffer creation parameters are correctly reflected for buffers invalid because of
// validation errors.
TEST_F(BufferValidationTest, CreationParameterReflectionForErrorBuffer) {
wgpu::BufferDescriptor desc;
desc.usage = wgpu::BufferUsage::Uniform;
desc.size = 19;
desc.mappedAtCreation = true;
// Error! MappedAtCreation requires size % 4 == 0.
wgpu::Buffer buf;
ASSERT_DEVICE_ERROR(buf = device.CreateBuffer(&desc));
// Reflection data is still exactly what was in the descriptor.
EXPECT_EQ(wgpu::BufferUsage::Uniform, buf.GetUsage());
EXPECT_EQ(19u, buf.GetSize());
}
// Test that the buffer creation parameters are correctly reflected for buffers invalid because of
// OOM.
TEST_F(BufferValidationTest, CreationParameterReflectionForOOMBuffer) {
constexpr uint64_t kAmazinglyLargeSize = 0x1234'5678'90AB'CDEF;
wgpu::BufferDescriptor desc;
desc.usage = wgpu::BufferUsage::Storage;
desc.size = kAmazinglyLargeSize;
// OOM!
wgpu::Buffer buf;
ASSERT_DEVICE_ERROR(buf = device.CreateBuffer(&desc));
// Reflection data is still exactly what was in the descriptor.
EXPECT_EQ(wgpu::BufferUsage::Storage, buf.GetUsage());
EXPECT_EQ(kAmazinglyLargeSize, buf.GetSize());
}
// Test that buffer reflection doesn't show internal usages
TEST_F(BufferValidationTest, CreationParameterReflectionNoInternalUsage) {
wgpu::BufferDescriptor desc;
desc.size = 16;
// QueryResolve also adds kInternalStorageBuffer for processing of queries.
desc.usage = wgpu::BufferUsage::QueryResolve;
wgpu::Buffer buf = device.CreateBuffer(&desc);
// The reflection shouldn't show kInternalStorageBuffer
EXPECT_EQ(wgpu::BufferUsage::QueryResolve, buf.GetUsage());
EXPECT_EQ(16u, buf.GetSize());
}
// Test that GetMapState() shows expected buffer map state
TEST_F(BufferValidationTest, GetMapState) {
// MappedAtCreation + Unmap
{
wgpu::Buffer buf = BufferMappedAtCreation(4, wgpu::BufferUsage::CopySrc);
EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState());
buf.Unmap();
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
}
// MappedAtCreation + Destroy
{
wgpu::Buffer buf = BufferMappedAtCreation(4, wgpu::BufferUsage::CopySrc);
EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState());
buf.Destroy();
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
}
}
// Test that GetMapState() shows expected buffer map state
TEST_P(BufferMappingValidationTest, GetMapState) {
// MapAsync + Unmap
{
wgpu::Buffer buf = CreateBuffer(4);
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buf.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
WaitForAllOperations();
EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState());
buf.Unmap();
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
}
// MapAsync + Unmap before the callback
{
wgpu::Buffer buf = CreateBuffer(4);
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Aborted, _)).Times(1);
buf.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
buf.Unmap();
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
WaitForAllOperations();
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
}
// MapAsync + Destroy
{
wgpu::Buffer buf = CreateBuffer(4);
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
buf.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
WaitForAllOperations();
EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState());
buf.Destroy();
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
}
// MapAsync + Destroy before the callback
{
wgpu::Buffer buf = CreateBuffer(4);
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
MockMapAsyncCallback mockCb;
EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Aborted, _)).Times(1);
buf.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
buf.Destroy();
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
WaitForAllOperations();
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
}
}
class BufferMapExtendedUsagesValidationTest : public BufferValidationTest {
protected:
void SetUp() override {
DAWN_SKIP_TEST_IF(UsesWire());
BufferValidationTest::SetUp();
}
std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
return {wgpu::FeatureName::BufferMapExtendedUsages};
}
};
// Test that MapRead or MapWrite can be combined with any other usage when creating
// a buffer.
TEST_F(BufferMapExtendedUsagesValidationTest, CreationMapUsageReadOrWriteNoRestrictions) {
constexpr wgpu::BufferUsage kNonMapUsages[] = {
wgpu::BufferUsage::CopySrc, wgpu::BufferUsage::CopyDst, wgpu::BufferUsage::Index,
wgpu::BufferUsage::Vertex, wgpu::BufferUsage::Uniform, wgpu::BufferUsage::Storage,
wgpu::BufferUsage::Indirect, wgpu::BufferUsage::QueryResolve,
};
// MapRead with anything is ok
{
wgpu::BufferDescriptor descriptor;
descriptor.size = 4;
for (const auto otherUsage : kNonMapUsages) {
descriptor.usage = wgpu::BufferUsage::MapRead | otherUsage;
device.CreateBuffer(&descriptor);
}
}
// MapWrite with anything is ok
{
wgpu::BufferDescriptor descriptor;
descriptor.size = 4;
for (const auto otherUsage : kNonMapUsages) {
descriptor.usage = wgpu::BufferUsage::MapWrite | otherUsage;
device.CreateBuffer(&descriptor);
}
}
// MapRead | MapWrite with anything is ok
{
wgpu::BufferDescriptor descriptor;
descriptor.size = 4;
for (const auto otherUsage : kNonMapUsages) {
descriptor.usage =
wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite | otherUsage;
device.CreateBuffer(&descriptor);
}
}
}