mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-06 22:53:35 +00:00
- Note that by default these are already errors, not warnings. Change-Id: I46afa6e54e7915ba54aa6990cd641288609108c1 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/128101 Reviewed-by: Shrek Shao <shrekshao@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Loko Kung <lokokung@google.com>
1344 lines
50 KiB
C++
1344 lines
50 KiB
C++
// 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 <limits>
|
|
#include <memory>
|
|
|
|
#include "dawn/common/Platform.h"
|
|
#include "dawn/tests/unittests/validation/ValidationTest.h"
|
|
#include "gmock/gmock.h"
|
|
|
|
using testing::_;
|
|
using testing::InvokeWithoutArgs;
|
|
|
|
class MockBufferMapAsyncCallback {
|
|
public:
|
|
MOCK_METHOD(void, Call, (WGPUBufferMapAsyncStatus status, void* userdata));
|
|
};
|
|
|
|
static std::unique_ptr<MockBufferMapAsyncCallback> mockBufferMapAsyncCallback;
|
|
static void ToMockBufferMapAsyncCallback(WGPUBufferMapAsyncStatus status, void* userdata) {
|
|
mockBufferMapAsyncCallback->Call(status, 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::Buffer BufferMappedAtCreation(uint64_t size, wgpu::BufferUsage usage) {
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.size = size;
|
|
descriptor.usage = usage;
|
|
descriptor.mappedAtCreation = true;
|
|
|
|
return device.CreateBuffer(&descriptor);
|
|
}
|
|
|
|
void AssertMapAsyncError(wgpu::Buffer buffer, wgpu::MapMode mode, size_t offset, size_t size) {
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1);
|
|
|
|
ASSERT_DEVICE_ERROR(
|
|
buffer.MapAsync(mode, offset, size, ToMockBufferMapAsyncCallback, nullptr));
|
|
}
|
|
|
|
wgpu::Queue queue;
|
|
|
|
private:
|
|
void SetUp() override {
|
|
ValidationTest::SetUp();
|
|
|
|
mockBufferMapAsyncCallback = std::make_unique<MockBufferMapAsyncCallback>();
|
|
queue = device.GetQueue();
|
|
}
|
|
|
|
void TearDown() override {
|
|
// Delete mocks so that expectations are checked
|
|
mockBufferMapAsyncCallback = 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 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));
|
|
}
|
|
}
|
|
|
|
// Test the success case for mapping buffer for reading
|
|
TEST_F(BufferValidationTest, MapAsync_ReadSuccess) {
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
|
|
WaitForAllOperations(device);
|
|
|
|
buf.Unmap();
|
|
}
|
|
|
|
// Test the success case for mapping buffer for writing
|
|
TEST_F(BufferValidationTest, MapAsync_WriteSuccess) {
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(4);
|
|
|
|
buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
|
|
WaitForAllOperations(device);
|
|
|
|
buf.Unmap();
|
|
}
|
|
|
|
// Test map async with a buffer that's an error
|
|
TEST_F(BufferValidationTest, 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, wgpu::MapMode::Read, 0, 4);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4);
|
|
}
|
|
|
|
// Test map async with an invalid offset and size alignment.
|
|
TEST_F(BufferValidationTest, MapAsync_OffsetSizeAlignment) {
|
|
// Control case, offset aligned to 8 and size to 4 is valid
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(12);
|
|
buffer.MapAsync(wgpu::MapMode::Read, 8, 4, nullptr, nullptr);
|
|
}
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(12);
|
|
buffer.MapAsync(wgpu::MapMode::Write, 8, 4, nullptr, nullptr);
|
|
}
|
|
|
|
// Error case, offset aligned to 4 is an error.
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(12);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Read, 4, 4);
|
|
}
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(12);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Write, 4, 4);
|
|
}
|
|
|
|
// Error case, size aligned to 2 is an error.
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(8);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 6);
|
|
}
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(8);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 6);
|
|
}
|
|
}
|
|
|
|
// Test map async with an invalid offset and size OOB checks
|
|
TEST_F(BufferValidationTest, MapAsync_OffsetSizeOOB) {
|
|
// Valid case: full buffer is ok.
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(8);
|
|
buffer.MapAsync(wgpu::MapMode::Read, 0, 8, nullptr, nullptr);
|
|
}
|
|
|
|
// Valid case: range in the middle of the buffer is ok.
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(16);
|
|
buffer.MapAsync(wgpu::MapMode::Read, 8, 4, nullptr, nullptr);
|
|
}
|
|
|
|
// Valid case: empty range at the end of the buffer is ok.
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(8);
|
|
buffer.MapAsync(wgpu::MapMode::Read, 8, 0, nullptr, nullptr);
|
|
}
|
|
|
|
// Error case, offset is larger than the buffer size (even if size is 0).
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(12);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Read, 16, 0);
|
|
}
|
|
|
|
// Error case, offset + size is larger than the buffer
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(12);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Read, 8, 8);
|
|
}
|
|
|
|
// Error case, offset + size is larger than the buffer, overflow case.
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(12);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Read, 8,
|
|
std::numeric_limits<size_t>::max() & ~size_t(7));
|
|
}
|
|
}
|
|
|
|
// Test map async with a buffer that has the wrong usage
|
|
TEST_F(BufferValidationTest, MapAsync_WrongUsage) {
|
|
{
|
|
wgpu::BufferDescriptor desc;
|
|
desc.usage = wgpu::BufferUsage::Vertex;
|
|
desc.size = 4;
|
|
wgpu::Buffer buffer = device.CreateBuffer(&desc);
|
|
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4);
|
|
}
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(4);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4);
|
|
}
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(4);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4);
|
|
}
|
|
}
|
|
|
|
// Test map async with a wrong mode
|
|
TEST_F(BufferValidationTest, MapAsync_WrongMode) {
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(4);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::None, 0, 4);
|
|
}
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(4);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Read | wgpu::MapMode::Write, 0, 4);
|
|
}
|
|
}
|
|
|
|
// Test map async with a buffer that's already mapped
|
|
TEST_F(BufferValidationTest, MapAsync_AlreadyMapped) {
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(4);
|
|
buffer.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr);
|
|
WaitForAllOperations(device);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4);
|
|
}
|
|
{
|
|
wgpu::Buffer buffer = BufferMappedAtCreation(4, wgpu::BufferUsage::MapRead);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4);
|
|
}
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(4);
|
|
buffer.MapAsync(wgpu::MapMode::Write, 0, 4, nullptr, nullptr);
|
|
WaitForAllOperations(device);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4);
|
|
}
|
|
{
|
|
wgpu::Buffer buffer = BufferMappedAtCreation(4, wgpu::BufferUsage::MapWrite);
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4);
|
|
}
|
|
}
|
|
|
|
// Test MapAsync() immediately causes a pending map error
|
|
TEST_F(BufferValidationTest, MapAsync_PendingMap) {
|
|
// Read + overlapping range
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(4);
|
|
// The first map async call should succeed while the second one should fail
|
|
buffer.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, this);
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Error, this + 1))
|
|
.Times(1);
|
|
buffer.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, this + 1);
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, this))
|
|
.Times(1);
|
|
WaitForAllOperations(device);
|
|
}
|
|
|
|
// Read + non-overlapping range
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(16);
|
|
// The first map async call should succeed while the second one should fail
|
|
buffer.MapAsync(wgpu::MapMode::Read, 0, 8, ToMockBufferMapAsyncCallback, this);
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Error, this + 1))
|
|
.Times(1);
|
|
buffer.MapAsync(wgpu::MapMode::Read, 8, 8, ToMockBufferMapAsyncCallback, this + 1);
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, this))
|
|
.Times(1);
|
|
WaitForAllOperations(device);
|
|
}
|
|
|
|
// Write + overlapping range
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(4);
|
|
// The first map async call should succeed while the second one should fail
|
|
buffer.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, this);
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Error, this + 1))
|
|
.Times(1);
|
|
buffer.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, this + 1);
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, this))
|
|
.Times(1);
|
|
WaitForAllOperations(device);
|
|
}
|
|
|
|
// Write + non-overlapping range
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(16);
|
|
// The first map async call should succeed while the second one should fail
|
|
buffer.MapAsync(wgpu::MapMode::Write, 0, 8, ToMockBufferMapAsyncCallback, this);
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Error, this + 1))
|
|
.Times(1);
|
|
buffer.MapAsync(wgpu::MapMode::Write, 8, 8, ToMockBufferMapAsyncCallback, this + 1);
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, this))
|
|
.Times(1);
|
|
WaitForAllOperations(device);
|
|
}
|
|
}
|
|
|
|
// Test map async with a buffer that's destroyed
|
|
TEST_F(BufferValidationTest, MapAsync_Destroy) {
|
|
{
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(4);
|
|
buffer.Destroy();
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4);
|
|
}
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(4);
|
|
buffer.Destroy();
|
|
AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4);
|
|
}
|
|
}
|
|
|
|
// Test map async but unmapping before the result is ready.
|
|
TEST_F(BufferValidationTest, MapAsync_UnmapBeforeResult) {
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback,
|
|
Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, _))
|
|
.Times(1);
|
|
buf.Unmap();
|
|
|
|
// The callback shouldn't be called again.
|
|
WaitForAllOperations(device);
|
|
}
|
|
{
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(4);
|
|
buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback,
|
|
Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, _))
|
|
.Times(1);
|
|
buf.Unmap();
|
|
|
|
// The callback shouldn't be called again.
|
|
WaitForAllOperations(device);
|
|
}
|
|
}
|
|
|
|
// 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_F(BufferValidationTest, MapAsync_UnmapBeforeResultAndMapAgain) {
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(16);
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 8, ToMockBufferMapAsyncCallback, this + 0);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback,
|
|
Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, this + 0))
|
|
.Times(1);
|
|
buf.Unmap();
|
|
|
|
buf.MapAsync(wgpu::MapMode::Read, 8, 8, ToMockBufferMapAsyncCallback, this + 1);
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, this + 1))
|
|
.Times(1);
|
|
WaitForAllOperations(device);
|
|
|
|
// Check that only the second MapAsync had an effect
|
|
ASSERT_EQ(nullptr, buf.GetConstMappedRange(0));
|
|
ASSERT_NE(nullptr, buf.GetConstMappedRange(8));
|
|
}
|
|
{
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(16);
|
|
buf.MapAsync(wgpu::MapMode::Write, 0, 8, ToMockBufferMapAsyncCallback, this + 0);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback,
|
|
Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, this + 0))
|
|
.Times(1);
|
|
buf.Unmap();
|
|
|
|
buf.MapAsync(wgpu::MapMode::Write, 8, 8, ToMockBufferMapAsyncCallback, this + 1);
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, this + 1))
|
|
.Times(1);
|
|
WaitForAllOperations(device);
|
|
|
|
// Check that only the second MapAsync had an effect
|
|
ASSERT_EQ(nullptr, buf.GetConstMappedRange(0));
|
|
ASSERT_NE(nullptr, buf.GetConstMappedRange(8));
|
|
}
|
|
}
|
|
|
|
// Test map async but destroying before the result is ready.
|
|
TEST_F(BufferValidationTest, MapAsync_DestroyBeforeResult) {
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback,
|
|
Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
|
|
.Times(1);
|
|
buf.Destroy();
|
|
|
|
// The callback shouldn't be called again.
|
|
WaitForAllOperations(device);
|
|
}
|
|
{
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(4);
|
|
buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback,
|
|
Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
|
|
.Times(1);
|
|
buf.Destroy();
|
|
|
|
// The callback shouldn't be called again.
|
|
WaitForAllOperations(device);
|
|
}
|
|
}
|
|
|
|
// Test that the MapCallback isn't fired twice when unmap() is called inside the callback
|
|
TEST_F(BufferValidationTest, MapAsync_UnmapCalledInCallback) {
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
|
|
.WillOnce(InvokeWithoutArgs([&]() { buf.Unmap(); }));
|
|
|
|
WaitForAllOperations(device);
|
|
}
|
|
{
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(4);
|
|
buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
|
|
.WillOnce(InvokeWithoutArgs([&]() { buf.Unmap(); }));
|
|
|
|
WaitForAllOperations(device);
|
|
}
|
|
}
|
|
|
|
// Test that the MapCallback isn't fired twice when destroy() is called inside the callback
|
|
TEST_F(BufferValidationTest, MapAsync_DestroyCalledInCallback) {
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
|
|
.WillOnce(InvokeWithoutArgs([&]() { buf.Destroy(); }));
|
|
|
|
WaitForAllOperations(device);
|
|
}
|
|
{
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(4);
|
|
buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
|
|
.WillOnce(InvokeWithoutArgs([&]() { buf.Destroy(); }));
|
|
|
|
WaitForAllOperations(device);
|
|
}
|
|
}
|
|
|
|
// Test MapAsync call in MapAsync success callback
|
|
// This test is disabled now because there seems to be a reeantrancy bug in the
|
|
// FlushWire call. See https://dawn-review.googlesource.com/c/dawn/+/116220 for the details.
|
|
TEST_F(BufferValidationTest, DISABLED_MapAsync_MapAsyncInMapAsyncSuccessCallback) {
|
|
// Test MapAsync call in MapAsync validation success callback
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
|
|
.WillOnce(InvokeWithoutArgs([&]() {
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Error, _));
|
|
// Should cause validation error because of already mapped buffer
|
|
ASSERT_DEVICE_ERROR(
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr));
|
|
}));
|
|
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
WaitForAllOperations(device);
|
|
// we need another wire flush to make the MapAsync in the callback to the server
|
|
WaitForAllOperations(device);
|
|
}
|
|
}
|
|
|
|
// Test MapAsync call in MapAsync rejection callback
|
|
TEST_F(BufferValidationTest, MapAsync_MapAsyncInMapAsyncRejectionCallback) {
|
|
// Test MapAsync call in MapAsync validation error callback
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Error, _))
|
|
.WillOnce(InvokeWithoutArgs([&]() {
|
|
// Retry with valid parameter and it should succeed
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _));
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
}));
|
|
|
|
// Write map mode on read buffer is invalid and it should reject with validation error
|
|
ASSERT_DEVICE_ERROR(
|
|
buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr));
|
|
|
|
WaitForAllOperations(device);
|
|
// we need another wire flush to make the MapAsync in the callback to the server
|
|
WaitForAllOperations(device);
|
|
}
|
|
|
|
// Test MapAsync call in MapAsync Unmapped before callback callback
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback,
|
|
Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, _))
|
|
.WillOnce(InvokeWithoutArgs([&]() {
|
|
// MapAsync call on unmapped buffer should be valid
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _));
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
}));
|
|
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
buf.Unmap();
|
|
WaitForAllOperations(device);
|
|
WaitForAllOperations(device);
|
|
}
|
|
|
|
// Test MapAsync call in MapAsync Destroyed before callback callback
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback,
|
|
Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
|
|
.WillOnce(InvokeWithoutArgs([&]() {
|
|
// MapAsync call on destroyed buffer should be invalid
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Error, _));
|
|
ASSERT_DEVICE_ERROR(
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr));
|
|
}));
|
|
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
buf.Destroy();
|
|
WaitForAllOperations(device);
|
|
WaitForAllOperations(device);
|
|
}
|
|
}
|
|
|
|
// 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_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 destroyed buffer
|
|
TEST_F(BufferValidationTest, DestroyDestroyedBuffer) {
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(4);
|
|
buf.Destroy();
|
|
buf.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_F(BufferValidationTest, UnmapDestroyedBuffer) {
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
buf.Destroy();
|
|
buf.Unmap();
|
|
}
|
|
{
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(4);
|
|
buf.Destroy();
|
|
buf.Unmap();
|
|
}
|
|
}
|
|
|
|
// Test that unmap then mapping a destroyed buffer is an error.
|
|
// Regression test for crbug.com/1388920.
|
|
TEST_F(BufferValidationTest, MapDestroyedBufferAfterUnmap) {
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(4);
|
|
buffer.Destroy();
|
|
buffer.Unmap();
|
|
|
|
ASSERT_DEVICE_ERROR(buffer.MapAsync(
|
|
wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
|
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
|
EXPECT_EQ(WGPUBufferMapAsyncStatus_Error, status);
|
|
},
|
|
nullptr));
|
|
WaitForAllOperations(device);
|
|
}
|
|
|
|
// 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.MapAsync(wgpu::MapMode::Write, 0, 4, nullptr, 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));
|
|
WaitForAllOperations(device);
|
|
}
|
|
{
|
|
wgpu::Buffer bufA = device.CreateBuffer(&descriptorA);
|
|
wgpu::Buffer bufB = device.CreateBuffer(&descriptorB);
|
|
|
|
bufB.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, 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));
|
|
WaitForAllOperations(device);
|
|
}
|
|
{
|
|
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(device);
|
|
}
|
|
{
|
|
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(device);
|
|
}
|
|
}
|
|
|
|
// 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_F(BufferValidationTest, UnmapUnmappedBuffer) {
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
// Buffer starts unmapped. Unmap shouldn't fail.
|
|
buf.Unmap();
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr);
|
|
buf.Unmap();
|
|
// Unmapping a second time shouldn't fail.
|
|
buf.Unmap();
|
|
}
|
|
{
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(4);
|
|
// Buffer starts unmapped. Unmap shouldn't fail.
|
|
buf.Unmap();
|
|
buf.MapAsync(wgpu::MapMode::Write, 0, 4, nullptr, nullptr);
|
|
buf.Unmap();
|
|
// Unmapping a second time shouldn't fail.
|
|
buf.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());
|
|
}
|
|
|
|
// Unmapped after MapAsync read case.
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
|
|
.Times(1);
|
|
WaitForAllOperations(device);
|
|
buf.Unmap();
|
|
|
|
ASSERT_EQ(nullptr, buf.GetMappedRange());
|
|
ASSERT_EQ(nullptr, buf.GetConstMappedRange());
|
|
}
|
|
|
|
// Unmapped after MapAsync write case.
|
|
{
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(4);
|
|
buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
|
|
.Times(1);
|
|
WaitForAllOperations(device);
|
|
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());
|
|
}
|
|
|
|
// Destroyed after MapAsync read case.
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
|
|
.Times(1);
|
|
WaitForAllOperations(device);
|
|
buf.Destroy();
|
|
|
|
ASSERT_EQ(nullptr, buf.GetMappedRange());
|
|
ASSERT_EQ(nullptr, buf.GetConstMappedRange());
|
|
}
|
|
|
|
// Destroyed after MapAsync write case.
|
|
{
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(4);
|
|
buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
|
|
.Times(1);
|
|
WaitForAllOperations(device);
|
|
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);
|
|
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
|
|
WaitForAllOperations(device);
|
|
|
|
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());
|
|
}
|
|
|
|
// GetMappedRange after MapAsync for reading case.
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr);
|
|
WaitForAllOperations(device);
|
|
|
|
ASSERT_NE(buf.GetConstMappedRange(), nullptr);
|
|
}
|
|
|
|
// GetMappedRange after MapAsync for writing case.
|
|
{
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(4);
|
|
|
|
buf.MapAsync(wgpu::MapMode::Write, 0, 4, nullptr, nullptr);
|
|
WaitForAllOperations(device);
|
|
|
|
ASSERT_NE(buf.GetConstMappedRange(), nullptr);
|
|
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_F(BufferValidationTest, GetMappedRange_OffsetSizeOOB) {
|
|
// Valid case: full range is ok
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(8);
|
|
buffer.MapAsync(wgpu::MapMode::Write, 0, 8, nullptr, nullptr);
|
|
WaitForAllOperations(device);
|
|
EXPECT_NE(buffer.GetMappedRange(0, 8), nullptr);
|
|
}
|
|
|
|
// Valid case: full range is ok with defaulted MapAsync size
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(8);
|
|
buffer.MapAsync(wgpu::MapMode::Write, 0, wgpu::kWholeMapSize, nullptr, nullptr);
|
|
WaitForAllOperations(device);
|
|
EXPECT_NE(buffer.GetMappedRange(0, 8), nullptr);
|
|
}
|
|
|
|
// Valid case: full range is ok with defaulted MapAsync size and defaulted GetMappedRangeSize
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(8);
|
|
buffer.MapAsync(wgpu::MapMode::Write, 0, wgpu::kWholeMapSize, nullptr, nullptr);
|
|
WaitForAllOperations(device);
|
|
EXPECT_NE(buffer.GetMappedRange(0, wgpu::kWholeMapSize), nullptr);
|
|
}
|
|
|
|
// Valid case: empty range at the end is ok
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(8);
|
|
buffer.MapAsync(wgpu::MapMode::Write, 0, 8, nullptr, nullptr);
|
|
WaitForAllOperations(device);
|
|
EXPECT_NE(buffer.GetMappedRange(8, 0), nullptr);
|
|
}
|
|
|
|
// Valid case: range in the middle is ok.
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(16);
|
|
buffer.MapAsync(wgpu::MapMode::Write, 0, 16, nullptr, nullptr);
|
|
WaitForAllOperations(device);
|
|
EXPECT_NE(buffer.GetMappedRange(8, 4), nullptr);
|
|
}
|
|
|
|
// Error case: offset is larger than the mapped range (even with size = 0)
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(8);
|
|
buffer.MapAsync(wgpu::MapMode::Write, 0, 8, nullptr, nullptr);
|
|
WaitForAllOperations(device);
|
|
EXPECT_EQ(buffer.GetMappedRange(9, 0), nullptr);
|
|
EXPECT_EQ(buffer.GetMappedRange(16, 0), nullptr);
|
|
EXPECT_EQ(buffer.GetMappedRange(std::numeric_limits<size_t>::max(), 0), nullptr);
|
|
}
|
|
|
|
// Error case: offset is larger than the buffer size (even with size = 0)
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(16);
|
|
buffer.MapAsync(wgpu::MapMode::Write, 8, 8, nullptr, nullptr);
|
|
WaitForAllOperations(device);
|
|
EXPECT_EQ(buffer.GetMappedRange(16, 4), nullptr);
|
|
EXPECT_EQ(buffer.GetMappedRange(24, 0), nullptr);
|
|
EXPECT_EQ(buffer.GetMappedRange(std::numeric_limits<size_t>::max(), 0), nullptr);
|
|
}
|
|
|
|
// Error case: offset + size is larger than the mapped range
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(12);
|
|
buffer.MapAsync(wgpu::MapMode::Write, 0, 12, nullptr, nullptr);
|
|
WaitForAllOperations(device);
|
|
EXPECT_EQ(buffer.GetMappedRange(8, 5), nullptr);
|
|
EXPECT_EQ(buffer.GetMappedRange(8, 8), nullptr);
|
|
}
|
|
|
|
// Error case: offset + size is larger than the mapped range, overflow case
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(12);
|
|
buffer.MapAsync(wgpu::MapMode::Write, 0, 12, nullptr, nullptr);
|
|
WaitForAllOperations(device);
|
|
// set size to (max - 1) to avoid being equal to kWholeMapSize
|
|
EXPECT_EQ(buffer.GetMappedRange(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 = CreateMapWriteBuffer(12);
|
|
buffer.MapAsync(wgpu::MapMode::Write, 0, 8, nullptr, nullptr);
|
|
WaitForAllOperations(device);
|
|
EXPECT_EQ(buffer.GetMappedRange(0), nullptr);
|
|
}
|
|
|
|
// Error case: offset is before the start of the range (even with size = 0)
|
|
{
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(12);
|
|
buffer.MapAsync(wgpu::MapMode::Write, 8, 4, nullptr, nullptr);
|
|
WaitForAllOperations(device);
|
|
EXPECT_EQ(buffer.GetMappedRange(7, 4), nullptr);
|
|
EXPECT_EQ(buffer.GetMappedRange(0, 4), nullptr);
|
|
EXPECT_EQ(buffer.GetMappedRange(0, 12), nullptr);
|
|
EXPECT_EQ(buffer.GetMappedRange(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) {
|
|
// MapRead + MapAsync + Unmap
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
|
|
.Times(1);
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
|
|
WaitForAllOperations(device);
|
|
EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState());
|
|
buf.Unmap();
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
}
|
|
|
|
// MapRead + MapAsync + Unmap before the callback
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback,
|
|
Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, _))
|
|
.Times(1);
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
|
|
buf.Unmap();
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
WaitForAllOperations(device);
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
}
|
|
|
|
// MapRead + MapAsync + Destroy
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
|
|
.Times(1);
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
|
|
WaitForAllOperations(device);
|
|
EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState());
|
|
buf.Destroy();
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
}
|
|
|
|
// MapRead + MapAsync + Destroy before the callback
|
|
{
|
|
wgpu::Buffer buf = CreateMapReadBuffer(4);
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback,
|
|
Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
|
|
.Times(1);
|
|
buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
|
|
buf.Destroy();
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
WaitForAllOperations(device);
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
}
|
|
|
|
// MapWrite + MapAsync + Unmap
|
|
{
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(4);
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
|
|
.Times(1);
|
|
buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
|
|
WaitForAllOperations(device);
|
|
EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState());
|
|
buf.Unmap();
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
}
|
|
|
|
// MapWrite + MapAsync + Unmap before the callback
|
|
{
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(4);
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback,
|
|
Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, _))
|
|
.Times(1);
|
|
buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
|
|
buf.Unmap();
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
WaitForAllOperations(device);
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
}
|
|
|
|
// MapWrite + MapAsync + Destroy
|
|
{
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(4);
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
|
|
.Times(1);
|
|
buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
|
|
WaitForAllOperations(device);
|
|
EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState());
|
|
buf.Destroy();
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
}
|
|
|
|
// MapWrite + MapAsync + Destroy before the callback
|
|
{
|
|
wgpu::Buffer buf = CreateMapWriteBuffer(4);
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
EXPECT_CALL(*mockBufferMapAsyncCallback,
|
|
Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
|
|
.Times(1);
|
|
buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
|
|
EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
|
|
buf.Destroy();
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
|
|
WaitForAllOperations(device);
|
|
EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.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());
|
|
}
|
|
}
|