use core::cell::RefCell;
use crate::cdf::{TileCdfContext, NUM_4X4_BLOCKS_HIGH, NUM_4X4_BLOCKS_WIDE};
use crate::encoder::partition_tree::{
write_block_syntax, write_partition_symbol, write_partition_tree_syntax, PartitionSyntaxWriter,
SyntaxBlock, SyntaxFrameParams, SyntaxNode, WriterScopeSnapshot,
};
use crate::encoder::symbol_writer::SymbolWriter;
use crate::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RateModel {
Heuristic,
Twin,
}
#[inline]
pub(crate) fn score256(distortion: u64, lambda: u64, rate_bits256: u64) -> u64 {
distortion * 256 + lambda * rate_bits256
}
#[derive(Debug, Clone)]
pub(crate) struct RateTwin {
inner: RefCell<TwinInner>,
disable_cdf_update: bool,
}
#[derive(Debug, Clone)]
struct TwinInner {
cdfs: TileCdfContext,
state: PartitionSyntaxWriter,
range: u32,
}
#[derive(Debug, Clone)]
pub(crate) struct TwinScope {
writer: WriterScopeSnapshot,
cdfs: TileCdfContext,
range: u32,
}
impl RateTwin {
pub fn snapshot(
cdfs: &TileCdfContext,
state: &PartitionSyntaxWriter,
writer: &SymbolWriter,
) -> Self {
Self {
inner: RefCell::new(TwinInner {
cdfs: cdfs.clone(),
state: state.clone(),
range: writer.range(),
}),
disable_cdf_update: writer.disable_cdf_update(),
}
}
pub fn arm_read_deltas(&mut self) {
self.inner.get_mut().state.arm_read_deltas();
}
pub fn deltas_pending(&self) -> bool {
self.inner.borrow().state.deltas_pending()
}
pub fn scope(&self, r: u32, c: u32, b_size: usize, params: &SyntaxFrameParams) -> TwinScope {
let g = self.inner.borrow();
TwinScope {
writer: g.state.snapshot_price_scope(
r,
c,
NUM_4X4_BLOCKS_WIDE[b_size] as u32,
NUM_4X4_BLOCKS_HIGH[b_size] as u32,
params,
),
cdfs: g.cdfs.clone(),
range: g.range,
}
}
pub fn restore(&mut self, scope: &TwinScope) {
let g = self.inner.get_mut();
g.state.restore_price_scope(&scope.writer);
g.cdfs.clone_from(&scope.cdfs);
g.range = scope.range;
}
pub fn commit_subtree(
&mut self,
node: &SyntaxNode,
r: u32,
c: u32,
b_size: usize,
params: &SyntaxFrameParams,
) -> Result<u64, Error> {
let g = self.inner.get_mut();
let mut w = SymbolWriter::new_counting(self.disable_cdf_update, g.range);
write_partition_tree_syntax(
&mut w,
&mut g.cdfs,
&mut g.state,
node,
r,
c,
b_size,
params,
)?;
g.range = w.range();
Ok(w.cost_bits256())
}
pub fn commit_partition_symbol(
&mut self,
partition: usize,
r: u32,
c: u32,
b_size: usize,
) -> Result<u64, Error> {
let g = self.inner.get_mut();
let mut w = SymbolWriter::new_counting(self.disable_cdf_update, g.range);
write_partition_symbol(&mut w, &mut g.cdfs, &g.state, partition, r, c, b_size)?;
g.range = w.range();
Ok(w.cost_bits256())
}
pub fn commit_block(
&mut self,
block: &SyntaxBlock,
r: u32,
c: u32,
b_size: usize,
params: &SyntaxFrameParams,
) -> Result<u64, Error> {
let g = self.inner.get_mut();
let mut w = SymbolWriter::new_counting(self.disable_cdf_update, g.range);
write_block_syntax(
&mut w,
&mut g.cdfs,
&mut g.state,
block,
r,
c,
b_size,
params,
)?;
g.range = w.range();
Ok(w.cost_bits256())
}
pub fn price_block(
&self,
block: &SyntaxBlock,
r: u32,
c: u32,
b_size: usize,
params: &SyntaxFrameParams,
) -> Result<u64, Error> {
let mut g = self.inner.borrow_mut();
let g = &mut *g;
let scope = g.state.snapshot_price_scope(
r,
c,
NUM_4X4_BLOCKS_WIDE[b_size] as u32,
NUM_4X4_BLOCKS_HIGH[b_size] as u32,
params,
);
let cdfs = g.cdfs.clone();
let mut w = SymbolWriter::new_counting(self.disable_cdf_update, g.range);
let res = write_block_syntax(
&mut w,
&mut g.cdfs,
&mut g.state,
block,
r,
c,
b_size,
params,
);
g.state.restore_price_scope(&scope);
g.cdfs = cdfs;
res?;
Ok(w.cost_bits256())
}
#[allow(clippy::too_many_arguments)]
pub fn price_motion_mode(
&self,
motion_mode: u8,
mi_size: usize,
is_compound: bool,
ref_frame: [i32; 2],
y_mode: u8,
num_samples: u32,
is_motion_mode_switchable: bool,
allow_warped_motion: bool,
force_integer_mv: bool,
gm_type: [i32; 8],
is_scaled_per_ref: [bool; 7],
has_overlappable: bool,
) -> Result<u64, Error> {
let g = self.inner.borrow();
let mut cdfs = g.cdfs.clone();
let mut w = SymbolWriter::new_counting(self.disable_cdf_update, g.range);
crate::encoder::block_mode_info::write_motion_mode(
&mut w,
&mut cdfs,
motion_mode,
mi_size,
0,
is_compound,
ref_frame,
y_mode,
num_samples,
is_motion_mode_switchable,
allow_warped_motion,
force_integer_mv,
gm_type,
is_scaled_per_ref,
has_overlappable,
)?;
Ok(w.cost_bits256())
}
#[allow(clippy::too_many_arguments)]
pub fn price_inter_mode(
&self,
ref_frame: [i32; 2],
y_mode: u8,
mv: [[i32; 2]; 2],
ref_mv_idx: u32,
mv_stack: &crate::cdf::FindMvStackResult,
mi_size: usize,
reference_select: bool,
avail_u: bool,
avail_l: bool,
above_ref_frame: [i32; 2],
left_ref_frame: [i32; 2],
force_integer_mv: bool,
allow_high_precision_mv: bool,
) -> Result<u64, Error> {
let g = self.inner.borrow();
let mut cdfs = g.cdfs.clone();
let mut w = SymbolWriter::new_counting(self.disable_cdf_update, g.range);
crate::encoder::block_mode_info::write_inter_mode_mv_prefix(
&mut w,
&mut cdfs,
ref_frame,
y_mode,
mv,
ref_mv_idx,
mv_stack,
mi_size,
0,
[0, 0],
false,
0,
false,
false,
reference_select,
avail_u,
avail_l,
above_ref_frame[1] <= 0,
left_ref_frame[1] <= 0,
above_ref_frame[0] <= 0,
left_ref_frame[0] <= 0,
above_ref_frame,
left_ref_frame,
force_integer_mv,
allow_high_precision_mv,
)?;
Ok(w.cost_bits256())
}
pub fn spatial_segment_pred(&self, mi_row: u32, mi_col: u32) -> u8 {
self.inner.borrow().state.segment_pred_ctx(mi_row, mi_col).0
}
pub fn matches(&self, cdfs: &TileCdfContext, writer: &SymbolWriter) -> bool {
let g = self.inner.borrow();
g.range == writer.range() && g.cdfs == *cdfs
}
pub fn matches_state(
&self,
cdfs: &TileCdfContext,
state: &PartitionSyntaxWriter,
writer: &SymbolWriter,
) -> bool {
let g = self.inner.borrow();
g.range == writer.range() && g.cdfs == *cdfs && g.state.differing_fields(state).is_empty()
}
#[doc(hidden)]
pub fn differing_state_fields(&self, state: &PartitionSyntaxWriter) -> Vec<&'static str> {
self.inner.borrow().state.differing_fields(state)
}
pub fn resync_coder(&mut self, cdfs: &TileCdfContext, writer: &SymbolWriter) {
let g = self.inner.get_mut();
g.cdfs.clone_from(cdfs);
g.range = writer.range();
}
pub fn tu_fork(&self) -> TuFork<'_> {
TuFork {
twin: self,
origin: RefCell::new(None),
}
}
}
pub(crate) struct TuFork<'a> {
twin: &'a RateTwin,
origin: RefCell<Option<TwinScope>>,
}
pub(crate) struct TuCtx<'a> {
pub params: &'a SyntaxFrameParams,
pub mi_row: u32,
pub mi_col: u32,
pub mi_size: usize,
pub base_x: u32,
pub base_y: u32,
pub is_inter: bool,
pub segment_id: u8,
pub y_mode: u8,
pub use_filter_intra: bool,
pub filter_intra_mode: Option<u8>,
}
impl TuCtx<'_> {
fn facade(&self, quant: &[i32], tx_type: u8) -> SyntaxBlock {
let mut b = SyntaxBlock::skip_leaf(self.y_mode, None);
b.segment_id = self.segment_id;
b.use_filter_intra = u8::from(self.use_filter_intra);
b.filter_intra_mode = self.filter_intra_mode;
b.residual_quant = vec![quant.to_vec()].into();
b.residual_tx_type = vec![tx_type];
b
}
}
impl TuFork<'_> {
fn ensure_origin(&self, ctx: &TuCtx<'_>) {
let mut o = self.origin.borrow_mut();
if o.is_none() {
*o = Some(
self.twin
.scope(ctx.mi_row, ctx.mi_col, ctx.mi_size, ctx.params),
);
}
}
pub fn price_luma_tu(
&self,
ctx: &TuCtx<'_>,
tx_sz: usize,
x: u32,
y: u32,
quant: &[i32],
tx_type: u8,
) -> Result<u64, Error> {
self.ensure_origin(ctx);
let mut g = self.twin.inner.borrow_mut();
let g = &mut *g;
let scope = g.state.snapshot_price_scope(
ctx.mi_row + y,
ctx.mi_col + x,
(crate::cdf::TX_WIDTH[tx_sz] / 4) as u32,
(crate::cdf::TX_HEIGHT[tx_sz] / 4) as u32,
ctx.params,
);
let cdfs = g.cdfs.clone();
let mut w = SymbolWriter::new_counting(self.twin.disable_cdf_update, g.range);
let res = crate::encoder::partition_tree::write_single_transform_block(
&mut w,
&mut g.cdfs,
&mut g.state,
&ctx.facade(quant, tx_type),
ctx.params,
0,
ctx.base_x,
ctx.base_y,
tx_sz,
x,
y,
ctx.mi_row,
ctx.mi_col,
ctx.mi_size,
ctx.is_inter,
);
g.state.restore_price_scope(&scope);
g.cdfs = cdfs;
res?;
Ok(w.cost_bits256())
}
pub fn commit_luma_tu(
&mut self,
ctx: &TuCtx<'_>,
tx_sz: usize,
x: u32,
y: u32,
quant: &[i32],
tx_type: u8,
) -> Result<(), Error> {
self.ensure_origin(ctx);
let mut g = self.twin.inner.borrow_mut();
let g = &mut *g;
let mut w = SymbolWriter::new_counting(self.twin.disable_cdf_update, g.range);
crate::encoder::partition_tree::write_single_transform_block(
&mut w,
&mut g.cdfs,
&mut g.state,
&ctx.facade(quant, tx_type),
ctx.params,
0,
ctx.base_x,
ctx.base_y,
tx_sz,
x,
y,
ctx.mi_row,
ctx.mi_col,
ctx.mi_size,
ctx.is_inter,
)?;
g.range = w.range();
Ok(())
}
}
impl Drop for TuFork<'_> {
fn drop(&mut self) {
if let Some(origin) = self.origin.get_mut().take() {
let mut g = self.twin.inner.borrow_mut();
g.state.restore_price_scope(&origin.writer);
g.cdfs = origin.cdfs;
g.range = origin.range;
}
}
}