diff --git a/addons/dbus/bin/libdbus.linux.template_debug.x86_64.so b/addons/dbus/bin/libdbus.linux.template_debug.x86_64.so index 4b4b3952..3007a58b 100755 Binary files a/addons/dbus/bin/libdbus.linux.template_debug.x86_64.so and b/addons/dbus/bin/libdbus.linux.template_debug.x86_64.so differ diff --git a/addons/dbus/bin/libdbus.linux.template_release.x86_64.so b/addons/dbus/bin/libdbus.linux.template_release.x86_64.so index fce85202..9da5568d 100755 Binary files a/addons/dbus/bin/libdbus.linux.template_release.x86_64.so and b/addons/dbus/bin/libdbus.linux.template_release.x86_64.so differ diff --git a/core/systems/performance/performance_amd_gpu.gd b/core/systems/performance/performance_amd_gpu.gd deleted file mode 100644 index 83848a5a..00000000 --- a/core/systems/performance/performance_amd_gpu.gd +++ /dev/null @@ -1,215 +0,0 @@ -extends PerformanceGPU -class_name PerformanceAMDGPU - -const FALLBACK_GPU_TEMP: float = 80 - -var hardware_manager := load("res://core/systems/hardware/hardware_manager.tres") as HardwareManager -var ryzenadj := RyzenAdj.new() -var logger := Log.get_logger("PerformanceAMDGPU") - - -## Retrieves the current TDP from ryzenadj for AMD APU's. -func get_tdp() -> Info: - var gpu := hardware_manager.gpu - - # Fetch the current info from ryzenadj - var info := await ryzenadj.get_info() - if not info: - logger.info("Unable to verify current tdp. Setting TDP to midpoint of range") - return null - - # Handle cases where ryzenadj failed to read particular values - var tdp_info := Info.new() - var current_fastppt := 0.0 - if info.ppt_limit_fast > 0: - current_fastppt = info.ppt_limit_fast - else: - logger.warn("RyzenAdj unable to read current PPT LIMIT FAST value. Setting to sane default.") - current_fastppt = float((gpu.tdp_max - gpu.tdp_min) / 2 + gpu.tdp_min) - - if info.stapm_limit > 0: - tdp_info.tdp_current = info.stapm_limit - else: - logger.warn("RyzenAdj unable to read current STAPM value. Setting to sane default.") - tdp_info.tdp_current = float((gpu.tdp_max - gpu.tdp_min) / 2 + gpu.tdp_min) - - if info.thm_limit_core > 0: - tdp_info.gpu_temp_current = info.thm_limit_core - else: - logger.warn("RyzenAdj unable to read current THM value. Setting to sane default.") - tdp_info.gpu_temp_current = FALLBACK_GPU_TEMP - - # Set the TDP boost - tdp_info.tdp_boost_current = current_fastppt - tdp_info.tdp_current - - return tdp_info - - -## Returns a string of the performance level. E.g. "manual", "auto" -func get_perf_level() -> String: - var card := hardware_manager.get_active_gpu_card() - if not card: - logger.warn("Unable to find active GPU to get perf level") - return "" - - return _read_sys("/sys/class/drm/" + card.name + "/device/power_dpm_force_performance_level") - - -## Called to set the maximum gpu clock -func set_gpu_freq_max(value: float) -> void: - if value == 0: - logger.warn("Cowardly refusing to set maximum clock rate to 0") - return - - # Get the active GPU - var card := hardware_manager.get_active_gpu_card() - if not card: - logger.warn("Unable to detect active GPU card to set frequency") - return - - # Get the current minimum clock rate - var values := card.get_clock_values() - if values == Vector2.ZERO: - logger.warn("Unable to read clock values to set frequency") - return - - if await set_gpu_freq(values.x, value) != OK: - logger.warn("Error setting GPU max frequency") - - -## Called to set the minimum gpu clock -func set_gpu_freq_min(value: float) -> void: - if value == 0: - logger.warn("Cowardly refusing to set minimum clock rate to 0") - return - - # Get the active GPU - var card := hardware_manager.get_active_gpu_card() - if not card: - logger.warn("Unable to detect active GPU card to set frequency") - return - - # Get the current maximum clock rate - var values := card.get_clock_values() - if values == Vector2.ZERO: - logger.warn("Unable to read clock values to set frequency") - return - - if await set_gpu_freq(value, values.y) != OK: - logger.warn("Error setting GPU min frequency") - - -# Sets the GPU frequency range to the given values -func set_gpu_freq(freq_min: int, freq_max: int) -> int: - # Get the active GPU - var card := hardware_manager.get_active_gpu_card() - if not card: - logger.warn("Unable to detect active GPU card to set frequency") - return -1 - - var args := ["amdGpuClock", str(freq_min), str(freq_max), card.name] - var cmd := Command.new(POWERTOOLS_PATH, args) - return await cmd.execute() - - -## Called to toggle auto/manual gpu clocking -func set_gpu_manual_enabled(enabled: bool) -> void: - logger.debug("Setting manual GPU clocking enabled: " + str(enabled)) - var card := hardware_manager.get_active_gpu_card() - if not card: - logger.warn("Unable to find active GPU to set manual mode") - return - - var mode := "manual" if enabled else "auto" - var args := ["pdfpl", mode, card.name] - var cmd := Command.new(POWERTOOLS_PATH, args) - if await cmd.execute() != OK: - logger.warn("Failed to set GPU frequency mode") - - -## Called to set the GPU Power Profile -func set_gpu_power_profile(mode: POWER_PROFILE) -> void: - logger.debug("Setting GPU power profile to: " + str(mode)) - var power_profile := ryzenadj.POWER_PROFILE.MAX_PERFORMANCE - if mode == POWER_PROFILE.POWER_SAVINGS: - power_profile = ryzenadj.POWER_PROFILE.POWER_SAVINGS - - if await ryzenadj.set_power_profile(power_profile) != OK: - logger.warn("Failed to set GPU power profile") - - -## Called to set the GPU Thermal Throttle Limit -func set_gpu_temp_current(value: float) -> void: - logger.debug("Setting GPU temperature limit to: " + str(value) + "C") - if await ryzenadj.set_tctl_temp(value) != OK: - logger.warn("Failed to set GPU temperature limit") - - -## Set long/short PPT on AMD APU's -func set_tdp_boost_value(value: float) -> void: - logger.debug("Setting TDP boost value to: " + str(value)) - var info := await get_tdp() - if await _set_ppt_limits(info.tdp_current, value) != OK: - logger.warn("Failed to set TDP boost values") - - -## Called to set the TDP average limit (STAPM on AMD APU's) -func set_tdp_value(value: float) -> void: - logger.debug("Setting STAPM limit to: " + str(value)) - await ryzenadj.set_stapm_limit(value * 1000) - - # Update the boost values as well - var info := await get_tdp() - if await _set_ppt_limits(value, info.tdp_boost_current) != OK: - logger.warn("Failed to set TDP values") - - -## Sets the thermal throttle mode for ASUS devices. -func set_thermal_profile(policy: THERMAL_THROTTLE_POLICY) -> void: - var platform := load("res://core/global/platform.tres") as Platform - if not platform.platform is HandheldPlatform: - logger.warn("Attempt to apply thermal profile on platform that is not HandheldPlatform.") - return - - var platform_provider := platform.platform as HandheldPlatform - if not FileAccess.file_exists(platform_provider.thermal_policy_path): - logger.warn("Thermal policy path does not exist.") - return - - var policy_str := "0" - match policy: - THERMAL_THROTTLE_POLICY.BALANCED: - logger.debug("Setting thermal throttle policy to Balanced") - policy_str = "0" - THERMAL_THROTTLE_POLICY.PERFORMANCE: - logger.debug("Setting thermal throttle policy to Performance") - policy_str = "1" - THERMAL_THROTTLE_POLICY.SILENT: - logger.debug("Setting thermal throttle policy to Silent") - policy_str = "2" - - var cmd := Command.new(POWERTOOLS_PATH, ["setThermalPolicy", policy_str]) - if await cmd.execute() != OK: - logger.warn("Failed to set thermal profile") - - -func enable_performance_write() -> void: - var card := hardware_manager.get_active_gpu_card() - if not card: - logger.warn("Unable to find active GPU to enable performance write on") - return - - var cmd := Command.new(POWERTOOLS_PATH, ["pdfpl", "write", card.name]) - if await cmd.execute() != OK: - logger.warn("Failed to set GPU performance write mode") - - -# Set the slow and fast ppt limits to allow "boost" -func _set_ppt_limits(tdp_current: float, boost_value: float) -> int: - var slowPPT: float = (floor(boost_value/2) + tdp_current) * 1000 - var fastPPT: float = (boost_value + tdp_current) * 1000 - var code: int - code += await ryzenadj.set_fast_limit(fastPPT) - code += await ryzenadj.set_slow_limit(slowPPT) - - return code diff --git a/core/systems/performance/performance_gpu.gd b/core/systems/performance/performance_gpu.gd deleted file mode 100644 index fc82db42..00000000 --- a/core/systems/performance/performance_gpu.gd +++ /dev/null @@ -1,115 +0,0 @@ -extends RefCounted -class_name PerformanceGPU - -## GPU performance implementation class -## -## This should be overridden by a child class with implementations for controlling -## GPU performance. - -const POWERTOOLS_PATH := "/usr/share/opengamepadui/scripts/powertools" - -enum POWER_PROFILE { - MAX_PERFORMANCE, - POWER_SAVINGS, -} - -enum THERMAL_THROTTLE_POLICY { - SILENT, - BALANCED, - PERFORMANCE, -} - - -## Returns the current TDP values -func get_tdp() -> Info: - return null - - -## Gets the clock limits of the gpu -func get_gpu_clock_limits() -> void: - pass - - -## Gets the current clock of the gpu -func get_gpu_clock_current() -> void: - pass - - -## Sets the GPU frequency range to the given values -func set_gpu_freq(freq_min: int, freq_max: int) -> int: - return -1 - - -## Called when gpu_freq_max_slider.value is changed. -func set_gpu_freq_max(value: float) -> void: - pass - - -## Called to set the minimum gpu clock is changed. -func set_gpu_freq_min(value: float) -> void: - pass - - -## Called to toggle auto/manual gpu clocking -func set_gpu_manual_enabled(state: bool) -> void: - pass - - -## Called to set the GPU Power Profile -func set_gpu_power_profile(mode: POWER_PROFILE) -> void: - pass - - -## Called to set the GPU Thermal Throttle Limit -func set_gpu_temp_current(value: float) -> void: - pass - - -## Called to set the TFP boost limit. -func set_tdp_boost_value(value: float) -> void: - pass - - -## Called to set the TDP average limit. -func set_tdp_value(value: float) -> void: - pass - - -## Sets the thermal throttle mode for ASUS devices. -func set_thermal_profile(index: THERMAL_THROTTLE_POLICY) -> void: - pass - - -func enable_performance_write() -> void: - pass - - -## Used to read values from sysfs -func _read_sys(path: String) -> String: - var file := FileAccess.open(path, FileAccess.READ) - var length := file.get_length() - var bytes := file.get_buffer(length) - return bytes.get_string_from_utf8().strip_escapes() - - -## Structure for holding GPU performance info -class Info: - var gpu_freq_max_current: float - var gpu_freq_min_current: float - var gpu_manual_enabled: bool - var gpu_power_profile: int - var gpu_temp_current: float - var tdp_boost_current: float - var tdp_current: float - var thermal_profile: int - - func _to_string() -> String: - return "" diff --git a/core/systems/performance/performance_intel_gpu.gd b/core/systems/performance/performance_intel_gpu.gd deleted file mode 100644 index 44294d7e..00000000 --- a/core/systems/performance/performance_intel_gpu.gd +++ /dev/null @@ -1,114 +0,0 @@ -extends PerformanceGPU -class_name PerformanceIntelGPU - -var hardware_manager := load("res://core/systems/hardware/hardware_manager.tres") as HardwareManager -var logger := Log.get_logger("PerformanceIntelGPU") - - -## Retrieves the current TDP from sysfs for Intel iGPU's. -func get_tdp() -> Info: - var tdp_info := Info.new() - var long_tdp := float(_read_sys("/sys/class/powercap/intel-rapl/intel-rapl:0/constraint_0_power_limit_uw")) - if not long_tdp: - logger.warn("Unable to determine long TDP.") - return null - - tdp_info.tdp_current = long_tdp / 1000000 - var peak_tdp := float(_read_sys("/sys/class/powercap/intel-rapl/intel-rapl:0/constraint_2_power_limit_uw")) - if not peak_tdp: - logger.warn("Unable to determine long TDP.") - return null - tdp_info.tdp_boost_current = peak_tdp / 1000000 - tdp_info.tdp_current - - return tdp_info - - -## Called to set the maximum gpu clock -func set_gpu_freq_max(value: float) -> void: - if value == 0: - logger.warn("Cowardly refusing to set maximum clock rate to 0") - return - - # Get the active GPU - var card := hardware_manager.get_active_gpu_card() - if not card: - logger.warn("Unable to detect active GPU card to set frequency") - return - - # Get the current minimum clock rate - var values := card.get_clock_values() - if values == Vector2.ZERO: - logger.warn("Unable to read clock values to set frequency") - return - - if await set_gpu_freq(values.x, value) != OK: - logger.warn("Error setting GPU max frequency") - - -## Called to set the minimum gpu clock -func set_gpu_freq_min(value: float) -> void: - if value == 0: - logger.warn("Cowardly refusing to set minimum clock rate to 0") - return - - # Get the active GPU - var card := hardware_manager.get_active_gpu_card() - if not card: - logger.warn("Unable to detect active GPU card to set frequency") - return - - # Get the current maximum clock rate - var values := card.get_clock_values() - if values == Vector2.ZERO: - logger.warn("Unable to read clock values to set frequency") - return - - if await set_gpu_freq(value, values.y) != OK: - logger.warn("Error setting GPU min frequency") - - -# Sets the GPU frequency range to the given values -## Sets the GPU frequency range to the given values -func set_gpu_freq(freq_min: int, freq_max: int) -> int: - # Get the active GPU - var card := hardware_manager.get_active_gpu_card() - if not card: - logger.warn("Unable to detect active GPU card to set frequency") - return -1 - - var args := ["intelGpuClock", str(freq_min), str(freq_max), card.name] - var cmd := Command.new(POWERTOOLS_PATH, args) - return await cmd.execute() - - -func set_tdp_boost_value(value: float) -> void: - logger.debug("Setting TDP boost value to: " + str(value)) - var tdp_info := get_tdp() - if not tdp_info: - logger.warn("Unable to get current TDP to set boost value") - return - - var shortTDP: float = (floor(tdp_info.tdp_boost_current/2) + tdp_info.tdp_current) * 1000000 - var peakTDP: float = (tdp_info.tdp_boost_current + tdp_info.tdp_current) * 1000000 - - var cmd1 := Command.new(POWERTOOLS_PATH, ["setRapl", "constraint_1_power_limit_uw", str(shortTDP)]) - if await cmd1.execute() != OK: - logger.warn("Unable to set constraint 1 power limit: " + cmd1.stdout) - return - - var cmd2 := Command.new(POWERTOOLS_PATH, ["setRapl", "constraint_2_power_limit_uw", str(peakTDP)]) - if await cmd2.execute() != OK: - logger.warn("Unable to set constraint 2 power limit: " + cmd2.stdout) - return - - -func set_tdp_value(value: float) -> void: - logger.debug("Setting TDP value to: " + str(value)) - var tdp_info := get_tdp() - if not tdp_info: - logger.warn("Unable to get current TDP to set value") - return - - var cmd := Command.new(POWERTOOLS_PATH, ["setRapl", "constraint_0_power_limit_uw", str(tdp_info.tdp_current * 1000000)]) - if await cmd.execute() != OK: - logger.warn("Error setting TDP value: " + cmd.stdout) diff --git a/core/systems/performance/performance_manager.gd b/core/systems/performance/performance_manager.gd index 3655e470..a81f0272 100644 --- a/core/systems/performance/performance_manager.gd +++ b/core/systems/performance/performance_manager.gd @@ -1,150 +1,83 @@ extends Resource class_name PerformanceManager -signal cpu_boost_toggled(state: bool) -signal cpu_cores_available_updated(available: int) -signal cpu_cores_used(count: int) -signal gpu_clk_current_updated(current_min: float, current_max: float) -signal gpu_clk_limits_updated(min: float, max: float) -signal gpu_manual_enabled_updated(state: bool) -signal gpu_power_profile_updated(index: int) -signal gpu_temp_limit_updated(current: float) -signal smt_toggled(state: bool) -signal tdp_updated(tdp_current: float, boost_current: float) -signal thermal_profile_updated(index: int) -signal perfomance_profile_applied(profile: PerformanceProfile) -signal pm_ready +## Manages, sets, and loads performance profiles +## +## The PerformanceManager is responsible for applying the appropriate performance +## profile when games launch and when the device is plugged in or unplugged. + +signal profile_applied(profile: PerformanceProfile) +signal profile_loaded(profile: PerformanceProfile) +signal profile_saved(profile: PerformanceProfile, path: String) const USER_PROFILES := "user://data/performance/profiles" -const POWERTOOLS_PATH := "/usr/share/opengamepadui/scripts/powertools" -const FALLBACK_GPU_TEMP: float = 80 -var _notification_manager := load("res://core/global/notification_manager.tres") as NotificationManager -var _platform := load("res://core/global/platform.tres") as Platform +const DOCKED_STATES := [PowerManager.DEVICE_STATE.CHARGING, PowerManager.DEVICE_STATE.FULLY_CHARGED, PowerManager.DEVICE_STATE.PENDING_CHARGE] + +## Performance profiles are separated into these states, so users can have different +## performance depending on whether or not they are plugged in to an external power +## source. +enum PROFILE_STATE { + DOCKED, + UNDOCKED, +} + var _hardware_manager := load("res://core/systems/hardware/hardware_manager.tres") as HardwareManager var _power_manager := load("res://core/systems/power/power_manager.tres") as PowerManager var _settings_manager := load("res://core/global/settings_manager.tres") as SettingsManager -var _shared_thread := load("res://core/systems/threading/utility_thread.tres") as SharedThread - -var cpu := _hardware_manager.get_cpu() -var batteries: Array[PowerManager.Device] -var library_item: LibraryLaunchItem -var profile: PerformanceProfile -var profile_state: String # docked or undocked -var initialized: bool = false -var logger := Log.get_logger("PerformanceManager", Log.LEVEL.INFO) - - -func _init(): - # Connect to CPU signals - var on_smt_updated := func(enabled: bool) -> void: - smt_toggled.emit(enabled) - cpu.smt_updated.connect(on_smt_updated) - - _shared_thread.start() - profile = PerformanceProfile.new() - if not _platform.loaded: - _platform.platform_loaded.connect(_setup, CONNECT_ONE_SHOT) - return - _setup() +var _power_station := load("res://core/systems/performance/power_station.tres") as PowerStation +var _launch_manager := load("res://core/global/launch_manager.tres") as LaunchManager + +var current_profile: PerformanceProfile +var current_profile_state: PROFILE_STATE # docked or undocked +var logger := Log.get_logger("PerformanceManager", Log.LEVEL.DEBUG) -func _setup(): - batteries = _power_manager.get_devices_by_type(PowerManager.DEVICE_TYPE.BATTERY) +func _init() -> void: + # Listen for signals when the current app switches so we can update the profile + # accordingly. + _launch_manager.app_switched.connect(_on_app_switched) + + # Connect to battery state changes to switch between "docked" and "undocked" + # performance profiles. + var batteries := _power_manager.get_devices_by_type(PowerManager.DEVICE_TYPE.BATTERY) if batteries.size() > 1: logger.warn("You somehow have more than one battery. We don't know what to do with that.") if batteries.size() > 0: var battery := batteries[0] - _update_profile_state(battery) - battery.updated.connect(_on_update_battery.bind(battery)) - - _shared_thread.exec(read_system_components) - - -func _on_update_battery(item: PowerManager.Device): - _update_profile_state(item) - load_profile() - - -func _update_profile_state(item: PowerManager.Device) -> void: - var to_state: String = "docked" - if item.state not in [PowerManager.DEVICE_STATE.CHARGING, PowerManager.DEVICE_STATE.FULLY_CHARGED, PowerManager.DEVICE_STATE.PENDING_CHARGE]: - to_state = "undocked" + battery.updated.connect(_on_battery_updated.bind(battery)) + + # Load and apply the default profile + var profile_state := get_profile_state() + var path := get_profile_filename(profile_state) + current_profile = load_or_create_profile(path) + apply_profile(current_profile) - if profile_state != to_state: - logger.debug("Setting system to " + to_state) - profile_state = to_state +## Returns a profile filename generated from the given profile state and library item. +## E.g. "Bravo_15_A4DDR_docked_default_profile.tres" +func get_profile_filename(profile_state: PROFILE_STATE, library_item: LibraryLaunchItem = null) -> String: + var profile_state_name := "docked" + if profile_state == PROFILE_STATE.UNDOCKED: + profile_state_name = "undocked" -## Returns the GPU performance manager -func get_gpu_performance_manager() -> PerformanceGPU: - var gpu := _hardware_manager.gpu - match gpu.vendor: - "AMD": - return PerformanceAMDGPU.new() - "Intel": - return PerformanceIntelGPU.new() - - return null - - -## Looks at system file decriptors to update components and their capabilities -## and current settings. -func read_system_components(power_profile: int = 1) -> void: - logger.debug("Update system components started") - var cpu := _hardware_manager.cpu - var gpu := _hardware_manager.gpu - logger.debug("CPU Data: " + str(cpu)) - logger.debug("GPU Data: " + str(gpu)) - - if gpu.tdp_capable: - await _read_tdp() - - if gpu.clk_capable: - await _enable_performance_write() - await _read_gpu_perf_level() - if profile.gpu_manual_enabled: - await _read_gpu_clk_limits() - await _read_gpu_clk_current() - - if _platform.platform is HandheldPlatform: - await _read_thermal_profile() - - # There's currently no known way to detect the set profile. Default to - # setting power_saving at startup for now. #TODO: Fix this? - if gpu.power_profile_capable: - profile.gpu_power_profile = power_profile - await _gpu_power_profile_change() - logger.debug("Update system components completed") - logger.debug(profile) - initialized = true - pm_ready.emit() - -### Manage PerformanceProfile funcs - -func _get_profile_name() -> String: - var prefix: String = _hardware_manager.get_product_name().replace(" ", "_") + "_" + profile_state + var prefix: String = _hardware_manager.get_product_name().replace(" ", "_") + "_" + profile_state_name var postfix: String = "_default_profile.tres" if library_item: postfix = "_" + library_item.name.sha256_text() + ".tres" - profile.name = library_item.name - return prefix+postfix + return prefix+postfix -## Saves a PerformanceProfile to the given path. -func save_profile() -> void: - var notify := Notification.new("") +## Saves the given PerformanceProfile to the given path. If a library item +## is passed, the user's settings will be updated to use the given profile. +func save_profile(profile: PerformanceProfile, profile_path: String, library_item: LibraryLaunchItem = null) -> void: # Try to save the profile if DirAccess.make_dir_recursive_absolute(USER_PROFILES) != OK: logger.debug("Unable to create performance profiles directory") - notify.text = "Unable to save performance profile" - _notification_manager.show(notify) return - var profile_path := "/".join([USER_PROFILES, _get_profile_name()]) + #var profile_path := "/".join([USER_PROFILES, _get_profile_name()]) if ResourceSaver.save(profile, profile_path) != OK: logger.error("Failed to save performance profile to: " + profile_path) - notify.text = "Failed to save performance profile" - _notification_manager.show(notify) return # Update the game settings to use this performance profile @@ -154,309 +87,192 @@ func save_profile() -> void: _settings_manager.set_value(section, "performace_profile", profile_path) logger.info("Saved performance profile to: " + profile_path) + profile_saved.emit(profile, profile_path) -## Loads a PerformanceProfile from the given path. -func load_profile(profile_path: String = "") -> void: - if not initialized: - logger.warn("Attempt to load profile before PerformanceManager was fully initialized.") - return - var notify := Notification.new("") - var loaded: PerformanceProfile - # If no path was specified, try to identify it. - if profile_path == "": - profile_path = "/".join([USER_PROFILES, _get_profile_name()]) - - # Check if given profile exists - logger.debug("Load Profile: " + profile_path) +## Create a new [PerformanceProfile] from the current performance settings. If +## a library item is passed, the profile will be named after the library item. +func create_profile(library_item: LibraryLaunchItem = null) -> PerformanceProfile: + var profile := PerformanceProfile.new() + if library_item: + profile.name = library_item.name + + # Configure the CPU settings + if _power_station.cpu: + profile.cpu_boost_enabled = _power_station.cpu.boost_enabled + profile.cpu_core_count_current = _power_station.cpu.cores_enabled + profile.cpu_smt_enabled = _power_station.cpu.smt_enabled + + # Detect all GPU cards + var cards: Array[PowerStation.GPUCard] = [] + if _power_station.gpu: + cards = _power_station.gpu.get_cards() + + # Configure GPU settings + # TODO: Support multiple GPUs? + for card in cards: + if card.class_type != "integrated": + continue + + profile.tdp_current = card.tdp + profile.tdp_boost_current = card.boost + profile.gpu_freq_min_current = card.clock_value_mhz_min + profile.gpu_freq_max_current = card.clock_value_mhz_max + profile.gpu_manual_enabled = card.manual_clock + #profile.gpu_power_profile = card.power_profile # TODO: Fix this + profile.gpu_temp_current = card.thermal_throttle_limit_c + + logger.debug("Created performance profile: " + profile.name) + return profile + + +## Loads a PerformanceProfile from the given path. Returns null if the profile +## fails to load. +func load_profile(profile_path: String) -> PerformanceProfile: + logger.debug("Loading profile: " + profile_path) if not FileAccess.file_exists(profile_path): - if library_item: - profile.name = library_item.name - notify.text = "Created new performance profile for " + profile_state + " " + profile.name - logger.debug(notify.text) - _notification_manager.show(notify) - save_profile() - _shared_thread.exec(_apply_profile) - return - - else: - loaded = load(profile_path) as PerformanceProfile - if not loaded: - notify.text = "Unable to load profile at: " + profile_path - logger.warn(notify.text) - _notification_manager.show(notify) - return - - if profile == loaded: - logger.debug("Loaded profile is current profile. Nothing to do.") - return - - profile = loaded - notify.text = "Loaded performance profile: " + profile_state + " " + profile.name - logger.info(notify.text) - logger.debug(profile) - _notification_manager.show(notify) - _shared_thread.exec(_apply_profile) - - -func apply_profile(profile: PerformanceProfile) -> void: - var cpu := _hardware_manager.cpu - var gpu := _hardware_manager.gpu - var gpu_performance := get_gpu_performance_manager() + return null + var loaded_profile := load(profile_path) as PerformanceProfile + if not loaded_profile: + logger.warn("Unable to load profile at: " + profile_path) + return loaded_profile + + +## Loads a PerformanceProfile from the given path. If the profile does not exist, +## it will create a new profile using the currently applied performance settings. +func load_or_create_profile(profile_path: String, library_item: LibraryLaunchItem = null) -> PerformanceProfile: + # Try to load the profile if it exists + var profile := load_profile(profile_path) + if profile: + return profile - if cpu.boost_capable: - logger.debug("Set CPU Boost from profile: " + str(profile.cpu_boost_enabled)) - cpu.set_boost_enabled(profile.cpu_boost_enabled) - - if cpu.smt_capable: - logger.debug("Set SMT state from profile: " + str(profile.cpu_smt_enabled)) - await cpu.set_smt_enabled(profile.cpu_smt_enabled) - logger.debug("Set core count from profile: " + str(profile.cpu_core_count_current)) - cpu.set_cpu_core_count(profile.cpu_core_count_current) - - if gpu.clk_capable: - logger.debug("Set gpu mode from profile: " + str(profile.gpu_manual_enabled)) - gpu_performance.set_gpu_manual_enabled(profile.gpu_manual_enabled) - if profile.gpu_manual_enabled: - logger.debug("Set gpu frequency range from profile: " + str(profile.gpu_freq_min_current) + "-" + str(profile.gpu_freq_max_current)) - gpu_performance.set_gpu_freq(profile.gpu_freq_min_current, profile.gpu_freq_max_current) - - if gpu.power_profile_capable: - logger.debug("Set gpu power profile from profile: " +str(profile.gpu_power_profile)) - gpu_performance.set_gpu_power_profile(profile.gpu_power_profile) - - if gpu.tj_temp_capable: - logger.debug("Set gpu temp target from profile: " +str(profile.gpu_temp_current)) - gpu_performance.set_gpu_temp_current(profile.gpu_temp_current) - - if gpu.tdp_capable: - logger.debug("Set tdp from profile: " + str(profile.tdp_current) + " boost:" + str(profile.tdp_boost_current)) - gpu_performance.set_tdp_value(profile.tdp_current) - gpu_performance.set_tdp_boost_value(profile.tdp_boost_current) - - if _platform.platform is HandheldPlatform: - logger.debug("Set thermal mode from profile: " +str(profile.thermal_profile)) - gpu_performance.set_thermal_profile(profile.thermal_profile) - - logger.info("Applied Performance Profile: " + profile_state + " " + profile.name) - perfomance_profile_applied.emit(profile) - - -func emit_profile_signals() -> void: - var cpu := _hardware_manager.cpu - var gpu := _hardware_manager.gpu - cpu_boost_toggled.emit(profile.cpu_boost_enabled) - cpu_cores_available_updated.emit(cpu.cores_available) - cpu_cores_used.emit(profile.cpu_core_count_current) - gpu_clk_limits_updated.emit(gpu.freq_min, gpu.freq_max) - gpu_clk_current_updated.emit(profile.gpu_freq_min_current, profile.gpu_freq_max_current) - gpu_manual_enabled_updated.emit(profile.gpu_manual_enabled) - gpu_power_profile_updated.emit(profile.gpu_power_profile) - gpu_temp_limit_updated.emit(profile.gpu_temp_current) - smt_toggled.emit(profile.cpu_smt_enabled) - tdp_updated.emit(profile.tdp_current, profile.tdp_boost_current) - thermal_profile_updated.emit(profile.thermal_profile) - - - -func on_app_switched(_from: RunningApp, to: RunningApp) -> void: - logger.debug("Detected app switch") - library_item = null - if to: - library_item = to.launch_item - load_profile() - + # If the profile does not exist, create one with the currently applied + # performance settings. + return create_profile(library_item) -### Adjust sysfs and update profile funcs -## Called to set the number of enabled CPU's -func set_cpu_core_count(value: int) -> void: - if profile.cpu_core_count_current == value: - return - profile.cpu_core_count_current = value - await _change_cpu_cores() - save_profile() - - -## Called to toggle cpu boost -func set_cpu_boost_enabled(state: bool) -> void: - if profile.cpu_boost_enabled == state: - return - profile.cpu_boost_enabled = state - await _apply_cpu_boost_state() - save_profile() - - -## Called to enable/disable CPU SMT. -func set_cpu_smt_enabled(state: bool) -> void: - if profile.cpu_smt_enabled == state: - return - profile.cpu_smt_enabled = state - await _apply_cpu_smt_state() - save_profile() - - -## Called when gpu_freq_max_slider.value is changed. -func set_gpu_freq_max(value: float) -> void: - if profile.gpu_freq_max_current == value: - return - profile.gpu_freq_max_current = value - if value < profile.gpu_freq_min_current: - profile.gpu_freq_min_current = value - await _gpu_freq_change() - save_profile() - - -## Called to set the minimum gpu clock is changed. -func set_gpu_freq_min(value: float) -> void: - if profile.gpu_freq_min_current == value: - return - profile.gpu_freq_min_current = value - if value > profile.gpu_freq_max_current: - profile.gpu_freq_max_current = value - await _gpu_freq_change() - save_profile() - - -## Called to toggle auto/manual gpu clocking -func set_gpu_manual_enabled(state: bool) -> void: - var gpu := _hardware_manager.gpu - if profile.gpu_manual_enabled == state: - return - profile.gpu_manual_enabled = state - await _gpu_manual_change() - - if profile.gpu_manual_enabled: - if not gpu.freq_max or not gpu.freq_min: - await _read_gpu_clk_limits() - await _read_gpu_clk_current() - save_profile() +## Applies the given performance profile to the system +func apply_profile(profile: PerformanceProfile) -> void: + logger.info("Applying performance profile: " + profile.name) + + # Apply CPU settings from the given profile + if _power_station.cpu: + logger.debug("Applying CPU performance settings from profile") + _power_station.cpu.boost_enabled = profile.cpu_boost_enabled + _power_station.cpu.smt_enabled = profile.cpu_smt_enabled + if profile.cpu_core_count_current > 0: + _power_station.cpu.cores_enabled = profile.cpu_core_count_current + + # Detect all GPU cards + var cards: Array[PowerStation.GPUCard] = [] + if _power_station.gpu: + cards = _power_station.gpu.get_cards() + + # Configure GPU settings + # TODO: Support mutliple GPUs? + for card in cards: + if card.class_type != "integrated": + continue + logger.debug("Applying GPU performance settings from profile") + if profile.tdp_current > 0: + card.tdp = profile.tdp_current + if profile.tdp_boost_current > 0: + card.boost = profile.tdp_boost_current + if profile.gpu_freq_min_current > 0: + card.clock_value_mhz_min = profile.gpu_freq_min_current + if profile.gpu_freq_max_current > 0: + card.clock_value_mhz_max = profile.gpu_freq_max_current + card.manual_clock = profile.gpu_manual_enabled + #if profile.gpu_power_profile > 0: + # card.power_profile = profile.gpu_power_profile # TODO: Fix this + if profile.gpu_temp_current > 0: + card.thermal_throttle_limit_c = profile.gpu_temp_current + + logger.info("Applied Performance Profile: " + profile.name) + profile_applied.emit(profile) + + +## Applies the given performance profile to the system and saves it based on +## the current profile state (e.g. docked or undocked) and current running app. +func apply_and_save_profile(profile: PerformanceProfile) -> void: + # Get the current profile state to see if we need to load the docked or + # undocked profile. + var profile_state := get_profile_state() + + # Get the currently running app, if there is one. + var current_app := _launch_manager.get_current_app() + var library_item: LibraryLaunchItem + if current_app: + library_item = current_app.launch_item + + # Load the performance profile based on the running game + var profile_path := get_profile_filename(profile_state, library_item) + profile_path = "/".join([USER_PROFILES, profile_path]) + apply_profile(profile) + save_profile(profile, profile_path, library_item) + + +## Returns the current profile state. I.e. whether or not the "docked" or "undocked" +## performance profiles should be used. +func get_profile_state() -> PROFILE_STATE: + var batteries := _power_manager.get_devices_by_type(PowerManager.DEVICE_TYPE.BATTERY) + if batteries.size() > 1: + logger.warn("You somehow have more than one battery. We don't know what to do with that.") + if batteries.size() > 0: + var battery := batteries[0] + return get_profile_state_from_battery(battery) + return PROFILE_STATE.DOCKED -## Called to set the GPU Power Profile -func set_gpu_power_profile(mode: int) -> void: - if profile.gpu_power_profile == mode: - return - profile.gpu_power_profile = mode - await _gpu_power_profile_change() - save_profile() +## Returns the current profile state. I.e. whether or not the "docked" or "undocked" +## performance profiles should be used. +func get_profile_state_from_battery(battery: PowerManager.Device) -> PROFILE_STATE: + if battery.state not in DOCKED_STATES: + return PROFILE_STATE.UNDOCKED + return PROFILE_STATE.DOCKED -## Called to set the GPU Thermal Throttle Limit -func set_gpu_temp_current(value: float) -> void: - if profile.gpu_temp_current == value: - return - profile.gpu_temp_current = value - await _gpu_temp_limit_change() - save_profile() +## Called whenever a battery is updated +func _on_battery_updated(battery: PowerManager.Device) -> void: + # Get the current profile state to see if we need to load the docked or + # undocked profile. + var profile_state := get_profile_state_from_battery(battery) -## Called to set the TFP boost limit. -func set_tdp_boost_value(value: float) -> void: - if profile.tdp_boost_current == value: + # If the profile state hasn't changed, do nothing. Otherwise update the + # state and load/apply the appropriate profile + if current_profile_state == profile_state: return - profile.tdp_boost_current = value - await _tdp_boost_value_change() - save_profile() + current_profile_state = profile_state + # Get the currently running app, if there is one. + var current_app := _launch_manager.get_current_app() + var library_item: LibraryLaunchItem + if current_app: + library_item = current_app.launch_item -## Called to set the TDP average limit. -func set_tdp_value(value: float) -> void: - if profile.tdp_current == value: - return - profile.tdp_current = value - await _tdp_value_change() - save_profile() + # Load the performance profile based on the running game + var profile_path := get_profile_filename(profile_state, library_item) + profile_path = "/".join([USER_PROFILES, profile_path]) + var profile := load_or_create_profile(profile_path, library_item) + current_profile = profile + profile_loaded.emit(profile) + apply_profile(profile) -## Sets the thermal throttle mode for ASUS devices. -func set_thermal_profile(index: int) -> void: - if profile.thermal_profile == index: +## Called whenever an app is switched. E.g. when a game is launched +func _on_app_switched(_from: RunningApp, to: RunningApp) -> void: + logger.debug("Detected app switch") + if not to: return - profile.thermal_profile = index - await _apply_thermal_profile() - save_profile() - - -### Adjust sysfs funcs -func _apply_cpu_boost_state() -> void: - var args := ["cpuBoost", "0"] - if profile.cpu_boost_enabled: - args = ["cpuBoost", "1"] - await _async_do_exec(POWERTOOLS_PATH, args) - cpu_boost_toggled.emit(profile.cpu_boost_enabled) + # Get the current profile state to see if we need to load the docked or + # undocked profile. + var profile_state := get_profile_state() -func _apply_thermal_profile() -> void: - if not _platform.platform is HandheldPlatform: - logger.warn("Attempt to apply thermal profile on platform that is not HandheldPlatform.") - return - var platform_provider := _platform.platform as HandheldPlatform - if not FileAccess.file_exists(platform_provider.thermal_policy_path): - logger.warn("Thermal policy path does not exist.") - return - match profile.thermal_profile: - "0": - logger.debug("Setting thermal throttle policy to Balanced") - "1": - logger.debug("Setting thermal throttle policy to Performance") - "2": - logger.debug("Setting thermal throttle policy to Silent") - var args := ["setThermalPolicy", str(profile.thermal_profile)] - await _async_do_exec(platform_provider.thermal_policy_path, args) - thermal_profile_updated.emit(profile.thermal_profile) - - -func _apply_cpu_smt_state() -> void: - var args := [] - if profile.cpu_smt_enabled: - args = ["smtToggle", "on"] - else: - args = ["smtToggle", "off"] - var output: Array = await _async_do_exec(POWERTOOLS_PATH, args) - var exit_code = output[1] - if exit_code: - logger.warn("_on_toggle_smt exit code: " + str(exit_code)) - return - smt_toggled.emit(profile.cpu_smt_enabled) - - -# Called to disable/enable cores by count as specified by value. -func _change_cpu_cores(): - var cpu := _hardware_manager.cpu - logger.debug("Enable cpu_cores: " +str(profile.cpu_core_count_current) + "/" + str(cpu.core_count)) - var args := [] - for cpu_no in range(1, cpu.core_count): - args = ["cpuToggle", str(cpu_no), "1"] - if profile.cpu_smt_enabled and cpu_no >= profile.cpu_core_count_current: - args[2] = "0" - elif not profile.cpu_smt_enabled: - if cpu_no % 2 != 0 or cpu_no >= (profile.cpu_core_count_current * 2) - 1: - args[2] = "0" - logger.debug("Set CPU No: " + str(args[1]) + " to " + args[2]) - await _async_do_exec(POWERTOOLS_PATH, args) - cpu_cores_used.emit(profile.cpu_core_count_current) - - -## Ensures the current boost doesn't exceed the max boost. -func _ensure_tdp_boost_limited() -> void: - var gpu := _hardware_manager.gpu - if profile.tdp_boost_current > gpu.max_boost: - profile.tdp_boost_current = gpu.max_boost - if profile.tdp_boost_current < 0: - profile.tdp_boost_current = 0 - - -# Sets the current TDP to the midpoint of the detected hardware. Used when we're not able to -# Determine the current settings. -func _set_sane_defaults() -> void: - var gpu := _hardware_manager.gpu - if not profile.tdp_current: - profile.tdp_current = float((gpu.tdp_max - gpu.tdp_min) / 2 + gpu.tdp_min) - if not profile.tdp_boost_current: - profile.tdp_boost_current = 0 - if not profile.gpu_temp_current: - profile.gpu_temp_current = FALLBACK_GPU_TEMP - await _tdp_value_change() - await _gpu_temp_limit_change() - + # Load the performance profile based on the running game + var profile_path := get_profile_filename(profile_state, to.launch_item) + var profile := load_or_create_profile(profile_path, to.launch_item) + current_profile = profile + profile_loaded.emit(profile) + apply_profile(profile) diff --git a/core/systems/performance/power_station.gd b/core/systems/performance/power_station.gd new file mode 100644 index 00000000..0c1249bc --- /dev/null +++ b/core/systems/performance/power_station.gd @@ -0,0 +1,400 @@ +extends Resource +class_name PowerStation + +## Proxy interface to PowerStation over DBus +## +## Provides wrapper classes and methods for interacting with PowerStation over +## DBus to control CPU and GPU performance. + +const POWERSTATION_BUS := "org.shadowblip.PowerStation" +const PERFORMANCE_PATH := "/org/shadowblip/Performance" +const CPU_PATH := "/org/shadowblip/Performance/CPU" +const GPU_PATH := "/org/shadowblip/Performance/GPU" +const IFACE_CPU := "org.shadowblip.CPU" +const IFACE_CPU_CORE := "org.shadowblip.CPU.Core" +const IFACE_GPU := "org.shadowblip.GPU" +const IFACE_GPU_CARD := "org.shadowblip.GPU.Card" +const IFACE_GPU_TDP := "org.shadowblip.GPU.Card.TDP" +const IFACE_GPU_CONNECTOR := "org.shadowblip.GPU.Card.Connector" + +var dbus := load("res://core/global/dbus_system.tres") as DBusManager +var cpu := CPUBus.new(dbus.create_proxy(POWERSTATION_BUS, CPU_PATH)) +var gpu := GPUBus.new(dbus.create_proxy(POWERSTATION_BUS, GPU_PATH)) + + +## Returns true if PowerStation can be used on this system +func supports_power_station() -> bool: + return dbus.bus_exists(POWERSTATION_BUS) + + +## CPUBus provides a DBus connection to the CPU bus for CPU controls +class CPUBus extends Resource: + signal properties_changed + signal updated + var _proxy: DBusManager.Proxy + + func _init(proxy: DBusManager.Proxy) -> void: + _proxy = proxy + _proxy.properties_changed.connect(_on_properties_changed) + + func _on_properties_changed(iface: String, props: Dictionary) -> void: + updated.emit() + + ## Returns a list of DBus object paths to every detected core + func enumerate_cores() -> PackedStringArray: + var result := _proxy.call_method(IFACE_CPU, "EnumerateCores") + var args := result.get_args() + if args.size() != 1: + return [] + if not args[0] is Array: + return [] + return args[0] + + ## Returns true if the CPU has the given feature + func has_feature(feature: String) -> bool: + var result := _proxy.call_method(IFACE_CPU, "HasFeature", [feature], "s") + var args := result.get_args() + if args.size() != 1: + return false + if not args[0] is bool: + return false + return args[0] + + var boost_enabled: bool: + set(v): + _proxy.set_property(IFACE_CPU, "BoostEnabled", v) + get: + var property = _proxy.get_property(IFACE_CPU, "BoostEnabled") + if not property is bool: + return false + return property + + var cores_count: int: + get: + var property = _proxy.get_property(IFACE_CPU, "CoresCount") + if not property is int: + return -1 + return property + + var cores_enabled: int: + set(v): + _proxy.set_property(IFACE_CPU, "CoresEnabled", DBus.uint32(v)) + get: + var property = _proxy.get_property(IFACE_CPU, "CoresEnabled") + if not property is int: + return -1 + return property + + var features: PackedStringArray: + get: + var property = _proxy.get_property(IFACE_CPU, "Features") + if not property is Array: + return [] + return property + + var smt_enabled: bool: + set(v): + _proxy.set_property(IFACE_CPU, "SmtEnabled", v) + get: + var property = _proxy.get_property(IFACE_CPU, "SmtEnabled") + if not property is bool: + return false + return property + + +## Provides an interface to enumerate all detected GPU cards +class GPUBus extends Resource: + signal properties_changed + signal updated + var _proxy: DBusManager.Proxy + + func _init(proxy: DBusManager.Proxy) -> void: + _proxy = proxy + _proxy.properties_changed.connect(_on_properties_changed) + + func _on_properties_changed(iface: String, props: Dictionary) -> void: + updated.emit() + + ## Returns a list of DBus object paths to every detected GPU card + func enumerate_cards() -> PackedStringArray: + var result := _proxy.call_method(IFACE_GPU, "EnumerateCards") + var args := result.get_args() + if args.size() != 1: + return [] + if not args[0] is Array: + return [] + return args[0] + + ## Returns a list of all GPUCard objects + func get_cards() -> Array[GPUCard]: + var dbus := load("res://core/global/dbus_system.tres") as DBusManager + var cards: Array[GPUCard] = [] + var paths := self.enumerate_cards() + for path in paths: + var card = GPUCard.new(dbus.create_proxy(POWERSTATION_BUS, path)) + cards.append(card) + + return cards + + +## GPUCard provides a DBus connection to the GPU for GPU control +class GPUCard extends Resource: + signal properties_changed + signal updated + var _proxy: DBusManager.Proxy + + func _init(proxy: DBusManager.Proxy) -> void: + _proxy = proxy + _proxy.properties_changed.connect(_on_properties_changed) + + func _on_properties_changed(iface: String, props: Dictionary) -> void: + updated.emit() + + ## Returns true if the card supports TDP control + func supports_tdp() -> bool: + return self.tdp > 1 + + ## Returns a list of DBus object paths to every detected GPU connector + func enumerate_connectors() -> PackedStringArray: + var result := _proxy.call_method(IFACE_GPU_CARD, "EnumerateConnectors") + var args := result.get_args() + if args.size() != 1: + return [] + if not args[0] is Array: + return [] + return args[0] + + ## Returns a list of all GPUConnector objects + func get_connectors() -> Array[GPUConnector]: + var dbus := load("res://core/global/dbus_system.tres") as DBusManager + var connectors: Array[GPUConnector] = [] + var paths := self.enumerate_connectors() + for path in paths: + var card = GPUConnector.new(dbus.create_proxy(POWERSTATION_BUS, path)) + connectors.append(card) + + return connectors + + var class_type: String: + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "Class") + if not property is String: + return "" + return property + + var class_id: String: + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "ClassId") + if not property is String: + return "" + return property + + var clock_limit_mhz_max: float: + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "ClockLimitMhzMax") + if not property is float: + return -1 + return property + + var clock_limit_mhz_min: float: + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "ClockLimitMhzMin") + if not property is float: + return -1 + return property + + var clock_value_mhz_max: float: + set(v): + _proxy.set_property(IFACE_GPU_CARD, "ClockValueMhzMax", float(v)) + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "ClockValueMhzMax") + if not property is float: + return -1 + return property + + var clock_value_mhz_min: float: + set(v): + _proxy.set_property(IFACE_GPU_CARD, "ClockValueMhzMin", float(v)) + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "ClockValueMhzMin") + if not property is float: + return -1 + return property + + var device: String: + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "Device") + if not property is String: + return "" + return property + + var device_id: String: + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "DeviceId") + if not property is String: + return "" + return property + + var manual_clock: bool: + set(v): + _proxy.set_property(IFACE_GPU_CARD, "ManualClock", v) + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "ManualClock") + if not property is bool: + return false + return property + + var name: String: + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "Name") + if not property is String: + return "" + return property + + var path: String: + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "Path") + if not property is String: + return "" + return property + + var revision_id: String: + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "RevisionId") + if not property is String: + return "" + return property + + var subdevice: String: + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "Subdevice") + if not property is String: + return "" + return property + + var subdevice_id: String: + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "SubdeviceId") + if not property is String: + return "" + return property + + var subvendor_id: String: + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "SubvendorId") + if not property is String: + return "" + return property + + var vendor: String: + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "Vendor") + if not property is String: + return "" + return property + + var vendor_id: String: + get: + var property = _proxy.get_property(IFACE_GPU_CARD, "VendorId") + if not property is String: + return "" + return property + + var boost: float: + set(v): + _proxy.set_property(IFACE_GPU_TDP, "Boost", float(v)) + get: + var property = _proxy.get_property(IFACE_GPU_TDP, "Boost") + if not property is float: + return -1 + return property + + var power_profile: String: + set(v): + _proxy.set_property(IFACE_GPU_TDP, "PowerProfile", v) + get: + var property = _proxy.get_property(IFACE_GPU_TDP, "PowerProfile") + if not property is String: + return "" + return property + + var tdp: float: + set(v): + _proxy.set_property(IFACE_GPU_TDP, "TDP", float(v)) + get: + var property = _proxy.get_property(IFACE_GPU_TDP, "TDP") + if not property is float: + return -1 + return property + + var thermal_throttle_limit_c: float: + set(v): + _proxy.set_property(IFACE_GPU_TDP, "ThermalThrottleLimitC", float(v)) + get: + var property = _proxy.get_property(IFACE_GPU_TDP, "ThermalThrottleLimitC") + if not property is float: + return -1 + return property + + +## GPUConnector provides a DBus connection to a GPU connector +class GPUConnector extends Resource: + signal properties_changed + signal updated + var _proxy: DBusManager.Proxy + + func _init(proxy: DBusManager.Proxy) -> void: + _proxy = proxy + _proxy.properties_changed.connect(_on_properties_changed) + + func _on_properties_changed(iface: String, props: Dictionary) -> void: + updated.emit() + + var dpms: bool: + get: + var property = _proxy.get_property(IFACE_GPU_CONNECTOR, "DPMS") + if not property is bool: + return false + return property + + var enabled: bool: + get: + var property = _proxy.get_property(IFACE_GPU_CONNECTOR, "Enabled") + if not property is bool: + return false + return property + + var id: int: + get: + var property = _proxy.get_property(IFACE_GPU_CONNECTOR, "Id") + if not property is int: + return -1 + return property + + var modes: PackedStringArray: + get: + var property = _proxy.get_property(IFACE_GPU_CONNECTOR, "Modes") + if not property is Array: + return [] + return property + + var name: String: + get: + var property = _proxy.get_property(IFACE_GPU_CONNECTOR, "Name") + if not property is String: + return "" + return property + + var path: String: + get: + var property = _proxy.get_property(IFACE_GPU_CONNECTOR, "Path") + if not property is String: + return "" + return property + + var status: String: + get: + var property = _proxy.get_property(IFACE_GPU_CONNECTOR, "Status") + if not property is String: + return "" + return property diff --git a/core/systems/performance/power_station.tres b/core/systems/performance/power_station.tres new file mode 100644 index 00000000..20b69611 --- /dev/null +++ b/core/systems/performance/power_station.tres @@ -0,0 +1,6 @@ +[gd_resource type="Resource" script_class="PowerStation" load_steps=2 format=3 uid="uid://cdyeux8mdf6l6"] + +[ext_resource type="Script" path="res://core/systems/performance/power_station.gd" id="1_bdrpq"] + +[resource] +script = ExtResource("1_bdrpq") diff --git a/core/systems/performance/power_station_test.gd b/core/systems/performance/power_station_test.gd new file mode 100644 index 00000000..d20f159e --- /dev/null +++ b/core/systems/performance/power_station_test.gd @@ -0,0 +1,95 @@ +extends GutTest + +var power_station := load("res://core/systems/performance/power_station.tres") as PowerStation + + +func test_cpu() -> void: + if not power_station.supports_power_station(): + pass_test("PowerStation is not running, skipping") + return + var cpu := power_station.cpu + assert_not_null(cpu, "should return CPU instance") + + # Test getting total number of cpu cores + var num_cores = cpu.cores_count + assert_ne(num_cores, -1, "should have returned total core count") + + # Test that CPU cores get set + cpu.cores_enabled = num_cores - 1 + assert_eq(cpu.cores_enabled, num_cores - 1, "should have disabled cores") + + # Set the cores back + cpu.cores_enabled = num_cores + assert_eq(cpu.cores_enabled, num_cores, "should have re-enabled all cores") + + # Test enumerating cores + var enumerated := cpu.enumerate_cores() + assert_gt(enumerated.size(), 0, "should return at least 1 cpu core path") + + # Test getting features + var features = cpu.features + assert_gt(features.size(), 1, "should return CPU features") + if features.size() > 1: + var feature := features[0] as String + assert_true(cpu.has_feature(feature), "should have CPU feature") + assert_false(cpu.has_feature("IdontXsist!"), "should not have CPU feature") + + # Test setting SMT + cpu.smt_enabled = false + assert_false(cpu.smt_enabled, "should have disabled SMT") + await wait_frames(1, "wait for change") + cpu.smt_enabled = true + assert_true(cpu.smt_enabled, "should have enabled SMT") + await wait_frames(1, "wait for change") + + # Test setting boost + cpu.boost_enabled = false + assert_false(cpu.boost_enabled, "should have disabled boost") + await wait_frames(1, "wait for change") + cpu.boost_enabled = true + assert_true(cpu.boost_enabled, "should have enabled boost") + await wait_frames(1, "wait for change") + + +func test_gpu() -> void: + if not power_station.supports_power_station(): + pass_test("PowerStation is not running, skipping") + return + var gpu := power_station.gpu + assert_not_null(gpu, "should return GPU instance") + + # Test enumerating cards + var card_paths := gpu.enumerate_cards() + #assert_gt(cards.size(), 0, "should return at least 1 gpu") + + # Test all GPU card methods + var cards := gpu.get_cards() + for card in cards: + gut.p("Got card: " + card.device) + if not card.supports_tdp(): + continue + gut.p("Card supports TDP control: " + card.device) + + # TDP Control + var tdp := card.tdp + card.tdp = 10.0 + assert_eq(card.tdp, 10.0, "TDP value should have changed") + card.tdp = tdp + + # TDP Boost + var boost := card.boost + card.boost = 2.0 + assert_eq(card.boost, 2.0, "Boost should have changed") + card.boost = boost + + # Power profile + var profile := card.power_profile + card.power_profile = "max-performance" + assert_eq(card.power_profile, "max-performance") + card.power_profile = profile + + # GPU temperature control + var temp := card.thermal_throttle_limit_c + card.thermal_throttle_limit_c = 97.0 + assert_eq(card.thermal_throttle_limit_c, 97.0) + card.thermal_throttle_limit_c = temp diff --git a/core/ui/common/quick_bar/powertools_menu.gd b/core/ui/common/quick_bar/powertools_menu.gd index d7242eee..733a68c5 100644 --- a/core/ui/common/quick_bar/powertools_menu.gd +++ b/core/ui/common/quick_bar/powertools_menu.gd @@ -3,7 +3,7 @@ extends VBoxContainer var hardware_manager := load("res://core/systems/hardware/hardware_manager.tres") as HardwareManager var platform := load("res://core/global/platform.tres") as Platform var performance_manager := load("res://core/systems/performance/performance_manager.tres") as PerformanceManager -var launch_manager := load("res://core/global/launch_manager.tres") as LaunchManager +var power_station := load("res://core/systems/performance/power_station.tres") as PowerStation @onready var cpu_boost_button := $CPUBoostButton as Toggle @onready var cpu_cores_slider := $CPUCoresSlider as ValueSlider @@ -19,338 +19,156 @@ var launch_manager := load("res://core/global/launch_manager.tres") as LaunchMan @onready var cpu_label := $CPUSectionLabel as Control @onready var gpu_label := $GPUSectionLabel as Control @onready var wait_label := $WaitLabel as Control -@onready var _to_visible: Array[Control] = [cpu_label, gpu_label] +@onready var service_timer := $ServiceTimer as Timer +@onready var apply_timer := $ApplyTimer as Timer -var command_timer: Timer +var power_station_running := false +var profile_loading := false +var current_profile: PerformanceProfile var logger := Log.get_logger("PowerTools", Log.LEVEL.INFO) # Called when the node enters the scene tree for the first time. # Finds default values and current settings of the hardware. func _ready() -> void: - - if not performance_manager.initialized: - performance_manager.pm_ready.connect(_set_initial_visibility.bind(performance_manager.profile), CONNECT_ONE_SHOT) - else: - _set_initial_visibility(performance_manager.profile) - - command_timer = Timer.new() - command_timer.set_autostart(false) - command_timer.set_one_shot(true) - add_child(command_timer) - - -func _setup_interface() -> void: - if hardware_manager.cpu.smt_capable: - logger.debug("CPU is SMT Capable") - _setup_cpu_core_range() - if hardware_manager.cpu.boost_capable: - logger.debug("CPU is Boost Capable") - _setup_cpu_boost() - if hardware_manager.gpu.tdp_capable: - logger.debug("SOC is TDP Capable") - _setup_tdp_range() - if hardware_manager.gpu.clk_capable: - logger.debug("GPU is Reclock Capable") - _setup_gpu_freq_range() - if hardware_manager.gpu.power_profile_capable: - logger.debug("GPU is Power Profile Capable") - _setup_power_profile() - if hardware_manager.gpu.tj_temp_capable: - logger.debug("GPU is TJ Temp Configurable") - _setup_gpu_temp() - if platform.platform is HandheldPlatform: - var platform_provider := platform.platform as HandheldPlatform - if FileAccess.file_exists(platform_provider.thermal_policy_path): - logger.debug("GPU is Thermal Mode Configurable") - _setup_thermal_profile() - - launch_manager.app_switched.connect(_on_app_switched) + # Listen for signals from performance manager + performance_manager.profile_loaded.connect(_on_profile_loaded) + # Configure a timer that will monitor the PowerStation DBus service + service_timer.timeout.connect(_on_service_timer_timeout) + # Configure a timer that will apply and save the performance profile + apply_timer.timeout.connect(_on_apply_timer_timeout) -func _set_initial_visibility(_profile: PerformanceProfile) -> void: + # Configure the interface _setup_interface() - wait_label.visible = false - for control in _to_visible: - control.visible = true - logger.debug(control.name + " set to visible.") - performance_manager.emit_profile_signals() - - -# Overrides or sets the command_timer.timeout signal connection function and -# (re)starts the timer. -func _setup_callback_func(callable: Callable, arg: Variant, delay: float = 1.2) -> void: - logger.debug("Setting callback func: " + callable.get_method() + " args: " + str(arg)) - _clear_callbacks(callable) - command_timer.timeout.connect(callable.bind(arg), CONNECT_ONE_SHOT) - command_timer.start(delay) - - -# Removes any existing signal connections to command_timer.timeout. -func _clear_callbacks(callable: Callable) -> void: - for connection in command_timer.timeout.get_connections(): - var old_callable := connection["callable"] as Callable - # Only clear methods that interfere with eachother. Otherwise we might miss - # a callback if multiple UI elements are used in less time than the delay - # in _setup_callback_func. (I.E. set toggle after slider) - if old_callable.get_method() == callable.get_method(): - command_timer.timeout.disconnect(old_callable) - logger.debug("Removed " + callable.get_method() + " as callback func.") - - -func _on_app_switched(from: RunningApp, to: RunningApp) -> void: - var app_name = "default" - if to: - app_name = to.launch_item.name - logger.debug("Detected app switch to " + app_name) - performance_manager.on_app_switched(from, to) - - -func _setup_cpu_boost() -> void: - _to_visible.append(cpu_boost_button) - cpu_boost_button.toggled.connect(_on_cpu_boost_button_toggled) - performance_manager.cpu_boost_toggled.connect(_update_cpu_boost) - - -func _update_cpu_boost(state: bool) -> void: - logger.debug("Received update for cpu_boost " +str(state)) - cpu_boost_button.button_pressed = state - - -func _setup_cpu_core_range() -> void: - _to_visible.append(smt_button) - _to_visible.append(cpu_cores_slider) - smt_button.toggled.connect(_on_smt_button_toggled) - cpu_cores_slider.value_changed.connect(_on_cpu_cores_slider_changed) - performance_manager.smt_toggled.connect(_update_smt_enabled) - performance_manager.cpu_cores_available_updated.connect(_update_cpu_cores_available) - performance_manager.cpu_cores_used.connect(_update_cpu_cores_used) - - -func _update_smt_enabled(smt_enabled) -> void: - logger.debug("Received update for smt_enabled: " + str(smt_enabled)) - smt_button.button_pressed = smt_enabled - - -func _update_cpu_cores_available(available: int) -> void: - logger.debug("Received update for cpu_cores_available: " + str(available)) - cpu_cores_slider.max_value = available - -func _update_cpu_cores_used(count: int) -> void: - logger.debug("Received update for cpu_cores_used: "+ str(count)) - cpu_cores_slider.value = count - - -# Gets the TDP Range for the detected hardware. -func _setup_tdp_range() -> void: - _to_visible.append(tdp_boost_slider) - _to_visible.append(tdp_slider) - tdp_slider.max_value = hardware_manager.gpu.tdp_max - tdp_slider.min_value = hardware_manager.gpu.tdp_min - tdp_boost_slider.max_value = hardware_manager.gpu.max_boost - tdp_boost_slider.value_changed.connect(_on_tdp_boost_value_slider_changed) - tdp_slider.value_changed.connect(_on_tdp_value_slider_changed) - performance_manager.tdp_updated.connect(_update_tdp) - - -func _update_tdp(tdp_current: float, boost_current: float) -> void: - logger.debug("Received update for tdp_updated: " + str(tdp_current) + " " + str(boost_current)) - tdp_slider.value = tdp_current - tdp_boost_slider.value = boost_current - - -func _setup_gpu_freq_range() -> void: - _to_visible.append(gpu_freq_enable) - gpu_freq_enable.toggled.connect(_on_gpu_freq_enable_button_toggled) - performance_manager.gpu_clk_limits_updated.connect(_update_gpu_freq_range) - performance_manager.gpu_clk_current_updated.connect(_update_gpu_freq_current) - performance_manager.gpu_manual_enabled_updated.connect(_update_gpu_manual_enabled) - - -func _update_gpu_freq_current(current_min: float, current_max: float) -> void: - logger.debug("Received update for gpu_clk_current_updated: " + str(current_min) + " " + str(current_max)) - gpu_freq_max_slider.value = current_max - gpu_freq_min_slider.value = current_min - logger.debug("gpu_clk_current_updated done.") - - -func _update_gpu_freq_range(gpu_freq_min: float, gpu_freq_max: float) -> void: - logger.debug("Received update for gpu_clk_limits_updated: " + str(gpu_freq_min) + " " + str(gpu_freq_max)) - # By default the sliders will set to the new mininum value if thier current value is - # less than the new minimim. On first run we dont want to override the max_freq so - # we connect the value changed signals after we have modified the min/max values. - # This can also happen the first time the slider is enabled. - var first_run: bool = false - if gpu_freq_max_slider.value == 0: - first_run = true - if gpu_freq_max_slider.value_changed.is_connected(_on_max_gpu_freq_slider_changed): - gpu_freq_max_slider.value_changed.disconnect(_on_max_gpu_freq_slider_changed) - if gpu_freq_min_slider.value_changed.is_connected(_on_min_gpu_freq_slider_changed): - gpu_freq_min_slider.value_changed.disconnect(_on_min_gpu_freq_slider_changed) - - gpu_freq_max_slider.max_value = gpu_freq_max - gpu_freq_max_slider.min_value = gpu_freq_min - gpu_freq_min_slider.max_value = gpu_freq_max - gpu_freq_min_slider.min_value = gpu_freq_min - - if first_run: - logger.debug("Set first run values: " + str(gpu_freq_min) + " " + str(gpu_freq_max)) - gpu_freq_max_slider.value = gpu_freq_max - gpu_freq_min_slider.value = gpu_freq_min - gpu_freq_max_slider.value_changed.connect(_on_max_gpu_freq_slider_changed) - gpu_freq_min_slider.value_changed.connect(_on_min_gpu_freq_slider_changed) - logger.debug("gpu_clk_limits_updated done.") - - -func _update_gpu_manual_enabled(state: bool) -> void: - logger.debug("Received update for gpu_manual_enabled: " + str(state)) - gpu_freq_enable.button_pressed = state - gpu_freq_max_slider.visible = state - gpu_freq_min_slider.visible = state - - -# Gets the TDP Range for the detected hardware. -func _setup_gpu_temp() -> void: - _to_visible.append(gpu_temp_slider) - gpu_temp_slider.value_changed.connect(_on_gpu_temp_limit_slider_changed) - performance_manager.gpu_temp_limit_updated.connect(_update_gpu_temp) - - -# Gets the TDP Range for the detected hardware. -func _update_gpu_temp(current: float) -> void: - logger.debug("Received update for gpu_temp_limit_updated: " +str(current)) - gpu_temp_slider.value = current - - -func _setup_power_profile() -> void: - _to_visible.append(power_profile_dropdown) - power_profile_dropdown.clear() - power_profile_dropdown.add_item("Max Performance", 0) - power_profile_dropdown.add_item("Power Saving", 1) - power_profile_dropdown.item_selected.connect(_on_power_profile_dropdown_changed) - performance_manager.gpu_power_profile_updated.connect(_update_power_profile) - - -func _update_power_profile(index: int) -> void: - logger.debug("Received update for gpu_power_profile_updated: " +str(index)) - power_profile_dropdown.select(index) - match index: - 0: - logger.debug("Power Profile at Max Performance") - 1: - logger.debug("Power Profile at Power Saving") - - -func _setup_thermal_profile() -> void: - _to_visible.append(thermal_profile_dropdown) + # Re-start the apply timer when changes happen + var on_changed := func() -> void: + if profile_loading: + return + apply_timer.start() + cpu_boost_button.pressed.connect(on_changed) + smt_button.pressed.connect(on_changed) + gpu_freq_enable.pressed.connect(on_changed) + + # Restart the timer when any slider changes happen + var on_slider_changed := func(_value) -> void: + if profile_loading: + return + apply_timer.start() + cpu_cores_slider.value_changed.connect(on_slider_changed) + tdp_slider.value_changed.connect(on_slider_changed) + tdp_boost_slider.value_changed.connect(on_slider_changed) + gpu_freq_min_slider.value_changed.connect(on_slider_changed) + gpu_freq_max_slider.value_changed.connect(on_slider_changed) + gpu_temp_slider.value_changed.connect(on_slider_changed) + + # Also restart the apply timer when dropdown changes happen + var on_dropdown_changed := func(_index) -> void: + if profile_loading: + return + apply_timer.start() + power_profile_dropdown.item_selected.connect(on_dropdown_changed) + thermal_profile_dropdown.item_selected.connect(on_dropdown_changed) + + # Toggle visibility when the GPU freq manual toggle is on + var on_manual_freq := func() -> void: + gpu_freq_min_slider.visible = gpu_freq_enable.button_pressed + gpu_freq_max_slider.visible = gpu_freq_enable.button_pressed + gpu_freq_enable.pressed.connect(on_manual_freq) + + # Setup dropdowns thermal_profile_dropdown.clear() thermal_profile_dropdown.add_item("Balanced", 0) thermal_profile_dropdown.add_item("Performance", 1) thermal_profile_dropdown.add_item("Silent", 2) - thermal_profile_dropdown.item_selected.connect(_on_thermal_policy_dropdown_changed) - performance_manager.thermal_profile_updated.connect(_update_thermal_profile) - - -func _update_thermal_profile(index: int) -> void: - logger.debug("Received update for thermal_profile_updated: " +str(index)) - thermal_profile_dropdown.select(index) - match index: - 0: - logger.debug("Thermal throttle policy currently at Balanced") - 1: - logger.debug("Thermal throttle policy currently at Performance") - 2: - logger.debug("Thermal throttle policy currently at Silent") - - -### UI Callback functions - -func _on_cpu_cores_slider_changed(value: float) -> void: - if value == performance_manager.profile.cpu_core_count_current: - return - logger.debug("cpu_cores_slider_changed: " + str (value)) - _setup_callback_func(performance_manager.set_cpu_core_count, value) - - -# Called to toggle cpu boost -func _on_cpu_boost_button_toggled(state: bool) -> void: - if state == performance_manager.profile.cpu_boost_enabled: - return - logger.debug("cpu_boost_button_toggled: " + str (state)) - _setup_callback_func(performance_manager.set_cpu_boost_enabled, state, 0) - - -# Called to toggle auo/manual gpu clocking -func _on_gpu_freq_enable_button_toggled(state: bool) -> void: - if state == performance_manager.profile.gpu_manual_enabled: - return - logger.debug("gpu_freq_enable_button_toggled: " + str (state)) - _setup_callback_func(performance_manager.set_gpu_manual_enabled, state, 0) - - -# Sets the T-junction temp using ryzenadj. -func _on_gpu_temp_limit_slider_changed(value: float) -> void: - if value == performance_manager.profile.gpu_temp_current: - return - logger.debug("gpu_temp_limit_slider_changed: " + str (value)) - _setup_callback_func(performance_manager.set_gpu_temp_current, value, 0) - - -# Called when gpu_freq_max_slider.value is changed. -func _on_max_gpu_freq_slider_changed(value: float) -> void: - if value == performance_manager.profile.gpu_freq_max_current: - return - logger.debug("max_gpu_freq_slider_changed: " + str (value)) - if value < gpu_freq_min_slider.value: - gpu_freq_min_slider.value = value - _setup_callback_func(performance_manager.set_gpu_freq_max, value) - - -# Called when gpu_freq_min_slider.value is changed. -func _on_min_gpu_freq_slider_changed(value: float) -> void: - if value == performance_manager.profile.gpu_freq_min_current: - return - logger.debug("min_gpu_freq_slider_changed: " + str (value)) - if value > gpu_freq_max_slider.value: - gpu_freq_max_slider.value = value - _setup_callback_func(performance_manager.set_gpu_freq_min, value) - - -func _on_power_profile_dropdown_changed(index: int) -> void: - if index == performance_manager.profile.gpu_power_profile: - return - logger.debug("power_profile_dropdown_changed: " + str (index)) - _setup_callback_func(performance_manager.set_gpu_power_profile, index, 0) + power_profile_dropdown.clear() + power_profile_dropdown.add_item("Max Performance", 0) + power_profile_dropdown.add_item("Power Saving", 1) -# Called to set the flow and fast boost TDP -func _on_tdp_boost_value_slider_changed(value: float) -> void: - if value == performance_manager.profile.tdp_boost_current: +# Triggers when the apply timer times out. The apply timer will start/restart +# whenever the user makes a change to any item. When the timer runs out, it will +# call this to apply the current profile. +func _on_apply_timer_timeout() -> void: + if not current_profile: + logger.debug("No loaded profile to apply") return - logger.debug("tdp_boost_value_slider_changed: " + str (value)) - _setup_callback_func(performance_manager.set_tdp_boost_value, value) + logger.debug("Applying and saving profile") + performance_manager.apply_and_save_profile(current_profile) -# Called to set the base average TDP -func _on_tdp_value_slider_changed(value: float) -> void: - if value == performance_manager.profile.tdp_current: +# Triggers every timeout to monitor the PowerStation DBus +func _on_service_timer_timeout() -> void: + var bus_running := power_station.supports_power_station() + if bus_running == power_station_running: return - logger.debug("tdp_value_slider_changed: " + str (value)) - _setup_callback_func(performance_manager.set_tdp_value, value) - -# Sets the thermal throttle policy for ASUS devices. -func _on_thermal_policy_dropdown_changed(index: int) -> void: - if index == performance_manager.profile.thermal_profile: - return - logger.debug("thermal_policy_dropdown_changed: " + str (index)) - _setup_callback_func(performance_manager.set_thermal_profile, index, 0) + # If the state of powerstation changes, update the interface accordingly + power_station_running = bus_running + _setup_interface() -# Called to toggle SMT -func _on_smt_button_toggled(state: bool) -> void: - if state == performance_manager.profile.cpu_smt_enabled: +## Called when a performance profile is loaded +func _on_profile_loaded(profile: PerformanceProfile) -> void: + # Keep track of the currently loaded profile + current_profile = profile + + # Update all UI components based on the loaded profile + profile_loading = true + cpu_boost_button.button_pressed = profile.cpu_boost_enabled + smt_button.button_pressed = profile.cpu_smt_enabled + cpu_cores_slider.value = profile.cpu_core_count_current + tdp_slider.value = profile.tdp_current + tdp_boost_slider.value = profile.tdp_boost_current + gpu_freq_enable.button_pressed = profile.gpu_manual_enabled + gpu_freq_enable.pressed.emit() + gpu_freq_min_slider.value = profile.gpu_freq_min_current + gpu_freq_max_slider.value = profile.gpu_freq_max_current + gpu_temp_slider.value = profile.gpu_temp_current + + power_profile_dropdown.select(profile.gpu_power_profile) + thermal_profile_dropdown.select(profile.thermal_profile) + profile_loading = false + + +# Configure the min/max values and visibility +func _setup_interface() -> void: + # If powerstation is not running, disable everything + if not power_station.supports_power_station(): + wait_label.visible = true + for node in get_children(): + if node == wait_label: + continue + if node == Control: + (node as Control).visible = false return - logger.debug("smt_button_toggled: " + str (state)) - _setup_callback_func(performance_manager.set_cpu_smt_enabled, state, 0) + + # Configure visibility for all components + wait_label.visible = false + + # Configure CPU components + if power_station.cpu: + var cpu := power_station.cpu + cpu_label.visible = true + cpu_boost_button.visible = cpu.has_feature("cpb") + smt_button.visible = cpu.has_feature("ht") + cpu_cores_slider.max_value = cpu.cores_count + + # Configure GPU components + if power_station.gpu: + var card: PowerStation.GPUCard + var cards := power_station.gpu.get_cards() + for c in cards: + if c.class_type != "integrated": + continue + card = c + + # Configure based on integrated graphics card + if card: + gpu_label.visible = true + tdp_slider.visible = true + tdp_slider.min_value = hardware_manager.gpu.tdp_min + tdp_slider.max_value = hardware_manager.gpu.tdp_max + tdp_boost_slider.visible = true + tdp_boost_slider.max_value = hardware_manager.gpu.max_boost diff --git a/core/ui/common/quick_bar/powertools_menu.tscn b/core/ui/common/quick_bar/powertools_menu.tscn index 2b92ed96..b0ca9bf6 100644 --- a/core/ui/common/quick_bar/powertools_menu.tscn +++ b/core/ui/common/quick_bar/powertools_menu.tscn @@ -12,10 +12,6 @@ anchors_preset = 15 anchor_right = 1.0 anchor_bottom = 1.0 -offset_left = 66.0 -offset_top = -116.0 -offset_right = 66.0 -offset_bottom = -116.0 grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_qncpn") @@ -24,10 +20,18 @@ script = ExtResource("1_qncpn") current_focus = NodePath("../CPUBoostButton") focus_stack = ExtResource("3_0iyf7") +[node name="ApplyTimer" type="Timer" parent="."] +wait_time = 1.5 +one_shot = true +autostart = true + +[node name="ServiceTimer" type="Timer" parent="."] +wait_time = 5.0 +autostart = true + [node name="WaitLabel" parent="." instance=ExtResource("4_1pfjc")] layout_mode = 2 -text = "Reading system info. -Please wait..." +text = "Waiting for PowerStation service..." horizontal_alignment = 1 [node name="CPUSectionLabel" parent="." instance=ExtResource("4_1pfjc")]