#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum QualityTarget {
VisuallyLossless,
High,
#[default]
Standard,
Low,
Vmaf(u8),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SpeedTier {
Draft,
#[default]
Standard,
Archive,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Rav1eParams {
pub quantizer: usize,
pub speed_preset: u8,
pub tile_rows: usize,
pub tile_cols: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NvencAv1Params {
pub rc_mode: NvencRateControl,
pub cq: u8,
pub preset_guid: [u8; 16],
pub tuning_info: u32,
pub aq_strength: u8,
pub lookahead_depth: u32,
pub num_tile_columns: u32,
pub num_tile_rows: u32,
pub output_annex_b_format: u32,
pub repeat_seq_hdr: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum NvencRateControl {
ConstQp = 0,
VbrTargetQuality = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AmfAv1Params {
pub rc_mode: AmfRateControl,
pub q_index_intra: u8,
pub q_index_inter: u8,
pub qvbr_quality: u8,
pub quality_preset: AmfQualityPreset,
pub gop_size: u32,
pub aq_mode: u32,
pub tiles_per_frame: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i64)]
pub enum AmfQualityPreset {
HighQuality = 10,
Quality = 30,
Balanced = 50,
#[allow(dead_code)]
Speed = 70,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i64)]
pub enum AmfRateControl {
Cqp = 1,
QualityVbr = 5,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct QsvAv1Params {
pub rc_mode: QsvRateControl,
pub icq_quality: u16,
pub qp_i: u16,
pub qp_p: u16,
pub target_usage: u16,
pub gop_pic_size: u16,
pub num_tile_columns: u8,
pub num_tile_rows: u8,
pub low_power: u16,
}
pub const MFX_CODINGOPTION_OFF: u16 = 32;
#[allow(dead_code)]
pub const MFX_CODINGOPTION_ON: u16 = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u16)]
pub enum QsvRateControl {
Cqp = 3,
Icq = 8,
}
const NV_ENC_PRESET_P5_GUID_BYTES: [u8; 16] = [
0xb4, 0xe6, 0xc6, 0x21, 0x7a, 0x29, 0xba, 0x4c, 0x99, 0x8f, 0xb6, 0xcb, 0xde, 0x72, 0xad, 0xe3,
];
const NV_ENC_PRESET_P6_GUID_BYTES: [u8; 16] = [
0x79, 0xc2, 0x75, 0x8e, 0x99, 0x62, 0xb6, 0x4a, 0x83, 0x02, 0x0b, 0x21, 0x5a, 0x33, 0x5c, 0xf5,
];
const NV_ENC_PRESET_P7_GUID_BYTES: [u8; 16] = [
0x12, 0x8c, 0x84, 0x84, 0x71, 0x6f, 0x13, 0x4c, 0x93, 0x1b, 0x53, 0xe2, 0x83, 0xf5, 0x79, 0x74,
];
pub const NVENC_TUNING_HIGH_QUALITY: u32 = 1;
pub fn rav1e_params(
target: QualityTarget,
tier: SpeedTier,
width: u32,
height: u32,
) -> Rav1eParams {
let libaom_cq = libaom_cq_for_target(target);
let quantizer = (libaom_cq as usize) * 4;
let speed_preset = match tier {
SpeedTier::Archive => 4,
SpeedTier::Standard => 6,
SpeedTier::Draft => 8,
};
let (tile_cols, tile_rows) = tile_grid_rav1e(width, height);
Rav1eParams {
quantizer,
speed_preset,
tile_rows,
tile_cols,
}
}
pub fn nvenc_av1_params(
target: QualityTarget,
tier: SpeedTier,
width: u32,
height: u32,
) -> NvencAv1Params {
let cq = nvenc_cq_for_target(target);
let (preset_guid, lookahead_depth, aq_strength) = match tier {
SpeedTier::Archive => (NV_ENC_PRESET_P7_GUID_BYTES, 32, 10),
SpeedTier::Standard => (NV_ENC_PRESET_P6_GUID_BYTES, 16, 8),
SpeedTier::Draft => (NV_ENC_PRESET_P5_GUID_BYTES, 0, 6),
};
let rc_mode = match target {
QualityTarget::VisuallyLossless => NvencRateControl::ConstQp,
_ => NvencRateControl::VbrTargetQuality,
};
let (num_tile_columns, num_tile_rows) = tile_grid_nvenc(width, height);
NvencAv1Params {
rc_mode,
cq,
preset_guid,
tuning_info: NVENC_TUNING_HIGH_QUALITY,
aq_strength,
lookahead_depth,
num_tile_columns: num_tile_columns as u32,
num_tile_rows: num_tile_rows as u32,
output_annex_b_format: 0, repeat_seq_hdr: 1,
}
}
pub fn amf_av1_params(
target: QualityTarget,
tier: SpeedTier,
width: u32,
height: u32,
) -> AmfAv1Params {
let q_index_intra = amf_q_index_for_target(target);
let q_index_inter = q_index_intra.saturating_add(8);
let qvbr_quality = match target {
QualityTarget::VisuallyLossless => 95,
QualityTarget::High => 85,
QualityTarget::Standard => 70,
QualityTarget::Low => 55,
QualityTarget::Vmaf(v) => vmaf_to_qvbr_quality(v),
};
let quality_preset = match tier {
SpeedTier::Archive => AmfQualityPreset::HighQuality,
SpeedTier::Standard => AmfQualityPreset::Quality,
SpeedTier::Draft => AmfQualityPreset::Balanced,
};
let rc_mode = match target {
QualityTarget::VisuallyLossless => AmfRateControl::Cqp,
_ => AmfRateControl::QualityVbr,
};
let (tile_cols, tile_rows) = tile_grid_hw(width, height);
let tiles_per_frame = (tile_cols * tile_rows) as u32;
AmfAv1Params {
rc_mode,
q_index_intra,
q_index_inter,
qvbr_quality,
quality_preset,
gop_size: 0, aq_mode: 1, tiles_per_frame,
}
}
pub fn qsv_av1_params(
target: QualityTarget,
tier: SpeedTier,
width: u32,
height: u32,
) -> QsvAv1Params {
let icq_quality = match target {
QualityTarget::VisuallyLossless => 16,
QualityTarget::High => 22,
QualityTarget::Standard => 26,
QualityTarget::Low => 31,
QualityTarget::Vmaf(v) => vmaf_to_qsv_icq(v),
};
let libaom_cq = libaom_cq_for_target(target);
let qp_i = (libaom_cq as u16 * 4).min(255);
let qp_p = qp_i.saturating_add(8).min(255);
let target_usage = match tier {
SpeedTier::Archive => 1,
SpeedTier::Standard => 4,
SpeedTier::Draft => 6,
};
let rc_mode = match target {
QualityTarget::VisuallyLossless => QsvRateControl::Cqp,
_ => QsvRateControl::Icq,
};
let (num_tile_columns, num_tile_rows) = tile_grid_hw(width, height);
QsvAv1Params {
rc_mode,
icq_quality,
qp_i,
qp_p,
target_usage,
gop_pic_size: 0, num_tile_columns: num_tile_columns as u8,
num_tile_rows: num_tile_rows as u8,
low_power: MFX_CODINGOPTION_ON,
}
}
pub fn libaom_cq_for_target(target: QualityTarget) -> u8 {
match target {
QualityTarget::VisuallyLossless => 20,
QualityTarget::High => 27,
QualityTarget::Standard => 32,
QualityTarget::Low => 38,
QualityTarget::Vmaf(v) => vmaf_to_libaom_cq(v),
}
}
fn nvenc_cq_for_target(target: QualityTarget) -> u8 {
match target {
QualityTarget::VisuallyLossless => 19,
QualityTarget::High => 25,
QualityTarget::Standard => 30,
QualityTarget::Low => 36,
QualityTarget::Vmaf(v) => vmaf_to_nvenc_cq(v),
}
}
const LIBAOM_ANCHORS: &[(i32, i32)] = &[
(100, 10), (98, 20),
(95, 27),
(90, 32),
(85, 38),
(70, 55), ];
const NVENC_ANCHORS: &[(i32, i32)] = &[(100, 10), (98, 19), (95, 25), (90, 30), (85, 36), (70, 52)];
fn piecewise_cq(vmaf: u8, anchors: &[(i32, i32)]) -> u8 {
let v = vmaf as i32;
if v >= anchors[0].0 {
return anchors[0].1.clamp(0, 63) as u8;
}
let last = anchors.len() - 1;
if v <= anchors[last].0 {
return anchors[last].1.clamp(0, 63) as u8;
}
for pair in anchors.windows(2) {
let (v_hi, cq_hi) = pair[0];
let (v_lo, cq_lo) = pair[1];
if v <= v_hi && v >= v_lo {
let span = v_hi - v_lo;
if span == 0 {
return cq_hi.clamp(0, 63) as u8;
}
let t = v_hi - v; let cq = cq_hi + (cq_lo - cq_hi) * t / span;
return cq.clamp(0, 63) as u8;
}
}
anchors[last].1.clamp(0, 63) as u8
}
fn vmaf_to_libaom_cq(vmaf: u8) -> u8 {
piecewise_cq(vmaf, LIBAOM_ANCHORS)
}
fn vmaf_to_nvenc_cq(vmaf: u8) -> u8 {
piecewise_cq(vmaf, NVENC_ANCHORS)
}
fn tile_grid_rav1e(width: u32, height: u32) -> (usize, usize) {
let max_dim = width.max(height);
if max_dim >= 3840 {
(4, 4) } else if max_dim >= 1920 {
(2, 2)
} else {
(1, 1)
}
}
fn tile_grid_nvenc(width: u32, height: u32) -> (usize, usize) {
let max_dim = width.max(height);
if max_dim >= 1920 { (2, 2) } else { (1, 1) }
}
fn tile_grid_hw(width: u32, height: u32) -> (usize, usize) {
tile_grid_nvenc(width, height)
}
fn amf_q_index_for_target(target: QualityTarget) -> u8 {
let base = match target {
QualityTarget::VisuallyLossless => 72, QualityTarget::High => 100, QualityTarget::Standard => 120, QualityTarget::Low => 144, QualityTarget::Vmaf(v) => vmaf_to_amf_q_index(v),
};
base.min(255) as u8
}
const AMF_Q_INDEX_ANCHORS: &[(i32, i32)] = &[
(100, 50), (98, 72),
(95, 100),
(90, 120),
(85, 144),
(70, 200),
];
fn vmaf_to_amf_q_index(vmaf: u8) -> u16 {
piecewise_quality(vmaf, AMF_Q_INDEX_ANCHORS, 0, 255) as u16
}
const AMF_QVBR_ANCHORS: &[(i32, i32)] =
&[(100, 100), (98, 95), (95, 85), (90, 70), (85, 55), (70, 35)];
fn vmaf_to_qvbr_quality(vmaf: u8) -> u8 {
piecewise_quality(vmaf, AMF_QVBR_ANCHORS, 1, 100)
}
const QSV_ICQ_ANCHORS: &[(i32, i32)] =
&[(100, 8), (98, 18), (95, 24), (90, 30), (85, 36), (70, 48)];
fn vmaf_to_qsv_icq(vmaf: u8) -> u16 {
piecewise_quality(vmaf, QSV_ICQ_ANCHORS, 1, 51) as u16
}
fn piecewise_quality(vmaf: u8, anchors: &[(i32, i32)], lo: i32, hi: i32) -> u8 {
let v = vmaf as i32;
if v >= anchors[0].0 {
return anchors[0].1.clamp(lo, hi) as u8;
}
let last = anchors.len() - 1;
if v <= anchors[last].0 {
return anchors[last].1.clamp(lo, hi) as u8;
}
for pair in anchors.windows(2) {
let (v_hi, q_hi) = pair[0];
let (v_lo, q_lo) = pair[1];
if v <= v_hi && v >= v_lo {
let span = v_hi - v_lo;
if span == 0 {
return q_hi.clamp(lo, hi) as u8;
}
let t = v_hi - v;
let q = q_hi + (q_lo - q_hi) * t / span;
return q.clamp(lo, hi) as u8;
}
}
anchors[last].1.clamp(lo, hi) as u8
}
#[cfg(test)]
mod tests {
use super::*;
const RESOLUTIONS: &[(u32, u32)] = &[
(640, 360), (854, 480), (1280, 720), (1920, 1080), (2560, 1440), (3840, 2160), ];
const TARGETS: &[QualityTarget] = &[
QualityTarget::VisuallyLossless,
QualityTarget::High,
QualityTarget::Standard,
QualityTarget::Low,
];
const TIERS: &[SpeedTier] = &[SpeedTier::Draft, SpeedTier::Standard, SpeedTier::Archive];
#[test]
fn rav1e_every_combination_returns_valid_params() {
for (w, h) in RESOLUTIONS {
for target in TARGETS {
for tier in TIERS {
let p = rav1e_params(*target, *tier, *w, *h);
assert!(p.quantizer <= 255, "quantizer {} oob", p.quantizer);
assert!(p.speed_preset <= 10, "speed_preset {} oob", p.speed_preset);
assert!(p.tile_rows >= 1);
assert!(p.tile_cols >= 1);
}
}
}
}
#[test]
fn nvenc_every_combination_returns_valid_params() {
for (w, h) in RESOLUTIONS {
for target in TARGETS {
for tier in TIERS {
let p = nvenc_av1_params(*target, *tier, *w, *h);
assert!(p.cq <= 63, "cq {} exceeds AV1 max 63", p.cq);
assert_eq!(p.tuning_info, NVENC_TUNING_HIGH_QUALITY);
assert_eq!(p.output_annex_b_format, 0, "must be LOB for MP4");
assert_eq!(p.repeat_seq_hdr, 1, "every IDR needs seq hdr");
assert!(p.aq_strength <= 15);
}
}
}
}
#[test]
fn rav1e_quantizer_monotonic_in_quality() {
let sd1080 = (1920, 1080);
let vl = rav1e_params(
QualityTarget::VisuallyLossless,
SpeedTier::Standard,
sd1080.0,
sd1080.1,
);
let hi = rav1e_params(QualityTarget::High, SpeedTier::Standard, sd1080.0, sd1080.1);
let std = rav1e_params(
QualityTarget::Standard,
SpeedTier::Standard,
sd1080.0,
sd1080.1,
);
let lo = rav1e_params(QualityTarget::Low, SpeedTier::Standard, sd1080.0, sd1080.1);
assert!(vl.quantizer < hi.quantizer);
assert!(hi.quantizer < std.quantizer);
assert!(std.quantizer < lo.quantizer);
}
#[test]
fn nvenc_cq_monotonic_in_quality() {
let sd = (1920, 1080);
let vl = nvenc_av1_params(
QualityTarget::VisuallyLossless,
SpeedTier::Standard,
sd.0,
sd.1,
);
let hi = nvenc_av1_params(QualityTarget::High, SpeedTier::Standard, sd.0, sd.1);
let std = nvenc_av1_params(QualityTarget::Standard, SpeedTier::Standard, sd.0, sd.1);
let lo = nvenc_av1_params(QualityTarget::Low, SpeedTier::Standard, sd.0, sd.1);
assert!(vl.cq < hi.cq);
assert!(hi.cq < std.cq);
assert!(std.cq < lo.cq);
}
#[test]
fn rav1e_speed_preset_monotonic_in_tier() {
let vl = QualityTarget::Standard;
let (w, h) = (1920, 1080);
let arc = rav1e_params(vl, SpeedTier::Archive, w, h);
let std = rav1e_params(vl, SpeedTier::Standard, w, h);
let drf = rav1e_params(vl, SpeedTier::Draft, w, h);
assert!(arc.speed_preset < std.speed_preset);
assert!(std.speed_preset < drf.speed_preset);
}
#[test]
fn tile_grid_rav1e_by_resolution() {
assert_eq!(tile_grid_rav1e(640, 360), (1, 1));
assert_eq!(tile_grid_rav1e(1280, 720), (1, 1));
assert_eq!(tile_grid_rav1e(1920, 1080), (2, 2));
assert_eq!(tile_grid_rav1e(2560, 1440), (2, 2));
assert_eq!(tile_grid_rav1e(3840, 2160), (4, 4));
assert_eq!(tile_grid_rav1e(4096, 2160), (4, 4));
assert_eq!(tile_grid_rav1e(1080, 1920), (2, 2));
}
#[test]
fn tile_grid_nvenc_caps_at_2x2() {
assert_eq!(tile_grid_nvenc(640, 360), (1, 1));
assert_eq!(tile_grid_nvenc(1280, 720), (1, 1));
assert_eq!(tile_grid_nvenc(1920, 1080), (2, 2));
assert_eq!(tile_grid_nvenc(2560, 1440), (2, 2));
assert_eq!(tile_grid_nvenc(3840, 2160), (2, 2));
assert_eq!(tile_grid_nvenc(4096, 2160), (2, 2));
assert_eq!(tile_grid_nvenc(1080, 1920), (2, 2));
}
#[test]
fn archive_tier_uses_constqp_at_lossless() {
let p = nvenc_av1_params(
QualityTarget::VisuallyLossless,
SpeedTier::Archive,
1920,
1080,
);
assert_eq!(p.rc_mode, NvencRateControl::ConstQp);
}
#[test]
fn non_archive_tiers_use_vbr_cq() {
for target in [
QualityTarget::High,
QualityTarget::Standard,
QualityTarget::Low,
] {
for tier in TIERS {
let p = nvenc_av1_params(target, *tier, 1920, 1080);
assert_eq!(
p.rc_mode,
NvencRateControl::VbrTargetQuality,
"target={:?} tier={:?} should use VBR+CQ",
target,
tier
);
}
}
}
#[test]
fn vmaf_escape_hatch_matches_named_targets() {
let vl = nvenc_cq_for_target(QualityTarget::VisuallyLossless);
let v98 = nvenc_cq_for_target(QualityTarget::Vmaf(98));
assert!(
(vl as i32 - v98 as i32).abs() <= 2,
"VMAF 98 escape hatch CQ={} should be within 2 of named VL CQ={}",
v98,
vl
);
let std = nvenc_cq_for_target(QualityTarget::Standard);
let v90 = nvenc_cq_for_target(QualityTarget::Vmaf(90));
assert!((std as i32 - v90 as i32).abs() <= 2);
}
#[test]
fn vmaf_escape_hatch_clamps_oob() {
let lo_cq = nvenc_cq_for_target(QualityTarget::Vmaf(0));
let hi_cq = nvenc_cq_for_target(QualityTarget::Vmaf(255));
assert!(lo_cq <= 63);
assert!(hi_cq <= 63);
assert!(lo_cq > hi_cq);
}
#[test]
fn preset_guids_are_distinct() {
assert_ne!(NV_ENC_PRESET_P5_GUID_BYTES, NV_ENC_PRESET_P6_GUID_BYTES);
assert_ne!(NV_ENC_PRESET_P6_GUID_BYTES, NV_ENC_PRESET_P7_GUID_BYTES);
assert_ne!(NV_ENC_PRESET_P5_GUID_BYTES, NV_ENC_PRESET_P7_GUID_BYTES);
}
#[test]
fn rav1e_quantizer_matches_libaom_4x_rule() {
let p = rav1e_params(QualityTarget::High, SpeedTier::Standard, 1920, 1080);
assert_eq!(p.quantizer, 27 * 4); let p = rav1e_params(QualityTarget::Standard, SpeedTier::Standard, 1920, 1080);
assert_eq!(p.quantizer, 32 * 4);
}
#[test]
fn default_quality_is_standard() {
let q: QualityTarget = Default::default();
assert_eq!(q, QualityTarget::Standard);
let t: SpeedTier = Default::default();
assert_eq!(t, SpeedTier::Standard);
}
#[test]
fn amf_every_combination_returns_valid_params() {
for (w, h) in RESOLUTIONS {
for target in TARGETS {
for tier in TIERS {
let p = amf_av1_params(*target, *tier, *w, *h);
assert!(p.q_index_inter >= p.q_index_intra);
assert!((1..=100).contains(&p.qvbr_quality));
assert!(p.tiles_per_frame >= 1);
assert!(matches!(
p.quality_preset,
AmfQualityPreset::HighQuality
| AmfQualityPreset::Quality
| AmfQualityPreset::Balanced
));
}
}
}
}
#[test]
fn qsv_every_combination_returns_valid_params() {
for (w, h) in RESOLUTIONS {
for target in TARGETS {
for tier in TIERS {
let p = qsv_av1_params(*target, *tier, *w, *h);
assert!((1..=51).contains(&p.icq_quality));
assert!(p.qp_i <= 255);
assert!(p.qp_p <= 255);
assert!((1..=6).contains(&p.target_usage));
assert_eq!(p.low_power, MFX_CODINGOPTION_ON);
assert!(p.num_tile_columns >= 1);
assert!(p.num_tile_rows >= 1);
}
}
}
}
#[test]
fn amf_q_index_monotonic_in_quality() {
let (w, h) = (1920, 1080);
let vl = amf_av1_params(QualityTarget::VisuallyLossless, SpeedTier::Standard, w, h);
let hi = amf_av1_params(QualityTarget::High, SpeedTier::Standard, w, h);
let std = amf_av1_params(QualityTarget::Standard, SpeedTier::Standard, w, h);
let lo = amf_av1_params(QualityTarget::Low, SpeedTier::Standard, w, h);
assert!(vl.q_index_intra < hi.q_index_intra);
assert!(hi.q_index_intra < std.q_index_intra);
assert!(std.q_index_intra < lo.q_index_intra);
}
#[test]
fn qsv_icq_monotonic_in_quality() {
let (w, h) = (1920, 1080);
let vl = qsv_av1_params(QualityTarget::VisuallyLossless, SpeedTier::Standard, w, h);
let hi = qsv_av1_params(QualityTarget::High, SpeedTier::Standard, w, h);
let std = qsv_av1_params(QualityTarget::Standard, SpeedTier::Standard, w, h);
let lo = qsv_av1_params(QualityTarget::Low, SpeedTier::Standard, w, h);
assert!(vl.icq_quality < hi.icq_quality);
assert!(hi.icq_quality < std.icq_quality);
assert!(std.icq_quality < lo.icq_quality);
}
#[test]
fn amf_archive_at_visually_lossless_uses_cqp() {
let p = amf_av1_params(
QualityTarget::VisuallyLossless,
SpeedTier::Archive,
1920,
1080,
);
assert_eq!(p.rc_mode, AmfRateControl::Cqp);
}
#[test]
fn amf_non_vl_uses_quality_vbr() {
for target in [
QualityTarget::High,
QualityTarget::Standard,
QualityTarget::Low,
] {
for tier in TIERS {
let p = amf_av1_params(target, *tier, 1920, 1080);
assert_eq!(p.rc_mode, AmfRateControl::QualityVbr);
}
}
}
#[test]
fn qsv_archive_at_visually_lossless_uses_cqp() {
let p = qsv_av1_params(
QualityTarget::VisuallyLossless,
SpeedTier::Archive,
1920,
1080,
);
assert_eq!(p.rc_mode, QsvRateControl::Cqp);
}
#[test]
fn amf_quality_preset_tier_mapping() {
let (w, h) = (1920, 1080);
let arc = amf_av1_params(QualityTarget::Standard, SpeedTier::Archive, w, h);
let std = amf_av1_params(QualityTarget::Standard, SpeedTier::Standard, w, h);
let drf = amf_av1_params(QualityTarget::Standard, SpeedTier::Draft, w, h);
assert_eq!(arc.quality_preset, AmfQualityPreset::HighQuality);
assert_eq!(std.quality_preset, AmfQualityPreset::Quality);
assert_eq!(drf.quality_preset, AmfQualityPreset::Balanced);
}
#[test]
fn qsv_target_usage_tier_ordering() {
let (w, h) = (1920, 1080);
let arc = qsv_av1_params(QualityTarget::Standard, SpeedTier::Archive, w, h);
let std = qsv_av1_params(QualityTarget::Standard, SpeedTier::Standard, w, h);
let drf = qsv_av1_params(QualityTarget::Standard, SpeedTier::Draft, w, h);
assert!(arc.target_usage < std.target_usage);
assert!(std.target_usage < drf.target_usage);
}
#[test]
fn amf_tile_count_caps_at_4() {
fn tiles(w: u32, h: u32) -> usize {
let (c, r) = tile_grid_hw(w, h);
c * r
}
assert_eq!(tiles(640, 360), 1);
assert_eq!(tiles(1280, 720), 1);
assert_eq!(tiles(1920, 1080), 4);
assert_eq!(tiles(3840, 2160), 4);
}
#[test]
fn qsv_tile_grid_caps_at_2x2() {
assert_eq!(tile_grid_hw(640, 360), (1, 1));
assert_eq!(tile_grid_hw(1280, 720), (1, 1));
assert_eq!(tile_grid_hw(1920, 1080), (2, 2));
assert_eq!(tile_grid_hw(3840, 2160), (2, 2));
}
#[test]
fn tile_grid_fits_av1_level_5_1() {
const MAX_TILE_COLS_L51: u32 = 8;
const MAX_TILES_L51: u32 = 64;
const MAX_TILE_WIDTH_L51: u32 = 4096;
const MAX_TILE_AREA_L51: u32 = 4_230_144;
for (w, h) in RESOLUTIONS {
for (label, (cols, rows)) in [
("rav1e", tile_grid_rav1e(*w, *h)),
("nvenc", tile_grid_nvenc(*w, *h)),
("hw", tile_grid_hw(*w, *h)),
] {
let cols = cols as u32;
let rows = rows as u32;
assert!(
cols <= MAX_TILE_COLS_L51,
"{} {}x{} emits {} tile cols; Level 5.1 max is {}",
label,
w,
h,
cols,
MAX_TILE_COLS_L51
);
assert!(
cols * rows <= MAX_TILES_L51,
"{} {}x{} emits {} total tiles; Level 5.1 max is {}",
label,
w,
h,
cols * rows,
MAX_TILES_L51
);
let tile_w = w.div_ceil(cols);
let tile_h = h.div_ceil(rows);
assert!(
tile_w <= MAX_TILE_WIDTH_L51,
"{} {}x{} per-tile width {} > Level 5.1 max {}",
label,
w,
h,
tile_w,
MAX_TILE_WIDTH_L51
);
assert!(
tile_w * tile_h <= MAX_TILE_AREA_L51,
"{} {}x{} per-tile area {} > Level 5.1 max {}",
label,
w,
h,
tile_w * tile_h,
MAX_TILE_AREA_L51
);
}
}
}
}