From dc3d9eecae5e91b5c096cd0706bea226c65510a2 Mon Sep 17 00:00:00 2001 From: Hoang Hong Quan Date: Thu, 5 Jun 2025 18:58:12 +0700 Subject: [PATCH] Improve GPU compatibility handling and kext selection --- Scripts/hardware_customizer.py | 216 +++++++++++++++++++++++++-------- Scripts/kext_maestro.py | 56 ++++++++- 2 files changed, 214 insertions(+), 58 deletions(-) diff --git a/Scripts/hardware_customizer.py b/Scripts/hardware_customizer.py index 10da76c..60f8557 100644 --- a/Scripts/hardware_customizer.py +++ b/Scripts/hardware_customizer.py @@ -57,11 +57,9 @@ class HardwareCustomizer: print("Type Device Device ID") print("------------------------------------------------------------------") for device_type, device_dict in self.selected_devices.items(): - device_name = list(device_dict.keys())[0] - device_props = list(device_dict.values())[0] - device_id = device_props.get("Device ID", "Unknown") - - print("{:<13} {:<42} {}".format(device_type, device_name[:38], device_id)) + for device_name, device_props in device_dict.items(): + device_id = device_props.get("Device ID", "Unknown") + print("{:<13} {:<42} {}".format(device_type, device_name[:38], device_id)) print("") print("All other devices of the same type have been disabled.") print("") @@ -69,36 +67,81 @@ class HardwareCustomizer: return self.customized_hardware, self.disabled_devices, needs_oclp + def _get_device_combinations(self, device_indices): + devices = sorted(list(device_indices)) + n = len(devices) + all_combinations = [] + + if n == 0: + return [] + + for i in range(1, 1 << n): + current_combination = [] + for j in range(n): + if (i >> j) & 1: + current_combination.append(devices[j]) + + if 1 <= len(current_combination) <= n: + all_combinations.append(current_combination) + + all_combinations.sort(key=lambda combo: (len(combo), combo)) + + return all_combinations + def _handle_device_selection(self, device_type): devices = self._get_compatible_devices(device_type) + device_groups = None + if len(devices) > 1: print("\n*** Multiple {} Devices Detected".format(device_type)) if device_type == "WiFi" or device_type == "Bluetooth": print(f"macOS works best with only one {device_type} device enabled.") elif device_type == "GPU": - _has_multiple_compatible_devices = False + _apu_index = None + _navi_22_indices = set() + _navi_indices = set() + _intel_gpu_indices = set() + _other_indices = set() - for gpu_name, gpu_props in devices.items(): + for index, (gpu_name, gpu_props) in enumerate(devices.items()): gpu_manufacturer = gpu_props.get("Manufacturer") gpu_codename = gpu_props.get("Codename") + gpu_type = gpu_props.get("Device Type") if gpu_manufacturer == "AMD": - if gpu_props.get("Device Type") == "Integrated GPU": - _has_multiple_compatible_devices = True - elif gpu_props.get("Device Type") == "Discrete GPU" and gpu_codename == "Navi 22": - _has_multiple_compatible_devices = True + if gpu_type == "Integrated GPU": + _apu_index = index + continue + elif gpu_type == "Discrete GPU": + if gpu_codename.startswith("Navi"): + _navi_indices.add(index) + if gpu_codename == "Navi 22": + _navi_22_indices.add(index) + continue + elif gpu_manufacturer == "Intel": + _intel_gpu_indices.add(index) + continue - if _has_multiple_compatible_devices: + _other_indices.add(index) + + if _apu_index or _navi_22_indices: print("Multiple active GPUs can cause kext conflicts in macOS.") - print("It's recommended to use only one GPU at a time.") - else: - return - selected_device = self._select_device(device_type, devices) - if selected_device: - self.selected_devices[device_type] = { - selected_device: devices[selected_device] - } + device_groups = [] + if _apu_index: + device_groups.append({_apu_index} | _other_indices) + if _navi_22_indices: + device_groups.append(_navi_22_indices | _other_indices) + if _navi_indices or _intel_gpu_indices or _other_indices: + device_groups.append(_navi_indices | _intel_gpu_indices | _other_indices) + + selected_devices = self._select_device(device_type, devices, device_groups) + if selected_devices: + for selected_device in selected_devices: + if not device_type in self.selected_devices: + self.selected_devices[device_type] = {} + + self.selected_devices[device_type][selected_device] = devices[selected_device] def _get_compatible_devices(self, device_type): compatible_devices = {} @@ -119,43 +162,112 @@ class HardwareCustomizer: return compatible_devices - def _select_device(self, device_type, devices): + def _select_device(self, device_type, devices, device_groups=None): print("") - print("Please select which {} device you want to use:".format(device_type)) + if device_groups: + print("Please select a {} combination configuration:".format(device_type)) + else: + print("Please select which {} device you want to use:".format(device_type)) print("") - for index, device_name in enumerate(devices, start=1): - device_props = devices[device_name] - compatibility = device_props.get("Compatibility") - - print("{}. {}".format(index, device_name)) - print(" Device ID: {}".format(device_props.get("Device ID", "Unknown"))) - print(" Compatibility: {}".format(self.compatibility_checker.show_macos_compatibility(compatibility))) - - if device_props.get("OCLP Compatibility"): - oclp_compatibility = device_props.get("OCLP Compatibility") + if device_groups: + valid_combinations = [] - if self.utils.parse_darwin_version(oclp_compatibility[0]) > self.utils.parse_darwin_version(compatibility[0]): - print(" OCLP Compatibility: {}".format(self.compatibility_checker.show_macos_compatibility((oclp_compatibility[0], os_data.get_lowest_darwin_version())))) - print() - - while True: - choice = self.utils.request_input(f"Select a {device_type} device (1-{len(devices)}): ") + for group in device_groups: + device_combinations = self._get_device_combinations(group) + for device_combination in device_combinations: + group_devices = [] + group_compatibility = None + group_indices = set() + has_oclp_required = False + + for index in device_combination: + device_name = list(devices.keys())[index] + device_props = devices[device_name] + group_devices.append(device_name) + group_indices.add(index) + compatibility = device_props.get("Compatibility") + if compatibility: + if group_compatibility is None: + group_compatibility = compatibility + else: + if self.utils.parse_darwin_version(compatibility[0]) < self.utils.parse_darwin_version(group_compatibility[0]): + group_compatibility = (compatibility[0], group_compatibility[1]) + if self.utils.parse_darwin_version(compatibility[1]) > self.utils.parse_darwin_version(group_compatibility[1]): + group_compatibility = (group_compatibility[0], compatibility[1]) + + if device_props.get("OCLP Compatibility"): + has_oclp_required = True + + if has_oclp_required and len(device_combination) > 1: + continue + + if group_devices and (group_devices, group_indices, group_compatibility) not in valid_combinations: + valid_combinations.append((group_devices, group_indices, group_compatibility)) + + valid_combinations.sort(key=lambda x: (len(x[0]), x[2][0])) - try: - choice_num = int(choice) - if 1 <= choice_num <= len(devices): - selected_device = list(devices)[choice_num - 1] - - for device in devices: - if device != selected_device: - self._disable_device(device_type, device, devices[device]) - - return selected_device - else: - print("Invalid option. Please try again.") - except: - print("Please enter a number.") + for idx, (group_devices, _, group_compatibility) in enumerate(valid_combinations, start=1): + print("{}. {}".format(idx, " + ".join(group_devices))) + if group_compatibility: + print(" Compatibility: {}".format(self.compatibility_checker.show_macos_compatibility(group_compatibility))) + if len(group_devices) == 1: + device_props = devices[group_devices[0]] + if device_props.get("OCLP Compatibility"): + oclp_compatibility = device_props.get("OCLP Compatibility") + if self.utils.parse_darwin_version(oclp_compatibility[0]) > self.utils.parse_darwin_version(group_compatibility[0]): + print(" OCLP Compatibility: {}".format(self.compatibility_checker.show_macos_compatibility((oclp_compatibility[0], os_data.get_lowest_darwin_version())))) + print("") + + while True: + choice = self.utils.request_input(f"Select a {device_type} combination (1-{len(valid_combinations)}): ") + + try: + choice_num = int(choice) + if 1 <= choice_num <= len(valid_combinations): + selected_devices, _, _ = valid_combinations[choice_num - 1] + + for device in devices: + if device not in selected_devices: + self._disable_device(device_type, device, devices[device]) + + return selected_devices + else: + print("Invalid option. Please try again.") + except ValueError: + print("Please enter a valid number.") + else: + for index, device_name in enumerate(devices, start=1): + device_props = devices[device_name] + compatibility = device_props.get("Compatibility") + + print("{}. {}".format(index, device_name)) + print(" Device ID: {}".format(device_props.get("Device ID", "Unknown"))) + print(" Compatibility: {}".format(self.compatibility_checker.show_macos_compatibility(compatibility))) + + if device_props.get("OCLP Compatibility"): + oclp_compatibility = device_props.get("OCLP Compatibility") + if self.utils.parse_darwin_version(oclp_compatibility[0]) > self.utils.parse_darwin_version(compatibility[0]): + print(" OCLP Compatibility: {}".format(self.compatibility_checker.show_macos_compatibility((oclp_compatibility[0], os_data.get_lowest_darwin_version())))) + print() + + while True: + choice = self.utils.request_input(f"Select a {device_type} device (1-{len(devices)}): ") + + try: + choice_num = int(choice) + if 1 <= choice_num <= len(devices): + selected_device = list(devices)[choice_num - 1] + + for device in devices: + if device != selected_device: + self._disable_device(device_type, device, devices[device]) + + return [selected_device] + else: + print("Invalid option. Please try again.") + except ValueError: + print("Please enter a valid number.") def _disable_device(self, device_type, device_name, device_props): if device_type == "WiFi": diff --git a/Scripts/kext_maestro.py b/Scripts/kext_maestro.py index 74f30ec..241fdbc 100644 --- a/Scripts/kext_maestro.py +++ b/Scripts/kext_maestro.py @@ -136,12 +136,56 @@ class KextMaestro: int(hardware_report.get("CPU").get("Core Count")) > 6: selected_kexts.append("CpuTopologyRebuild") - if "AMD" in hardware_report.get("CPU").get("Manufacturer") and \ - "Integrated GPU" in list(hardware_report.get("GPU").items())[0][-1].get("Device Type") and \ - "Integrated GPU" in list(hardware_report.get("GPU").items())[-1][-1].get("Device Type"): - selected_kexts.append("NootedRed") - else: - selected_kexts.append("NootRX" if "Navi 22" in list(hardware_report.get("GPU").items())[0][-1].get("Codename") else "WhateverGreen") + for gpu_name, gpu_props in hardware_report.get("GPU", {}).items(): + if "Integrated GPU" in gpu_props.get("Device Type"): + if "AMD" in gpu_props.get("Manufacturer"): + selected_kexts.append("NootedRed") + else: + if "Navi 22" in gpu_props.get("Codename"): + selected_kexts.append("NootRX") + elif gpu_props.get("Codename") in {"Navi 21", "Navi 23"}: + print("\n*** Found {} is AMD {} GPU.".format(gpu_name, gpu_props.get("Codename"))) + print("") + print("\033[91mImportant: Black Screen Fix\033[0m") + print("If you experience a black screen after verbose mode:") + print(" 1. Use ProperTree to open config.plist") + print(" 2. Navigate to NVRAM -> Add -> 7C436110-AB2A-4BBB-A880-FE41995C9F82 -> boot-args") + print(" 3. Remove \"-v debug=0x100 keepsyms=1\" from boot-args") + print("") + print("\033[93mNote:\033[0m - AMD {} GPUs have two available kext options:".format(gpu_props.get("Codename"))) + print(" - You can try different kexts after installation to find the best one for your system") + print("") + print("1. \033[1mNootRX\033[0m - Uses latest GPU firmware") + print("2. \033[1mWhateverGreen\033[0m - Uses original Apple firmware") + print("") + + if any(other_gpu_props.get("Manufacturer") == "Intel" for other_gpu_props in hardware_report.get("GPU", {}).values()): + print("\033[91mImportant:\033[0m - NootRX kext is not compatible with Intel GPUs") + print(" - Automatically selecting WhateverGreen kext due to Intel GPU compatibility") + print("") + self.utils.request_input("Press Enter to continue...") + continue + + recommended_option = 2 + recommended_name = "WhateverGreen" + + kext_option = self.utils.request_input("Select kext for your AMD {} GPU (default: {}): ".format(gpu_props.get("Codename"), recommended_name)).strip() or str(recommended_option) + + if kext_option.isdigit() and 0 < int(kext_option) < 3: + selected_option = int(kext_option) + else: + print("\033[91mInvalid selection, using recommended option: {}\033[0m".format(recommended_option)) + selected_option = recommended_option + + if selected_option == 2: + selected_kexts.append("WhateverGreen") + else: + selected_kexts.append("NootRX") + elif gpu_props.get("Codename").startswith("Navi 1"): + selected_kexts.append("WhateverGreen") + + if not "NootedRed" in selected_kexts and not "NootRX" in selected_kexts and not "WhateverGreen" in selected_kexts: + selected_kexts.append("WhateverGreen") if "Laptop" in hardware_report.get("Motherboard").get("Platform") and ("ASUS" in hardware_report.get("Motherboard").get("Name") or "NootedRed" in selected_kexts): selected_kexts.append("ForgedInvariant")