Handle OOM buffer allocations better

This CL checks buffer sizes before creating map read/write handles.
It is an error to map a buffer that can't be addressed on the CPU.

It also changes client-side synchronous errors on mapAsync to be
normal map failures, and not device lost errors. These should be
recoverable.

The CL adds additional testing for really large, but not UINT64_MAX
buffers, and fixes a VVL warning when buffer allocations exceed the
size of their memory heap.

Bug: dawn:450, dawn:398, chromium:1014740
Change-Id: Ieb34c04c3d01c429b7e3b7810729d5e91ecb6270
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/22626
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Austin Eng 2020-06-15 23:42:13 +00:00 committed by Commit Bot service account
parent c3c2c9b6b0
commit cf1fdf413c
12 changed files with 248 additions and 46 deletions

View File

@ -520,6 +520,11 @@
{"name": "descriptor", "type": "buffer descriptor", "annotation": "const*"} {"name": "descriptor", "type": "buffer descriptor", "annotation": "const*"}
] ]
}, },
{
"name": "create error buffer",
"returns": "buffer",
"TODO": "enga@: Make this part of a dawn_wire extension"
},
{ {
"name": "create buffer mapped", "name": "create buffer mapped",
"returns": "create buffer mapped result", "returns": "create buffer mapped result",

View File

@ -210,7 +210,7 @@ namespace dawn_native {
void BufferBase::CallMapReadCallback(uint32_t serial, void BufferBase::CallMapReadCallback(uint32_t serial,
WGPUBufferMapAsyncStatus status, WGPUBufferMapAsyncStatus status,
const void* pointer, const void* pointer,
uint32_t dataLength) { uint64_t dataLength) {
ASSERT(!IsError()); ASSERT(!IsError());
if (mMapReadCallback != nullptr && serial == mMapSerial) { if (mMapReadCallback != nullptr && serial == mMapSerial) {
ASSERT(mMapWriteCallback == nullptr); ASSERT(mMapWriteCallback == nullptr);
@ -231,7 +231,7 @@ namespace dawn_native {
void BufferBase::CallMapWriteCallback(uint32_t serial, void BufferBase::CallMapWriteCallback(uint32_t serial,
WGPUBufferMapAsyncStatus status, WGPUBufferMapAsyncStatus status,
void* pointer, void* pointer,
uint32_t dataLength) { uint64_t dataLength) {
ASSERT(!IsError()); ASSERT(!IsError());
if (mMapWriteCallback != nullptr && serial == mMapSerial) { if (mMapWriteCallback != nullptr && serial == mMapSerial) {
ASSERT(mMapReadCallback == nullptr); ASSERT(mMapReadCallback == nullptr);

View File

@ -71,11 +71,11 @@ namespace dawn_native {
void CallMapReadCallback(uint32_t serial, void CallMapReadCallback(uint32_t serial,
WGPUBufferMapAsyncStatus status, WGPUBufferMapAsyncStatus status,
const void* pointer, const void* pointer,
uint32_t dataLength); uint64_t dataLength);
void CallMapWriteCallback(uint32_t serial, void CallMapWriteCallback(uint32_t serial,
WGPUBufferMapAsyncStatus status, WGPUBufferMapAsyncStatus status,
void* pointer, void* pointer,
uint32_t dataLength); uint64_t dataLength);
void DestroyInternal(); void DestroyInternal();

View File

@ -731,6 +731,12 @@ namespace dawn_native {
return result; return result;
} }
// For Dawn Wire
BufferBase* DeviceBase::CreateErrorBuffer() {
return BufferBase::MakeError(this);
}
// Other Device API methods // Other Device API methods
void DeviceBase::Tick() { void DeviceBase::Tick() {

View File

@ -158,6 +158,9 @@ namespace dawn_native {
TextureViewBase* CreateTextureView(TextureBase* texture, TextureViewBase* CreateTextureView(TextureBase* texture,
const TextureViewDescriptor* descriptor); const TextureViewDescriptor* descriptor);
// For Dawn Wire
BufferBase* CreateErrorBuffer();
QueueBase* GetDefaultQueue(); QueueBase* GetDefaultQueue();
void InjectError(wgpu::ErrorType type, const char* message); void InjectError(wgpu::ErrorType type, const char* message);

View File

@ -185,8 +185,12 @@ namespace dawn_native { namespace d3d12 {
ResourceHeapAllocation directAllocation; ResourceHeapAllocation directAllocation;
DAWN_TRY_ASSIGN(directAllocation, DAWN_TRY_ASSIGN(directAllocation,
CreateCommittedResource(heapType, resourceDescriptor, initialUsage)); CreateCommittedResource(heapType, resourceDescriptor, initialUsage));
if (directAllocation.GetInfo().mMethod != AllocationMethod::kInvalid) {
return std::move(directAllocation);
}
return std::move(directAllocation); // If direct allocation fails, the system is probably out of memory.
return DAWN_OUT_OF_MEMORY_ERROR("Allocation failed");
} }
void ResourceAllocatorManager::Tick(Serial completedSerial) { void ResourceAllocatorManager::Tick(Serial completedSerial) {

View File

@ -148,7 +148,7 @@ namespace dawn_native { namespace vulkan {
createInfo.pQueueFamilyIndices = 0; createInfo.pQueueFamilyIndices = 0;
Device* device = ToBackend(GetDevice()); Device* device = ToBackend(GetDevice());
DAWN_TRY(CheckVkSuccess( DAWN_TRY(CheckVkOOMThenSuccess(
device->fn.CreateBuffer(device->GetVkDevice(), &createInfo, nullptr, &*mHandle), device->fn.CreateBuffer(device->GetVkDevice(), &createInfo, nullptr, &*mHandle),
"vkCreateBuffer")); "vkCreateBuffer"));

View File

@ -14,6 +14,7 @@
#include "dawn_native/vulkan/ResourceMemoryAllocatorVk.h" #include "dawn_native/vulkan/ResourceMemoryAllocatorVk.h"
#include "common/Math.h"
#include "dawn_native/BuddyMemoryAllocator.h" #include "dawn_native/BuddyMemoryAllocator.h"
#include "dawn_native/ResourceHeapAllocator.h" #include "dawn_native/ResourceHeapAllocator.h"
#include "dawn_native/vulkan/DeviceVk.h" #include "dawn_native/vulkan/DeviceVk.h"
@ -28,8 +29,7 @@ namespace dawn_native { namespace vulkan {
// TODO(cwallez@chromium.org): This is a hardcoded heurstic to choose when to // TODO(cwallez@chromium.org): This is a hardcoded heurstic to choose when to
// suballocate but it should ideally depend on the size of the memory heaps and other // suballocate but it should ideally depend on the size of the memory heaps and other
// factors. // factors.
constexpr uint64_t kMaxBuddySystemSize = 32ull * 1024ull * 1024ull * 1024ull; // 32GB constexpr uint64_t kMaxSizeForSubAllocation = 4ull * 1024ull * 1024ull; // 4MiB
constexpr uint64_t kMaxSizeForSubAllocation = 4ull * 1024ull * 1024ull; // 4MB
// Have each bucket of the buddy system allocate at least some resource of the maximum // Have each bucket of the buddy system allocate at least some resource of the maximum
// size // size
@ -42,10 +42,18 @@ namespace dawn_native { namespace vulkan {
class ResourceMemoryAllocator::SingleTypeAllocator : public ResourceHeapAllocator { class ResourceMemoryAllocator::SingleTypeAllocator : public ResourceHeapAllocator {
public: public:
SingleTypeAllocator(Device* device, size_t memoryTypeIndex) SingleTypeAllocator(Device* device, size_t memoryTypeIndex, VkDeviceSize memoryHeapSize)
: mDevice(device), : mDevice(device),
mMemoryTypeIndex(memoryTypeIndex), mMemoryTypeIndex(memoryTypeIndex),
mBuddySystem(kMaxBuddySystemSize, kBuddyHeapsSize, this) { mMemoryHeapSize(memoryHeapSize),
mBuddySystem(
// Round down to a power of 2 that's <= mMemoryHeapSize. This will always
// be a multiple of kBuddyHeapsSize because kBuddyHeapsSize is a power of 2.
uint64_t(1) << Log2(mMemoryHeapSize),
// Take the min in the very unlikely case the memory heap is tiny.
std::min(uint64_t(1) << Log2(mMemoryHeapSize), kBuddyHeapsSize),
this) {
ASSERT(IsPowerOfTwo(kBuddyHeapsSize));
} }
~SingleTypeAllocator() override = default; ~SingleTypeAllocator() override = default;
@ -62,6 +70,10 @@ namespace dawn_native { namespace vulkan {
ResultOrError<std::unique_ptr<ResourceHeapBase>> AllocateResourceHeap( ResultOrError<std::unique_ptr<ResourceHeapBase>> AllocateResourceHeap(
uint64_t size) override { uint64_t size) override {
if (size > mMemoryHeapSize) {
return DAWN_OUT_OF_MEMORY_ERROR("Allocation size too large");
}
VkMemoryAllocateInfo allocateInfo; VkMemoryAllocateInfo allocateInfo;
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocateInfo.pNext = nullptr; allocateInfo.pNext = nullptr;
@ -87,6 +99,7 @@ namespace dawn_native { namespace vulkan {
private: private:
Device* mDevice; Device* mDevice;
size_t mMemoryTypeIndex; size_t mMemoryTypeIndex;
VkDeviceSize mMemoryHeapSize;
BuddyMemoryAllocator mBuddySystem; BuddyMemoryAllocator mBuddySystem;
}; };
@ -97,7 +110,8 @@ namespace dawn_native { namespace vulkan {
mAllocatorsPerType.reserve(info.memoryTypes.size()); mAllocatorsPerType.reserve(info.memoryTypes.size());
for (size_t i = 0; i < info.memoryTypes.size(); i++) { for (size_t i = 0; i < info.memoryTypes.size(); i++) {
mAllocatorsPerType.emplace_back(std::make_unique<SingleTypeAllocator>(mDevice, i)); mAllocatorsPerType.emplace_back(std::make_unique<SingleTypeAllocator>(
mDevice, i, info.memoryHeaps[info.memoryTypes[i].heapIndex].size));
} }
} }

View File

@ -54,12 +54,25 @@ namespace dawn_wire { namespace client {
uint32_t serial = buffer->requestSerial++; uint32_t serial = buffer->requestSerial++;
ASSERT(buffer->requests.find(serial) == buffer->requests.end()); ASSERT(buffer->requests.find(serial) == buffer->requests.end());
if (buffer->size > std::numeric_limits<size_t>::max()) {
// On buffer creation, we check that mappable buffers do not exceed this size.
// So this buffer must not have mappable usage. Inject a validation error.
ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(buffer->device),
WGPUErrorType_Validation,
"Buffer needs the correct map usage bit");
callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata);
return;
}
// Create a ReadHandle for the map request. This is the client's intent to read GPU // Create a ReadHandle for the map request. This is the client's intent to read GPU
// memory. // memory.
MemoryTransferService::ReadHandle* readHandle = MemoryTransferService::ReadHandle* readHandle =
buffer->device->GetClient()->GetMemoryTransferService()->CreateReadHandle(buffer->size); buffer->device->GetClient()->GetMemoryTransferService()->CreateReadHandle(
static_cast<size_t>(buffer->size));
if (readHandle == nullptr) { if (readHandle == nullptr) {
callback(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, userdata); ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(buffer->device),
WGPUErrorType_OutOfMemory, "Failed to create buffer mapping");
callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata);
return; return;
} }
@ -84,13 +97,25 @@ namespace dawn_wire { namespace client {
uint32_t serial = buffer->requestSerial++; uint32_t serial = buffer->requestSerial++;
ASSERT(buffer->requests.find(serial) == buffer->requests.end()); ASSERT(buffer->requests.find(serial) == buffer->requests.end());
if (buffer->size > std::numeric_limits<size_t>::max()) {
// On buffer creation, we check that mappable buffers do not exceed this size.
// So this buffer must not have mappable usage. Inject a validation error.
ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(buffer->device),
WGPUErrorType_Validation,
"Buffer needs the correct map usage bit");
callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata);
return;
}
// Create a WriteHandle for the map request. This is the client's intent to write GPU // Create a WriteHandle for the map request. This is the client's intent to write GPU
// memory. // memory.
MemoryTransferService::WriteHandle* writeHandle = MemoryTransferService::WriteHandle* writeHandle =
buffer->device->GetClient()->GetMemoryTransferService()->CreateWriteHandle( buffer->device->GetClient()->GetMemoryTransferService()->CreateWriteHandle(
buffer->size); static_cast<size_t>(buffer->size));
if (writeHandle == nullptr) { if (writeHandle == nullptr) {
callback(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, userdata); ClientDeviceInjectError(reinterpret_cast<WGPUDevice>(buffer->device),
WGPUErrorType_OutOfMemory, "Failed to create buffer mapping");
callback(WGPUBufferMapAsyncStatus_Error, nullptr, 0, userdata);
return; return;
} }
@ -112,6 +137,13 @@ namespace dawn_wire { namespace client {
Device* device = reinterpret_cast<Device*>(cDevice); Device* device = reinterpret_cast<Device*>(cDevice);
Client* wireClient = device->GetClient(); Client* wireClient = device->GetClient();
if ((descriptor->usage & (WGPUBufferUsage_MapRead | WGPUBufferUsage_MapWrite)) != 0 &&
descriptor->size > std::numeric_limits<size_t>::max()) {
ClientDeviceInjectError(cDevice, WGPUErrorType_OutOfMemory,
"Buffer is too large for map usage");
return ClientDeviceCreateErrorBuffer(cDevice);
}
auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device); auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device);
Buffer* buffer = bufferObjectAndSerial->object.get(); Buffer* buffer = bufferObjectAndSerial->object.get();
// Store the size of the buffer so that mapping operations can allocate a // Store the size of the buffer so that mapping operations can allocate a
@ -136,15 +168,18 @@ namespace dawn_wire { namespace client {
Device* device = reinterpret_cast<Device*>(cDevice); Device* device = reinterpret_cast<Device*>(cDevice);
Client* wireClient = device->GetClient(); Client* wireClient = device->GetClient();
auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device);
Buffer* buffer = bufferObjectAndSerial->object.get();
buffer->size = descriptor->size;
WGPUCreateBufferMappedResult result; WGPUCreateBufferMappedResult result;
result.buffer = reinterpret_cast<WGPUBuffer>(buffer);
result.data = nullptr; result.data = nullptr;
result.dataLength = 0; result.dataLength = 0;
// This buffer is too large to be mapped and to make a WriteHandle for.
if (descriptor->size > std::numeric_limits<size_t>::max()) {
ClientDeviceInjectError(cDevice, WGPUErrorType_OutOfMemory,
"Buffer is too large for mapping");
result.buffer = ClientDeviceCreateErrorBuffer(cDevice);
return result;
}
// Create a WriteHandle for the map request. This is the client's intent to write GPU // Create a WriteHandle for the map request. This is the client's intent to write GPU
// memory. // memory.
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle = std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle =
@ -152,7 +187,9 @@ namespace dawn_wire { namespace client {
wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size)); wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size));
if (writeHandle == nullptr) { if (writeHandle == nullptr) {
// TODO(enga): Support context lost generated by the client. ClientDeviceInjectError(cDevice, WGPUErrorType_OutOfMemory,
"Buffer mapping allocation failed");
result.buffer = ClientDeviceCreateErrorBuffer(cDevice);
return result; return result;
} }
@ -161,15 +198,21 @@ namespace dawn_wire { namespace client {
// Open the WriteHandle. This returns a pointer and size of mapped memory. // Open the WriteHandle. This returns a pointer and size of mapped memory.
// |result.data| may be null on error. // |result.data| may be null on error.
std::tie(result.data, result.dataLength) = writeHandle->Open(); std::tie(result.data, result.dataLength) = writeHandle->Open();
if (result.data == nullptr) { if (result.data == nullptr) {
// TODO(enga): Support context lost generated by the client. ClientDeviceInjectError(cDevice, WGPUErrorType_OutOfMemory,
"Buffer mapping allocation failed");
result.buffer = ClientDeviceCreateErrorBuffer(cDevice);
return result; return result;
} }
auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device);
Buffer* buffer = bufferObjectAndSerial->object.get();
buffer->size = descriptor->size;
// Successfully created staging memory. The buffer now owns the WriteHandle. // Successfully created staging memory. The buffer now owns the WriteHandle.
buffer->writeHandle = std::move(writeHandle); buffer->writeHandle = std::move(writeHandle);
result.buffer = reinterpret_cast<WGPUBuffer>(buffer);
// Get the serialization size of the WriteHandle. // Get the serialization size of the WriteHandle.
size_t handleCreateInfoLength = buffer->writeHandle->SerializeCreateSize(); size_t handleCreateInfoLength = buffer->writeHandle->SerializeCreateSize();

View File

@ -43,7 +43,10 @@ namespace dawn_wire { namespace client {
return false; return false;
} }
mStagingData = std::unique_ptr<uint8_t[]>(new uint8_t[mSize]); mStagingData = std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[mSize]);
if (!mStagingData) {
return false;
}
memcpy(mStagingData.get(), deserializePointer, mSize); memcpy(mStagingData.get(), deserializePointer, mSize);
ASSERT(data != nullptr); ASSERT(data != nullptr);
@ -74,7 +77,10 @@ namespace dawn_wire { namespace client {
} }
std::pair<void*, size_t> Open() override { std::pair<void*, size_t> Open() override {
mStagingData = std::unique_ptr<uint8_t[]>(new uint8_t[mSize]); mStagingData = std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[mSize]);
if (!mStagingData) {
return std::make_pair(nullptr, 0);
}
memset(mStagingData.get(), 0, mSize); memset(mStagingData.get(), 0, mSize);
return std::make_pair(mStagingData.get(), mSize); return std::make_pair(mStagingData.get(), mSize);
} }

View File

@ -463,17 +463,6 @@ TEST_P(CreateBufferMappedTests, CreateThenMapBeforeUnmapFailure) {
EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0); EXPECT_BUFFER_U32_EQ(myData, result.buffer, 0);
} }
// Test that creating a very large buffers fails gracefully.
TEST_P(CreateBufferMappedTests, LargeBufferFails) {
// TODO(http://crbug.com/dawn/27): Missing support.
DAWN_SKIP_TEST_IF(IsOpenGL());
wgpu::BufferDescriptor descriptor;
descriptor.size = std::numeric_limits<uint64_t>::max();
descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
}
// Test that creating a zero-sized buffer mapped is allowed. // Test that creating a zero-sized buffer mapped is allowed.
TEST_P(CreateBufferMappedTests, ZeroSized) { TEST_P(CreateBufferMappedTests, ZeroSized) {
wgpu::BufferDescriptor descriptor; wgpu::BufferDescriptor descriptor;
@ -534,14 +523,148 @@ TEST_P(BufferTests, ZeroSizedBuffer) {
} }
// Test that creating a very large buffers fails gracefully. // Test that creating a very large buffers fails gracefully.
TEST_P(BufferTests, LargeBufferFails) { TEST_P(BufferTests, CreateBufferOOM) {
// TODO(http://crbug.com/dawn/27): Missing support. // TODO(http://crbug.com/dawn/27): Missing support.
DAWN_SKIP_TEST_IF(IsOpenGL()); DAWN_SKIP_TEST_IF(IsOpenGL());
wgpu::BufferDescriptor descriptor; wgpu::BufferDescriptor descriptor;
descriptor.usage = wgpu::BufferUsage::CopyDst;
descriptor.size = std::numeric_limits<uint64_t>::max(); descriptor.size = std::numeric_limits<uint64_t>::max();
descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor)); ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
// UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails
// This hangs on the Metal AMD driver
if (!(IsMetal() && IsAMD())) {
descriptor.size = 1ull << 50;
ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
}
}
// Test that a very large CreateBufferMapped fails gracefully.
TEST_P(BufferTests, CreateBufferMappedOOM) {
// TODO(http://crbug.com/dawn/27): Missing support.
DAWN_SKIP_TEST_IF(IsOpenGL());
// Test non-mappable buffer
{
wgpu::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = wgpu::BufferUsage::CopyDst;
// Control: test a small buffer works.
device.CreateBufferMapped(&descriptor);
// Test an enormous buffer fails
descriptor.size = std::numeric_limits<uint64_t>::max();
ASSERT_DEVICE_ERROR(device.CreateBufferMapped(&descriptor));
// UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails
// This hangs on the Metal AMD driver
if (!(IsMetal() && IsAMD())) {
descriptor.size = 1ull << 50;
ASSERT_DEVICE_ERROR(device.CreateBufferMapped(&descriptor));
}
}
// Test mappable buffer
{
wgpu::BufferDescriptor descriptor;
descriptor.size = 4;
descriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite;
// Control: test a small buffer works.
device.CreateBufferMapped(&descriptor);
// Test an enormous buffer fails
descriptor.size = std::numeric_limits<uint64_t>::max();
ASSERT_DEVICE_ERROR(device.CreateBufferMapped(&descriptor));
if (!(IsMetal() && IsAMD())) {
// UINT64_MAX may be special cased. Test a smaller, but really large buffer also fails
descriptor.size = 1ull << 50;
ASSERT_DEVICE_ERROR(device.CreateBufferMapped(&descriptor));
}
}
}
// Test that mapping an OOM buffer for reading fails gracefully
TEST_P(BufferTests, CreateBufferOOMMapReadAsync) {
// TODO(http://crbug.com/dawn/27): Missing support.
DAWN_SKIP_TEST_IF(IsOpenGL());
auto RunTest = [this](const wgpu::BufferDescriptor& descriptor) {
wgpu::Buffer buffer;
ASSERT_DEVICE_ERROR(buffer = device.CreateBuffer(&descriptor));
bool done = false;
ASSERT_DEVICE_ERROR(buffer.MapReadAsync(
[](WGPUBufferMapAsyncStatus status, const void* ptr, uint64_t dataLength,
void* userdata) {
EXPECT_EQ(status, WGPUBufferMapAsyncStatus_Error);
EXPECT_EQ(ptr, nullptr);
EXPECT_EQ(dataLength, 0u);
*static_cast<bool*>(userdata) = true;
},
&done));
while (!done) {
WaitABit();
}
};
// Test an enormous buffer
wgpu::BufferDescriptor descriptor;
descriptor.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead;
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
// This hangs on the Metal AMD driver
if (!(IsMetal() && IsAMD())) {
descriptor.size = 1ull << 50;
RunTest(descriptor);
}
}
// Test that mapping an OOM buffer for reading fails gracefully
TEST_P(BufferTests, CreateBufferOOMMapWriteAsync) {
// TODO(http://crbug.com/dawn/27): Missing support.
DAWN_SKIP_TEST_IF(IsOpenGL());
auto RunTest = [this](const wgpu::BufferDescriptor& descriptor) {
wgpu::Buffer buffer;
ASSERT_DEVICE_ERROR(buffer = device.CreateBuffer(&descriptor));
bool done = false;
ASSERT_DEVICE_ERROR(buffer.MapWriteAsync(
[](WGPUBufferMapAsyncStatus status, void* ptr, uint64_t dataLength, void* userdata) {
EXPECT_EQ(status, WGPUBufferMapAsyncStatus_Error);
EXPECT_EQ(ptr, nullptr);
EXPECT_EQ(dataLength, 0u);
*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
// This hangs on the Metal AMD driver
if (!(IsMetal() && IsAMD())) {
descriptor.size = 1ull << 50;
RunTest(descriptor);
}
} }
DAWN_INSTANTIATE_TEST(BufferTests, DAWN_INSTANTIATE_TEST(BufferTests,

View File

@ -474,10 +474,9 @@ TEST_F(WireMemoryTransferServiceTests, BufferMapReadHandleCreationFailure) {
// Mock a ReadHandle creation failure // Mock a ReadHandle creation failure
MockReadHandleCreationFailure(); MockReadHandleCreationFailure();
// Failed creation of a ReadHandle is a fatal failure and the client synchronously receives a // Failed creation of a ReadHandle is a mapping failure and the client synchronously receives
// DEVICE_LOST callback. // an error callback.
EXPECT_CALL(*mockBufferMapReadCallback, EXPECT_CALL(*mockBufferMapReadCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0, _))
Call(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, _))
.Times(1); .Times(1);
wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr); wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr);
@ -716,10 +715,9 @@ TEST_F(WireMemoryTransferServiceTests, BufferMapWriteHandleCreationFailure) {
// Mock a WriteHandle creation failure // Mock a WriteHandle creation failure
MockWriteHandleCreationFailure(); MockWriteHandleCreationFailure();
// Failed creation of a WriteHandle is a fatal failure and the client synchronously receives a // Failed creation of a WriteHandle is a mapping failure and the client synchronously receives
// DEVICE_LOST callback. // an error callback.
EXPECT_CALL(*mockBufferMapWriteCallback, EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0, _))
Call(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, _))
.Times(1); .Times(1);
wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr); wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr);