use std::sync::Mutex;
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
pub use crate::gfx::quality_preset::QualityPreset;
pub use concinnity_core::render::rt_geom::RtDynamicMode;
use crate::gfx::quality_preset::{preset_at, preset_index};
static ENABLED: AtomicBool = AtomicBool::new(false);
static PENDING_ANIMATIONS: AtomicBool = AtomicBool::new(false);
static CAPTURE: AtomicBool = AtomicBool::new(false);
static VALIDATION: AtomicU8 = AtomicU8::new(0);
static QUALITY_PRESET: AtomicU8 = AtomicU8::new(0);
static RT_DYNAMIC: AtomicU8 = AtomicU8::new(0);
const RT_DYNAMIC_ORDER: [RtDynamicMode; 4] = [
RtDynamicMode::Off,
RtDynamicMode::Auto,
RtDynamicMode::Rebuild,
RtDynamicMode::Tlas,
];
static RT_SKINNED_GEOMETRY: AtomicU8 = AtomicU8::new(0);
static WORLD_JSONL_PATH: Mutex<Option<String>> = Mutex::new(None);
pub fn set_enabled(v: bool) {
ENABLED.store(v, Ordering::SeqCst);
}
pub(crate) fn enabled() -> bool {
ENABLED.load(Ordering::SeqCst)
}
pub(crate) fn set_capture(v: bool) {
CAPTURE.store(v, Ordering::SeqCst);
}
pub(crate) fn capture() -> bool {
CAPTURE.load(Ordering::SeqCst)
}
pub fn set_pending_animations() {
PENDING_ANIMATIONS.store(true, Ordering::SeqCst);
}
pub fn take_pending_animations() -> bool {
PENDING_ANIMATIONS.swap(false, Ordering::SeqCst)
}
pub fn set_validation(v: Option<bool>) {
let encoded = match v {
None => 0,
Some(false) => 1,
Some(true) => 2,
};
VALIDATION.store(encoded, Ordering::SeqCst);
}
pub(crate) fn validation() -> Option<bool> {
match VALIDATION.load(Ordering::SeqCst) {
1 => Some(false),
2 => Some(true),
_ => None,
}
}
pub(crate) fn resolve_validation() -> bool {
validation().unwrap_or(cfg!(debug_assertions))
}
pub fn set_quality_preset(preset: Option<QualityPreset>) {
let encoded = preset.map_or(0, |p| preset_index(p) as u8 + 1);
QUALITY_PRESET.store(encoded, Ordering::SeqCst);
}
pub(crate) fn quality_preset() -> Option<QualityPreset> {
match QUALITY_PRESET.load(Ordering::SeqCst) {
0 => None,
n => Some(preset_at(n as usize - 1)),
}
}
pub(crate) fn resolve_quality_preset(persisted: Option<QualityPreset>) -> Option<QualityPreset> {
quality_preset().or(persisted)
}
pub fn set_rt_dynamic(mode: Option<RtDynamicMode>) {
let encoded = mode.map_or(0, |m| {
RT_DYNAMIC_ORDER
.iter()
.position(|&candidate| candidate == m)
.expect("RT_DYNAMIC_ORDER covers every mode") as u8
+ 1
});
RT_DYNAMIC.store(encoded, Ordering::SeqCst);
}
pub(crate) fn rt_dynamic() -> Option<RtDynamicMode> {
RT_DYNAMIC_ORDER
.get(RT_DYNAMIC.load(Ordering::SeqCst).wrapping_sub(1) as usize)
.copied()
}
pub(crate) fn resolve_rt_dynamic() -> RtDynamicMode {
rt_dynamic().unwrap_or_default()
}
pub fn set_rt_skinned_geometry(v: Option<bool>) {
let encoded = match v {
None => 0,
Some(false) => 1,
Some(true) => 2,
};
RT_SKINNED_GEOMETRY.store(encoded, Ordering::SeqCst);
}
pub(crate) fn rt_skinned_geometry() -> Option<bool> {
match RT_SKINNED_GEOMETRY.load(Ordering::SeqCst) {
1 => Some(false),
2 => Some(true),
_ => None,
}
}
pub(crate) fn resolve_rt_skinned_geometry() -> bool {
rt_skinned_geometry().unwrap_or(true)
}
pub fn set_world_jsonl_path(path: Option<String>) {
*WORLD_JSONL_PATH.lock().unwrap() = path;
}
pub(crate) fn world_jsonl_path() -> Option<String> {
WORLD_JSONL_PATH.lock().unwrap().clone()
}
#[cfg(test)]
pub(crate) struct WriteAccess {
_guard: concinnity_testing::ExclusiveAccess,
enabled: bool,
validation: Option<bool>,
quality_preset: Option<QualityPreset>,
rt_dynamic: Option<RtDynamicMode>,
rt_skinned_geometry: Option<bool>,
world_jsonl_path: Option<String>,
}
#[cfg(test)]
pub(crate) fn write_access() -> WriteAccess {
WriteAccess {
_guard: concinnity_testing::exclusive(),
enabled: enabled(),
validation: validation(),
quality_preset: quality_preset(),
rt_dynamic: rt_dynamic(),
rt_skinned_geometry: rt_skinned_geometry(),
world_jsonl_path: world_jsonl_path(),
}
}
#[cfg(test)]
impl Drop for WriteAccess {
fn drop(&mut self) {
set_enabled(self.enabled);
set_validation(self.validation);
set_quality_preset(self.quality_preset);
set_rt_dynamic(self.rt_dynamic);
set_rt_skinned_geometry(self.rt_skinned_geometry);
set_world_jsonl_path(self.world_jsonl_path.take());
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn defaults_off_and_round_trips() {
let _flags = write_access();
set_enabled(false);
assert!(!enabled());
set_enabled(true);
assert!(enabled());
}
#[test]
fn validation_tristate_round_trips() {
let _flags = write_access();
set_validation(None);
assert_eq!(validation(), None);
set_validation(Some(true));
assert_eq!(validation(), Some(true));
set_validation(Some(false));
assert_eq!(validation(), Some(false));
}
#[test]
fn every_quality_preset_round_trips_through_the_flag() {
let _flags = write_access();
set_quality_preset(None);
assert_eq!(quality_preset(), None);
for preset in QualityPreset::ALL {
set_quality_preset(Some(preset));
assert_eq!(quality_preset(), Some(preset));
}
}
#[test]
fn the_quality_flag_outranks_the_persisted_choice() {
let _flags = write_access();
set_quality_preset(None);
assert_eq!(resolve_quality_preset(None), None);
assert_eq!(
resolve_quality_preset(Some(QualityPreset::Auto)),
Some(QualityPreset::Auto)
);
set_quality_preset(Some(QualityPreset::Ultra));
assert_eq!(
resolve_quality_preset(Some(QualityPreset::Auto)),
Some(QualityPreset::Ultra)
);
assert_eq!(resolve_quality_preset(None), Some(QualityPreset::Ultra));
}
#[test]
fn every_rt_dynamic_mode_round_trips_and_unset_is_auto() {
let _flags = write_access();
set_rt_dynamic(None);
assert_eq!(rt_dynamic(), None);
assert_eq!(resolve_rt_dynamic(), RtDynamicMode::Auto);
for mode in RT_DYNAMIC_ORDER {
set_rt_dynamic(Some(mode));
assert_eq!(rt_dynamic(), Some(mode));
assert_eq!(resolve_rt_dynamic(), mode);
}
}
#[test]
fn skinned_rt_geometry_is_in_unless_the_flag_clears_it() {
let _flags = write_access();
set_rt_skinned_geometry(None);
assert_eq!(rt_skinned_geometry(), None);
assert!(resolve_rt_skinned_geometry());
set_rt_skinned_geometry(Some(true));
assert!(resolve_rt_skinned_geometry());
set_rt_skinned_geometry(Some(false));
assert!(!resolve_rt_skinned_geometry());
}
#[test]
fn the_launch_flag_outranks_the_build_profile() {
let _flags = write_access();
set_validation(None);
assert_eq!(resolve_validation(), cfg!(debug_assertions));
set_validation(Some(false));
assert!(!resolve_validation());
set_validation(Some(true));
assert!(resolve_validation());
}
}