Detect Apple Silicon GPU. Returns empty list on non-macOS or failure.
()
| 23 | |
| 24 | |
| 25 | def detect_apple_gpu() -> list[GPUInfo]: |
| 26 | """Detect Apple Silicon GPU. Returns empty list on non-macOS or failure.""" |
| 27 | try: |
| 28 | result = subprocess.run( |
| 29 | ["system_profiler", "SPHardwareDataType", "-json"], |
| 30 | capture_output=True, |
| 31 | text=True, |
| 32 | timeout=10, |
| 33 | ) |
| 34 | if result.returncode != 0: |
| 35 | return [] |
| 36 | data = json.loads(result.stdout) |
| 37 | except (FileNotFoundError, subprocess.TimeoutExpired, json.JSONDecodeError): |
| 38 | logger.debug("system_profiler not available (not macOS)") |
| 39 | return [] |
| 40 | |
| 41 | try: |
| 42 | hw_items = data["SPHardwareDataType"] |
| 43 | hw = hw_items[0] |
| 44 | chip_name = hw.get("chip_type", "") |
| 45 | if not chip_name: |
| 46 | return [] |
| 47 | |
| 48 | # Apple Silicon uses unified memory - get total physical memory |
| 49 | memory_str = hw.get("physical_memory", "0 GB") |
| 50 | # Parse "32 GB" -> bytes |
| 51 | parts = memory_str.split() |
| 52 | mem_value = int(parts[0]) |
| 53 | mem_unit = parts[1].upper() if len(parts) > 1 else "GB" |
| 54 | multiplier = {"GB": 1024**3, "TB": 1024**4, "MB": 1024**2}.get( |
| 55 | mem_unit, 1024**3 |
| 56 | ) |
| 57 | unified_memory = mem_value * multiplier |
| 58 | |
| 59 | return [ |
| 60 | GPUInfo( |
| 61 | name=chip_name, |
| 62 | vendor="apple", |
| 63 | vram_bytes=unified_memory, # unified memory |
| 64 | memory_bandwidth_gbps=_lookup_bandwidth(chip_name), |
| 65 | shared_memory=True, |
| 66 | ) |
| 67 | ] |
| 68 | except (KeyError, IndexError, ValueError) as e: |
| 69 | logger.debug(f"Failed to parse Apple hardware info: {e}") |
| 70 | return [] |
| 71 | |
| 72 | |
| 73 | # ---- Asahi Linux (Apple Silicon on Linux) ---- |
no test coverage detected