use concinnity_core::components::{SettingCommand, SettingOp};
use concinnity_core::ecs::PipelineContext;
use concinnity_core::render::backend::QualitySettings;
use concinnity_core::render::error::RenderError;
use concinnity_core::render::ops::RenderOps;
use super::SettingsState;
use super::apply::RowOptions;
use super::rows::{set_cached_row_label, set_label_content};
use crate::config::Settings;
use crate::gfx::quality_preset;
use crate::gfx::system as gsys;
use crate::settings;
use crate::settings::SettingKey;
use crate::settings::quality_rows::{QUALITY_CYCLES, QUALITY_TOGGLES, QualityCycle, QualityToggle};
impl SettingsState {
pub(super) fn apply_quality_preset(
&mut self,
ctx: &mut PipelineContext,
ops: &mut RenderOps,
cfg: &mut Settings,
cmd: &SettingCommand,
) -> bool {
let opts = settings::options(SettingKey::GraphicsQuality).unwrap_or(&[]);
let cur = quality_preset::preset_index(self.quality_preset);
let next = settings::cycle(cur, opts.len(), cmd.op);
let preset = quality_preset::preset_at(next);
self.quality_preset = preset;
let ceiling = quality_preset::resolve_ceiling(preset, &self.gpu_profile);
self.post_config = self.authored_post_config.clone();
for row in &QUALITY_TOGGLES {
if !(row.allowed)(&ceiling) {
(row.set)(&mut self.post_config, false);
}
}
for row in &QUALITY_CYCLES {
(row.clamp)(&mut self.post_config, &ceiling);
}
self.post_process.fxaa = self.post_config.aa_mode.fxaa_flag();
let quality = gsys::derive_quality_settings(&self.post_config);
record_quality_apply(ops, quality);
let params = self.post_process;
ops.record(move |backend| backend.update_post_process(params));
self.render_scale = quality_preset::more_aggressive_upscale(
self.authored_post_config.upscale_quality,
ceiling.min_upscale,
);
self.shadow_map_size = self.authored_shadow_map_size.min(ceiling.shadow_map_size);
self.shadow_update =
quality_preset::clamp_shadow_update(self.authored_shadow_update, &ceiling);
let update = self.shadow_update;
ops.record(move |backend| backend.set_shadow_update(update));
self.shadow_distance = self.authored_shadow_distance.min(ceiling.shadow_distance);
let distance = self.shadow_distance;
ops.record(move |backend| backend.set_shadow_distance(distance));
self.shadow_cascades = self.authored_shadow_cascades.min(ceiling.shadow_cascades);
let count = self.shadow_cascades;
ops.record(move |backend| backend.set_shadow_cascades(count));
self.anisotropy = self.authored_anisotropy.min(ceiling.anisotropy);
cfg.graphics.quality_preset = Some(preset);
for row in &QUALITY_TOGGLES {
*(row.persisted.get_mut)(&mut cfg.graphics) = None;
}
for row in &QUALITY_CYCLES {
(row.clear)(&mut cfg.graphics);
}
cfg.graphics.shadow_map_size = None;
cfg.graphics.shadow_update = None;
cfg.graphics.shadow_distance = None;
cfg.graphics.shadow_cascades = None;
cfg.graphics.anisotropy = None;
cfg.graphics.render_scale = None;
self.relabel_preset_dependents(ctx);
let label = quality_preset::preset_label(preset, &self.gpu_profile);
if let Some(id) = cmd.value_label {
set_label_content(ctx, id, &label);
}
true
}
fn relabel_preset_dependents(&self, ctx: &mut PipelineContext) {
for row in &QUALITY_TOGGLES {
let on = (row.get)(&self.post_config);
self.relabel_option(ctx, row.key, on as usize);
}
self.relabel_option(
ctx,
SettingKey::RenderScale,
settings::render_scale_index(self.render_scale),
);
for row in &QUALITY_CYCLES {
self.relabel_option(ctx, row.key, (row.index)(&self.post_config));
}
for (key, idx) in [
(
SettingKey::ShadowMapSize,
settings::shadow_resolution_index(self.shadow_map_size),
),
(
SettingKey::ShadowUpdate,
settings::shadow_update_index(self.shadow_update),
),
(
SettingKey::ShadowDistance,
settings::shadow_distance_index(self.shadow_distance),
),
(
SettingKey::ShadowCascades,
settings::shadow_cascades_index(self.shadow_cascades),
),
(
SettingKey::Anisotropy,
settings::anisotropy_index(self.anisotropy),
),
] {
self.relabel_option(ctx, key, idx);
}
}
fn relabel_option(&self, ctx: &mut PipelineContext, key: SettingKey, index: usize) {
if let Some(text) = settings::options(key).and_then(|o| o.get(index).copied()) {
set_cached_row_label(&self.cycle_value_labels, ctx, key, text);
}
}
pub(super) fn apply_quality_toggle(
&mut self,
ctx: &mut PipelineContext,
ops: &mut RenderOps,
cfg: &mut Settings,
row: &QualityToggle,
opts: RowOptions,
op: SettingOp,
) -> &'static str {
let cur = (row.get)(&self.post_config);
let next = settings::cycle(cur as usize, opts.len(), op);
let on = next == 1;
(row.set)(&mut self.post_config, on);
*(row.persisted.get_mut)(&mut cfg.graphics) = Some(on);
self.opt_out_of_preset(ctx, cfg);
let quality = gsys::derive_quality_settings(&self.post_config);
record_quality_apply(ops, quality);
if row.key == SettingKey::AutoExposure {
let params = self.post_process;
ops.record(move |backend| backend.update_post_process(params));
}
opts[next]
}
pub(super) fn apply_quality_cycle(
&mut self,
ctx: &mut PipelineContext,
ops: &mut RenderOps,
cfg: &mut Settings,
row: &QualityCycle,
opts: RowOptions,
op: SettingOp,
) -> &'static str {
let cur = (row.index)(&self.post_config);
let next = settings::cycle(cur, opts.len(), op);
(row.set)(&mut self.post_config, next);
(row.persist)(&self.post_config, &mut cfg.graphics);
self.opt_out_of_preset(ctx, cfg);
let quality = gsys::derive_quality_settings(&self.post_config);
record_quality_apply(ops, quality);
if row.key == SettingKey::AaMode {
self.post_process.fxaa = self.post_config.aa_mode.fxaa_flag();
let params = self.post_process;
ops.record(move |backend| backend.update_post_process(params));
}
opts[next]
}
pub(super) fn opt_out_of_preset(&mut self, ctx: &mut PipelineContext, cfg: &mut Settings) {
self.quality_preset = quality_preset::QualityPreset::Custom;
cfg.graphics.quality_preset = Some(self.quality_preset);
set_cached_row_label(
&self.cycle_value_labels,
ctx,
SettingKey::GraphicsQuality,
self.quality_preset.name(),
);
}
}
pub(super) fn record_quality_apply(ops: &mut RenderOps, quality: QualitySettings) {
ops.record_with(move |backend, out| {
if let Err(e) = backend.apply_quality_settings(quality) {
tracing::error!("SettingsSystem: quality rebuild failed: {e}");
out.memory_pressure |= matches!(e, RenderError::OutOfDeviceMemory(_));
}
});
}