Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GPU frequency sensor #480

Merged
merged 9 commits into from
Mar 10, 2024
5 changes: 5 additions & 0 deletions library/sensors/sensors.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ def fps() -> int:
def fan_percent() -> float:
pass

@staticmethod
@abstractmethod
def frequency() -> float:
pass

@staticmethod
@abstractmethod
def is_available() -> bool:
Expand Down
23 changes: 22 additions & 1 deletion library/sensors/sensors_librehardwaremonitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,13 +323,34 @@ def fan_percent(cls) -> float:
try:
for sensor in gpu_to_use.Sensors:
if sensor.SensorType == Hardware.SensorType.Control:
return float(sensor.Value)
if sensor.Value:
return float(sensor.Value)
except:
pass

# No Fan Speed sensor for this GPU model
return math.nan

@classmethod
def frequency(cls) -> float:
gpu_to_use = cls.get_gpu_to_use()
if gpu_to_use is None:
# GPU not supported
return math.nan

try:
for sensor in gpu_to_use.Sensors:
if sensor.SensorType == Hardware.SensorType.Clock:
# Keep only real core clocks, ignore effective core clocks
if "Core" in str(sensor.Name) and "Effective" not in str(sensor.Name):
if sensor.Value:
return float(sensor.Value)
except:
pass

# No Frequency sensor for this GPU model
return math.nan

@classmethod
def is_available(cls) -> bool:
cls.gpu_name = get_gpu_name()
Expand Down
58 changes: 40 additions & 18 deletions library/sensors/sensors_python.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@ def fan_percent() -> float:
else:
return math.nan

@staticmethod
def frequency() -> float:
global DETECTED_GPU
if DETECTED_GPU == GpuType.AMD:
return GpuAmd.frequency()
elif DETECTED_GPU == GpuType.NVIDIA:
return GpuNvidia.frequency()
else:
return math.nan

@staticmethod
def is_available() -> bool:
global DETECTED_GPU
Expand Down Expand Up @@ -247,6 +257,11 @@ def fan_percent() -> float:

return math.nan

@staticmethod
def frequency() -> float:
# Not supported by Python libraries
return math.nan

@staticmethod
def is_available() -> bool:
try:
Expand All @@ -260,52 +275,43 @@ class GpuAmd(sensors.Gpu):
def stats() -> Tuple[float, float, float, float]: # load (%) / used mem (%) / used mem (Mb) / temp (°C)
if pyamdgpuinfo:
# Unlike other sensors, AMD GPU with pyamdgpuinfo pulls in all the stats at once
i = 0
amd_gpus = []
while i < pyamdgpuinfo.detect_gpus():
amd_gpus.append(pyamdgpuinfo.get_gpu(i))
i = i + 1
pyamdgpuinfo.detect_gpus()
amd_gpu = pyamdgpuinfo.get_gpu(0)

try:
memory_used_all = [item.query_vram_usage() for item in amd_gpus]
memory_used_bytes = sum(memory_used_all) / len(memory_used_all)
memory_used_bytes = amd_gpu.query_vram_usage()
memory_used = memory_used_bytes / 1000000
except:
memory_used_bytes = math.nan
memory_used = math.nan

try:
memory_total_all = [item.memory_info["vram_size"] for item in amd_gpus]
memory_total_bytes = sum(memory_total_all) / len(memory_total_all)
memory_total_bytes = amd_gpu.memory_info["vram_size"]
memory_percentage = (memory_used_bytes / memory_total_bytes) * 100
except:
memory_percentage = math.nan

try:
load_all = [item.query_load() for item in amd_gpus]
load = (sum(load_all) / len(load_all)) * 100
load = amd_gpu.query_load()
except:
load = math.nan

try:
temperature_all = [item.query_temperature() for item in amd_gpus]
temperature = sum(temperature_all) / len(temperature_all)
temperature = amd_gpu.query_temperature()
except:
temperature = math.nan

return load, memory_percentage, memory_used, temperature
elif pyadl:
amd_gpus = pyadl.ADLManager.getInstance().getDevices()
amd_gpu = pyadl.ADLManager.getInstance().getDevices()[0]

try:
load_all = [item.getCurrentUsage() for item in amd_gpus]
load = (sum(load_all) / len(load_all))
load = amd_gpu.getCurrentUsage()
except:
load = math.nan

try:
temperature_all = [item.getCurrentTemperature() for item in amd_gpus]
temperature = sum(temperature_all) / len(temperature_all)
temperature = amd_gpu.getCurrentTemperature()
except:
temperature = math.nan

Expand All @@ -320,17 +326,33 @@ def fps() -> int:
@staticmethod
def fan_percent() -> float:
try:
# Try with psutil fans
fans = sensors_fans_percent()
if fans:
for name, entries in fans.items():
for entry in entries:
if "gpu" in (entry.label or name):
return entry.current

# Try with pyadl if psutil did not find GPU fan
if pyadl:
return pyadl.ADLManager.getInstance().getDevices()[0].getCurrentFanSpeed(
pyadl.ADL_DEVICE_FAN_SPEED_TYPE_PERCENTAGE)
except:
pass

return math.nan

@staticmethod
def frequency() -> float:
if pyamdgpuinfo:
pyamdgpuinfo.detect_gpus()
return pyamdgpuinfo.get_gpu(0).query_sclk() / 1000000
elif pyadl:
return pyadl.ADLManager.getInstance().getDevices()[0].getCurrentEngineClock()
else:
return math.nan

@staticmethod
def is_available() -> bool:
try:
Expand Down
4 changes: 4 additions & 0 deletions library/sensors/sensors_stub_random.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ def fps() -> int:
def fan_percent() -> float:
return random.uniform(0, 100)

@staticmethod
def frequency() -> float:
return random.uniform(800, 3400)

@staticmethod
def is_available() -> bool:
return True
Expand Down
5 changes: 5 additions & 0 deletions library/sensors/sensors_stub_static.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
GPU_MEM_TOTAL_SIZE_GB = 32
NETWORK_SPEED_BYTES = 1061000000
GPU_FPS = 120
GPU_FREQ_MHZ = 1500.0


class Cpu(sensors.Cpu):
Expand Down Expand Up @@ -73,6 +74,10 @@ def fps() -> int:
def fan_percent() -> float:
return PERCENTAGE_SENSOR_VALUE

@staticmethod
def frequency() -> float:
return GPU_FREQ_MHZ

@staticmethod
def is_available() -> bool:
return True
Expand Down
43 changes: 41 additions & 2 deletions library/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ class CPU:
last_values_cpu_percentage = []
last_values_cpu_temperature = []
last_values_cpu_fan_speed = []
last_values_cpu_frequency = []

@classmethod
def percentage(cls):
Expand All @@ -258,12 +259,26 @@ def percentage(cls):

@classmethod
def frequency(cls):
freq_ghz = sensors.Cpu.frequency() / 1000
theme_data = config.THEME_DATA['STATS']['CPU']['FREQUENCY']

save_last_value(freq_ghz, cls.last_values_cpu_frequency,
theme_data['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))

display_themed_value(
theme_data=config.THEME_DATA['STATS']['CPU']['FREQUENCY']['TEXT'],
value=f'{sensors.Cpu.frequency() / 1000:.2f}',
theme_data=theme_data['TEXT'],
value=f'{freq_ghz:.2f}',
unit=" GHz",
min_size=4
)
display_themed_progress_bar(theme_data['GRAPH'], freq_ghz)
display_themed_radial_bar(
theme_data=theme_data['RADIAL'],
value=f'{freq_ghz:.2f}',
unit=" GHz",
min_size=4
)
display_themed_line_graph(theme_data['LINE_GRAPH'], cls.last_values_cpu_frequency)

@classmethod
def load(cls):
Expand Down Expand Up @@ -336,12 +351,14 @@ class Gpu:
last_values_gpu_temperature = []
last_values_gpu_fps = []
last_values_gpu_fan_speed = []
last_values_gpu_frequency = []

@classmethod
def stats(cls):
load, memory_percentage, memory_used_mb, temperature = sensors.Gpu.stats()
fps = sensors.Gpu.fps()
fan_percent = sensors.Gpu.fan_percent()
freq_ghz = sensors.Gpu.frequency() / 1000

theme_gpu_data = config.THEME_DATA['STATS']['GPU']

Expand All @@ -355,6 +372,8 @@ def stats(cls):
theme_gpu_data['FPS']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
save_last_value(fan_percent, cls.last_values_gpu_fan_speed,
theme_gpu_data['FAN_SPEED']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
save_last_value(freq_ghz, cls.last_values_gpu_frequency,
theme_gpu_data['FREQUENCY']['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))

################################ for backward compatibility only
gpu_mem_graph_data = theme_gpu_data['MEMORY']['GRAPH']
Expand Down Expand Up @@ -512,6 +531,26 @@ def stats(cls):
display_themed_percent_radial_bar(gpu_fan_radial_data, fan_percent)
display_themed_line_graph(gpu_fan_line_graph_data, cls.last_values_gpu_fan_speed)

# GPU Frequency (Ghz)
gpu_freq_text_data = theme_gpu_data['FREQUENCY']['TEXT']
gpu_freq_radial_data = theme_gpu_data['FREQUENCY']['RADIAL']
gpu_freq_graph_data = theme_gpu_data['FREQUENCY']['GRAPH']
gpu_freq_line_graph_data = theme_gpu_data['FREQUENCY']['LINE_GRAPH']
display_themed_value(
theme_data=gpu_freq_text_data,
value=f'{freq_ghz:.2f}',
unit=" GHz",
min_size=4
)
display_themed_progress_bar(gpu_freq_graph_data, freq_ghz)
display_themed_radial_bar(
theme_data=gpu_freq_radial_data,
value=f'{freq_ghz:.2f}',
unit=" GHz",
min_size=4
)
display_themed_line_graph(gpu_freq_line_graph_data, cls.last_values_gpu_frequency)

@staticmethod
def is_available():
return sensors.Gpu.is_available()
Expand Down
1 change: 1 addition & 0 deletions res/themes/Cyberdeck/theme.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ STATS:
FPS:
TEXT:
SHOW: True
SHOW_UNIT: False
X: 46
Y: 279
MIN_SIZE: 4
Expand Down
Loading