952 lines
33 KiB
C++
952 lines
33 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 <array>
|
|
#include <cstring>
|
|
#include <limits>
|
|
#include <vector>
|
|
|
|
#include "dawn/tests/DawnTest.h"
|
|
|
|
class BufferMappingTests : public DawnTest {
|
|
protected:
|
|
void MapAsyncAndWait(const wgpu::Buffer& buffer,
|
|
wgpu::MapMode mode,
|
|
size_t offset,
|
|
size_t size) {
|
|
bool done = false;
|
|
buffer.MapAsync(
|
|
mode, offset, size,
|
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
|
ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
|
*static_cast<bool*>(userdata) = true;
|
|
},
|
|
&done);
|
|
|
|
while (!done) {
|
|
WaitABit();
|
|
}
|
|
}
|
|
|
|
wgpu::Buffer CreateMapReadBuffer(uint64_t size) {
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.size = size;
|
|
descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
|
|
return device.CreateBuffer(&descriptor);
|
|
}
|
|
|
|
wgpu::Buffer CreateMapWriteBuffer(uint64_t size) {
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.size = size;
|
|
descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
|
|
return device.CreateBuffer(&descriptor);
|
|
}
|
|
};
|
|
|
|
void CheckMapping(const void* actual, const void* expected, size_t size) {
|
|
EXPECT_NE(actual, nullptr);
|
|
if (actual != nullptr) {
|
|
EXPECT_EQ(0, memcmp(actual, expected, size));
|
|
}
|
|
}
|
|
|
|
// Test that the simplest map read works
|
|
TEST_P(BufferMappingTests, MapRead_Basic) {
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(4);
|
|
|
|
uint32_t myData = 0x01020304;
|
|
constexpr size_t kSize = sizeof(myData);
|
|
queue.WriteBuffer(buffer, 0, &myData, kSize);
|
|
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, 4);
|
|
CheckMapping(buffer.GetConstMappedRange(), &myData, kSize);
|
|
CheckMapping(buffer.GetConstMappedRange(0, kSize), &myData, kSize);
|
|
buffer.Unmap();
|
|
}
|
|
|
|
// Test map-reading a zero-sized buffer.
|
|
TEST_P(BufferMappingTests, MapRead_ZeroSized) {
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(0);
|
|
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, wgpu::kWholeMapSize);
|
|
ASSERT_NE(buffer.GetConstMappedRange(), nullptr);
|
|
buffer.Unmap();
|
|
}
|
|
|
|
// Test map-reading with a non-zero offset
|
|
TEST_P(BufferMappingTests, MapRead_NonZeroOffset) {
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(12);
|
|
|
|
uint32_t myData[3] = {0x01020304, 0x05060708, 0x090A0B0C};
|
|
queue.WriteBuffer(buffer, 0, &myData, sizeof(myData));
|
|
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Read, 8, 4);
|
|
ASSERT_EQ(myData[2], *static_cast<const uint32_t*>(buffer.GetConstMappedRange(8)));
|
|
buffer.Unmap();
|
|
}
|
|
|
|
// Map read and unmap twice. Test that both of these two iterations work.
|
|
TEST_P(BufferMappingTests, MapRead_Twice) {
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(4);
|
|
|
|
uint32_t myData = 0x01020304;
|
|
queue.WriteBuffer(buffer, 0, &myData, sizeof(myData));
|
|
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, 4);
|
|
ASSERT_EQ(myData, *static_cast<const uint32_t*>(buffer.GetConstMappedRange()));
|
|
buffer.Unmap();
|
|
|
|
myData = 0x05060708;
|
|
queue.WriteBuffer(buffer, 0, &myData, sizeof(myData));
|
|
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, 4);
|
|
ASSERT_EQ(myData, *static_cast<const uint32_t*>(buffer.GetConstMappedRange()));
|
|
buffer.Unmap();
|
|
}
|
|
|
|
// Map read and test multiple get mapped range data
|
|
TEST_P(BufferMappingTests, MapRead_MultipleMappedRange) {
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(12);
|
|
|
|
uint32_t myData[] = {0x00010203, 0x04050607, 0x08090a0b};
|
|
queue.WriteBuffer(buffer, 0, &myData, 12);
|
|
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, 12);
|
|
ASSERT_EQ(myData[0], *static_cast<const uint32_t*>(buffer.GetConstMappedRange(0)));
|
|
ASSERT_EQ(myData[1], *(static_cast<const uint32_t*>(buffer.GetConstMappedRange(0)) + 1));
|
|
ASSERT_EQ(myData[2], *(static_cast<const uint32_t*>(buffer.GetConstMappedRange(0)) + 2));
|
|
ASSERT_EQ(myData[2], *static_cast<const uint32_t*>(buffer.GetConstMappedRange(8)));
|
|
buffer.Unmap();
|
|
}
|
|
|
|
// Test map-reading a large buffer.
|
|
TEST_P(BufferMappingTests, MapRead_Large) {
|
|
constexpr uint32_t kDataSize = 1000 * 1000;
|
|
constexpr size_t kByteSize = kDataSize * sizeof(uint32_t);
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(kByteSize);
|
|
|
|
std::vector<uint32_t> myData;
|
|
for (uint32_t i = 0; i < kDataSize; ++i) {
|
|
myData.push_back(i);
|
|
}
|
|
queue.WriteBuffer(buffer, 0, myData.data(), kByteSize);
|
|
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, kByteSize);
|
|
EXPECT_EQ(nullptr, buffer.GetConstMappedRange(0, kByteSize + 4));
|
|
EXPECT_EQ(0, memcmp(buffer.GetConstMappedRange(), myData.data(), kByteSize));
|
|
EXPECT_EQ(0, memcmp(buffer.GetConstMappedRange(8), myData.data() + 2, kByteSize - 8));
|
|
EXPECT_EQ(
|
|
0, memcmp(buffer.GetConstMappedRange(8, kByteSize - 8), myData.data() + 2, kByteSize - 8));
|
|
buffer.Unmap();
|
|
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Read, 16, kByteSize - 16);
|
|
// Size is too big.
|
|
EXPECT_EQ(nullptr, buffer.GetConstMappedRange(16, kByteSize - 12));
|
|
// Offset defaults to 0 which is less than 16
|
|
EXPECT_EQ(nullptr, buffer.GetConstMappedRange());
|
|
// Offset less than 8 is less than 16
|
|
EXPECT_EQ(nullptr, buffer.GetConstMappedRange(8));
|
|
|
|
// Test a couple values.
|
|
EXPECT_EQ(0, memcmp(buffer.GetConstMappedRange(16), myData.data() + 4, kByteSize - 16));
|
|
EXPECT_EQ(0, memcmp(buffer.GetConstMappedRange(24), myData.data() + 6, kByteSize - 24));
|
|
|
|
buffer.Unmap();
|
|
}
|
|
|
|
// Test that GetConstMappedRange works inside map-read callback
|
|
TEST_P(BufferMappingTests, MapRead_InCallback) {
|
|
constexpr size_t kBufferSize = 12;
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(kBufferSize);
|
|
|
|
uint32_t myData[3] = {0x01020304, 0x05060708, 0x090A0B0C};
|
|
static constexpr size_t kSize = sizeof(myData);
|
|
queue.WriteBuffer(buffer, 0, &myData, kSize);
|
|
|
|
struct UserData {
|
|
bool done;
|
|
wgpu::Buffer buffer;
|
|
void* expected;
|
|
};
|
|
UserData user{false, buffer, &myData};
|
|
|
|
buffer.MapAsync(
|
|
wgpu::MapMode::Read, 0, kBufferSize,
|
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
|
UserData* user = static_cast<UserData*>(userdata);
|
|
|
|
EXPECT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
|
if (status == WGPUBufferMapAsyncStatus_Success) {
|
|
CheckMapping(user->buffer.GetConstMappedRange(), user->expected, kSize);
|
|
CheckMapping(user->buffer.GetConstMappedRange(0, kSize), user->expected, kSize);
|
|
|
|
CheckMapping(user->buffer.GetConstMappedRange(8, 4),
|
|
static_cast<const uint32_t*>(user->expected) + 2, sizeof(uint32_t));
|
|
|
|
user->buffer.Unmap();
|
|
}
|
|
user->done = true;
|
|
},
|
|
&user);
|
|
|
|
while (!user.done) {
|
|
WaitABit();
|
|
}
|
|
}
|
|
|
|
// Test that the simplest map write works.
|
|
TEST_P(BufferMappingTests, MapWrite_Basic) {
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(4);
|
|
|
|
uint32_t myData = 2934875;
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, 4);
|
|
ASSERT_NE(nullptr, buffer.GetMappedRange());
|
|
ASSERT_NE(nullptr, buffer.GetConstMappedRange());
|
|
memcpy(buffer.GetMappedRange(), &myData, sizeof(myData));
|
|
buffer.Unmap();
|
|
|
|
EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
|
|
}
|
|
|
|
// Test that the simplest map write works with a range.
|
|
TEST_P(BufferMappingTests, MapWrite_BasicRange) {
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(4);
|
|
|
|
uint32_t myData = 2934875;
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, 4);
|
|
ASSERT_NE(nullptr, buffer.GetMappedRange(0, 4));
|
|
ASSERT_NE(nullptr, buffer.GetConstMappedRange(0, 4));
|
|
memcpy(buffer.GetMappedRange(), &myData, sizeof(myData));
|
|
buffer.Unmap();
|
|
|
|
EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
|
|
}
|
|
|
|
// Test map-writing a zero-sized buffer.
|
|
TEST_P(BufferMappingTests, MapWrite_ZeroSized) {
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(0);
|
|
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, wgpu::kWholeMapSize);
|
|
ASSERT_NE(buffer.GetConstMappedRange(), nullptr);
|
|
ASSERT_NE(buffer.GetMappedRange(), nullptr);
|
|
buffer.Unmap();
|
|
}
|
|
|
|
// Test map-writing with a non-zero offset.
|
|
TEST_P(BufferMappingTests, MapWrite_NonZeroOffset) {
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(12);
|
|
|
|
uint32_t myData = 2934875;
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, 8, 4);
|
|
memcpy(buffer.GetMappedRange(8), &myData, sizeof(myData));
|
|
buffer.Unmap();
|
|
|
|
EXPECT_BUFFER_U32_EQ(myData, buffer, 8);
|
|
}
|
|
|
|
// Map, write and unmap twice. Test that both of these two iterations work.
|
|
TEST_P(BufferMappingTests, MapWrite_Twice) {
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(4);
|
|
|
|
uint32_t myData = 2934875;
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, 4);
|
|
memcpy(buffer.GetMappedRange(), &myData, sizeof(myData));
|
|
buffer.Unmap();
|
|
|
|
EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
|
|
|
|
myData = 9999999;
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, 4);
|
|
memcpy(buffer.GetMappedRange(), &myData, sizeof(myData));
|
|
buffer.Unmap();
|
|
|
|
EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
|
|
}
|
|
|
|
// Map write and unmap twice with different ranges and make sure the first write is preserved
|
|
TEST_P(BufferMappingTests, MapWrite_TwicePreserve) {
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(12);
|
|
|
|
uint32_t data1 = 0x08090a0b;
|
|
size_t offset1 = 8;
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset1, wgpu::kWholeMapSize);
|
|
memcpy(buffer.GetMappedRange(offset1), &data1, sizeof(data1));
|
|
buffer.Unmap();
|
|
|
|
uint32_t data2 = 0x00010203;
|
|
size_t offset2 = 0;
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset2, wgpu::kWholeMapSize);
|
|
memcpy(buffer.GetMappedRange(offset2), &data2, sizeof(data2));
|
|
buffer.Unmap();
|
|
|
|
EXPECT_BUFFER_U32_EQ(data1, buffer, offset1);
|
|
EXPECT_BUFFER_U32_EQ(data2, buffer, offset2);
|
|
}
|
|
|
|
// Map write and unmap twice with overlapping ranges and make sure data is updated correctly
|
|
TEST_P(BufferMappingTests, MapWrite_TwiceRangeOverlap) {
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(16);
|
|
|
|
uint32_t data1[] = {0x01234567, 0x89abcdef};
|
|
size_t offset1 = 8;
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset1, 8);
|
|
memcpy(buffer.GetMappedRange(offset1, 8), data1, 8);
|
|
buffer.Unmap();
|
|
|
|
EXPECT_BUFFER_U32_EQ(0x00000000, buffer, 0);
|
|
EXPECT_BUFFER_U32_EQ(0x00000000, buffer, 4);
|
|
EXPECT_BUFFER_U32_EQ(0x01234567, buffer, 8);
|
|
EXPECT_BUFFER_U32_EQ(0x89abcdef, buffer, 12);
|
|
|
|
uint32_t data2[] = {0x01234567, 0x89abcdef, 0x55555555};
|
|
size_t offset2 = 0;
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset2, 12);
|
|
memcpy(buffer.GetMappedRange(offset2, 12), data2, 12);
|
|
buffer.Unmap();
|
|
|
|
EXPECT_BUFFER_U32_EQ(0x01234567, buffer, 0);
|
|
EXPECT_BUFFER_U32_EQ(0x89abcdef, buffer, 4);
|
|
EXPECT_BUFFER_U32_EQ(0x55555555, buffer, 8);
|
|
EXPECT_BUFFER_U32_EQ(0x89abcdef, buffer, 12);
|
|
}
|
|
|
|
// Map write and test multiple mapped range data get updated correctly
|
|
TEST_P(BufferMappingTests, MapWrite_MultipleMappedRange) {
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(12);
|
|
|
|
uint32_t data1 = 0x08090a0b;
|
|
size_t offset1 = 8;
|
|
uint32_t data2 = 0x00010203;
|
|
size_t offset2 = 0;
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, 12);
|
|
memcpy(buffer.GetMappedRange(offset1), &data1, sizeof(data1));
|
|
memcpy(buffer.GetMappedRange(offset2), &data2, sizeof(data2));
|
|
buffer.Unmap();
|
|
|
|
EXPECT_BUFFER_U32_EQ(data1, buffer, offset1);
|
|
EXPECT_BUFFER_U32_EQ(data2, buffer, offset2);
|
|
}
|
|
|
|
// Test mapping a large buffer.
|
|
TEST_P(BufferMappingTests, MapWrite_Large) {
|
|
constexpr uint32_t kDataSize = 1000 * 1000;
|
|
constexpr size_t kByteSize = kDataSize * sizeof(uint32_t);
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(kDataSize * sizeof(uint32_t));
|
|
|
|
std::vector<uint32_t> myData;
|
|
for (uint32_t i = 0; i < kDataSize; ++i) {
|
|
myData.push_back(i);
|
|
}
|
|
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, 16, kByteSize - 20);
|
|
EXPECT_EQ(nullptr, buffer.GetMappedRange());
|
|
EXPECT_EQ(nullptr, buffer.GetMappedRange(0));
|
|
EXPECT_EQ(nullptr, buffer.GetMappedRange(8));
|
|
EXPECT_EQ(nullptr, buffer.GetMappedRange(16, kByteSize - 8));
|
|
memcpy(buffer.GetMappedRange(16, kByteSize - 20), myData.data(), kByteSize - 20);
|
|
buffer.Unmap();
|
|
EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), buffer, 16, kDataSize - 5);
|
|
}
|
|
|
|
// Stress test mapping many buffers.
|
|
TEST_P(BufferMappingTests, MapWrite_ManySimultaneous) {
|
|
constexpr uint32_t kDataSize = 1000;
|
|
std::vector<uint32_t> myData;
|
|
for (uint32_t i = 0; i < kDataSize; ++i) {
|
|
myData.push_back(i);
|
|
}
|
|
|
|
constexpr uint32_t kBuffers = 100;
|
|
std::array<wgpu::Buffer, kBuffers> buffers;
|
|
uint32_t mapCompletedCount = 0;
|
|
|
|
// Create buffers and request mapping them.
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.size = static_cast<uint32_t>(kDataSize * sizeof(uint32_t));
|
|
descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc;
|
|
for (uint32_t i = 0; i < kBuffers; ++i) {
|
|
buffers[i] = device.CreateBuffer(&descriptor);
|
|
|
|
buffers[i].MapAsync(
|
|
wgpu::MapMode::Write, 0, descriptor.size,
|
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
|
ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
|
(*static_cast<uint32_t*>(userdata))++;
|
|
},
|
|
&mapCompletedCount);
|
|
}
|
|
|
|
// Wait for all mappings to complete
|
|
while (mapCompletedCount != kBuffers) {
|
|
WaitABit();
|
|
}
|
|
|
|
// All buffers are mapped, write into them and unmap them all.
|
|
for (uint32_t i = 0; i < kBuffers; ++i) {
|
|
memcpy(buffers[i].GetMappedRange(0, descriptor.size), myData.data(), descriptor.size);
|
|
buffers[i].Unmap();
|
|
}
|
|
|
|
// Check the content of the buffers.
|
|
for (uint32_t i = 0; i < kBuffers; ++i) {
|
|
EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), buffers[i], 0, kDataSize);
|
|
}
|
|
}
|
|
|
|
// Test that the map offset isn't updated when the call is an error.
|
|
TEST_P(BufferMappingTests, OffsetNotUpdatedOnError) {
|
|
uint32_t data[3] = {0xCA7, 0xB0A7, 0xBA7};
|
|
wgpu::Buffer buffer = CreateMapReadBuffer(sizeof(data));
|
|
queue.WriteBuffer(buffer, 0, data, sizeof(data));
|
|
|
|
// Map the buffer but do not wait on the result yet.
|
|
bool done1 = false;
|
|
bool done2 = false;
|
|
buffer.MapAsync(
|
|
wgpu::MapMode::Read, 8, 4,
|
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
|
ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
|
*static_cast<bool*>(userdata) = true;
|
|
},
|
|
&done1);
|
|
|
|
// Call MapAsync another time, it is an error because the buffer is already being mapped so
|
|
// mMapOffset is not updated.
|
|
ASSERT_DEVICE_ERROR(buffer.MapAsync(
|
|
wgpu::MapMode::Read, 0, 4,
|
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
|
*static_cast<bool*>(userdata) = true;
|
|
},
|
|
&done2));
|
|
|
|
while (!done1 || !done2) {
|
|
WaitABit();
|
|
}
|
|
|
|
// mMapOffset has not been updated so it should still be 4, which is data[1]
|
|
ASSERT_EQ(0, memcmp(buffer.GetConstMappedRange(8), &data[2], sizeof(uint32_t)));
|
|
}
|
|
|
|
// Test that Get(Const)MappedRange work inside map-write callback.
|
|
TEST_P(BufferMappingTests, MapWrite_InCallbackDefault) {
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(4);
|
|
|
|
static constexpr uint32_t myData = 2934875;
|
|
static constexpr size_t kSize = sizeof(myData);
|
|
|
|
struct UserData {
|
|
bool done;
|
|
wgpu::Buffer buffer;
|
|
};
|
|
UserData user{false, buffer};
|
|
|
|
buffer.MapAsync(
|
|
wgpu::MapMode::Write, 0, kSize,
|
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
|
UserData* user = static_cast<UserData*>(userdata);
|
|
|
|
EXPECT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
|
if (status == WGPUBufferMapAsyncStatus_Success) {
|
|
EXPECT_NE(nullptr, user->buffer.GetConstMappedRange());
|
|
void* ptr = user->buffer.GetMappedRange();
|
|
EXPECT_NE(nullptr, ptr);
|
|
if (ptr != nullptr) {
|
|
uint32_t data = myData;
|
|
memcpy(ptr, &data, kSize);
|
|
}
|
|
|
|
user->buffer.Unmap();
|
|
}
|
|
user->done = true;
|
|
},
|
|
&user);
|
|
|
|
while (!user.done) {
|
|
WaitABit();
|
|
}
|
|
|
|
EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
|
|
}
|
|
|
|
// Test that Get(Const)MappedRange with range work inside map-write callback.
|
|
TEST_P(BufferMappingTests, MapWrite_InCallbackRange) {
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(4);
|
|
|
|
static constexpr uint32_t myData = 2934875;
|
|
static constexpr size_t kSize = sizeof(myData);
|
|
|
|
struct UserData {
|
|
bool done;
|
|
wgpu::Buffer buffer;
|
|
};
|
|
UserData user{false, buffer};
|
|
|
|
buffer.MapAsync(
|
|
wgpu::MapMode::Write, 0, kSize,
|
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
|
UserData* user = static_cast<UserData*>(userdata);
|
|
|
|
EXPECT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
|
if (status == WGPUBufferMapAsyncStatus_Success) {
|
|
EXPECT_NE(nullptr, user->buffer.GetConstMappedRange(0, kSize));
|
|
void* ptr = user->buffer.GetMappedRange(0, kSize);
|
|
EXPECT_NE(nullptr, ptr);
|
|
if (ptr != nullptr) {
|
|
uint32_t data = myData;
|
|
memcpy(ptr, &data, kSize);
|
|
}
|
|
|
|
user->buffer.Unmap();
|
|
}
|
|
user->done = true;
|
|
},
|
|
&user);
|
|
|
|
while (!user.done) {
|
|
WaitABit();
|
|
}
|
|
|
|
EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
|
|
}
|
|
|
|
// Regression test for crbug.com/dawn/969 where this test
|
|
// produced invalid barriers.
|
|
TEST_P(BufferMappingTests, MapWrite_ZeroSizedTwice) {
|
|
wgpu::Buffer buffer = CreateMapWriteBuffer(0);
|
|
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, wgpu::kWholeMapSize);
|
|
buffer.Unmap();
|
|
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, wgpu::kWholeMapSize);
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST(BufferMappingTests,
|
|
D3D12Backend(),
|
|
MetalBackend(),
|
|
OpenGLBackend(),
|
|
OpenGLESBackend(),
|
|
VulkanBackend());
|
|
|
|
class BufferMappedAtCreationTests : public DawnTest {
|
|
protected:
|
|
static void MapCallback(WGPUBufferMapAsyncStatus status, void* userdata) {
|
|
EXPECT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
|
*static_cast<bool*>(userdata) = true;
|
|
}
|
|
|
|
const void* MapAsyncAndWait(const wgpu::Buffer& buffer, wgpu::MapMode mode, size_t size) {
|
|
bool done = false;
|
|
buffer.MapAsync(mode, 0, size, MapCallback, &done);
|
|
|
|
while (!done) {
|
|
WaitABit();
|
|
}
|
|
|
|
return buffer.GetConstMappedRange(0, size);
|
|
}
|
|
|
|
void UnmapBuffer(const wgpu::Buffer& buffer) { buffer.Unmap(); }
|
|
|
|
wgpu::Buffer BufferMappedAtCreation(wgpu::BufferUsage usage, uint64_t size) {
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.size = size;
|
|
descriptor.usage = usage;
|
|
descriptor.mappedAtCreation = true;
|
|
return device.CreateBuffer(&descriptor);
|
|
}
|
|
|
|
wgpu::Buffer BufferMappedAtCreationWithData(wgpu::BufferUsage usage,
|
|
const std::vector<uint32_t>& data) {
|
|
size_t byteLength = data.size() * sizeof(uint32_t);
|
|
wgpu::Buffer buffer = BufferMappedAtCreation(usage, byteLength);
|
|
memcpy(buffer.GetMappedRange(), data.data(), byteLength);
|
|
return buffer;
|
|
}
|
|
};
|
|
|
|
// Test that the simplest mappedAtCreation works for MapWrite buffers.
|
|
TEST_P(BufferMappedAtCreationTests, MapWriteUsageSmall) {
|
|
uint32_t myData = 230502;
|
|
wgpu::Buffer buffer = BufferMappedAtCreationWithData(
|
|
wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc, {myData});
|
|
UnmapBuffer(buffer);
|
|
EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
|
|
}
|
|
|
|
// Test that the simplest mappedAtCreation works for MapRead buffers.
|
|
TEST_P(BufferMappedAtCreationTests, MapReadUsageSmall) {
|
|
uint32_t myData = 230502;
|
|
wgpu::Buffer buffer = BufferMappedAtCreationWithData(wgpu::BufferUsage::MapRead, {myData});
|
|
UnmapBuffer(buffer);
|
|
|
|
const void* mappedData = MapAsyncAndWait(buffer, wgpu::MapMode::Read, 4);
|
|
ASSERT_EQ(myData, *reinterpret_cast<const uint32_t*>(mappedData));
|
|
UnmapBuffer(buffer);
|
|
}
|
|
|
|
// Test that the simplest mappedAtCreation works for non-mappable buffers.
|
|
TEST_P(BufferMappedAtCreationTests, NonMappableUsageSmall) {
|
|
uint32_t myData = 4239;
|
|
wgpu::Buffer buffer = BufferMappedAtCreationWithData(wgpu::BufferUsage::CopySrc, {myData});
|
|
UnmapBuffer(buffer);
|
|
|
|
EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
|
|
}
|
|
|
|
// Test mappedAtCreation for a large MapWrite buffer
|
|
TEST_P(BufferMappedAtCreationTests, MapWriteUsageLarge) {
|
|
constexpr uint64_t kDataSize = 1000 * 1000;
|
|
std::vector<uint32_t> myData;
|
|
for (uint32_t i = 0; i < kDataSize; ++i) {
|
|
myData.push_back(i);
|
|
}
|
|
|
|
wgpu::Buffer buffer = BufferMappedAtCreationWithData(
|
|
wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc, {myData});
|
|
UnmapBuffer(buffer);
|
|
|
|
EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), buffer, 0, kDataSize);
|
|
}
|
|
|
|
// Test mappedAtCreation for a large MapRead buffer
|
|
TEST_P(BufferMappedAtCreationTests, MapReadUsageLarge) {
|
|
constexpr uint64_t kDataSize = 1000 * 1000;
|
|
std::vector<uint32_t> myData;
|
|
for (uint32_t i = 0; i < kDataSize; ++i) {
|
|
myData.push_back(i);
|
|
}
|
|
|
|
wgpu::Buffer buffer = BufferMappedAtCreationWithData(wgpu::BufferUsage::MapRead, myData);
|
|
UnmapBuffer(buffer);
|
|
|
|
const void* mappedData =
|
|
MapAsyncAndWait(buffer, wgpu::MapMode::Read, kDataSize * sizeof(uint32_t));
|
|
ASSERT_EQ(0, memcmp(mappedData, myData.data(), kDataSize * sizeof(uint32_t)));
|
|
UnmapBuffer(buffer);
|
|
}
|
|
|
|
// Test mappedAtCreation for a large non-mappable buffer
|
|
TEST_P(BufferMappedAtCreationTests, NonMappableUsageLarge) {
|
|
constexpr uint64_t kDataSize = 1000 * 1000;
|
|
std::vector<uint32_t> myData;
|
|
for (uint32_t i = 0; i < kDataSize; ++i) {
|
|
myData.push_back(i);
|
|
}
|
|
|
|
wgpu::Buffer buffer = BufferMappedAtCreationWithData(wgpu::BufferUsage::CopySrc, {myData});
|
|
UnmapBuffer(buffer);
|
|
|
|
EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), buffer, 0, kDataSize);
|
|
}
|
|
|
|
// Test destroying a non-mappable buffer mapped at creation.
|
|
// This is a regression test for an issue where the D3D12 backend thought the buffer was actually
|
|
// mapped and tried to unlock the heap residency (when actually the buffer was using a staging
|
|
// buffer)
|
|
TEST_P(BufferMappedAtCreationTests, DestroyNonMappableWhileMappedForCreation) {
|
|
wgpu::Buffer buffer = BufferMappedAtCreation(wgpu::BufferUsage::CopySrc, 4);
|
|
buffer.Destroy();
|
|
}
|
|
|
|
// Test destroying a mappable buffer mapped at creation.
|
|
TEST_P(BufferMappedAtCreationTests, DestroyMappableWhileMappedForCreation) {
|
|
wgpu::Buffer buffer = BufferMappedAtCreation(wgpu::BufferUsage::MapRead, 4);
|
|
buffer.Destroy();
|
|
}
|
|
|
|
// Test that mapping a buffer is valid after mappedAtCreation and Unmap
|
|
TEST_P(BufferMappedAtCreationTests, CreateThenMapSuccess) {
|
|
static uint32_t myData = 230502;
|
|
wgpu::Buffer buffer = BufferMappedAtCreationWithData(
|
|
wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc, {myData});
|
|
UnmapBuffer(buffer);
|
|
|
|
EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
|
|
|
|
bool done = false;
|
|
buffer.MapAsync(
|
|
wgpu::MapMode::Write, 0, 4,
|
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
|
ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
|
|
*static_cast<bool*>(userdata) = true;
|
|
},
|
|
&done);
|
|
|
|
while (!done) {
|
|
WaitABit();
|
|
}
|
|
|
|
UnmapBuffer(buffer);
|
|
}
|
|
|
|
// Test that is is invalid to map a buffer twice when using mappedAtCreation
|
|
TEST_P(BufferMappedAtCreationTests, CreateThenMapBeforeUnmapFailure) {
|
|
uint32_t myData = 230502;
|
|
wgpu::Buffer buffer = BufferMappedAtCreationWithData(
|
|
wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc, {myData});
|
|
|
|
ASSERT_DEVICE_ERROR([&]() {
|
|
bool done = false;
|
|
buffer.MapAsync(
|
|
wgpu::MapMode::Write, 0, 4,
|
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
|
ASSERT_EQ(WGPUBufferMapAsyncStatus_Error, status);
|
|
*static_cast<bool*>(userdata) = true;
|
|
},
|
|
&done);
|
|
|
|
while (!done) {
|
|
WaitABit();
|
|
}
|
|
}());
|
|
|
|
// mappedAtCreation is unaffected by the MapWrite error.
|
|
UnmapBuffer(buffer);
|
|
}
|
|
|
|
// Test that creating a zero-sized buffer mapped is allowed.
|
|
TEST_P(BufferMappedAtCreationTests, ZeroSized) {
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.size = 0;
|
|
descriptor.usage = wgpu::BufferUsage::Vertex;
|
|
descriptor.mappedAtCreation = true;
|
|
wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
|
|
|
|
ASSERT_NE(nullptr, buffer.GetMappedRange());
|
|
|
|
// Check that unmapping the buffer works too.
|
|
UnmapBuffer(buffer);
|
|
}
|
|
|
|
// Test that creating a zero-sized mapppable buffer mapped. (it is a different code path)
|
|
TEST_P(BufferMappedAtCreationTests, ZeroSizedMappableBuffer) {
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.size = 0;
|
|
descriptor.usage = wgpu::BufferUsage::MapWrite;
|
|
descriptor.mappedAtCreation = true;
|
|
wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
|
|
|
|
ASSERT_NE(nullptr, buffer.GetMappedRange());
|
|
|
|
// Check that unmapping the buffer works too.
|
|
UnmapBuffer(buffer);
|
|
}
|
|
|
|
// Test that creating a zero-sized error buffer mapped. (it is a different code path)
|
|
TEST_P(BufferMappedAtCreationTests, ZeroSizedErrorBuffer) {
|
|
DAWN_TEST_UNSUPPORTED_IF(HasToggleEnabled("skip_validation"));
|
|
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.size = 0;
|
|
descriptor.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::Storage;
|
|
descriptor.mappedAtCreation = true;
|
|
wgpu::Buffer buffer;
|
|
ASSERT_DEVICE_ERROR(buffer = device.CreateBuffer(&descriptor));
|
|
|
|
ASSERT_NE(nullptr, buffer.GetMappedRange());
|
|
}
|
|
|
|
// Test the result of GetMappedRange when mapped at creation.
|
|
TEST_P(BufferMappedAtCreationTests, GetMappedRange) {
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.size = 4;
|
|
descriptor.usage = wgpu::BufferUsage::CopyDst;
|
|
descriptor.mappedAtCreation = true;
|
|
wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
|
|
|
|
ASSERT_EQ(buffer.GetMappedRange(), buffer.GetConstMappedRange());
|
|
ASSERT_NE(buffer.GetMappedRange(), nullptr);
|
|
buffer.Unmap();
|
|
}
|
|
|
|
// Test the result of GetMappedRange when mapped at creation for a zero-sized buffer.
|
|
TEST_P(BufferMappedAtCreationTests, GetMappedRangeZeroSized) {
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.size = 0;
|
|
descriptor.usage = wgpu::BufferUsage::CopyDst;
|
|
descriptor.mappedAtCreation = true;
|
|
wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
|
|
|
|
ASSERT_EQ(buffer.GetMappedRange(), buffer.GetConstMappedRange());
|
|
ASSERT_NE(buffer.GetMappedRange(), nullptr);
|
|
buffer.Unmap();
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST(BufferMappedAtCreationTests,
|
|
D3D12Backend(),
|
|
D3D12Backend({}, {"use_d3d12_resource_heap_tier2"}),
|
|
MetalBackend(),
|
|
OpenGLBackend(),
|
|
OpenGLESBackend(),
|
|
VulkanBackend());
|
|
|
|
class BufferTests : public DawnTest {};
|
|
|
|
// Test that creating a zero-buffer is allowed.
|
|
TEST_P(BufferTests, ZeroSizedBuffer) {
|
|
wgpu::BufferDescriptor desc;
|
|
desc.size = 0;
|
|
desc.usage = wgpu::BufferUsage::CopyDst;
|
|
device.CreateBuffer(&desc);
|
|
}
|
|
|
|
// Test that creating a very large buffers fails gracefully.
|
|
TEST_P(BufferTests, CreateBufferOOM) {
|
|
// TODO(http://crbug.com/dawn/749): Missing support.
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGL());
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
|
|
DAWN_TEST_UNSUPPORTED_IF(IsAsan());
|
|
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.usage = wgpu::BufferUsage::CopyDst;
|
|
|
|
descriptor.size = std::numeric_limits<uint64_t>::max();
|
|
// TODO(dawn:1525): remove warning expectation after the deprecation period.
|
|
ASSERT_DEVICE_ERROR(EXPECT_DEPRECATION_WARNING(device.CreateBuffer(&descriptor)));
|
|
|
|
// UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails
|
|
descriptor.size = 1ull << 50;
|
|
// TODO(dawn:1525): remove warning expectation after the deprecation period.
|
|
ASSERT_DEVICE_ERROR(EXPECT_DEPRECATION_WARNING(device.CreateBuffer(&descriptor)));
|
|
}
|
|
|
|
// Test that a very large buffer mappedAtCreation fails gracefully.
|
|
TEST_P(BufferTests, BufferMappedAtCreationOOM) {
|
|
// 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_SUPPRESS_TEST_IF(DAWN_PLATFORM_IS(MACOS) && DAWN_PLATFORM_IS(ARM64));
|
|
|
|
// TODO(http://crbug.com/dawn/749): Missing support.
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGL());
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
|
|
DAWN_TEST_UNSUPPORTED_IF(IsAsan());
|
|
|
|
// Test non-mappable buffer
|
|
{
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.size = 4;
|
|
descriptor.usage = wgpu::BufferUsage::CopyDst;
|
|
descriptor.mappedAtCreation = true;
|
|
|
|
// Control: test a small buffer works.
|
|
device.CreateBuffer(&descriptor);
|
|
|
|
// Test an enormous buffer fails
|
|
descriptor.size = std::numeric_limits<uint64_t>::max();
|
|
ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
|
|
|
|
// UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails
|
|
descriptor.size = 1ull << 50;
|
|
// TODO(dawn:1525): remove warning expectation after the deprecation period.
|
|
ASSERT_DEVICE_ERROR(EXPECT_DEPRECATION_WARNING(device.CreateBuffer(&descriptor)));
|
|
}
|
|
|
|
// Test mappable buffer
|
|
{
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.size = 4;
|
|
descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite;
|
|
descriptor.mappedAtCreation = true;
|
|
|
|
// Control: test a small buffer works.
|
|
device.CreateBuffer(&descriptor);
|
|
|
|
// Test an enormous buffer fails
|
|
descriptor.size = std::numeric_limits<uint64_t>::max();
|
|
ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
|
|
|
|
// UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails
|
|
descriptor.size = 1ull << 50;
|
|
// TODO(dawn:1525): remove warning expectation after the deprecation period.
|
|
ASSERT_DEVICE_ERROR(EXPECT_DEPRECATION_WARNING(device.CreateBuffer(&descriptor)));
|
|
}
|
|
}
|
|
|
|
// Test that mapping an OOM buffer fails gracefully
|
|
TEST_P(BufferTests, CreateBufferOOMMapAsync) {
|
|
// TODO(http://crbug.com/dawn/749): Missing support.
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGL());
|
|
DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
|
|
DAWN_TEST_UNSUPPORTED_IF(IsAsan());
|
|
|
|
auto RunTest = [this](const wgpu::BufferDescriptor& descriptor) {
|
|
wgpu::Buffer buffer;
|
|
// TODO(dawn:1525): remove warning expectation after the deprecation period.
|
|
ASSERT_DEVICE_ERROR(EXPECT_DEPRECATION_WARNING(buffer = device.CreateBuffer(&descriptor)));
|
|
|
|
bool done = false;
|
|
ASSERT_DEVICE_ERROR(buffer.MapAsync(
|
|
wgpu::MapMode::Write, 0, 4,
|
|
[](WGPUBufferMapAsyncStatus status, void* userdata) {
|
|
EXPECT_EQ(status, WGPUBufferMapAsyncStatus_Error);
|
|
*static_cast<bool*>(userdata) = true;
|
|
},
|
|
&done));
|
|
|
|
while (!done) {
|
|
WaitABit();
|
|
}
|
|
};
|
|
|
|
wgpu::BufferDescriptor descriptor;
|
|
descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite;
|
|
|
|
// Test an enormous buffer
|
|
descriptor.size = std::numeric_limits<uint64_t>::max();
|
|
RunTest(descriptor);
|
|
|
|
// UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails
|
|
descriptor.size = 1ull << 50;
|
|
RunTest(descriptor);
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST(BufferTests,
|
|
D3D12Backend(),
|
|
MetalBackend(),
|
|
OpenGLBackend(),
|
|
OpenGLESBackend(),
|
|
VulkanBackend());
|
|
|
|
class BufferNoSuballocationTests : public DawnTest {};
|
|
|
|
// Regression test for crbug.com/1313172
|
|
// This tests a buffer. It then performs writeBuffer and immediately destroys
|
|
// it. Though writeBuffer references a destroyed buffer, it should not crash.
|
|
TEST_P(BufferNoSuballocationTests, WriteBufferThenDestroy) {
|
|
uint32_t myData = 0x01020304;
|
|
|
|
wgpu::BufferDescriptor desc;
|
|
desc.size = 1024;
|
|
desc.usage = wgpu::BufferUsage::CopyDst;
|
|
wgpu::Buffer buffer = device.CreateBuffer(&desc);
|
|
|
|
// Enqueue a pending write into the buffer.
|
|
constexpr size_t kSize = sizeof(myData);
|
|
queue.WriteBuffer(buffer, 0, &myData, kSize);
|
|
|
|
// Destroy the buffer.
|
|
buffer.Destroy();
|
|
|
|
// Flush and wait for all commands.
|
|
queue.Submit(0, nullptr);
|
|
WaitForAllOperations();
|
|
}
|
|
|
|
DAWN_INSTANTIATE_TEST(BufferNoSuballocationTests,
|
|
D3D12Backend({"disable_resource_suballocation"}),
|
|
MetalBackend({"disable_resource_suballocation"}),
|
|
OpenGLBackend({"disable_resource_suballocation"}),
|
|
OpenGLESBackend({"disable_resource_suballocation"}),
|
|
VulkanBackend({"disable_resource_suballocation"}));
|