#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum TrellisSpeedMode {
Thorough,
#[default]
Adaptive,
Level(u8),
Custom {
tier1_threshold: u8,
tier1_lookback: u8,
tier1_candidates: u8,
tier2_threshold: u8,
tier2_lookback: u8,
tier2_candidates: u8,
},
}
impl TrellisSpeedMode {
#[inline]
pub fn get_limits(&self, nonzero_count: i32) -> (usize, usize) {
match *self {
Self::Thorough => (63, 16),
Self::Adaptive => {
if nonzero_count > 55 {
(8, 3)
} else if nonzero_count > 48 {
(16, 4)
} else {
(63, 16)
}
}
Self::Level(level) => {
let level = level.min(10) as i32;
if level == 0 {
return (63, 16);
}
let threshold = 61 - level * 3;
if nonzero_count > threshold {
let lookback = (26 - level * 2).max(4) as usize;
let candidates = (9 - (level + 1) / 2).max(2) as usize;
(lookback, candidates)
} else {
(63, 16)
}
}
Self::Custom {
tier1_threshold,
tier1_lookback,
tier1_candidates,
tier2_threshold,
tier2_lookback,
tier2_candidates,
} => {
if nonzero_count > tier1_threshold as i32 {
(tier1_lookback as usize, tier1_candidates as usize)
} else if nonzero_count > tier2_threshold as i32 {
(tier2_lookback as usize, tier2_candidates as usize)
} else {
(63, 16)
}
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct TrellisConfig {
pub enabled: bool,
pub dc_enabled: bool,
pub lambda_log_scale1: f32,
pub lambda_log_scale2: f32,
pub speed_mode: TrellisSpeedMode,
pub delta_dc_weight: f32,
}
const DEFAULT_LAMBDA_LOG_SCALE1: f32 = 14.75;
const DEFAULT_LAMBDA_LOG_SCALE2: f32 = 16.5;
impl Default for TrellisConfig {
fn default() -> Self {
Self {
enabled: true,
dc_enabled: true,
lambda_log_scale1: DEFAULT_LAMBDA_LOG_SCALE1,
lambda_log_scale2: DEFAULT_LAMBDA_LOG_SCALE2,
speed_mode: TrellisSpeedMode::Adaptive,
delta_dc_weight: 0.0,
}
}
}
impl TrellisConfig {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub const fn disabled() -> Self {
Self {
enabled: false,
dc_enabled: false,
lambda_log_scale1: DEFAULT_LAMBDA_LOG_SCALE1,
lambda_log_scale2: DEFAULT_LAMBDA_LOG_SCALE2,
speed_mode: TrellisSpeedMode::Adaptive,
delta_dc_weight: 0.0,
}
}
#[must_use]
pub fn favor_size() -> Self {
Self {
lambda_log_scale1: 14.0, lambda_log_scale2: 17.0, ..Self::default()
}
}
#[must_use]
pub fn favor_quality() -> Self {
Self {
lambda_log_scale1: 15.5, lambda_log_scale2: 16.0, ..Self::default()
}
}
#[must_use]
pub fn thorough() -> Self {
Self {
speed_mode: TrellisSpeedMode::Thorough,
..Self::default()
}
}
#[must_use]
pub fn ac_trellis(mut self, enabled: bool) -> Self {
self.enabled = enabled;
self
}
#[must_use]
pub fn dc_trellis(mut self, enabled: bool) -> Self {
self.dc_enabled = enabled;
self
}
#[must_use]
pub fn lambda_scales(mut self, scale1: f32, scale2: f32) -> Self {
self.lambda_log_scale1 = scale1;
self.lambda_log_scale2 = scale2;
self
}
#[must_use]
pub fn rd_factor(mut self, factor: f32) -> Self {
self.lambda_log_scale1 = DEFAULT_LAMBDA_LOG_SCALE1 + factor.log2();
self
}
#[must_use]
pub fn speed_mode(mut self, mode: TrellisSpeedMode) -> Self {
self.speed_mode = mode;
self
}
#[deprecated(
since = "0.7.0",
note = "Use speed_mode(TrellisSpeedMode::Level(n)) instead"
)]
#[must_use]
pub fn speed_level(mut self, level: u8) -> Self {
self.speed_mode = TrellisSpeedMode::Level(level.min(10));
self
}
#[must_use]
pub fn delta_dc_weight(mut self, weight: f32) -> Self {
self.delta_dc_weight = weight.max(0.0);
self
}
#[must_use]
pub fn is_ac_enabled(&self) -> bool {
self.enabled
}
#[must_use]
pub fn is_dc_enabled(&self) -> bool {
self.dc_enabled
}
#[must_use]
pub fn get_speed_mode(&self) -> TrellisSpeedMode {
self.speed_mode
}
#[deprecated(since = "0.7.0", note = "Use get_speed_mode() instead")]
#[must_use]
pub fn get_speed_level(&self) -> u8 {
match self.speed_mode {
TrellisSpeedMode::Thorough => 0,
TrellisSpeedMode::Adaptive => 7,
TrellisSpeedMode::Level(l) => l,
TrellisSpeedMode::Custom { .. } => 7, }
}
#[must_use]
pub fn is_enabled(&self) -> bool {
self.enabled || self.dc_enabled
}
#[must_use]
pub fn get_delta_dc_weight(&self) -> f32 {
self.delta_dc_weight
}
#[must_use]
pub fn lambda_log_scale1(&self) -> f32 {
self.lambda_log_scale1
}
#[must_use]
pub fn lambda_log_scale2(&self) -> f32 {
self.lambda_log_scale2
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default() {
let config = TrellisConfig::default();
assert!(config.enabled);
assert!(config.dc_enabled);
assert_eq!(config.speed_mode, TrellisSpeedMode::Adaptive);
assert!((config.lambda_log_scale1 - 14.75).abs() < 0.01);
assert!((config.delta_dc_weight - 0.0).abs() < 0.001);
}
#[test]
fn test_disabled() {
let config = TrellisConfig::disabled();
assert!(!config.enabled);
assert!(!config.dc_enabled);
assert!(!config.is_enabled());
}
#[test]
fn test_presets() {
let favor_size = TrellisConfig::favor_size();
assert!(favor_size.lambda_log_scale1 < DEFAULT_LAMBDA_LOG_SCALE1);
let favor_quality = TrellisConfig::favor_quality();
assert!(favor_quality.lambda_log_scale1 > DEFAULT_LAMBDA_LOG_SCALE1);
let thorough = TrellisConfig::thorough();
assert_eq!(thorough.speed_mode, TrellisSpeedMode::Thorough);
}
#[test]
fn test_builder_chain() {
let config = TrellisConfig::default()
.ac_trellis(true)
.dc_trellis(false)
.speed_mode(TrellisSpeedMode::Level(5))
.lambda_scales(15.0, 17.0);
assert!(config.enabled);
assert!(!config.dc_enabled);
assert_eq!(config.speed_mode, TrellisSpeedMode::Level(5));
assert!((config.lambda_log_scale1 - 15.0).abs() < 0.01);
assert!((config.lambda_log_scale2 - 17.0).abs() < 0.01);
}
#[test]
fn test_rd_factor() {
let config = TrellisConfig::default().rd_factor(1.0);
assert!((config.lambda_log_scale1 - DEFAULT_LAMBDA_LOG_SCALE1).abs() < 0.01);
let config = TrellisConfig::default().rd_factor(2.0);
assert!((config.lambda_log_scale1 - (DEFAULT_LAMBDA_LOG_SCALE1 + 1.0)).abs() < 0.01);
let config = TrellisConfig::default().rd_factor(0.5);
assert!((config.lambda_log_scale1 - (DEFAULT_LAMBDA_LOG_SCALE1 - 1.0)).abs() < 0.01);
}
#[test]
#[allow(deprecated)]
fn test_speed_level_clamping() {
let config = TrellisConfig::default().speed_level(15);
assert_eq!(config.speed_mode, TrellisSpeedMode::Level(10)); }
#[test]
fn test_speed_mode_variants() {
let thorough = TrellisConfig::default().speed_mode(TrellisSpeedMode::Thorough);
assert_eq!(thorough.speed_mode, TrellisSpeedMode::Thorough);
let adaptive = TrellisConfig::default().speed_mode(TrellisSpeedMode::Adaptive);
assert_eq!(adaptive.speed_mode, TrellisSpeedMode::Adaptive);
let level = TrellisConfig::default().speed_mode(TrellisSpeedMode::Level(5));
assert_eq!(level.speed_mode, TrellisSpeedMode::Level(5));
let custom = TrellisConfig::default().speed_mode(TrellisSpeedMode::Custom {
tier1_threshold: 55,
tier1_lookback: 8,
tier1_candidates: 3,
tier2_threshold: 48,
tier2_lookback: 16,
tier2_candidates: 4,
});
assert!(matches!(custom.speed_mode, TrellisSpeedMode::Custom { .. }));
}
#[test]
fn test_speed_mode_get_limits() {
assert_eq!(TrellisSpeedMode::Thorough.get_limits(60), (63, 16));
assert_eq!(TrellisSpeedMode::Adaptive.get_limits(56), (8, 3)); assert_eq!(TrellisSpeedMode::Adaptive.get_limits(50), (16, 4)); assert_eq!(TrellisSpeedMode::Adaptive.get_limits(40), (63, 16));
assert_eq!(TrellisSpeedMode::Level(0).get_limits(60), (63, 16));
}
#[test]
fn test_delta_dc_weight() {
let config = TrellisConfig::default().delta_dc_weight(0.5);
assert!((config.get_delta_dc_weight() - 0.5).abs() < 0.001);
let config = TrellisConfig::default().delta_dc_weight(-1.0);
assert!((config.get_delta_dc_weight() - 0.0).abs() < 0.001);
}
}