Bug 1843296 - Better handle shm allocation failure when creating buffers. r=gfx-reviewers,ErichDonGubler

Differential Revision: https://phabricator.services.mozilla.com/D184220
This commit is contained in:
Nicolas Silva
2023-08-07 16:43:56 +00:00
parent 0e0b7f1685
commit 540688c723
3 changed files with 59 additions and 28 deletions

View File

@@ -16,6 +16,7 @@
#include "nsContentUtils.h"
#include "nsWrapperCache.h"
#include "Device.h"
#include "mozilla/webgpu/ffi/wgpu.h"
namespace mozilla::webgpu {
@@ -70,35 +71,52 @@ already_AddRefed<Buffer> Buffer::Create(Device* aDevice, RawId aDeviceId,
bool hasMapFlags = aDesc.mUsage & (dom::GPUBufferUsage_Binding::MAP_WRITE |
dom::GPUBufferUsage_Binding::MAP_READ);
bool allocSucceeded = false;
if (hasMapFlags || aDesc.mMappedAtCreation) {
// If shmem allocation fails, we continue and provide the parent side with
// an empty shmem which it will interpret as an OOM situtation.
const auto checked = CheckedInt<size_t>(aDesc.mSize);
if (!checked.isValid()) {
aRv.ThrowRangeError("Mappable size is too large");
return nullptr;
}
size_t size = checked.value();
const size_t maxSize = WGPUMAX_BUFFER_SIZE;
if (checked.isValid() && checked.value() > 0 && checked.value() < maxSize) {
size_t size = checked.value();
auto maybeShmem = ipc::UnsafeSharedMemoryHandle::CreateAndMap(size);
auto maybeShmem = ipc::UnsafeSharedMemoryHandle::CreateAndMap(size);
if (maybeShmem.isNothing()) {
aRv.ThrowRangeError(
nsPrintfCString("Unable to allocate shmem of size %" PRIuPTR, size));
return nullptr;
if (maybeShmem.isSome()) {
allocSucceeded = true;
handle = std::move(maybeShmem.ref().first);
mapping = std::move(maybeShmem.ref().second);
MOZ_RELEASE_ASSERT(mapping.Size() >= size);
// zero out memory
memset(mapping.Bytes().data(), 0, size);
}
}
handle = std::move(maybeShmem.ref().first);
mapping = std::move(maybeShmem.ref().second);
if (checked.value() == 0) {
// Zero-sized buffers is a special case. We don't create a shmem since
// allocating the memory would not make sense, however mappable null
// buffers are allowed by the spec so we just pass the null handle which
// in practice deserializes into a null handle on the parent side and
// behaves like a zero-sized allocation.
allocSucceeded = true;
}
}
MOZ_RELEASE_ASSERT(mapping.Size() >= size);
// zero out memory
memset(mapping.Bytes().data(), 0, size);
// If mapped at creation and the shmem allocation failed, immediately throw
// a range error and don't attempt to create the buffer.
if (aDesc.mMappedAtCreation && !allocSucceeded) {
aRv.ThrowRangeError("Allocation failed");
return nullptr;
}
RawId id = actor->DeviceCreateBuffer(aDeviceId, aDesc, std::move(handle));
RefPtr<Buffer> buffer =
new Buffer(aDevice, id, aDesc.mSize, aDesc.mUsage, std::move(mapping));
if (aDesc.mMappedAtCreation) {
// Mapped at creation's raison d'être is write access, since the buffer is
// being created and there isn't anything interesting to read in it yet.