Protect against huge buffer allocations on macOS 10.12, 10.13
|MTLDevice maxBufferLength| is not available until 10.14, so on
lower versions of macOS, try using |recommendedMaxWorkingSetSize|
Bug: dawn:450
Change-Id: I52dc4211cf3d7014771d580a9af9c72abe92a375
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/23263
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
diff --git a/src/dawn_native/metal/BufferMTL.mm b/src/dawn_native/metal/BufferMTL.mm
index 4d00d69..ccfd3b3 100644
--- a/src/dawn_native/metal/BufferMTL.mm
+++ b/src/dawn_native/metal/BufferMTL.mm
@@ -24,6 +24,10 @@
// largest alignment of supported data types
static constexpr uint32_t kMinUniformOrStorageBufferAlignment = 16u;
+ // The maximum buffer size if querying the maximum buffer size or recommended working set size
+ // is not available. This is a somewhat arbitrary limit of 1 GiB.
+ static constexpr uint32_t kMaxBufferSizeFallback = 1024u * 1024u * 1024u;
+
// static
ResultOrError<Buffer*> Buffer::Create(Device* device, const BufferDescriptor* descriptor) {
Ref<Buffer> buffer = AcquireRef(new Buffer(device, descriptor));
@@ -63,6 +67,17 @@
if (currentSize > maxBufferSize) {
return DAWN_OUT_OF_MEMORY_ERROR("Buffer allocation is too large");
}
+ } else if (@available(macOS 10.12, *)) {
+ // |maxBufferLength| isn't always available on older systems. If available, use
+ // |recommendedMaxWorkingSetSize| instead. We can probably allocate more than this,
+ // but don't have a way to discover a better limit. MoltenVK also uses this heuristic.
+ uint64_t maxWorkingSetSize =
+ [ToBackend(GetDevice())->GetMTLDevice() recommendedMaxWorkingSetSize];
+ if (currentSize > maxWorkingSetSize) {
+ return DAWN_OUT_OF_MEMORY_ERROR("Buffer allocation is too large");
+ }
+ } else if (currentSize > kMaxBufferSizeFallback) {
+ return DAWN_OUT_OF_MEMORY_ERROR("Buffer allocation is too large");
}
mMtlBuffer = [ToBackend(GetDevice())->GetMTLDevice() newBufferWithLength:currentSize
diff --git a/src/tests/end2end/BufferTests.cpp b/src/tests/end2end/BufferTests.cpp
index f29bacb..989b919 100644
--- a/src/tests/end2end/BufferTests.cpp
+++ b/src/tests/end2end/BufferTests.cpp
@@ -535,11 +535,8 @@
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));
- }
+ descriptor.size = 1ull << 50;
+ ASSERT_DEVICE_ERROR(device.CreateBuffer(&descriptor));
}
// Test that a very large CreateBufferMapped fails gracefully.
@@ -562,11 +559,8 @@
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));
- }
+ descriptor.size = 1ull << 50;
+ ASSERT_DEVICE_ERROR(device.CreateBufferMapped(&descriptor));
}
// Test mappable buffer
@@ -582,11 +576,9 @@
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));
- }
+ // 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));
}
}
@@ -624,11 +616,8 @@
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);
- }
+ descriptor.size = 1ull << 50;
+ RunTest(descriptor);
}
// Test that mapping an OOM buffer for reading fails gracefully
@@ -664,11 +653,8 @@
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);
- }
+ descriptor.size = 1ull << 50;
+ RunTest(descriptor);
}
DAWN_INSTANTIATE_TEST(BufferTests,