Skip to content

Commit 2a25630

Browse files
authored
Merge pull request #480 from mathoudebine/feature/103-gpu-core-clock-metric
2 parents c6101cd + 50f86e6 commit 2a25630

File tree

9 files changed

+251
-47
lines changed

9 files changed

+251
-47
lines changed

library/sensors/sensors.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ def fps() -> int:
6666
def fan_percent() -> float:
6767
pass
6868

69+
@staticmethod
70+
@abstractmethod
71+
def frequency() -> float:
72+
pass
73+
6974
@staticmethod
7075
@abstractmethod
7176
def is_available() -> bool:

library/sensors/sensors_librehardwaremonitor.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -323,13 +323,34 @@ def fan_percent(cls) -> float:
323323
try:
324324
for sensor in gpu_to_use.Sensors:
325325
if sensor.SensorType == Hardware.SensorType.Control:
326-
return float(sensor.Value)
326+
if sensor.Value:
327+
return float(sensor.Value)
327328
except:
328329
pass
329330

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

334+
@classmethod
335+
def frequency(cls) -> float:
336+
gpu_to_use = cls.get_gpu_to_use()
337+
if gpu_to_use is None:
338+
# GPU not supported
339+
return math.nan
340+
341+
try:
342+
for sensor in gpu_to_use.Sensors:
343+
if sensor.SensorType == Hardware.SensorType.Clock:
344+
# Keep only real core clocks, ignore effective core clocks
345+
if "Core" in str(sensor.Name) and "Effective" not in str(sensor.Name):
346+
if sensor.Value:
347+
return float(sensor.Value)
348+
except:
349+
pass
350+
351+
# No Frequency sensor for this GPU model
352+
return math.nan
353+
333354
@classmethod
334355
def is_available(cls) -> bool:
335356
cls.gpu_name = get_gpu_name()

library/sensors/sensors_python.py

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,16 @@ def fan_percent() -> float:
176176
else:
177177
return math.nan
178178

179+
@staticmethod
180+
def frequency() -> float:
181+
global DETECTED_GPU
182+
if DETECTED_GPU == GpuType.AMD:
183+
return GpuAmd.frequency()
184+
elif DETECTED_GPU == GpuType.NVIDIA:
185+
return GpuNvidia.frequency()
186+
else:
187+
return math.nan
188+
179189
@staticmethod
180190
def is_available() -> bool:
181191
global DETECTED_GPU
@@ -247,6 +257,11 @@ def fan_percent() -> float:
247257

248258
return math.nan
249259

260+
@staticmethod
261+
def frequency() -> float:
262+
# Not supported by Python libraries
263+
return math.nan
264+
250265
@staticmethod
251266
def is_available() -> bool:
252267
try:
@@ -260,52 +275,43 @@ class GpuAmd(sensors.Gpu):
260275
def stats() -> Tuple[float, float, float, float]: # load (%) / used mem (%) / used mem (Mb) / temp (°C)
261276
if pyamdgpuinfo:
262277
# Unlike other sensors, AMD GPU with pyamdgpuinfo pulls in all the stats at once
263-
i = 0
264-
amd_gpus = []
265-
while i < pyamdgpuinfo.detect_gpus():
266-
amd_gpus.append(pyamdgpuinfo.get_gpu(i))
267-
i = i + 1
278+
pyamdgpuinfo.detect_gpus()
279+
amd_gpu = pyamdgpuinfo.get_gpu(0)
268280

269281
try:
270-
memory_used_all = [item.query_vram_usage() for item in amd_gpus]
271-
memory_used_bytes = sum(memory_used_all) / len(memory_used_all)
282+
memory_used_bytes = amd_gpu.query_vram_usage()
272283
memory_used = memory_used_bytes / 1000000
273284
except:
274285
memory_used_bytes = math.nan
275286
memory_used = math.nan
276287

277288
try:
278-
memory_total_all = [item.memory_info["vram_size"] for item in amd_gpus]
279-
memory_total_bytes = sum(memory_total_all) / len(memory_total_all)
289+
memory_total_bytes = amd_gpu.memory_info["vram_size"]
280290
memory_percentage = (memory_used_bytes / memory_total_bytes) * 100
281291
except:
282292
memory_percentage = math.nan
283293

284294
try:
285-
load_all = [item.query_load() for item in amd_gpus]
286-
load = (sum(load_all) / len(load_all)) * 100
295+
load = amd_gpu.query_load()
287296
except:
288297
load = math.nan
289298

290299
try:
291-
temperature_all = [item.query_temperature() for item in amd_gpus]
292-
temperature = sum(temperature_all) / len(temperature_all)
300+
temperature = amd_gpu.query_temperature()
293301
except:
294302
temperature = math.nan
295303

296304
return load, memory_percentage, memory_used, temperature
297305
elif pyadl:
298-
amd_gpus = pyadl.ADLManager.getInstance().getDevices()
306+
amd_gpu = pyadl.ADLManager.getInstance().getDevices()[0]
299307

300308
try:
301-
load_all = [item.getCurrentUsage() for item in amd_gpus]
302-
load = (sum(load_all) / len(load_all))
309+
load = amd_gpu.getCurrentUsage()
303310
except:
304311
load = math.nan
305312

306313
try:
307-
temperature_all = [item.getCurrentTemperature() for item in amd_gpus]
308-
temperature = sum(temperature_all) / len(temperature_all)
314+
temperature = amd_gpu.getCurrentTemperature()
309315
except:
310316
temperature = math.nan
311317

@@ -320,17 +326,33 @@ def fps() -> int:
320326
@staticmethod
321327
def fan_percent() -> float:
322328
try:
329+
# Try with psutil fans
323330
fans = sensors_fans_percent()
324331
if fans:
325332
for name, entries in fans.items():
326333
for entry in entries:
327334
if "gpu" in (entry.label or name):
328335
return entry.current
336+
337+
# Try with pyadl if psutil did not find GPU fan
338+
if pyadl:
339+
return pyadl.ADLManager.getInstance().getDevices()[0].getCurrentFanSpeed(
340+
pyadl.ADL_DEVICE_FAN_SPEED_TYPE_PERCENTAGE)
329341
except:
330342
pass
331343

332344
return math.nan
333345

346+
@staticmethod
347+
def frequency() -> float:
348+
if pyamdgpuinfo:
349+
pyamdgpuinfo.detect_gpus()
350+
return pyamdgpuinfo.get_gpu(0).query_sclk() / 1000000
351+
elif pyadl:
352+
return pyadl.ADLManager.getInstance().getDevices()[0].getCurrentEngineClock()
353+
else:
354+
return math.nan
355+
334356
@staticmethod
335357
def is_available() -> bool:
336358
try:

library/sensors/sensors_stub_random.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ def fps() -> int:
6060
def fan_percent() -> float:
6161
return random.uniform(0, 100)
6262

63+
@staticmethod
64+
def frequency() -> float:
65+
return random.uniform(800, 3400)
66+
6367
@staticmethod
6468
def is_available() -> bool:
6569
return True

library/sensors/sensors_stub_static.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
GPU_MEM_TOTAL_SIZE_GB = 32
3636
NETWORK_SPEED_BYTES = 1061000000
3737
GPU_FPS = 120
38+
GPU_FREQ_MHZ = 1500.0
3839

3940

4041
class Cpu(sensors.Cpu):
@@ -73,6 +74,10 @@ def fps() -> int:
7374
def fan_percent() -> float:
7475
return PERCENTAGE_SENSOR_VALUE
7576

77+
@staticmethod
78+
def frequency() -> float:
79+
return GPU_FREQ_MHZ
80+
7681
@staticmethod
7782
def is_available() -> bool:
7883
return True

library/stats.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ class CPU:
240240
last_values_cpu_percentage = []
241241
last_values_cpu_temperature = []
242242
last_values_cpu_fan_speed = []
243+
last_values_cpu_frequency = []
243244

244245
@classmethod
245246
def percentage(cls):
@@ -258,12 +259,26 @@ def percentage(cls):
258259

259260
@classmethod
260261
def frequency(cls):
262+
freq_ghz = sensors.Cpu.frequency() / 1000
263+
theme_data = config.THEME_DATA['STATS']['CPU']['FREQUENCY']
264+
265+
save_last_value(freq_ghz, cls.last_values_cpu_frequency,
266+
theme_data['LINE_GRAPH'].get("HISTORY_SIZE", DEFAULT_HISTORY_SIZE))
267+
261268
display_themed_value(
262-
theme_data=config.THEME_DATA['STATS']['CPU']['FREQUENCY']['TEXT'],
263-
value=f'{sensors.Cpu.frequency() / 1000:.2f}',
269+
theme_data=theme_data['TEXT'],
270+
value=f'{freq_ghz:.2f}',
271+
unit=" GHz",
272+
min_size=4
273+
)
274+
display_themed_progress_bar(theme_data['GRAPH'], freq_ghz)
275+
display_themed_radial_bar(
276+
theme_data=theme_data['RADIAL'],
277+
value=f'{freq_ghz:.2f}',
264278
unit=" GHz",
265279
min_size=4
266280
)
281+
display_themed_line_graph(theme_data['LINE_GRAPH'], cls.last_values_cpu_frequency)
267282

268283
@classmethod
269284
def load(cls):
@@ -336,12 +351,14 @@ class Gpu:
336351
last_values_gpu_temperature = []
337352
last_values_gpu_fps = []
338353
last_values_gpu_fan_speed = []
354+
last_values_gpu_frequency = []
339355

340356
@classmethod
341357
def stats(cls):
342358
load, memory_percentage, memory_used_mb, temperature = sensors.Gpu.stats()
343359
fps = sensors.Gpu.fps()
344360
fan_percent = sensors.Gpu.fan_percent()
361+
freq_ghz = sensors.Gpu.frequency() / 1000
345362

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

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

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

534+
# GPU Frequency (Ghz)
535+
gpu_freq_text_data = theme_gpu_data['FREQUENCY']['TEXT']
536+
gpu_freq_radial_data = theme_gpu_data['FREQUENCY']['RADIAL']
537+
gpu_freq_graph_data = theme_gpu_data['FREQUENCY']['GRAPH']
538+
gpu_freq_line_graph_data = theme_gpu_data['FREQUENCY']['LINE_GRAPH']
539+
display_themed_value(
540+
theme_data=gpu_freq_text_data,
541+
value=f'{freq_ghz:.2f}',
542+
unit=" GHz",
543+
min_size=4
544+
)
545+
display_themed_progress_bar(gpu_freq_graph_data, freq_ghz)
546+
display_themed_radial_bar(
547+
theme_data=gpu_freq_radial_data,
548+
value=f'{freq_ghz:.2f}',
549+
unit=" GHz",
550+
min_size=4
551+
)
552+
display_themed_line_graph(gpu_freq_line_graph_data, cls.last_values_gpu_frequency)
553+
515554
@staticmethod
516555
def is_available():
517556
return sensors.Gpu.is_available()

res/themes/Cyberdeck/theme.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ STATS:
185185
FPS:
186186
TEXT:
187187
SHOW: True
188+
SHOW_UNIT: False
188189
X: 46
189190
Y: 279
190191
MIN_SIZE: 4

0 commit comments

Comments
 (0)