Remember power preerence and force fallback

This commit is contained in:
2026-08-08 22:44:00 -04:00
parent 05d00eee5a
commit 0a8663a010
5 changed files with 116 additions and 11 deletions
+10 -5
View File
@@ -23,7 +23,7 @@ use winit::{
};
use crate::{
config::CliArgs,
config::{AppConfig, CliArgs},
controller::ControllerManager,
emulator::{EmulatorClient, EmulatorCommand, SimId},
images::ImageTextureLoader,
@@ -94,8 +94,9 @@ impl Application {
handle: OwnedDisplayHandle,
persistence: Persistence,
args: CliArgs,
config: AppConfig,
) -> Self {
let wgpu = WgpuState::new(handle);
let wgpu = WgpuState::new(handle, config);
let icon = load_icon().ok().map(Arc::new);
let mappings = MappingProvider::new(persistence.clone(), args.player2_controller);
let shortcuts = ShortcutProvider::new(persistence.clone());
@@ -496,10 +497,11 @@ impl ApplicationHandler<UserEvent> for Application {
struct WgpuState {
instance: wgpu::Instance,
device_descriptor: Arc<dyn Fn(&wgpu::Adapter) -> wgpu::DeviceDescriptor<'static> + Send + Sync>,
config: AppConfig,
}
impl WgpuState {
fn new(handle: OwnedDisplayHandle) -> Self {
fn new(handle: OwnedDisplayHandle, config: AppConfig) -> Self {
#[allow(unused_variables)]
let egui_wgpu::WgpuSetupCreateNew {
instance_descriptor: wgpu::InstanceDescriptor { backends, .. },
@@ -517,16 +519,19 @@ impl WgpuState {
Self {
instance,
device_descriptor,
config,
}
}
fn init(&mut self, window: &Window) -> egui_wgpu::WgpuSetupExisting {
let power_preference =
wgpu::PowerPreference::from_env().unwrap_or(wgpu::PowerPreference::LowPower);
wgpu::PowerPreference::from_env().unwrap_or(self.config.power_preference);
debug!("power preference: {power_preference:?}");
let force_fallback_adapter = std::env::var("WGPU_FORCE_FALLBACK")
.is_ok_and(|f| f.eq_ignore_ascii_case("true") || f.eq_ignore_ascii_case("1"));
.map_or(self.config.force_fallback, |f| {
f.eq_ignore_ascii_case("true") || f.eq_ignore_ascii_case("1")
});
debug!("force fallback: {force_fallback_adapter}");
let surface = self
+69 -4
View File
@@ -1,5 +1,5 @@
use anyhow::Result;
use clap::Parser;
use clap::{Parser, ValueEnum};
use egui::{Color32, Pos2, Vec2};
use serde::{Deserialize, Serialize};
@@ -47,6 +47,28 @@ pub struct CliArgs {
/// Map the first connected controller to Player 2
#[arg(long)]
pub player2_controller: bool,
/// Force the application to render with CPU, rather than GPU.
#[arg(long)]
pub wgpu_force_fallback: Option<bool>,
/// Set a preference for whether to use a low-power or high-performance GPU adapter
#[arg(long)]
pub wgpu_power_preference: Option<PowerPreferenceWrapper>,
}
#[derive(ValueEnum, Clone, Copy)]
pub enum PowerPreferenceWrapper {
Low,
High,
None,
}
impl From<PowerPreferenceWrapper> for wgpu::PowerPreference {
fn from(value: PowerPreferenceWrapper) -> Self {
match value {
PowerPreferenceWrapper::Low => wgpu::PowerPreference::LowPower,
PowerPreferenceWrapper::High => wgpu::PowerPreference::HighPerformance,
PowerPreferenceWrapper::None => wgpu::PowerPreference::None,
}
}
}
pub const COLOR_PRESETS: [[Color32; 2]; 3] = [
@@ -64,6 +86,49 @@ pub const COLOR_PRESETS: [[Color32; 2]; 3] = [
],
];
const fn default_power_preference() -> wgpu::PowerPreference {
wgpu::PowerPreference::LowPower
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct AppConfig {
#[serde(default = "default_power_preference")]
pub power_preference: wgpu::PowerPreference,
#[serde(default)]
pub force_fallback: bool,
}
impl AppConfig {
pub fn load(persistence: &Persistence) -> Self {
if let Ok(config) = persistence.load_config(APP_CONFIG_FILENAME) {
return config;
}
Self {
power_preference: default_power_preference(),
force_fallback: false,
}
}
pub fn save(&self, persistence: &Persistence) -> Result<()> {
persistence.save_config(APP_CONFIG_FILENAME, &self)
}
pub fn update(&mut self, args: &CliArgs) -> bool {
let mut updated = false;
if let Some(pref) = args.wgpu_power_preference {
self.power_preference = pref.into();
updated = true;
}
if let Some(force_fallback) = args.wgpu_force_fallback {
self.force_fallback = force_fallback;
updated = true;
}
updated
}
}
const APP_CONFIG_FILENAME: &str = "config";
const fn default_audio_enabled() -> bool {
true
}
@@ -81,7 +146,7 @@ pub struct SimConfig {
impl SimConfig {
pub fn load(persistence: &Persistence, sim_id: SimId) -> Self {
if let Ok(config) = persistence.load_config(config_filename(sim_id)) {
if let Ok(config) = persistence.load_config(sim_config_filename(sim_id)) {
return config;
}
Self {
@@ -94,11 +159,11 @@ impl SimConfig {
}
pub fn save(&self, persistence: &Persistence, sim_id: SimId) -> Result<()> {
persistence.save_config(config_filename(sim_id), self)
persistence.save_config(sim_config_filename(sim_id), self)
}
}
fn config_filename(sim_id: SimId) -> &'static str {
fn sim_config_filename(sim_id: SimId) -> &'static str {
match sim_id {
SimId::Player1 => "config_p1",
SimId::Player2 => "config_p2",
+6 -1
View File
@@ -13,7 +13,7 @@ use tracing_subscriber::{EnvFilter, Layer, layer::SubscriberExt, util::Subscribe
use winit::event_loop::{ControlFlow, EventLoop};
use crate::{
config::{CliArgs, SimConfig},
config::{AppConfig, CliArgs, SimConfig},
emulator::SimId,
persistence::Persistence,
};
@@ -110,6 +110,10 @@ fn main() -> Result<()> {
if args.profile {
builder = builder.start_paused(true)
}
let mut config = AppConfig::load(&persistence);
if config.update(&args) {
let _ = config.save(&persistence);
}
let p1 = SimConfig::load(&persistence, SimId::Player1);
let p2 = SimConfig::load(&persistence, SimId::Player2);
@@ -143,6 +147,7 @@ fn main() -> Result<()> {
handle,
persistence,
args,
config,
))?;
Ok(())
}