use crate::components::{PostProcessConfig, PostProcessResolve, ShadowUpdate};
use crate::config::GraphicsSettings;
use crate::gfx::graphics_system::{clamp_quality_cycle, set_quality_toggle};
use crate::gfx::quality_preset::{QualityCeiling, clamp_shadow_update};
use crate::gfx::render_types::PostProcessTunables;
use crate::gfx::settings::slider_apply_value;
pub(crate) fn shadow_map_size(
authored: u32,
user: &GraphicsSettings,
ceiling: &QualityCeiling,
) -> u32 {
user.shadow_map_size
.unwrap_or(authored.min(ceiling.shadow_map_size))
}
pub(crate) fn shadow_update(
authored: ShadowUpdate,
user: &GraphicsSettings,
ceiling: &QualityCeiling,
) -> ShadowUpdate {
match user.shadow_update {
Some(v) => v,
None => clamp_shadow_update(authored, ceiling),
}
}
pub(crate) fn shadow_distance(
authored: u32,
user: &GraphicsSettings,
ceiling: &QualityCeiling,
) -> u32 {
user.shadow_distance
.unwrap_or(authored.min(ceiling.shadow_distance))
}
pub(crate) fn shadow_cascades(
authored: u32,
user: &GraphicsSettings,
ceiling: &QualityCeiling,
) -> u32 {
user.shadow_cascades
.unwrap_or(authored.min(ceiling.shadow_cascades))
}
pub(crate) fn anisotropy(authored: u32, user: &GraphicsSettings, ceiling: &QualityCeiling) -> u32 {
user.anisotropy.unwrap_or(authored.min(ceiling.anisotropy))
}
pub(crate) fn post_process_params(
config: Option<&PostProcessConfig>,
user: &GraphicsSettings,
) -> PostProcessTunables {
let mut params = config
.map(|c| c.resolve())
.unwrap_or(PostProcessTunables::DEFAULT);
if let Some(v) = user.exposure_ev {
params.exposure = slider_apply_value("exposure", v);
}
if let Some(v) = user.bloom_intensity {
params.bloom_intensity = slider_apply_value("bloom_intensity", v);
}
if let Some(v) = user.bloom_threshold {
params.bloom_threshold = slider_apply_value("bloom_threshold", v);
}
if let Some(v) = user.bloom_knee {
params.bloom_knee = slider_apply_value("bloom_knee", v);
}
if let Some(v) = user.vignette {
params.vignette = slider_apply_value("vignette", v);
}
if let Some(v) = user.lut_strength {
params.lut_strength = slider_apply_value("lut_strength", v);
}
params
}
pub(crate) fn ambient_intensity(
config: Option<&PostProcessConfig>,
user: &GraphicsSettings,
) -> f32 {
let world = config.map(|c| c.ambient_intensity()).unwrap_or(1.0);
slider_apply_value("ambient_intensity", user.ambient_intensity.unwrap_or(world))
}
pub(crate) fn overlay_quality_scalars(cfg: &mut PostProcessConfig, user: &GraphicsSettings) {
if let Some(v) = user.ssao_radius {
cfg.ssao_radius = slider_apply_value("ssao_radius", v);
}
if let Some(v) = user.ssao_intensity {
cfg.ssao_intensity = slider_apply_value("ssao_intensity", v);
}
if let Some(v) = user.ssr_intensity {
cfg.ssr_intensity = slider_apply_value("ssr_intensity", v);
}
if let Some(v) = user.ssr_max_distance {
cfg.ssr_max_distance = slider_apply_value("ssr_max_distance", v);
}
if let Some(v) = user.ssgi_intensity {
cfg.ssgi_intensity = slider_apply_value("ssgi_intensity", v);
}
if let Some(v) = user.ssgi_max_distance {
cfg.ssgi_max_distance = slider_apply_value("ssgi_max_distance", v);
}
if let Some(v) = user.auto_exposure_min_ev {
cfg.auto_exposure_min_ev = slider_apply_value("auto_exposure_min_ev", v);
}
if let Some(v) = user.auto_exposure_max_ev {
cfg.auto_exposure_max_ev = slider_apply_value("auto_exposure_max_ev", v);
}
if let Some(v) = user.auto_exposure_speed {
cfg.auto_exposure_speed = slider_apply_value("auto_exposure_speed", v);
}
}
pub(crate) fn overlay_quality_overrides(cfg: &mut PostProcessConfig, user: &GraphicsSettings) {
for (key, value) in [
("ssao", user.ssao),
("ssr", user.ssr),
("ray_traced_reflections", user.ray_traced_reflections),
("ssgi", user.ssgi),
("auto_exposure", user.auto_exposure),
] {
if let Some(v) = value {
set_quality_toggle(cfg, key, v);
}
}
if let Some(v) = user.aa_mode {
cfg.aa_mode = v;
}
if let Some(v) = user.ssgi_resolution {
cfg.ssgi_resolution = v;
}
if let Some(v) = user.ssgi_rays {
cfg.ssgi_rays = v;
}
if let Some(v) = user.ssgi_steps {
cfg.ssgi_steps = v;
}
if let Some(v) = user.reflection_blur_resolution {
cfg.reflection_blur_resolution = v;
}
overlay_quality_scalars(cfg, user);
}
pub(crate) fn clamp_quality_under_ceiling(
cfg: &mut PostProcessConfig,
user: &GraphicsSettings,
ceiling: &QualityCeiling,
) {
for (key, overridden, allowed) in [
("ssao", user.ssao.is_some(), ceiling.ssao),
("ssr", user.ssr.is_some(), ceiling.ssr),
(
"ray_traced_reflections",
user.ray_traced_reflections.is_some(),
ceiling.ray_traced_reflections,
),
("ssgi", user.ssgi.is_some(), ceiling.ssgi),
(
"auto_exposure",
user.auto_exposure.is_some(),
ceiling.auto_exposure,
),
] {
if !overridden && !allowed {
set_quality_toggle(cfg, key, false);
}
}
for key in crate::gfx::settings::QUALITY_CYCLE_KEYS {
let overridden = match key {
"aa_mode" => user.aa_mode.is_some(),
"ssgi_resolution" => user.ssgi_resolution.is_some(),
"ssgi_rays" => user.ssgi_rays.is_some(),
"ssgi_steps" => user.ssgi_steps.is_some(),
"reflection_blur_resolution" => user.reflection_blur_resolution.is_some(),
_ => false,
};
clamp_quality_cycle(cfg, key, ceiling, overridden);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::components::{AaMode, IndirectLighting};
use crate::gfx::backend::{GpuProfile, GpuTier};
use crate::gfx::quality_preset::{QualityPreset, resolve_ceiling};
fn ceiling_for(preset: QualityPreset, tier: GpuTier) -> QualityCeiling {
resolve_ceiling(
preset,
&GpuProfile {
tier,
..GpuProfile::UNKNOWN
},
)
}
#[test]
fn the_low_ceiling_clamps_the_schema_defaults_off() {
let mut cfg = PostProcessConfig::default();
clamp_quality_under_ceiling(
&mut cfg,
&GraphicsSettings::default(),
&ceiling_for(QualityPreset::Low, GpuTier::Integrated),
);
assert!(!cfg.ssao);
assert!(!cfg.ssr);
assert!(!cfg.ray_traced_reflections);
assert_eq!(cfg.indirect_lighting, IndirectLighting::Ibl);
assert_eq!(cfg.aa_mode, AaMode::Fxaa);
}
#[test]
fn the_top_ceiling_leaves_the_schema_defaults_alone() {
let mut cfg = PostProcessConfig::default();
clamp_quality_under_ceiling(
&mut cfg,
&GraphicsSettings::default(),
&ceiling_for(QualityPreset::Ultra, GpuTier::HighDiscrete),
);
assert!(cfg.ssao);
assert!(cfg.ssr);
assert!(cfg.ray_traced_reflections);
assert_eq!(cfg.indirect_lighting, IndirectLighting::Ssgi);
assert_eq!(cfg.aa_mode, AaMode::Taa);
}
#[test]
fn a_ceiling_never_turns_a_feature_back_on() {
let mut cfg = PostProcessConfig {
ssao: false,
ssr: false,
ray_traced_reflections: false,
indirect_lighting: IndirectLighting::Ibl,
aa_mode: AaMode::Off,
..Default::default()
};
clamp_quality_under_ceiling(
&mut cfg,
&GraphicsSettings::default(),
&ceiling_for(QualityPreset::Ultra, GpuTier::HighDiscrete),
);
assert!(!cfg.ssao);
assert!(!cfg.ssr);
assert!(!cfg.ray_traced_reflections);
assert_eq!(cfg.indirect_lighting, IndirectLighting::Ibl);
assert_eq!(cfg.aa_mode, AaMode::Off);
}
#[test]
fn a_user_override_wins_over_the_ceiling() {
let user = GraphicsSettings {
ssao: Some(true),
aa_mode: Some(AaMode::Taa),
..GraphicsSettings::default()
};
let mut cfg = PostProcessConfig {
ssao: false,
aa_mode: AaMode::Off,
..Default::default()
};
overlay_quality_overrides(&mut cfg, &user);
assert!(cfg.ssao, "the override applies over the world's value");
clamp_quality_under_ceiling(
&mut cfg,
&user,
&ceiling_for(QualityPreset::Low, GpuTier::Integrated),
);
assert!(
cfg.ssao,
"the Low ceiling does not clear an explicit choice"
);
assert_eq!(cfg.aa_mode, AaMode::Taa);
assert!(!cfg.ssr);
}
}