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>
diff --git a/dawn.json b/dawn.json
index aa26700..1686917 100644
--- a/dawn.json
+++ b/dawn.json
@@ -521,6 +521,11 @@
]
},
{
+ "name": "create error buffer",
+ "returns": "buffer",
+ "TODO": "enga@: Make this part of a dawn_wire extension"
+ },
+ {
"name": "create buffer mapped",
"returns": "create buffer mapped result",
"args": [
diff --git a/src/dawn_native/Buffer.cpp b/src/dawn_native/Buffer.cpp
index 4df366a..3c97655 100644
--- a/src/dawn_native/Buffer.cpp
+++ b/src/dawn_native/Buffer.cpp
@@ -210,7 +210,7 @@
void BufferBase::CallMapReadCallback(uint32_t serial,
WGPUBufferMapAsyncStatus status,
const void* pointer,
- uint32_t dataLength) {
+ uint64_t dataLength) {
ASSERT(!IsError());
if (mMapReadCallback != nullptr && serial == mMapSerial) {
ASSERT(mMapWriteCallback == nullptr);
@@ -231,7 +231,7 @@
void BufferBase::CallMapWriteCallback(uint32_t serial,
WGPUBufferMapAsyncStatus status,
void* pointer,
- uint32_t dataLength) {
+ uint64_t dataLength) {
ASSERT(!IsError());
if (mMapWriteCallback != nullptr && serial == mMapSerial) {
ASSERT(mMapReadCallback == nullptr);
diff --git a/src/dawn_native/Buffer.h b/src/dawn_native/Buffer.h
index b05e341..8a1feb7 100644
--- a/src/dawn_native/Buffer.h
+++ b/src/dawn_native/Buffer.h
@@ -71,11 +71,11 @@
void CallMapReadCallback(uint32_t serial,
WGPUBufferMapAsyncStatus status,
const void* pointer,
- uint32_t dataLength);
+ uint64_t dataLength);
void CallMapWriteCallback(uint32_t serial,
WGPUBufferMapAsyncStatus status,
void* pointer,
- uint32_t dataLength);
+ uint64_t dataLength);
void DestroyInternal();
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index 697b741..7a0d04e 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -731,6 +731,12 @@
return result;
}
+ // For Dawn Wire
+
+ BufferBase* DeviceBase::CreateErrorBuffer() {
+ return BufferBase::MakeError(this);
+ }
+
// Other Device API methods
void DeviceBase::Tick() {
diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h
index b94f762..94d916f 100644
--- a/src/dawn_native/Device.h
+++ b/src/dawn_native/Device.h
@@ -158,6 +158,9 @@
TextureViewBase* CreateTextureView(TextureBase* texture,
const TextureViewDescriptor* descriptor);
+ // For Dawn Wire
+ BufferBase* CreateErrorBuffer();
+
QueueBase* GetDefaultQueue();
void InjectError(wgpu::ErrorType type, const char* message);
diff --git a/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp b/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp
index 1b01a07..4e48f2e 100644
--- a/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp
+++ b/src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp
@@ -185,8 +185,12 @@
ResourceHeapAllocation directAllocation;
DAWN_TRY_ASSIGN(directAllocation,
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) {
diff --git a/src/dawn_native/vulkan/BufferVk.cpp b/src/dawn_native/vulkan/BufferVk.cpp
index cd35b4c..0b9385e 100644
--- a/src/dawn_native/vulkan/BufferVk.cpp
+++ b/src/dawn_native/vulkan/BufferVk.cpp
@@ -148,7 +148,7 @@
createInfo.pQueueFamilyIndices = 0;
Device* device = ToBackend(GetDevice());
- DAWN_TRY(CheckVkSuccess(
+ DAWN_TRY(CheckVkOOMThenSuccess(
device->fn.CreateBuffer(device->GetVkDevice(), &createInfo, nullptr, &*mHandle),
"vkCreateBuffer"));
diff --git a/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp b/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp
index cff7434..22523a3 100644
--- a/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp
+++ b/src/dawn_native/vulkan/ResourceMemoryAllocatorVk.cpp
@@ -14,6 +14,7 @@
#include "dawn_native/vulkan/ResourceMemoryAllocatorVk.h"
+#include "common/Math.h"
#include "dawn_native/BuddyMemoryAllocator.h"
#include "dawn_native/ResourceHeapAllocator.h"
#include "dawn_native/vulkan/DeviceVk.h"
@@ -28,8 +29,7 @@
// 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
// factors.
- constexpr uint64_t kMaxBuddySystemSize = 32ull * 1024ull * 1024ull * 1024ull; // 32GB
- constexpr uint64_t kMaxSizeForSubAllocation = 4ull * 1024ull * 1024ull; // 4MB
+ constexpr uint64_t kMaxSizeForSubAllocation = 4ull * 1024ull * 1024ull; // 4MiB
// Have each bucket of the buddy system allocate at least some resource of the maximum
// size
@@ -42,10 +42,18 @@
class ResourceMemoryAllocator::SingleTypeAllocator : public ResourceHeapAllocator {
public:
- SingleTypeAllocator(Device* device, size_t memoryTypeIndex)
+ SingleTypeAllocator(Device* device, size_t memoryTypeIndex, VkDeviceSize memoryHeapSize)
: mDevice(device),
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;
@@ -62,6 +70,10 @@
ResultOrError<std::unique_ptr<ResourceHeapBase>> AllocateResourceHeap(
uint64_t size) override {
+ if (size > mMemoryHeapSize) {
+ return DAWN_OUT_OF_MEMORY_ERROR("Allocation size too large");
+ }
+
VkMemoryAllocateInfo allocateInfo;
allocateInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocateInfo.pNext = nullptr;
@@ -87,6 +99,7 @@
private:
Device* mDevice;
size_t mMemoryTypeIndex;
+ VkDeviceSize mMemoryHeapSize;
BuddyMemoryAllocator mBuddySystem;
};
@@ -97,7 +110,8 @@
mAllocatorsPerType.reserve(info.memoryTypes.size());
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));
}
}
diff --git a/src/dawn_wire/client/ApiProcs.cpp b/src/dawn_wire/client/ApiProcs.cpp
index 6106901..64f6f56 100644
--- a/src/dawn_wire/client/ApiProcs.cpp
+++ b/src/dawn_wire/client/ApiProcs.cpp
@@ -54,12 +54,25 @@
uint32_t serial = buffer->requestSerial++;
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
// memory.
MemoryTransferService::ReadHandle* readHandle =
- buffer->device->GetClient()->GetMemoryTransferService()->CreateReadHandle(buffer->size);
+ buffer->device->GetClient()->GetMemoryTransferService()->CreateReadHandle(
+ static_cast<size_t>(buffer->size));
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;
}
@@ -84,13 +97,25 @@
uint32_t serial = buffer->requestSerial++;
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
// memory.
MemoryTransferService::WriteHandle* writeHandle =
buffer->device->GetClient()->GetMemoryTransferService()->CreateWriteHandle(
- buffer->size);
+ static_cast<size_t>(buffer->size));
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;
}
@@ -112,6 +137,13 @@
Device* device = reinterpret_cast<Device*>(cDevice);
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);
Buffer* buffer = bufferObjectAndSerial->object.get();
// Store the size of the buffer so that mapping operations can allocate a
@@ -136,15 +168,18 @@
Device* device = reinterpret_cast<Device*>(cDevice);
Client* wireClient = device->GetClient();
- auto* bufferObjectAndSerial = wireClient->BufferAllocator().New(device);
- Buffer* buffer = bufferObjectAndSerial->object.get();
- buffer->size = descriptor->size;
-
WGPUCreateBufferMappedResult result;
- result.buffer = reinterpret_cast<WGPUBuffer>(buffer);
result.data = nullptr;
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
// memory.
std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle =
@@ -152,7 +187,9 @@
wireClient->GetMemoryTransferService()->CreateWriteHandle(descriptor->size));
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;
}
@@ -161,15 +198,21 @@
// Open the WriteHandle. This returns a pointer and size of mapped memory.
// |result.data| may be null on error.
std::tie(result.data, result.dataLength) = writeHandle->Open();
-
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;
}
+ 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.
buffer->writeHandle = std::move(writeHandle);
+ result.buffer = reinterpret_cast<WGPUBuffer>(buffer);
+
// Get the serialization size of the WriteHandle.
size_t handleCreateInfoLength = buffer->writeHandle->SerializeCreateSize();
diff --git a/src/dawn_wire/client/ClientInlineMemoryTransferService.cpp b/src/dawn_wire/client/ClientInlineMemoryTransferService.cpp
index bfd6634..e773710 100644
--- a/src/dawn_wire/client/ClientInlineMemoryTransferService.cpp
+++ b/src/dawn_wire/client/ClientInlineMemoryTransferService.cpp
@@ -43,7 +43,10 @@
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);
ASSERT(data != nullptr);
@@ -74,7 +77,10 @@
}
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);
return std::make_pair(mStagingData.get(), mSize);
}
diff --git a/src/tests/end2end/BufferTests.cpp b/src/tests/end2end/BufferTests.cpp
index ceaf27c..e854c44 100644
--- a/src/tests/end2end/BufferTests.cpp
+++ b/src/tests/end2end/BufferTests.cpp
@@ -463,17 +463,6 @@
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_P(CreateBufferMappedTests, ZeroSized) {
wgpu::BufferDescriptor descriptor;
@@ -534,14 +523,148 @@
}
// 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.
DAWN_SKIP_TEST_IF(IsOpenGL());
wgpu::BufferDescriptor descriptor;
+ descriptor.usage = wgpu::BufferUsage::CopyDst;
+
descriptor.size = std::numeric_limits<uint64_t>::max();
- descriptor.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
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,
diff --git a/src/tests/unittests/wire/WireMemoryTransferServiceTests.cpp b/src/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
index 1d7d828..bb1efb6 100644
--- a/src/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
+++ b/src/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
@@ -474,10 +474,9 @@
// Mock a ReadHandle creation failure
MockReadHandleCreationFailure();
- // Failed creation of a ReadHandle is a fatal failure and the client synchronously receives a
- // DEVICE_LOST callback.
- EXPECT_CALL(*mockBufferMapReadCallback,
- Call(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, _))
+ // Failed creation of a ReadHandle is a mapping failure and the client synchronously receives
+ // an error callback.
+ EXPECT_CALL(*mockBufferMapReadCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0, _))
.Times(1);
wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr);
@@ -716,10 +715,9 @@
// Mock a WriteHandle creation failure
MockWriteHandleCreationFailure();
- // Failed creation of a WriteHandle is a fatal failure and the client synchronously receives a
- // DEVICE_LOST callback.
- EXPECT_CALL(*mockBufferMapWriteCallback,
- Call(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, _))
+ // Failed creation of a WriteHandle is a mapping failure and the client synchronously receives
+ // an error callback.
+ EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0, _))
.Times(1);
wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr);