| // Copyright 2017 The Dawn Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "tests/DawnTest.h" |
| |
| #include <array> |
| #include <cstring> |
| |
| 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, sizeof(data1)); |
| memcpy(buffer.GetMappedRange(offset1), &data1, sizeof(data1)); |
| buffer.Unmap(); |
| |
| uint32_t data2 = 0x00010203; |
| size_t offset2 = 0; |
| MapAsyncAndWait(buffer, wgpu::MapMode::Write, offset2, sizeof(data2)); |
| 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), 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), 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), 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(); |
| 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; |
| ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor)); |
| } |
| |
| // Test that a very large buffer mappedAtCreation fails gracefully. |
| TEST_P(BufferTests, BufferMappedAtCreationOOM) { |
| // 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; |
| ASSERT_DEVICE_ERROR(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; |
| ASSERT_DEVICE_ERROR(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; |
| ASSERT_DEVICE_ERROR(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()); |