forked from ShadowBlip/OpenGamepadUI
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(PerformanceManager): split into multiple classes
- Loading branch information
1 parent
d2f36ca
commit 56816a6
Showing
13 changed files
with
1,045 additions
and
538 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,230 @@ | ||
extends Resource | ||
class_name CPU | ||
|
||
## Read and manage the system CPU | ||
|
||
## Emitted when CPU boost is updated | ||
signal boost_updated(enabled: bool) | ||
## Emitted when SMT is updated | ||
signal smt_updated(enabled: bool) | ||
|
||
const POWERTOOLS_PATH := "/usr/share/opengamepadui/scripts/powertools" | ||
const BOOST_PATH := "/sys/devices/system/cpu/cpufreq/boost" | ||
const SMT_PATH := "/sys/devices/system/cpu/smt/control" | ||
const CPUS_PATH := "/sys/bus/cpu/devices" | ||
|
||
var mutex := Mutex.new() | ||
var core_count := get_total_core_count() | ||
var boost_capable: bool = false | ||
var boost_enabled := false: | ||
get: | ||
mutex.lock() | ||
var prop := boost_enabled | ||
mutex.unlock() | ||
return prop | ||
set(v): | ||
if boost_enabled == v: | ||
return | ||
mutex.lock() | ||
boost_enabled = v | ||
mutex.unlock() | ||
emit_signal.call_deferred("changed") | ||
emit_signal.call_deferred("boost_updated", v) | ||
var vendor: String | ||
var model: String | ||
var smt_capable: bool = false | ||
var smt_enabled := false: | ||
get: | ||
mutex.lock() | ||
var prop := smt_enabled | ||
mutex.unlock() | ||
return prop | ||
set(v): | ||
if smt_enabled == v: | ||
return | ||
mutex.lock() | ||
smt_enabled = v | ||
mutex.unlock() | ||
emit_signal.call_deferred("changed") | ||
emit_signal.call_deferred("smt_updated", v) | ||
var logger := Log.get_logger("CPU") | ||
|
||
|
||
func _init() -> void: | ||
var cpu_raw := _get_lscpu_info() | ||
for param in cpu_raw: | ||
var parts := param.split(" ", false) as Array | ||
if parts.is_empty(): | ||
continue | ||
if parts[0] == "Flags:": | ||
if "ht" in parts: | ||
smt_capable = true | ||
if "cpb" in parts and FileAccess.file_exists(BOOST_PATH): | ||
boost_capable = true | ||
if parts[0] == "Vendor" and parts[1] == "ID:": | ||
# Delete parts of the string we don't want | ||
parts.remove_at(1) | ||
parts.remove_at(0) | ||
vendor = str(" ".join(parts)) | ||
if parts[0] == "Model" and parts[1] == "name:": | ||
# Delete parts of the string we don't want | ||
parts.remove_at(1) | ||
parts.remove_at(0) | ||
model = str(" ".join(parts)) | ||
# TODO: We can get min/max CPU freq here. | ||
update() | ||
|
||
|
||
## Returns the count of number of enabled CPU cores | ||
func get_enabled_core_count() -> int: | ||
return OS.get_processor_count() | ||
|
||
|
||
## Returns true if boost is currently enabled | ||
func get_boost_enabled() -> bool: | ||
if not boost_capable: | ||
return false | ||
var value := _get_property(BOOST_PATH).to_lower().strip_edges() | ||
|
||
return value == "on" or value == "1" | ||
|
||
|
||
## Set CPU boost to the given value | ||
func set_boost_enabled(enabled: bool) -> int: | ||
if not boost_capable: | ||
return -1 | ||
var enabled_str := "1" if enabled else "0" | ||
var args := ["cpuBoost", enabled_str] | ||
var cmd := CommandSync.new(POWERTOOLS_PATH, args) | ||
if cmd.execute() != OK: | ||
logger.warn("Failed to set CPU boost") | ||
return cmd.code | ||
update() | ||
|
||
return cmd.code | ||
|
||
|
||
## Returns true if SMT is currently enabled | ||
func get_smt_enabled() -> bool: | ||
if not smt_capable: | ||
return false | ||
var value := _get_property(SMT_PATH).to_lower().strip_edges() | ||
|
||
return value == "on" or value == "1" | ||
|
||
|
||
## Set CPU smt to the given value | ||
func set_smt_enabled(enabled: bool) -> int: | ||
if not smt_capable: | ||
return -1 | ||
var enabled_str := "1" if enabled else "0" | ||
var args := ["smtToggle", enabled_str] | ||
var cmd := CommandSync.new(POWERTOOLS_PATH, args) | ||
if cmd.execute() != OK: | ||
logger.warn("Failed to set CPU smt") | ||
return cmd.code | ||
update() | ||
|
||
return cmd.code | ||
|
||
|
||
## Returns an instance of the given CPU core | ||
func get_core(num: int) -> CPUCore: | ||
# Try to load the core info if it already exists | ||
var res_path := "hardware://cpu/" + str(num) | ||
if ResourceLoader.exists(res_path): | ||
var core := load(res_path) as CPUCore | ||
core.update() | ||
return core | ||
|
||
# Create a new core instance and take over the caching path | ||
var core := CPUCore.new(num) | ||
core.take_over_path(res_path) | ||
core.update() | ||
|
||
return core | ||
|
||
|
||
## Returns an array of all CPU cores | ||
func get_cores() -> Array[CPUCore]: | ||
var cores: Array[CPUCore] = [] | ||
for core_num in range(0, core_count): | ||
var core := get_core(core_num) | ||
if core: | ||
cores.append(core) | ||
|
||
return cores | ||
|
||
|
||
## Returns the total number of detected CPU cores | ||
func get_total_core_count() -> int: | ||
var core_dirs := DirAccess.get_directories_at(CPUS_PATH) | ||
if core_dirs.size() == 0: | ||
logger.warn("Unable to determine total CPU count") | ||
return 1 | ||
return core_dirs.size() | ||
|
||
|
||
## Returns the total number of CPU cores that are online | ||
func get_online_core_count() -> int: | ||
var count := 0 | ||
for core in get_cores(): | ||
if not core.online: | ||
continue | ||
count += 1 | ||
return count | ||
|
||
|
||
## Called to set the number of enabled CPU's | ||
func set_cpu_core_count(value: int) -> void: | ||
# Update the state of the CPU | ||
update() | ||
var cores := get_cores() | ||
logger.debug("Enable cpu cores: " + str(value) + "/" + str(cores.size())) | ||
|
||
for core in cores: | ||
if core.num == 0: | ||
continue | ||
var online := true | ||
if smt_enabled and core.num >= value: | ||
online = false | ||
elif not smt_enabled: | ||
if core.num % 2 != 0 or core.num >= (value * 2) - 1: | ||
online = false | ||
logger.debug("Set CPU No: " + str(core.num) + " online: " + str(online)) | ||
core.set_online(online) | ||
|
||
|
||
## Fetches the current CPU info | ||
func update() -> void: | ||
boost_enabled = get_boost_enabled() | ||
smt_enabled = get_smt_enabled() | ||
|
||
|
||
func _get_property(prop_path: String) -> String: | ||
if not FileAccess.file_exists(prop_path): | ||
return "" | ||
var file := FileAccess.open(prop_path, FileAccess.READ) | ||
var length := file.get_length() | ||
var bytes := file.get_buffer(length) | ||
|
||
return bytes.get_string_from_utf8() | ||
|
||
|
||
## Provides info on the GPU vendor, model, and capabilities. | ||
func _get_lscpu_info() -> PackedStringArray: | ||
var cmd := CommandSync.new("lscpu") | ||
if cmd.execute() != OK: | ||
return [] | ||
return cmd.stdout.split("\n") | ||
|
||
|
||
func _to_string() -> String: | ||
return "<CPU:" \ | ||
+ " Vendor: (" + str(vendor) \ | ||
+ ") Model: (" + str(model) \ | ||
+ ") Core count: (" + str(core_count) \ | ||
+ ") Boost Capable: (" + str(boost_capable) \ | ||
+ ") SMT Capable: (" + str(smt_capable) \ | ||
+ ")>" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
extends Resource | ||
class_name CPUCore | ||
|
||
## Instance of a single CPU core | ||
|
||
var path: String | ||
var num: int | ||
var online: bool: | ||
get: | ||
var prop := online | ||
return prop | ||
set(v): | ||
if online == v: | ||
return | ||
online = v | ||
changed.emit() | ||
|
||
func _init(n: int) -> void: | ||
num = n | ||
path = "/".join([CPU.CPUS_PATH, "cpu" + str(n)]) | ||
|
||
|
||
## Update the state of the CPU core | ||
func update() -> void: | ||
online = get_online() | ||
|
||
|
||
## Returns whether or not the core is online | ||
func get_online() -> bool: | ||
if num == 0: | ||
return true | ||
var online_str := _get_property("online").strip_escapes() | ||
return online_str == "1" | ||
|
||
|
||
## Sets the online state of the CPU core | ||
func set_online(enabled: bool) -> int: | ||
var logger := Log.get_logger("CPUInfo") | ||
if num == 0: | ||
logger.warn("Unable to disable CPU 0") | ||
return -1 | ||
var enabled_str := "1" if enabled else "0" | ||
var cmd := CommandSync.new(CPU.POWERTOOLS_PATH, ["cpuToggle", str(num), enabled_str]) | ||
if cmd.execute() != OK: | ||
logger.warn("Failed to update CPU core: " + cmd.stdout) | ||
update() | ||
logger.info("Set core " + str(num) + " enabled: " + str(enabled)) | ||
|
||
return cmd.code | ||
|
||
|
||
func _get_property(prop: String) -> String: | ||
var prop_path := "/".join([path, prop]) | ||
if not FileAccess.file_exists(prop_path): | ||
return "" | ||
var file := FileAccess.open(prop_path, FileAccess.READ) | ||
var length := file.get_length() | ||
var bytes := file.get_buffer(length) | ||
|
||
return bytes.get_string_from_utf8() | ||
|
||
|
||
func _to_string() -> String: | ||
return "<Core" + str(num) + " Online: " + str(online) + ">" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
extends GutTest | ||
|
||
var hardware_manager := load("res://core/systems/hardware/hardware_manager.tres") as HardwareManager | ||
var cpu := hardware_manager.get_cpu() | ||
|
||
func before_all() -> void: | ||
gut.p(cpu) | ||
|
||
|
||
func test_get_cpu_cores() -> void: | ||
for core in cpu.get_cores(): | ||
gut.p(core) | ||
|
||
pass_test("Skipping") | ||
|
||
|
||
func test_cpu_boost() -> void: | ||
cpu.set_boost_enabled(false) | ||
gut.p("Boost: " + str(cpu.get_boost_enabled())) | ||
cpu.set_boost_enabled(true) | ||
gut.p("Boost: " + str(cpu.get_boost_enabled())) | ||
|
||
pass_test("Skipping") | ||
|
||
|
||
func test_cpu_core_enable() -> void: | ||
var core := cpu.get_core(10) | ||
core.changed.connect(_on_core_changed) | ||
if core.set_online(false) != OK: | ||
gut.p("Failed to turn off core") | ||
if core.set_online(true) != OK: | ||
gut.p("Failed to turn on core") | ||
|
||
pass_test("Skipping") | ||
|
||
|
||
func test_set_cpu_core_count() -> void: | ||
cpu.set_cpu_core_count(2) | ||
cpu.set_cpu_core_count(cpu.get_total_core_count()) | ||
|
||
pass_test("Skipping") | ||
|
||
|
||
func _on_core_changed() -> void: | ||
gut.p("Core state changed!") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.