[Bf-blender-cvs] [40fce61a6ab] blender-v3.1-release: Cycles: enable Metal on AMD GPUs, set macOS minimum versions

Michael Jones noreply at git.blender.org
Fri Feb 11 19:45:20 CET 2022


Commit: 40fce61a6abe79508022d3e0cd3a29e187f18e74
Author: Michael Jones
Date:   Fri Feb 11 19:19:51 2022 +0100
Branches: blender-v3.1-release
https://developer.blender.org/rB40fce61a6abe79508022d3e0cd3a29e187f18e74

Cycles: enable Metal on AMD GPUs, set macOS minimum versions

* Apple Silicon support enabled on macOS 12.2+
* AMD support enabled on macOS 12.3+

This patch also fixes a device enumeration crash on certain AMD configs which
was caused by over-release of MTLDevice objects.

Differential Revision: https://developer.blender.org/D14090

===================================================================

M	intern/cycles/device/metal/device.mm
M	intern/cycles/device/metal/device_impl.mm
M	intern/cycles/device/metal/util.h
M	intern/cycles/device/metal/util.mm

===================================================================

diff --git a/intern/cycles/device/metal/device.mm b/intern/cycles/device/metal/device.mm
index bc893adea17..ef592438980 100644
--- a/intern/cycles/device/metal/device.mm
+++ b/intern/cycles/device/metal/device.mm
@@ -39,33 +39,20 @@ bool device_metal_init()
   return true;
 }
 
-static int device_metal_get_num_devices_safe(uint32_t *num_devices)
-{
-  *num_devices = MTLCopyAllDevices().count;
-  return 0;
-}
-
 void device_metal_info(vector<DeviceInfo> &devices)
 {
-  uint32_t num_devices = 0;
-  device_metal_get_num_devices_safe(&num_devices);
-  if (num_devices == 0) {
-    return;
-  }
-
-  vector<MetalPlatformDevice> usable_devices;
-  MetalInfo::get_usable_devices(&usable_devices);
+  auto usable_devices = MetalInfo::get_usable_devices();
   /* Devices are numbered consecutively across platforms. */
   set<string> unique_ids;
   int device_index = 0;
-  for (MetalPlatformDevice &device : usable_devices) {
+  for (id<MTLDevice> &device : usable_devices) {
     /* Compute unique ID for persistent user preferences. */
-    const string &device_name = device.device_name;
+    string device_name = [device.name UTF8String];
     string id = string("METAL_") + device_name;
 
     /* Hardware ID might not be unique, add device number in that case. */
     if (unique_ids.find(id) != unique_ids.end()) {
-      id += string_printf("_ID_%d", num_devices);
+      id += string_printf("_ID_%d", device_index);
     }
     unique_ids.insert(id);
 
@@ -94,15 +81,13 @@ void device_metal_info(vector<DeviceInfo> &devices)
 string device_metal_capabilities()
 {
   string result = "";
-  string error_msg = "";
-  uint32_t num_devices = 0;
-  assert(device_metal_get_num_devices_safe(&num_devices));
+  auto allDevices = MTLCopyAllDevices();
+  uint32_t num_devices = allDevices.count;
   if (num_devices == 0) {
     return "No Metal devices found\n";
   }
   result += string_printf("Number of devices: %u\n", num_devices);
 
-  NSArray<id<MTLDevice>> *allDevices = MTLCopyAllDevices();
   for (id<MTLDevice> device in allDevices) {
     result += string_printf("\t\tDevice: %s\n", [device.name UTF8String]);
   }
diff --git a/intern/cycles/device/metal/device_impl.mm b/intern/cycles/device/metal/device_impl.mm
index cdaafc60ab0..8ced0210e30 100644
--- a/intern/cycles/device/metal/device_impl.mm
+++ b/intern/cycles/device/metal/device_impl.mm
@@ -53,16 +53,10 @@ MetalDevice::MetalDevice(const DeviceInfo &info, Stats &stats, Profiler &profile
   mtlDevId = info.num;
 
   /* select chosen device */
-  vector<MetalPlatformDevice> usable_devices;
-  MetalInfo::get_usable_devices(&usable_devices);
-  if (usable_devices.size() == 0) {
-    set_error("Metal: no devices found.");
-    return;
-  }
+  auto usable_devices = MetalInfo::get_usable_devices();
   assert(mtlDevId < usable_devices.size());
-  MetalPlatformDevice &platform_device = usable_devices[mtlDevId];
-  mtlDevice = platform_device.device_id;
-  device_name = platform_device.device_name;
+  mtlDevice = usable_devices[mtlDevId];
+  device_name = [mtlDevice.name UTF8String];
   device_vendor = MetalInfo::get_vendor_from_device_name(device_name);
   assert(device_vendor != METAL_GPU_UNKNOWN);
   metal_printf("Creating new Cycles device for Metal: %s\n", device_name.c_str());
@@ -458,7 +452,8 @@ MetalDevice::MetalMem *MetalDevice::generic_alloc(device_memory &mem)
   id<MTLBuffer> metal_buffer = nil;
   MTLResourceOptions options = default_storage_mode;
 
-  /* Workaround for "bake" unit tests which fail if RenderBuffers is allocated with MTLResourceStorageModeShared. */
+  /* Workaround for "bake" unit tests which fail if RenderBuffers is allocated with
+   * MTLResourceStorageModeShared. */
   if (strstr(mem.name, "RenderBuffers")) {
     options = MTLResourceStorageModeManaged;
   }
@@ -769,9 +764,11 @@ void MetalDevice::tex_alloc(device_texture &mem)
   /* Check that dimensions fit within maximum allowable size.
      See https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf
   */
-  if (mem.data_width > 16384 ||
-      mem.data_height > 16384) {
-    set_error(string_printf("Texture exceeds maximum allowed size of 16384 x 16384 (requested: %zu x %zu)", mem.data_width, mem.data_height));
+  if (mem.data_width > 16384 || mem.data_height > 16384) {
+    set_error(string_printf(
+        "Texture exceeds maximum allowed size of 16384 x 16384 (requested: %zu x %zu)",
+        mem.data_width,
+        mem.data_height));
     return;
   }
 
diff --git a/intern/cycles/device/metal/util.h b/intern/cycles/device/metal/util.h
index dbeb3a5d064..084ba626370 100644
--- a/intern/cycles/device/metal/util.h
+++ b/intern/cycles/device/metal/util.h
@@ -36,33 +36,10 @@ enum MetalGPUVendor {
   METAL_GPU_INTEL = 3,
 };
 
-/* Retains a named MTLDevice for device enumeration. */
-struct MetalPlatformDevice {
-  MetalPlatformDevice(id<MTLDevice> device, const string &device_name)
-      : device_id(device), device_name(device_name)
-  {
-    [device_id retain];
-  }
-  ~MetalPlatformDevice()
-  {
-    [device_id release];
-  }
-  id<MTLDevice> device_id;
-  string device_name;
-};
-
 /* Contains static Metal helper functions. */
 struct MetalInfo {
-  static bool device_version_check(id<MTLDevice> device);
-  static void get_usable_devices(vector<MetalPlatformDevice> *usable_devices);
+  static vector<id<MTLDevice>> const &get_usable_devices();
   static MetalGPUVendor get_vendor_from_device_name(string const &device_name);
-
-  /* Platform information. */
-  static bool get_num_devices(uint32_t *num_platforms);
-  static uint32_t get_num_devices();
-
-  static bool get_device_name(id<MTLDevice> device_id, string *device_name);
-  static string get_device_name(id<MTLDevice> device_id);
 };
 
 /* Pool of MTLBuffers whose lifetime is linked to a single MTLCommandBuffer */
diff --git a/intern/cycles/device/metal/util.mm b/intern/cycles/device/metal/util.mm
index 763a37cb503..f0eb22e57f3 100644
--- a/intern/cycles/device/metal/util.mm
+++ b/intern/cycles/device/metal/util.mm
@@ -43,83 +43,45 @@ MetalGPUVendor MetalInfo::get_vendor_from_device_name(string const &device_name)
   return METAL_GPU_UNKNOWN;
 }
 
-bool MetalInfo::device_version_check(id<MTLDevice> device)
+vector<id<MTLDevice>> const &MetalInfo::get_usable_devices()
 {
-  /* Metal Cycles doesn't work correctly on macOS versions older than 12.0 */
-  if (@available(macos 12.0, *)) {
-    MetalGPUVendor vendor = get_vendor_from_device_name([[device name] UTF8String]);
+  static vector<id<MTLDevice>> usable_devices;
+  static bool already_enumerated = false;
 
-    /* Metal Cycles works on Apple Silicon GPUs at present */
-    return (vendor == METAL_GPU_APPLE);
+  if (already_enumerated) {
+    return usable_devices;
   }
 
-  return false;
-}
+  metal_printf("Usable Metal devices:\n");
+  for (id<MTLDevice> device in MTLCopyAllDevices()) {
+    const char *device_name = [device.name UTF8String];
 
-void MetalInfo::get_usable_devices(vector<MetalPlatformDevice> *usable_devices)
-{
-  static bool first_time = true;
-#  define FIRST_VLOG(severity) \
-    if (first_time) \
-    VLOG(severity)
-
-  usable_devices->clear();
-
-  NSArray<id<MTLDevice>> *allDevices = MTLCopyAllDevices();
-  for (id<MTLDevice> device in allDevices) {
-    string device_name;
-    if (!get_device_name(device, &device_name)) {
-      FIRST_VLOG(2) << "Failed to get device name, ignoring.";
-      continue;
+    MetalGPUVendor vendor = get_vendor_from_device_name(device_name);
+    bool usable = false;
+
+    if (@available(macos 12.2, *)) {
+      usable |= (vendor == METAL_GPU_APPLE);
     }
 
-    static const char *forceIntelStr = getenv("CYCLES_METAL_FORCE_INTEL");
-    bool forceIntel = forceIntelStr ? (atoi(forceIntelStr) != 0) : false;
-    if (forceIntel && device_name.find("Intel") == string::npos) {
-      FIRST_VLOG(2) << "CYCLES_METAL_FORCE_INTEL causing non-Intel device " << device_name
-                    << " to be ignored.";
-      continue;
+    if (@available(macos 12.3, *)) {
+      usable |= (vendor == METAL_GPU_AMD);
     }
 
-    if (!device_version_check(device)) {
-      FIRST_VLOG(2) << "Ignoring device " << device_name << " due to too old compiler version.";
-      continue;
+    if (usable) {
+      metal_printf("- %s\n", device_name);
+      [device retain];
+      usable_devices.push_back(device);
+    }
+    else {
+      metal_printf("  (skipping \"%s\")\n", device_name);
     }
-    FIRST_VLOG(2) << "Adding new device " << device_name << ".";
-    string hardware_id;
-    usable_devices->push_back(MetalPlatformDevice(device, device_name));
   }
-  first_time = false;
-}
-
-bool MetalInfo::get_num_devices(uint32_t *num_devices)
-{
-  *num_devices = MTLCopyAllDevices().count;
-  return true;
-}
-
-uint32_t MetalInfo::get_num_devices()
-{
-  uint32_t num_devices;
-  if (!get_num_devices(&num_devices)) {
-    return 0;
+  if (usable_devices.empty()) {
+    metal_printf("   No usable Metal devices found\n");
   }
-  return num_devices;
-}
-
-bool MetalInfo::get_device_name(id<MTLDevice> device, string *platform_name)
-{
-  *platform_name = [device.name UTF8String];
-  return true;
-}
+  already_enumerated = true;
 
-string MetalInfo::get_device_name(id<MTLDevice> device)
-{
-  string platform_name;
-  if (!get_device_name(device, &platform_name)) {
-    return "";
-  }
-  return platform_name;
+  return usable_devices;
 }
 
 id<MTLBuffer> MetalBufferPool::get_buffer(id<MTLDevice> device,



More information about the Bf-blender-cvs mailing list