From 012aeeaf431b6b131abeb380448c3d6d8a242457 Mon Sep 17 00:00:00 2001 From: w-e-w <40751091+w-e-w@users.noreply.github.com> Date: Fri, 15 Sep 2023 19:49:15 +0900 Subject: [PATCH] AMD & NVIDIA GPU OpenHardwareMonitor on Windows --- .gitignore | 2 + README.md | 2 +- scripts/constant.py | 19 ---- scripts/gpu_temperature_protection.py | 126 +++++++++++++++++--------- scripts/install.py | 26 ------ scripts/settings_storage.py | 38 -------- 6 files changed, 86 insertions(+), 127 deletions(-) delete mode 100644 scripts/constant.py delete mode 100644 scripts/install.py delete mode 100644 scripts/settings_storage.py diff --git a/.gitignore b/.gitignore index 68bc17f..87bce89 100644 --- a/.gitignore +++ b/.gitignore @@ -158,3 +158,5 @@ cython_debug/ # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ + +OpenHardwareMonitor/OpenHardwareMonitorLib.dll diff --git a/README.md b/README.md index 9a0b47d..08cb735 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ this extension uses nvidia-smi to monitor GPU temperature at the end of each ste ## Support - Nvidia GPU on Windows and Linux - - AMD GPU on Linux with ROCm and Windows with openHardwareMonitor Lib + - AMD GPU on Linux with ROCm and Windows with [OpenHardwareMonitor](https://openhardwaremonitor.org/downloads) ## Installation - method 1: Install using extensions tab `Available` extensions diff --git a/scripts/constant.py b/scripts/constant.py deleted file mode 100644 index 4d2633b..0000000 --- a/scripts/constant.py +++ /dev/null @@ -1,19 +0,0 @@ -import os -from pathlib import Path - -#OpenHardwareMonitorLib download url -OpenHardwareMonitorLibDownloadUrl = "https://openhardwaremonitor.org/files/openhardwaremonitor-v0.9.6.zip" - -extensionRootPath = os.path.abspath(os.path.join(Path().absolute(),'./extensions/stable-diffusion-webui-GPU-temperature-protection/')) - -#path of settings Storage -settingsStorageJsonPath = os.path.abspath(os.path.join(extensionRootPath,'./settingsStorage.json')) - -# folder path -openHardwareMonitorDirPath = os.path.abspath(os.path.join(extensionRootPath,'./openHardwareMonitor')) - -#path to load openHardwareMonitorLib using pythonnet -openHardwareMonitorLibPath = os.path.abspath(os.path.join(openHardwareMonitorDirPath,'./OpenHardwareMonitorLib')) - -#dll path need for openhardwaremonitor zip extact -OpenHardwareMonitorLibdllFilePath = os.path.abspath(os.path.join(openHardwareMonitorDirPath,'./OpenHardwareMonitorLib.dll')) \ No newline at end of file diff --git a/scripts/gpu_temperature_protection.py b/scripts/gpu_temperature_protection.py index a7d8bbd..20bc9d3 100644 --- a/scripts/gpu_temperature_protection.py +++ b/scripts/gpu_temperature_protection.py @@ -1,19 +1,28 @@ from modules import scripts, shared, sd_samplers_common +from pathlib import Path +import urllib.request import gradio as gr import subprocess +import zipfile +import launch import time import re import os -from scripts import constant,settings_storage,install -import launch -if shared.opts.gpu_temps_sleep_temperature_src == 'NVIDIA & AMD - openHardwareMonitor' and os.name == 'nt' and launch.is_installed("pythonnet"): - # check is OpenHardwareMonitorLib exist if not will download - if not os.path.isfile(constant.OpenHardwareMonitorLibdllFilePath): - install.downloadOpenHardwareMonitorLib() - import clr # the pythonnet module. - clr.AddReference(constant.openHardwareMonitorLibPath) - from OpenHardwareMonitor.Hardware import Computer +OpenHardwareMonitorLibDownloadUrl = "https://openhardwaremonitor.org/files/openhardwaremonitor-v0.9.6.zip" +OpenHardwareMonitor_path = Path(scripts.current_basedir).joinpath('OpenHardwareMonitor') +OpenHardwareMonitorLib_path = OpenHardwareMonitor_path.joinpath('OpenHardwareMonitorLib') +OpenHardwareMonitorLib_dll_path = OpenHardwareMonitor_path.joinpath('OpenHardwareMonitorLib.dll') + + +def download_open_hardware_monitor(): + if not OpenHardwareMonitorLib_dll_path.is_file(): + OpenHardwareMonitor_path.mkdir(parents=True, exist_ok=True) + print("Downloading OpenHardwareMonitor") + zip_path, _ = urllib.request.urlretrieve(OpenHardwareMonitorLibDownloadUrl) + with zipfile.ZipFile(zip_path, "r") as z: + with open(os.path.realpath(OpenHardwareMonitorLib_dll_path), 'wb') as f: + f.write(z.read('OpenHardwareMonitor/OpenHardwareMonitorLib.dll')) class GPUTemperatureProtection(scripts.Script): @@ -64,44 +73,66 @@ class GPUTemperatureProtection(scripts.Script): hardware = None @staticmethod - def get_gpu_temperature_open_hardware_monitor(): - if not launch.is_installed("pythonnet"): - print("\n[Error GPU temperature protection] openHardwareMonitor : you need restart to install and download requirement") - if os.name != "nt": - print("\n[Error GPU temperature protection] openHardwareMonitor : only works on windows") - return 0 + def init_open_hardware_monitor(): try: - if GPUTemperatureProtection.computer is None: - GPUTemperatureProtection.computer = Computer() - GPUTemperatureProtection.computer.CPUEnabled = True # get the Info about CPU - GPUTemperatureProtection.computer.GPUEnabled = True # get the Info about GPU - GPUTemperatureProtection.computer.Open() - if GPUTemperatureProtection.sensors is None: - for a in range(0, len(GPUTemperatureProtection.computer.Hardware)): - if shared.opts.gpu_temps_sleep_gpu_name in str(GPUTemperatureProtection.computer.Hardware[a].Name): - for b in range(0, len(GPUTemperatureProtection.computer.Hardware[a].Sensors)): - if "/temperature" in str(GPUTemperatureProtection.computer.Hardware[a].Sensors[b].Identifier): - GPUTemperatureProtection.sensors = GPUTemperatureProtection.computer.Hardware[a].Sensors[b] - GPUTemperatureProtection.hardware = GPUTemperatureProtection.computer.Hardware[a] - if GPUTemperatureProtection.sensors is None: - print("\n[Error GPU temperature protection] openHardwareMonitor : Couldn't read temperature from OpenHardwareMonitorLib") - return 0 + # install and import Python.NET module + if not launch.is_installed("pythonnet"): + launch.run_pip("install pythonnet==3.0.2", "Installing requirements for OpenHardwareMonitorLib") + import clr # import pythonnet module. + # download OpenHardwareMonitor if not found + download_open_hardware_monitor() + + # initialize OpenHardwareMonitor + if GPUTemperatureProtection.computer is None: + clr.AddReference(str(OpenHardwareMonitorLib_path)) + from OpenHardwareMonitor.Hardware import Computer + GPUTemperatureProtection.computer = Computer() + GPUTemperatureProtection.computer.CPUEnabled = False # Disable CPU + GPUTemperatureProtection.computer.GPUEnabled = True # Enable GPU + GPUTemperatureProtection.computer.Open() + + # find the first matching temperature sensor for the specified hardware + if GPUTemperatureProtection.sensors is None or shared.opts.gpu_temps_sleep_gpu_name not in str(GPUTemperatureProtection.hardware.Name): + for hardware in GPUTemperatureProtection.computer.Hardware: + if shared.opts.gpu_temps_sleep_gpu_name in str(hardware.Name): + for sensor in hardware.Sensors: + if '/temperature' in str(sensor.Identifier): + GPUTemperatureProtection.sensors = sensor + GPUTemperatureProtection.hardware = hardware + return # sensor is found early return + + # sensor not found + GPUTemperatureProtection.sensors = None + GPUTemperatureProtection.hardware = None + print(f"[Error GPU temperature protection] OpenHardwareMonitor Couldn't find temperature sensor for {shared.opts.gpu_temps_sleep_gpu_name}") + + except Exception as e: + print(f"[Error GPU temperature protection] Failed to initialize OpenHardwareMonitor \: {e}") + + @staticmethod + def get_gpu_temperature_open_hardware_monitor(): + try: GPUTemperatureProtection.hardware.Update() return int(GPUTemperatureProtection.sensors.get_Value()) except Exception as e: - print(f'\n[Error GPU temperature protection] openHardwareMonitor : {e}') + print(f"\n[Error GPU temperature protection] OpenHardwareMonitor: Couldn't read temperature{e}") return 0 @staticmethod - def onChangeGpuTempsSleepTemperatureSrc(): - settings_storage.settingsStorage.set("gpu_temps_sleep_temperature_src",shared.opts.gpu_temps_sleep_temperature_src) - settings_storage.settingsStorage.save() + def on_change_temps_src(): + if shared.opts.gpu_temps_sleep_temperature_src == 'NVIDIA & AMD - OpenHardwareMonitor': + if os.name == 'nt': + GPUTemperatureProtection.init_open_hardware_monitor() + else: + assert False, "NVIDIA & AMD - OpenHardwareMonitor it's only supported on Windows" + elif shared.opts.gpu_temps_sleep_temperature_src == 'AMD - ROCm-smi' and os.name == 'nt': + assert False, "AMD - ROCm-smi is not supported on Windows" temperature_src_dict = { "NVIDIA - nvidia-smi": get_gpu_temperature_nvidia_smi, "AMD - ROCm-smi": get_gpu_temperature_amd_rocm_smi, - "NVIDIA & AMD - openHardwareMonitor": get_gpu_temperature_open_hardware_monitor + "NVIDIA & AMD - OpenHardwareMonitor": get_gpu_temperature_open_hardware_monitor } @staticmethod @@ -156,12 +187,13 @@ if hasattr(shared, "OptionHTML"): # < 1.6.0 support shared.options_templates.update(shared.options_section(('GPU_temperature_protection', "GPU Temperature"), { "gpu_temps_sleep_temperature_src_explanation": shared.OptionHTML("""NVIDIA - nvidia-smi is available on both Windows and Linux.
AMD - ROCm-smi is Linux only and does not support specifying GPU device index.
-NVIDIA & AMD - openHardwareMonitor is windows only suport NVIDIA and AMD. +NVIDIA & AMD - OpenHardwareMonitor is Windows only supports NVIDIA and AMD. """) })) + shared.options_templates.update(shared.options_section(('GPU_temperature_protection', "GPU Temperature"), { - "gpu_temps_sleep_temperature_src": shared.OptionInfo("NVIDIA - nvidia-smi", "Temperature source", gr.Radio, {"choices": list(GPUTemperatureProtection.temperature_src_dict.keys())}, onchange=GPUTemperatureProtection.onChangeGpuTempsSleepTemperatureSrc).needs_restart(), + "gpu_temps_sleep_temperature_src": shared.OptionInfo("NVIDIA - nvidia-smi", "Temperature source", gr.Radio, {"choices": list(GPUTemperatureProtection.temperature_src_dict.keys())}, GPUTemperatureProtection.on_change_temps_src), "gpu_temps_sleep_enable": shared.OptionInfo(True, "Enable GPU temperature protection"), "gpu_temps_sleep_print": shared.OptionInfo(True, "Print GPU Core temperature while sleeping in terminal"), "gpu_temps_sleep_minimum_interval": shared.OptionInfo(5.0, "GPU temperature monitor minimum interval", gr.Number).info("won't check the temperature again until this amount of seconds have passed"), @@ -169,12 +201,20 @@ shared.options_templates.update(shared.options_section(('GPU_temperature_protect "gpu_temps_sleep_max_sleep_time": shared.OptionInfo(10.0, "Max sleep Time", gr.Number).info("max number of seconds that it's allowed to pause, 0=unlimited"), "gpu_temps_sleep_sleep_temp": shared.OptionInfo(75.0, "GPU sleep temperature", gr.Slider, {"minimum": 0, "maximum": 125}).info("generation will pause if GPU core temperature exceeds this temperature"), "gpu_temps_sleep_wake_temp": shared.OptionInfo(75.0, "GPU wake temperature", gr.Slider, {"minimum": 0, "maximum": 125}).info("generation will pause until GPU core temperature drops below this temperature"), - "gpu_temps_sleep_gpu_index": shared.OptionInfo(0, "GPU device index", gr.Number, {"precision": 0}).info("selecting the correct temperature reading for multi GPU systems, for systems with 3 gpus the value should be an integer between 0~2, default 0"), + "gpu_temps_sleep_gpu_index": shared.OptionInfo(0, "GPU device index - nvidia-smi", gr.Number, {"precision": 0}).info("selecting the correct temperature reading for multi GPU systems, for systems with 3 gpus the value should be an integer between 0~2, default 0"), })) if os.name == 'nt': - all_lines = subprocess.check_output(['cmd.exe', '/c', 'wmic path win32_VideoController get name']).decode().strip("\nName").splitlines() - names_list = [name.rstrip() for name in all_lines if not re.compile("^ +$").match(name) and name != ''] - shared.options_templates.update(shared.options_section(('GPU_temperature_protection', "GPU Temperature"), { - "gpu_temps_sleep_gpu_name": shared.OptionInfo( "none" if len(names_list) == 0 else names_list[0] , "GPU Name", gr.Radio, {"choices": names_list, "interactive":shared.opts.gpu_temps_sleep_temperature_src == 'NVIDIA & AMD - openHardwareMonitor' }).info("select your gpu, only for openHardwareMonitor - windows"), - })) \ No newline at end of file + try: + all_lines = subprocess.check_output(['cmd.exe', '/c', 'wmic path win32_VideoController get name']).decode().strip("\nName").splitlines() + video_controller_filter = re.compile(r"^\s+$") + names_list = [name.strip() for name in all_lines if not video_controller_filter.match(name) and name != ''] + shared.options_templates.update(shared.options_section(('GPU_temperature_protection', "GPU Temperature"), { + "gpu_temps_sleep_gpu_name": shared.OptionInfo("None" if len(names_list) == 0 else names_list[0], "GPU Name - OpenHardwareMonitor", gr.Radio, {"choices": names_list}, GPUTemperatureProtection.on_change_temps_src).info("select your gpu"), + })) + except Exception as _e: + if shared.opts.gpu_temps_sleep_temperature_src == 'NVIDIA & AMD - OpenHardwareMonitor': + print(f'[Error GPU temperature protection] Failed to retrieve list of video controllers: \n{_e}') + +if shared.opts.gpu_temps_sleep_temperature_src == 'NVIDIA & AMD - OpenHardwareMonitor': + GPUTemperatureProtection.init_open_hardware_monitor() diff --git a/scripts/install.py b/scripts/install.py deleted file mode 100644 index ef968d0..0000000 --- a/scripts/install.py +++ /dev/null @@ -1,26 +0,0 @@ -import launch -import os -from pathlib import Path -from scripts import constant,settings_storage -import urllib -import zipfile - - -def downloadOpenHardwareMonitorLib(): - zip_path, _ = urllib.request.urlretrieve(constant.OpenHardwareMonitorLibDownloadUrl) - with zipfile.ZipFile(zip_path, "r") as z: - with open(os.path.realpath(constant.OpenHardwareMonitorLibdllFilePath) , 'wb') as f: - f.write(z.read('OpenHardwareMonitor/OpenHardwareMonitorLib.dll')) - -# install pythonnet required for openHardwareMonitor Lib -# shared.opts.gpu_temps_sleep_temperature_src == 'NVIDIA & AMD - openHardwareMonitor' and -if os.name == 'nt' : - - if settings_storage.settingsStorage.get("gpu_temps_sleep_temperature_src") == "NVIDIA & AMD - openHardwareMonitor": - if not launch.is_installed("pythonnet"): - launch.run_pip("install pythonnet==3.0.2", "requirements for windows OpenHardwareMonitorLib") - - # check and create OpenHardwareMonitor folder - Path(constant.openHardwareMonitorDirPath).mkdir(parents=True, exist_ok=True) - - diff --git a/scripts/settings_storage.py b/scripts/settings_storage.py deleted file mode 100644 index 50dd613..0000000 --- a/scripts/settings_storage.py +++ /dev/null @@ -1,38 +0,0 @@ -import json -from scripts import constant -import os - -class _SettingsStorage: - - def __init__(self,): - self.json_decoded = json.loads("{}") - - # check if not exist create empty file - if not os.path.isfile(constant.settingsStorageJsonPath) or not os.access(constant.settingsStorageJsonPath, os.R_OK): - with open(constant.settingsStorageJsonPath, 'w') as json_file: - json_file.write(json.dumps({})) - json_file.close() - - - with open(constant.settingsStorageJsonPath) as json_file: - self.json_decoded = json.loads(json_file.read()) - json_file.close() - - def save(self): - with open(constant.settingsStorageJsonPath, 'w') as json_file: - json_file.write(json.dumps(self.json_decoded)) - #json.dump(self.json_decoded, json_file) - json_file.close() - - - def get(self, key): - if key in self.json_decoded: - return self.json_decoded[key] - return None - - def set(self, key, val): - self.json_decoded[key] = val - - - -settingsStorage = _SettingsStorage() \ No newline at end of file