macOS: Enumerate GPUs first; prefer low-power non-removable; fall back to system default (#38164)
cacaosteve
,
张小白
, and
Kate
created
Problem: Some macOS environments report no devices via
MTLCopyAllDevices, causing startup failure with “unable to access a
compatible graphics device,” especially on Apple Silicon.
Change: Prefer MTLCreateSystemDefaultDevice
(metal::Device::system_default()) first. If None, enumerate devices and
select a non‑removable, low‑power device by preference.
Why this works: On Apple Silicon the system default is the unified GPU;
on Intel, the fallback keeps a stable policy and avoids accidentally
picking removable/high‑power devices.
Impact: Fixes startup on affected ASi systems; improves selection
consistency on Intel multi‑GPU. Behavior unchanged where
system_default() succeeds.
Risk: Low. Aligns with Apple’s recommended selection path. Still fails
early with a clearer message if no Metal devices exist.
Closes #37689.
Release Notes:
- Fixed: Startup failure on some Apple Silicon machines when Metal
device enumeration returned no devices by falling back to the system
default device.
---------
Co-authored-by: 张小白 <364772080@qq.com>
Co-authored-by: Kate <work@localcc.cc>
@@ -132,11 +132,21 @@ impl MetalRenderer {
// Prefer low‐power integrated GPUs on Intel Mac. On Apple
// Silicon, there is only ever one GPU, so this is equivalent to
// `metal::Device::system_default()`.
- let mut devices = metal::Device::all();- devices.sort_by_key(|device| (device.is_removable(), device.is_low_power()));- let Some(device) = devices.pop() else {- log::error!("unable to access a compatible graphics device");- std::process::exit(1);
+ let device = if let Some(d) = metal::Device::all()
+ .into_iter()
+ .min_by_key(|d| (d.is_removable(), !d.is_low_power()))
+ {
+ d
+ } else {
+ // For some reason `all()` can return an empty list, see https://github.com/zed-industries/zed/issues/37689
+ // In that case, we fall back to the system default device.
+ log::error!(
+ "Unable to enumerate Metal devices; attempting to use system default device"
+ );
+ metal::Device::system_default().unwrap_or_else(|| {
+ log::error!("unable to access a compatible graphics device");
+ std::process::exit(1);
+ })
};
let layer = metal::MetalLayer::new();