Skip to content

Commit ed552c4

Browse files
committed
Add developer CLI flags for tuning encoder internals
Currently adds the following: - `--deblock-strength` - `--deblock-sharpness` - `--ip-qidx-ratio` - `--temporal-rdo-strength` These flags will be available only if the `devel` feature flag is active, as they are intended to be used to ease development and not to be tweaked by end users. rav1e continues to subscribe to the philosophy that end users should not need to be cargo culting their command lines to get optimal results, and we would prefer to tune the encoder internals so that users do not have to.
1 parent f869e16 commit ed552c4

File tree

8 files changed

+138
-15
lines changed

8 files changed

+138
-15
lines changed

.github/workflows/rav1e.yml

+1-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ on:
1212

1313
jobs:
1414
rustfmt-clippy:
15-
1615
runs-on: ubuntu-22.04
1716

1817
steps:
@@ -193,7 +192,7 @@ jobs:
193192
- name: Check extra features
194193
if: matrix.toolchain == 'stable' && matrix.conf == 'check-extra-feats'
195194
run: |
196-
cargo check --features=check_asm,capi,dump_lookahead_data,serialize,bench --all-targets
195+
cargo check --features=check_asm,capi,dump_lookahead_data,serialize,bench,devel --all-targets
197196
- name: Check extra features
198197
if: matrix.toolchain == 'stable' && matrix.conf == 'check-unstable-feats'
199198
run: |

Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ default-run = "rav1e"
2424

2525
[features]
2626
unstable = []
27+
# Exposes extra flags for tuning compiler internals.
28+
# Intended to be used by developers to find ideal internal settings.
29+
devel = []
2730
channel-api = ["crossbeam"]
2831
decode_test = ["aom-sys"]
2932
decode_test_dav1d = ["dav1d-sys"]

src/api/config/encoder.rs

+33
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,38 @@ pub struct EncoderConfig {
108108

109109
/// Settings which affect the encoding speed vs. quality trade-off.
110110
pub speed_settings: SpeedSettings,
111+
112+
/// Advanced settings which are intended for use by developers.
113+
/// Non-developers should use the default values.
114+
pub advanced_flags: AdvancedTuning,
115+
}
116+
117+
/// Advanced settings that are intended for use by developers
118+
/// for tuning compiler internals.
119+
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
120+
pub struct AdvancedTuning {
121+
/// Controls the strength of the deblock filter, as a multiplier to the default.
122+
pub deblock_strength: f32,
123+
/// Controls the sharpness of the deblock filter. Accepts a value from 0-7.
124+
pub deblock_sharpness: u8,
125+
/// Controls the ratio between intra frame and inter frame quantizers, as a multiplier.
126+
/// Default is 1.0. Higher values create a higher quantizer difference, while lower values
127+
/// create a lower quantizer difference. A value of 0.0 would mean that I and P quantizers
128+
/// are the same.
129+
pub ip_qidx_ratio: f32,
130+
/// Controls the strength of temporal RDO, as a multiplier to the default.
131+
pub temporal_rdo_strength: f32,
132+
}
133+
134+
impl Default for AdvancedTuning {
135+
fn default() -> Self {
136+
Self {
137+
deblock_strength: 1.0,
138+
deblock_sharpness: 0,
139+
ip_qidx_ratio: 1.0,
140+
temporal_rdo_strength: 1.0,
141+
}
142+
}
111143
}
112144

113145
/// Default preset for `EncoderConfig`: it is a balance between quality and
@@ -163,6 +195,7 @@ impl EncoderConfig {
163195
tile_rows: 0,
164196
tiles: 0,
165197
speed_settings: SpeedSettings::from_preset(speed),
198+
advanced_flags: Default::default(),
166199
}
167200
}
168201

src/api/test.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2164,6 +2164,7 @@ fn log_q_exp_overflow() {
21642164
},
21652165
..Default::default()
21662166
},
2167+
advanced_flags: Default::default(),
21672168
};
21682169
let config = Config::new().with_encoder_config(enc).with_threads(1);
21692170

@@ -2240,6 +2241,7 @@ fn guess_frame_subtypes_assert() {
22402241
},
22412242
..Default::default()
22422243
},
2244+
advanced_flags: Default::default(),
22432245
};
22442246
let config = Config::new().with_encoder_config(enc).with_threads(1);
22452247

src/bin/common.rs

+40-1
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,34 @@ pub struct CliOptions {
242242
#[clap(long, short, value_parser, help_heading = "DEBUGGING")]
243243
pub reconstruction: Option<PathBuf>,
244244

245+
/// Controls the strength of the deblock filter, as a multiplier to the default.
246+
#[clap(long, value_parser = positive_float, default_value_t=1.0f32, help_heading = "ADVANCED")]
247+
pub deblock_strength: f32,
248+
/// Controls the sharpness of the deblock filter. Accepts a value from 0-7.
249+
#[clap(long, value_parser = clap::value_parser!(u8).range(0..=7), default_value_t=0, help_heading = "ADVANCED")]
250+
pub deblock_sharpness: u8,
251+
/// Controls the ratio between intra frame and inter frame quantizers, as a multiplier.
252+
/// Higher values create a higher quantizer difference, while lower values
253+
/// create a lower quantizer difference. A value of 0.0 would mean that I and P quantizers
254+
/// are the same.
255+
#[clap(long, value_parser = positive_float, default_value_t=1.0f32, help_heading = "ADVANCED")]
256+
pub ip_qidx_ratio: f32,
257+
/// Controls the strength of temporal RDO, as a multiplier to the default.
258+
#[clap(long, value_parser = positive_float, default_value_t=1.0f32, help_heading = "ADVANCED")]
259+
pub temporal_rdo_strength: f32,
260+
245261
#[clap(subcommand)]
246262
pub command: Option<Commands>,
247263
}
248264

265+
fn positive_float(input: &str) -> Result<f32, String> {
266+
let value = input.parse::<f32>().map_err(|e| e.to_string())?;
267+
if value < 0.0 {
268+
return Err("Value must not be negative".to_string());
269+
}
270+
Ok(value)
271+
}
272+
249273
fn get_version() -> &'static str {
250274
static VERSION_STR: Lazy<String> = Lazy::new(|| {
251275
format!(
@@ -299,7 +323,7 @@ pub enum Commands {
299323
#[clap(long, short, value_parser)]
300324
save_config: Option<PathBuf>,
301325
/// Load the encoder configuration from a toml file
302-
#[clap(long, short, value_parser, conflicts_with = "save-config")]
326+
#[clap(long, short, value_parser, conflicts_with = "save_config")]
303327
load_config: Option<PathBuf>,
304328
},
305329
}
@@ -484,6 +508,16 @@ pub fn parse_cli() -> Result<ParsedCliOptions, CliError> {
484508
})
485509
}
486510

511+
#[cfg(feature = "devel")]
512+
const fn parse_advanced_flags(cli: &CliOptions) -> AdvancedTuning {
513+
AdvancedTuning {
514+
deblock_strength: cli.deblock_strength,
515+
deblock_sharpness: cli.deblock_sharpness,
516+
ip_qidx_ratio: cli.ip_qidx_ratio,
517+
temporal_rdo_strength: cli.temporal_rdo_strength,
518+
}
519+
}
520+
487521
fn parse_config(matches: &CliOptions) -> Result<EncoderConfig, CliError> {
488522
let maybe_quantizer = matches.quantizer;
489523
let maybe_bitrate = matches.bitrate;
@@ -674,5 +708,10 @@ fn parse_config(matches: &CliOptions) -> Result<EncoderConfig, CliError> {
674708
cfg.speed_settings.scene_detection_mode = SceneDetectionSpeed::None;
675709
}
676710

711+
#[cfg(feature = "devel")]
712+
{
713+
cfg.advanced_flags = parse_advanced_flags(matches);
714+
}
715+
677716
Ok(cfg)
678717
}

src/encoder.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ impl<T: Pixel> FrameState<T> {
501501
cdfs: CDFContext::new(0),
502502
context_update_tile_id: 0,
503503
max_tile_size_bytes: 0,
504-
deblock: Default::default(),
504+
deblock: DeblockState::new(&fi.config),
505505
segmentation: Default::default(),
506506
restoration: rs,
507507
frame_me_stats: FrameMEStats::new_arc_array(fi.w_in_b, fi.h_in_b),
@@ -543,6 +543,19 @@ pub struct DeblockState {
543543
pub block_delta_multi: bool,
544544
}
545545

546+
impl DeblockState {
547+
pub fn new(config: &EncoderConfig) -> Self {
548+
let mut state = DeblockState { ..Default::default() };
549+
for level in &mut state.levels {
550+
*level = ((*level as f32) * config.advanced_flags.deblock_strength)
551+
.min(MAX_LOOP_FILTER as f32)
552+
.round() as u8;
553+
}
554+
state.sharpness = config.advanced_flags.deblock_sharpness;
555+
state
556+
}
557+
}
558+
546559
impl Default for DeblockState {
547560
fn default() -> Self {
548561
DeblockState {

src/rate.rs

+27-11
Original file line numberDiff line numberDiff line change
@@ -703,11 +703,12 @@ impl RCState {
703703

704704
pub(crate) fn select_first_pass_qi(
705705
&self, bit_depth: usize, fti: usize, chroma_sampling: ChromaSampling,
706+
ft_ratio: f64,
706707
) -> QuantizerParameters {
707708
// Adjust the quantizer for the frame type, result is Q57:
708709
let log_q = ((self.pass1_log_base_q + (1i64 << 11)) >> 12)
709710
* (MQP_Q12[fti] as i64)
710-
+ DQP_Q57[fti];
711+
+ (DQP_Q57[fti] as f64 * ft_ratio) as i64;
711712
QuantizerParameters::new_from_log_q(
712713
self.pass1_log_base_q,
713714
log_q,
@@ -723,14 +724,20 @@ impl RCState {
723724
&self, ctx: &ContextInner<T>, output_frameno: u64, fti: usize,
724725
maybe_prev_log_base_q: Option<i64>, log_isqrt_mean_scale: i64,
725726
) -> QuantizerParameters {
727+
let ft_ratio = ctx.config.advanced_flags.ip_qidx_ratio as f64;
728+
726729
// Is rate control active?
727730
if self.target_bitrate <= 0 {
728731
// Rate control is not active.
729732
// Derive quantizer directly from frame type.
730733
let bit_depth = ctx.config.bit_depth;
731734
let chroma_sampling = ctx.config.chroma_sampling;
732-
let (log_base_q, log_q) =
733-
Self::calc_flat_quantizer(ctx.config.quantizer as u8, bit_depth, fti);
735+
let (log_base_q, log_q) = Self::calc_flat_quantizer(
736+
ctx.config.quantizer as u8,
737+
bit_depth,
738+
fti,
739+
ft_ratio,
740+
);
734741
QuantizerParameters::new_from_log_q(
735742
log_base_q,
736743
log_q,
@@ -752,6 +759,7 @@ impl RCState {
752759
ctx.config.bit_depth,
753760
fti,
754761
ctx.config.chroma_sampling,
762+
ft_ratio,
755763
);
756764
}
757765
// Second pass of 2-pass mode: we know exactly how much of each frame
@@ -925,7 +933,7 @@ impl RCState {
925933
// Modulate base quantizer by frame type.
926934
let log_q = ((log_base_q + (1i64 << 11)) >> 12)
927935
* (MQP_Q12[ftj] as i64)
928-
+ DQP_Q57[ftj];
936+
+ (DQP_Q57[ftj] as f64 * ft_ratio) as i64;
929937
// All the fields here are Q57 except for the exponent, which is
930938
// Q6.
931939
bits += (nframes[ftj] as i64)
@@ -959,7 +967,7 @@ impl RCState {
959967
// Modulate base quantizer by frame type.
960968
let mut log_q = ((log_base_q + (1i64 << 11)) >> 12)
961969
* (MQP_Q12[fti] as i64)
962-
+ DQP_Q57[fti];
970+
+ (DQP_Q57[fti] as f64 * ft_ratio) as i64;
963971
// The above allocation looks only at the total rate we'll accumulate
964972
// in the next reservoir_frame_delay frames.
965973
// However, we could overflow the bit reservoir on the very next
@@ -1019,14 +1027,22 @@ impl RCState {
10191027
}
10201028

10211029
if let Some(qi_max) = self.maybe_ac_qi_max {
1022-
let (max_log_base_q, max_log_q) =
1023-
Self::calc_flat_quantizer(qi_max, ctx.config.bit_depth, fti);
1030+
let (max_log_base_q, max_log_q) = Self::calc_flat_quantizer(
1031+
qi_max,
1032+
ctx.config.bit_depth,
1033+
fti,
1034+
ft_ratio,
1035+
);
10241036
log_base_q = cmp::min(log_base_q, max_log_base_q);
10251037
log_q = cmp::min(log_q, max_log_q);
10261038
}
10271039
if self.ac_qi_min > 0 {
1028-
let (min_log_base_q, min_log_q) =
1029-
Self::calc_flat_quantizer(self.ac_qi_min, ctx.config.bit_depth, fti);
1040+
let (min_log_base_q, min_log_q) = Self::calc_flat_quantizer(
1041+
self.ac_qi_min,
1042+
ctx.config.bit_depth,
1043+
fti,
1044+
ft_ratio,
1045+
);
10301046
log_base_q = cmp::max(log_base_q, min_log_base_q);
10311047
log_q = cmp::max(log_q, min_log_q);
10321048
}
@@ -1044,7 +1060,7 @@ impl RCState {
10441060
// Computes a quantizer directly from the frame type and base quantizer index,
10451061
// without consideration for rate control.
10461062
fn calc_flat_quantizer(
1047-
base_qi: u8, bit_depth: usize, fti: usize,
1063+
base_qi: u8, bit_depth: usize, fti: usize, ft_ratio: f64,
10481064
) -> (i64, i64) {
10491065
// TODO: Rename "quantizer" something that indicates it is a quantizer
10501066
// index, and move it somewhere more sensible (or choose a better way to
@@ -1063,7 +1079,7 @@ impl RCState {
10631079
let log_base_q = (log_ac_q + log_dc_q + 1) >> 1;
10641080
// Adjust the quantizer for the frame type, result is Q57:
10651081
let log_q = ((log_base_q + (1i64 << 11)) >> 12) * (MQP_Q12[fti] as i64)
1066-
+ DQP_Q57[fti];
1082+
+ (DQP_Q57[fti] as f64 * ft_ratio) as i64;
10671083
(log_base_q, log_q)
10681084
}
10691085

src/rdo.rs

+18
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ pub fn distortion_scale<T: Pixel>(
455455

456456
let coded_data = fi.coded_frame_data.as_ref().unwrap();
457457
coded_data.distortion_scales[y * coded_data.w_in_imp_b + x]
458+
.strength_adjusted(fi.config.advanced_flags.temporal_rdo_strength as f64)
458459
}
459460

460461
/// # Panics
@@ -504,6 +505,7 @@ pub fn spatiotemporal_scale<T: Pixel>(
504505
.sum::<u64>();
505506
}
506507
DistortionScale(((sum + (den >> 1)) / den) as u32)
508+
.strength_adjusted(fi.config.advanced_flags.temporal_rdo_strength as f64)
507509
}
508510

509511
pub fn distortion_scale_for(
@@ -617,6 +619,22 @@ impl DistortionScale {
617619
pub const fn mul_u64(self, dist: u64) -> u64 {
618620
(self.0 as u64 * dist + (1 << Self::SHIFT >> 1)) >> Self::SHIFT
619621
}
622+
623+
#[inline]
624+
#[cfg(feature = "devel")]
625+
pub fn strength_adjusted(self, strength: f64) -> Self {
626+
let diff = 1.0 - f64::from(self);
627+
let add = diff * strength;
628+
DistortionScale::from((1.0 + add).max(0.0))
629+
}
630+
631+
#[inline(always)]
632+
#[cfg(not(feature = "devel"))]
633+
pub fn strength_adjusted(self, _strength: f64) -> Self {
634+
// If we aren't using a devel build, just return self
635+
// so we do not add any performance cost.
636+
self
637+
}
620638
}
621639

622640
impl std::ops::Mul for DistortionScale {

0 commit comments

Comments
 (0)