[Impeller] Enable support for Apple Silicon Macs and tvOS devices. (#35287)
diff --git a/impeller/renderer/allocator.cc b/impeller/renderer/allocator.cc
index 8151eec..49e6948 100644
--- a/impeller/renderer/allocator.cc
+++ b/impeller/renderer/allocator.cc
@@ -13,20 +13,6 @@
Allocator::~Allocator() = default;
-bool Allocator::RequiresExplicitHostSynchronization(StorageMode mode) {
- if (mode != StorageMode::kHostVisible) {
- return false;
- }
-
-#if FML_OS_IOS
- // StorageMode::kHostVisible is MTLStorageModeShared already.
- return false;
-#else // FML_OS_IOS
- // StorageMode::kHostVisible is MTLResourceStorageModeManaged.
- return true;
-#endif // FML_OS_IOS
-}
-
std::shared_ptr<DeviceBuffer> Allocator::CreateBufferWithCopy(
const uint8_t* buffer,
size_t length) {
diff --git a/impeller/renderer/allocator.h b/impeller/renderer/allocator.h
index 10e1eb3..eb88a62 100644
--- a/impeller/renderer/allocator.h
+++ b/impeller/renderer/allocator.h
@@ -69,8 +69,6 @@
std::shared_ptr<DeviceBuffer> CreateBufferWithCopy(
const fml::Mapping& mapping);
- static bool RequiresExplicitHostSynchronization(StorageMode mode);
-
protected:
Allocator();
diff --git a/impeller/renderer/backend/metal/allocator_mtl.h b/impeller/renderer/backend/metal/allocator_mtl.h
index 256ed3a..64f4c59 100644
--- a/impeller/renderer/backend/metal/allocator_mtl.h
+++ b/impeller/renderer/backend/metal/allocator_mtl.h
@@ -21,11 +21,10 @@
private:
friend class ContextMTL;
- // In the prototype, we are going to be allocating resources directly with the
- // MTLDevice APIs. But, in the future, this could be backed by named heaps
- // with specific limits.
id<MTLDevice> device_;
std::string allocator_label_;
+ bool supports_memoryless_targets_ = false;
+ bool supports_uma_ = false;
bool is_valid_ = false;
AllocatorMTL(id<MTLDevice> device, std::string label);
diff --git a/impeller/renderer/backend/metal/allocator_mtl.mm b/impeller/renderer/backend/metal/allocator_mtl.mm
index 7b01205..4a5dafa 100644
--- a/impeller/renderer/backend/metal/allocator_mtl.mm
+++ b/impeller/renderer/backend/metal/allocator_mtl.mm
@@ -14,12 +14,51 @@
namespace impeller {
+static bool DeviceSupportsMemorylessTargets(id<MTLDevice> device) {
+ // Refer to the "Memoryless render targets" feature in the table below:
+ // https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
+ if (@available(ios 13.0, tvos 13.0, macos 10.15, *)) {
+ return [device supportsFamily:MTLGPUFamilyApple2];
+ } else {
+#if FML_OS_IOS
+ // This is perhaps redundant. But, just in case we somehow get into a case
+ // where Impeller runs on iOS versions less than 8.0 and/or without A8
+ // GPUs, we explicitly check feature set support.
+ return [device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily2_v1];
+#else
+ // MacOS devices with Apple GPUs are only available with macos 10.15 and
+ // above. So, if we are here, it is safe to assume that memory-less targets
+ // are not supported.
+ return false;
+#endif
+ }
+ FML_UNREACHABLE();
+}
+
+static bool DeviceHasUnifiedMemoryArchitecture(id<MTLDevice> device) {
+ if (@available(ios 13.0, tvos 13.0, macOS 10.15, *)) {
+ return [device hasUnifiedMemory];
+ } else {
+#if FML_OS_IOS
+ // iOS devices where the availability check can fail always have had UMA.
+ return true;
+#else
+ // Mac devices where the availability check can fail have never had UMA.
+ return false;
+#endif
+ }
+ FML_UNREACHABLE();
+}
+
AllocatorMTL::AllocatorMTL(id<MTLDevice> device, std::string label)
: device_(device), allocator_label_(std::move(label)) {
if (!device_) {
return;
}
+ supports_memoryless_targets_ = DeviceSupportsMemorylessTargets(device_);
+ supports_uma_ = DeviceHasUnifiedMemoryArchitecture(device_);
+
is_valid_ = true;
}
@@ -29,56 +68,86 @@
return is_valid_;
}
-static MTLResourceOptions ToMTLResourceOptions(StorageMode type) {
+static MTLResourceOptions ToMTLResourceOptions(StorageMode type,
+ bool supports_memoryless_targets,
+ bool supports_uma) {
switch (type) {
case StorageMode::kHostVisible:
#if FML_OS_IOS
return MTLResourceStorageModeShared;
#else
- return MTLResourceStorageModeManaged;
+ if (supports_uma) {
+ return MTLResourceStorageModeShared;
+ } else {
+ return MTLResourceStorageModeManaged;
+ }
#endif
case StorageMode::kDevicePrivate:
return MTLResourceStorageModePrivate;
case StorageMode::kDeviceTransient:
-#if FML_OS_IOS
- return MTLResourceStorageModeMemoryless;
-#else
- return MTLResourceStorageModePrivate;
-#endif
+ if (supports_memoryless_targets) {
+ // Device may support but the OS has not been updated.
+ if (@available(macOS 11.0, *)) {
+ return MTLResourceStorageModeMemoryless;
+ } else {
+ return MTLResourceStorageModePrivate;
+ }
+ } else {
+ return MTLResourceStorageModePrivate;
+ }
+ FML_UNREACHABLE();
}
-
- return MTLResourceStorageModePrivate;
+ FML_UNREACHABLE();
}
-static MTLStorageMode ToMTLStorageMode(StorageMode mode) {
+static MTLStorageMode ToMTLStorageMode(StorageMode mode,
+ bool supports_memoryless_targets,
+ bool supports_uma) {
switch (mode) {
case StorageMode::kHostVisible:
#if FML_OS_IOS
return MTLStorageModeShared;
#else
- return MTLStorageModeManaged;
+ if (supports_uma) {
+ return MTLStorageModeShared;
+ } else {
+ return MTLStorageModeManaged;
+ }
#endif
case StorageMode::kDevicePrivate:
return MTLStorageModePrivate;
case StorageMode::kDeviceTransient:
-#if FML_OS_IOS
- return MTLStorageModeMemoryless;
-#else
- return MTLStorageModePrivate;
-#endif
+ if (supports_memoryless_targets) {
+ // Device may support but the OS has not been updated.
+ if (@available(macOS 11.0, *)) {
+ return MTLStorageModeMemoryless;
+ } else {
+ return MTLStorageModePrivate;
+ }
+ } else {
+ return MTLStorageModePrivate;
+ }
+ FML_UNREACHABLE();
}
- return MTLStorageModeShared;
+ FML_UNREACHABLE();
}
std::shared_ptr<DeviceBuffer> AllocatorMTL::CreateBuffer(StorageMode mode,
size_t length) {
- auto buffer = [device_ newBufferWithLength:length
- options:ToMTLResourceOptions(mode)];
+ const auto resource_options =
+ ToMTLResourceOptions(mode, supports_memoryless_targets_, supports_uma_);
+ const auto storage_mode =
+ ToMTLStorageMode(mode, supports_memoryless_targets_, supports_uma_);
+
+ auto buffer = [device_ newBufferWithLength:length options:resource_options];
if (!buffer) {
return nullptr;
}
- return std::shared_ptr<DeviceBufferMTL>(
- new DeviceBufferMTL(buffer, length, mode));
+ return std::shared_ptr<DeviceBufferMTL>(new DeviceBufferMTL(buffer, //
+ length, //
+ mode, //
+ storage_mode //
+ ));
}
std::shared_ptr<Texture> AllocatorMTL::CreateTexture(
@@ -95,7 +164,8 @@
return nullptr;
}
- mtl_texture_desc.storageMode = ToMTLStorageMode(mode);
+ mtl_texture_desc.storageMode =
+ ToMTLStorageMode(mode, supports_memoryless_targets_, supports_uma_);
auto texture = [device_ newTextureWithDescriptor:mtl_texture_desc];
if (!texture) {
return nullptr;
diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.h b/impeller/renderer/backend/metal/device_buffer_mtl.h
index e167dd8..1120779 100644
--- a/impeller/renderer/backend/metal/device_buffer_mtl.h
+++ b/impeller/renderer/backend/metal/device_buffer_mtl.h
@@ -27,8 +27,12 @@
friend class AllocatorMTL;
const id<MTLBuffer> buffer_;
+ const MTLStorageMode storage_mode_;
- DeviceBufferMTL(id<MTLBuffer> buffer, size_t size, StorageMode mode);
+ DeviceBufferMTL(id<MTLBuffer> buffer,
+ size_t size,
+ StorageMode mode,
+ MTLStorageMode storage_mode);
// |DeviceBuffer|
bool CopyHostBuffer(const uint8_t* source,
diff --git a/impeller/renderer/backend/metal/device_buffer_mtl.mm b/impeller/renderer/backend/metal/device_buffer_mtl.mm
index 32e7c55..e784bcf 100644
--- a/impeller/renderer/backend/metal/device_buffer_mtl.mm
+++ b/impeller/renderer/backend/metal/device_buffer_mtl.mm
@@ -13,8 +13,9 @@
DeviceBufferMTL::DeviceBufferMTL(id<MTLBuffer> buffer,
size_t size,
- StorageMode mode)
- : DeviceBuffer(size, mode), buffer_(buffer) {}
+ StorageMode mode,
+ MTLStorageMode storage_mode)
+ : DeviceBuffer(size, mode), buffer_(buffer), storage_mode_(storage_mode) {}
DeviceBufferMTL::~DeviceBufferMTL() = default;
@@ -45,15 +46,11 @@
::memmove(dest + offset, source + source_range.offset, source_range.length);
}
-// |RequiresExplicitHostSynchronization| always returns false on iOS. But the
-// compiler is mad that `didModifyRange:` appears in a TU meant for iOS. So,
+// MTLStorageModeManaged is never present on always returns false on iOS. But
+// the compiler is mad that `didModifyRange:` appears in a TU meant for iOS. So,
// just compile it away.
-//
-// Making this call is never necessary on iOS because there is no
-// MTLResourceStorageModeManaged mode. Only the MTLStorageModeShared mode is
-// available.
#if !FML_OS_IOS
- if (Allocator::RequiresExplicitHostSynchronization(mode_)) {
+ if (storage_mode_ == MTLStorageModeManaged) {
[buffer_ didModifyRange:NSMakeRange(offset, source_range.length)];
}
#endif
@@ -76,7 +73,6 @@
[buffer_ addDebugMarker:@(label.c_str())
range:NSMakeRange(range.offset, range.length)];
return true;
- FML_UNREACHABLE();
}
} // namespace impeller