use std::cell::RefCell;
thread_local! {
pub static GRID: RefCell<RefmvsGrid> = RefCell::new(RefmvsGrid::default());
pub static BANK: RefCell<RefmvBank> = RefCell::new(RefmvBank::default());
pub static WARPBANK: RefCell<RefmvWarpBank> = RefCell::new(RefmvWarpBank::default());
}
pub fn reset_refmvs() {
GRID.with(|g| *g.borrow_mut() = RefmvsGrid::default());
BANK.with(|b| *b.borrow_mut() = RefmvBank::default());
WARPBANK.with(|w| *w.borrow_mut() = RefmvWarpBank::default());
}
pub fn reset_sb(bx4: usize, by4: usize, sbsz: usize, iw4: usize, first_sb_row: bool) {
BANK.with(|bk| {
let mut b = bk.borrow_mut();
b.hits0 = 0;
b.hits1 = 0;
b.avail = 0;
});
WARPBANK.with(|wb| wb.borrow_mut().hits = 0);
if first_sb_row {
return;
}
let end_x4 = (bx4 + sbsz).min(iw4);
GRID.with(|g| {
let grid = g.borrow();
BANK.with(|bk| {
let mut bank = bk.borrow_mut();
WARPBANK.with(|wb| {
let mut warp = wb.borrow_mut();
let mut hits = 0;
let mut x = bx4;
while x < end_x4 {
let r = *grid.at(by4 - 1, x & !1);
let sz4 = (crate::av2_decode::BLOCK_DIMENSIONS[r.bs as usize][0] as usize).max(1);
if r.mv[0].y != -0x8000 {
let seed = if r.mf & 2 != 0 { r.lmv } else { r.mv };
bank.add_raw_pair(r.ref_[0], r.ref_[1], seed, (r.mf >> 2) as i8);
if r.mf & 2 != 0 {
warp.add(r.ref_[0], r.matrix);
}
hits += 1;
if hits == 4 {
break;
}
}
x += sz4;
}
});
});
});
}
pub fn reset_sbrow() {
BANK.with(|bk| {
let mut b = bk.borrow_mut();
b.size = [0; 9];
b.idx = [0; 9];
});
WARPBANK.with(|wb| {
let mut w = wb.borrow_mut();
w.size = [0; 7];
w.idx = [0; 7];
});
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
pub struct Mv {
pub y: i32,
pub x: i32,
}
#[derive(Clone, Copy, Debug)]
pub struct RefmvsBlock {
pub mv: [Mv; 2],
pub ref_: [i8; 2], pub bs: u8,
pub mf: u8, pub bx4: u16,
pub by4: u16,
pub lmv: [Mv; 2], pub matrix: [i32; 6], }
impl Default for RefmvsBlock {
fn default() -> Self {
RefmvsBlock {
mv: [Mv { y: -0x8000, x: -0x8000 }; 2],
ref_: [-1, -1],
bs: 0,
mf: 0,
bx4: 0,
by4: 0,
lmv: [Mv { y: -0x8000, x: -0x8000 }; 2],
matrix: [0; 6],
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn get_warpmv_proj(m: &[i32; 6], x: i32, y: i32, minx: i32, maxx: i32, miny: i32, maxy: i32) -> Mv {
let xc = (m[2] - (1 << 16)) * x + m[3] * y + m[0];
let yc = (m[5] - (1 << 16)) * y + m[4] * x + m[1];
let ry = (((yc + 0x1000 - (yc < 0) as i32) >> 13).clamp(-0xffff, 0xffff)).clamp(miny, maxy);
let rx = (((xc + 0x1000 - (xc < 0) as i32) >> 13).clamp(-0xffff, 0xffff)).clamp(minx, maxx);
Mv { y: ry, x: rx }
}
pub fn mv_reduce_prec(m: &mut Mv, mv_prec: i32) {
if mv_prec == 6 {
return;
}
let rnd = 32 >> mv_prec;
m.x = m.x + rnd - (m.x > 0) as i32;
m.y = m.y + rnd - (m.y > 0) as i32;
let mask = !(rnd * 2 - 1);
m.x &= mask;
m.y &= mask;
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum WarpType {
Identity,
Translation,
RotZoom,
Affine,
}
#[derive(Clone, Copy, Debug)]
pub struct GmvModel {
pub ty: WarpType,
pub matrix: [i32; 6],
}
impl Default for GmvModel {
fn default() -> Self {
GmvModel { ty: WarpType::Identity, matrix: [0; 6] }
}
}
pub fn get_gmv_2d(g: &GmvModel, bx4: i32, by4: i32, bw4: i32, bh4: i32, iw4: i32, ih4: i32) -> Mv {
match g.ty {
WarpType::Identity => Mv { y: 0, x: 0 },
WarpType::Translation => {
let mut y = g.matrix[0] >> 13;
let mut x = g.matrix[1] >> 13;
y = y.clamp(-(by4 + bh4 + 4) * 32, (ih4 - by4 + 4) * 32);
x = x.clamp(-(bx4 + bw4 + 4) * 32, (iw4 - bx4 + 4) * 32);
Mv { y, x }
}
_ => Mv { y: 0, x: 0 },
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Candidate {
pub mv: [Mv; 2],
pub weight: i32,
pub y_off: i32,
pub x_off: i32,
pub cwp: i8,
}
impl Default for Candidate {
fn default() -> Self {
Candidate { mv: [Mv::default(); 2], weight: 0, y_off: 0, x_off: 0, cwp: 8 }
}
}
#[allow(clippy::too_many_arguments)]
pub fn add_candidate_comp(
mvstack: &mut [Candidate],
cnt: &mut usize,
max_cnt: usize,
weight: i32,
cwp: i8,
cand_mv: [Mv; 2],
iter_cntr: &mut i32,
max_iter: i32,
) -> bool {
let last = *cnt;
if *iter_cntr < max_iter {
for n in 0..last {
if !crate::av2_recon::work_tick("refmvs:239") { break; }
if mvstack[n].mv[0] == cand_mv[0] && mvstack[n].mv[1] == cand_mv[1] {
*iter_cntr += n as i32 + 1;
mvstack[n].weight += weight;
return false;
}
}
*iter_cntr += last as i32;
}
if last >= max_cnt {
return false;
}
mvstack[last].mv = cand_mv;
mvstack[last].weight = weight;
mvstack[last].cwp = cwp;
mvstack[last].y_off = 0;
mvstack[last].x_off = 0;
*cnt = last + 1;
true
}
#[allow(clippy::too_many_arguments)]
pub fn add_candidate_sngl(
mvstack: &mut [Candidate],
cnt: &mut usize,
max_cnt: usize,
weight: i32,
cand_mv: Mv,
y_off_s: i32,
x_off_s: i32,
iter_cntr: &mut i32,
max_iter: i32,
) -> bool {
let last = *cnt;
if *iter_cntr < max_iter {
for m in 0..last {
if !crate::av2_recon::work_tick("refmvs:278") { break; }
if mvstack[m].mv[0] == cand_mv {
*iter_cntr += m as i32 + 1;
mvstack[m].weight += weight;
return false;
}
}
*iter_cntr += last as i32;
}
if last >= max_cnt {
return false;
}
mvstack[last].mv[0] = cand_mv;
mvstack[last].weight = weight;
mvstack[last].y_off = y_off_s;
mvstack[last].x_off = x_off_s;
*cnt = last + 1;
true
}
pub fn set_affine_mv2d(bw4: usize, bh4: usize, mv: Mv, m: &mut [i32; 6], bx4: usize, by4: usize) {
let rsuy = 2 * bh4 as i64 - 1;
let rsux = 2 * bw4 as i64 - 1;
let isuy = by4 as i64 * 4 + rsuy;
let isux = bx4 as i64 * 4 + rsux;
m[0] = (mv.x as i64 * 0x2000 - isux * (m[2] as i64 - 0x10000) - isuy * m[3] as i64).clamp(-0x8000000, 0x7ffffc0) as i32;
m[1] = (mv.y as i64 * 0x2000 - isux * m[4] as i64 - isuy * (m[5] as i64 - 0x10000)).clamp(-0x8000000, 0x7ffffc0) as i32;
}
pub fn reconstruct_warp_delta_matrix(warp_base: [i32; 6], delta: [i32; 4], mv: Mv, bw4: usize, bh4: usize, bx4: usize, by4: usize) -> [i32; 6] {
let mut m = warp_base;
let mut n = 0usize;
while n < 4 && delta[n] != -0x80 {
if delta[n] != 0 {
let base: i32 = if (n as u32).wrapping_sub(1) >= 2 { 0x10000 } else { 0 };
m[2 + n] = (warp_base[2 + n] + delta[n] * (1 << 10)).clamp(base - 0x7fc0, base + 0x7fc0);
} else {
m[2 + n] = warp_base[2 + n];
}
n += 1;
}
if delta[2] == -0x80 {
m[5] = m[2];
m[4] = -m[3];
}
set_affine_mv2d(bw4, bh4, mv, &mut m, bx4, by4);
m
}
fn resolve_divisor_64(d: u64) -> (i32, i32) {
let mut shift = 63 - d.leading_zeros() as i32; let e = d as i64 - (1i64 << shift);
let f = if shift > 7 { (e + (1i64 << (shift - 8))) >> (shift - 7) } else { e << (7 - shift) };
shift += 9;
(crate::av2_ipred::DIV_RECIP[f as usize] as i32, shift)
}
fn get_mult_shift_ndiag(px: i64, idet: i64, rnd: i64, sh: i32) -> i32 {
let v1 = px * idet;
let v2 = ((v1 + rnd - (v1 < 0) as i64) >> sh) as i32;
let v3 = (v2 + 0x20 - (v2 < 0) as i32) & !0x3f;
v3.clamp(-0x7fc0, 0x7fc0)
}
fn get_mult_shift_diag(px: i64, idet: i64, rnd: i64, sh: i32) -> i32 {
let v1 = px * idet;
let v2 = ((v1 + rnd - (v1 < 0) as i64) >> sh) as i32;
let v3 = (v2 + 0x20 - (v2 < 0x10000) as i32) & !0x3f;
v3.clamp(0x8040, 0x17fc0)
}
pub fn find_affine_int(pts: &[[[i32; 2]; 2]], bw4: usize, bh4: usize, mv: Mv, bx4: usize, by4: usize) -> [i32; 6] {
let (mut a00, mut a01, mut a11) = (0i64, 0i64, 0i64);
let (mut bx0, mut bx1, mut by0, mut by1) = (0i64, 0i64, 0i64, 0i64);
let rsuy = 2 * bh4 as i32 - 1;
let rsux = 2 * bw4 as i32 - 1;
let (suy, sux) = (rsuy * 8, rsux * 8);
let (duy, dux) = (suy + mv.y, sux + mv.x);
for p in pts {
if !crate::av2_recon::work_tick("refmvs:367") { break; }
let dx = (p[1][0] - dux) as i64;
let dy = (p[1][1] - duy) as i64;
let sx = (p[0][0] - sux) as i64;
let sy = (p[0][1] - suy) as i64;
if (sx - dx).abs() < 256 && (sy - dy).abs() < 256 {
a00 += ((sx * sx) >> 2) + sx * 2 + 8;
a01 += ((sx * sy) >> 2) + sx + sy + 4;
a11 += ((sy * sy) >> 2) + sy * 2 + 8;
bx0 += ((sx * dx) >> 2) + sx + dx + 8;
bx1 += ((sy * dx) >> 2) + sy + dx + 4;
by0 += ((sx * dy) >> 2) + sx + dy + 4;
by1 += ((sy * dy) >> 2) + sy + dy + 8;
}
}
let mut m = [0i32; 6];
let det = a00 * a11 - a01 * a01;
if det == 0 {
m[2] = 0x10000;
m[5] = 0x10000;
set_affine_mv2d(bw4, bh4, mv, &mut m, bx4, by4);
return m;
}
let (recip, mut shift) = resolve_divisor_64(det.unsigned_abs());
let mut idet = (if det < 0 { -recip } else { recip }) as i64;
shift -= 16;
if shift < 0 {
idet <<= -shift;
shift = 0;
}
let r = (1i64 << shift) >> 1;
m[2] = get_mult_shift_diag(a11 * bx0 - a01 * bx1, idet, r, shift);
m[3] = get_mult_shift_ndiag(a00 * bx1 - a01 * bx0, idet, r, shift);
m[4] = get_mult_shift_ndiag(a11 * by0 - a01 * by1, idet, r, shift);
m[5] = get_mult_shift_diag(a00 * by1 - a01 * by0, idet, r, shift);
set_affine_mv2d(bw4, bh4, mv, &mut m, bx4, by4);
m
}
#[allow(clippy::too_many_arguments)]
pub fn derive_warpmv(grid: &RefmvsGrid, bx4: usize, by4: usize, bw4: usize, bh4: usize, w4: usize, h4: usize, ref0: i8, mv: Mv, sbsz: usize, col_end: usize) -> Option<[i32; 6]> {
use crate::av2_decode::BLOCK_DIMENSIONS;
let mut pts = [[[0i32; 2]; 2]; 8];
let mut np = 0usize;
let is_not_sb_boundary = (by4 & (sbsz - 1)) != 0;
let have_top = by4 > 0;
let have_left = bx4 > 0;
let mut have_topleft = false;
let mut have_topright = false;
let mut add_sample = |pts: &mut [[[i32; 2]; 2]; 8], np: &mut usize, dx: i32, dy: i32, sx: i32, sy: i32, rp: &RefmvsBlock| {
let bd = BLOCK_DIMENSIONS[rp.bs as usize];
let rmv = if rp.mf & 2 != 0 { rp.lmv } else { rp.mv };
for n in 0..2usize {
if !crate::av2_recon::work_tick("refmvs:426") { break; }
if *np >= 8 {
return;
}
let matches = if n == 0 { rp.ref_[0] == ref0 } else { rp.ref_[1] == ref0 };
if !matches {
continue;
}
let ix = 16 * (2 * dx + sx * bd[0] as i32) - 8;
let iy = 16 * (2 * dy + sy * bd[1] as i32) - 8;
pts[*np] = [[ix, iy], [ix + rmv[n].x, iy + rmv[n].y]];
*np += 1;
}
};
if have_top {
if is_not_sb_boundary {
let first = *grid.at(by4 - 1, bx4);
let mut off = first.bx4 as i32 - bx4 as i32;
have_topleft = off == 0;
loop {
if !crate::av2_recon::work_tick("refmvs_loop:451") { break; }
let cell = *grid.at(by4 - 1, (bx4 as i32 + off) as usize);
add_sample(&mut pts, &mut np, off, 0, 1, -1, &cell);
off += (BLOCK_DIMENSIONS[cell.bs as usize][0] as i32).max(1);
if !(off < w4 as i32 && np < 8) {
break;
}
}
have_topright = off <= bw4 as i32;
} else {
have_topleft = true;
let r2 = *grid.at(by4 - 1, (bx4 >> 1) * 2);
let mut off = (r2.bx4 as i32 + BLOCK_DIMENSIONS[r2.bs as usize][0] as i32 <= bx4 as i32) as i32;
let tr_ext = ((bx4 + bw4) & (sbsz - 1) != 0
&& bx4 + bw4 < col_end
&& (grid.at(by4 - 1, ((bx4 + bw4) >> 1) * 2).bx4 as i32) < (bx4 + bw4) as i32) as i32;
loop {
if !crate::av2_recon::work_tick("refmvs_loop:469") { break; }
let off8 = (bx4 as i32 + off) >> 1;
let cell = *grid.at(by4 - 1, (off8 * 2) as usize);
let ioff = cell.bx4 as i32 - bx4 as i32;
add_sample(&mut pts, &mut np, ioff, 0, 1, -1, &cell);
off = ioff + BLOCK_DIMENSIONS[cell.bs as usize][0] as i32 + 1;
if !(off < w4 as i32 + tr_ext && np < 8) {
break;
}
}
have_topright = true;
}
have_topright &= bw4 <= 16
&& bx4 + bw4 + ((!is_not_sb_boundary) as usize) < col_end
&& ((by4 & (sbsz - 1)) == 0 || ((bx4 + bw4) & (sbsz - 1) != 0 && grid.at(by4 - 1, bx4 + bw4).mv[0].y != -0x8000));
}
if np < 8 && have_left {
let first = *grid.at(by4, bx4 - 1);
let mut off = first.by4 as i32 - by4 as i32;
have_topleft &= off == 0;
loop {
if !crate::av2_recon::work_tick("refmvs_loop:490") { break; }
let cell = *grid.at((by4 as i32 + off) as usize, bx4 - 1);
add_sample(&mut pts, &mut np, 0, off, -1, 1, &cell);
off += (BLOCK_DIMENSIONS[cell.bs as usize][1] as i32).max(1);
if !(off < h4 as i32 && np < 8) {
break;
}
}
} else {
have_topleft = false;
}
if is_not_sb_boundary {
if np < 8 && have_topleft {
let c = *grid.at(by4 - 1, bx4 - 1);
add_sample(&mut pts, &mut np, 0, 0, -1, -1, &c);
}
if np < 8 && have_topright {
let c = *grid.at(by4 - 1, bx4 + bw4);
add_sample(&mut pts, &mut np, bw4 as i32, 0, 1, -1, &c);
}
} else {
if np < 8 && have_topleft {
let c = *grid.at(by4 - 1, (((bx4 as i32 - 1) >> 1) * 2) as usize);
if BLOCK_DIMENSIONS[c.bs as usize][0] as i32 + c.bx4 as i32 == bx4 as i32 {
add_sample(&mut pts, &mut np, 0, 0, -1, -1, &c);
}
}
if np < 8 && have_topright {
let c = *grid.at(by4 - 1, ((bx4 + bw4 + 1) >> 1) * 2);
if c.bx4 as i32 == (bx4 + bw4) as i32 {
add_sample(&mut pts, &mut np, bw4 as i32, 0, 1, -1, &c);
}
}
}
if np == 0 {
return None;
}
Some(find_affine_int(&pts[..np], bw4, bh4, mv, bx4, by4))
}
#[allow(clippy::too_many_arguments)]
pub fn get_warpmv_2d(m: &[i32; 6], bx4: i32, by4: i32, bw4: i32, bh4: i32, iw4: i32, ih4: i32, mv_precision: i32) -> Mv {
let x = (bx4 * 4 + bw4 * 2 - 1) as i64;
let y = (by4 * 4 + bh4 * 2 - 1) as i64;
let xc = (m[2] as i64 - (1 << 16)) * x + m[3] as i64 * y + m[0] as i64;
let yc = (m[5] as i64 - (1 << 16)) * y + m[4] as i64 * x + m[1] as i64;
let not_epel = (mv_precision < 6) as i64;
let shift = 13 + not_epel;
let rnd = (1i64 << shift) >> 1;
let max = 0xffff - not_epel as i32;
let sgn = |v: i64, s: i64| if s < 0 { -v } else { v };
let mut ry = (sgn(((yc.abs() + rnd) >> shift) << not_epel, yc)).clamp(-max as i64, max as i64) as i32;
let mut rx = (sgn(((xc.abs() + rnd) >> shift) << not_epel, xc)).clamp(-max as i64, max as i64) as i32;
ry = ry.clamp(-(by4 + bh4 + 4) * 32, (ih4 - by4 + 4) * 32);
rx = rx.clamp(-(bx4 + bw4 + 4) * 32, (iw4 - bx4 + 4) * 32);
Mv { y: ry, x: rx }
}
pub fn model_from_corners(tl: Mv, tr: Mv, bl: Mv, xpos: i32, ypos: i32, b_dim: [u8; 4]) -> Option<[i32; 6]> {
if tr == tl && bl == tl {
return None;
}
if tl.x.min(bl.x).min(tr.x + b_dim[0] as i32 * 32) < -xpos * 8 {
return None;
}
if tl.y.min(tr.y).min(bl.y + b_dim[1] as i32 * 32) < -ypos * 8 {
return None;
}
let clip32 = |v: i64| v.clamp(i32::MIN as i64, i32::MAX as i64) as i32;
let clip_m01 = |v: i64| v.clamp(-0x8000000, 0x7ffffc0) as i32;
let mut m = [0i32; 6];
m[2] = clip32(((tr.x - tl.x) as i64 * (1 << 11)) >> b_dim[2]);
m[4] = clip32(((tr.y - tl.y) as i64 * (1 << 11)) >> b_dim[2]);
m[3] = clip32(((bl.x - tl.x) as i64 * (1 << 11)) >> b_dim[3]);
m[5] = clip32(((bl.y - tl.y) as i64 * (1 << 11)) >> b_dim[3]);
m[0] = clip_m01(tl.x as i64 * (1 << 13) - xpos as i64 * m[2] as i64 - ypos as i64 * m[3] as i64);
m[1] = clip_m01(tl.y as i64 * (1 << 13) - xpos as i64 * m[4] as i64 - ypos as i64 * m[5] as i64);
for i in 2..6 {
if !crate::av2_recon::work_tick("refmvs:573") { break; }
m[i] = m[i].clamp(-0x7fc0, 0x7fc0);
m[i] += 0x20 - (m[i] < 0) as i32;
m[i] &= !0x3f;
}
m[2] += 0x10000;
m[5] += 0x10000;
Some(m)
}
pub const IDENTITY_WARP: [i32; 6] = [0, 0, 0x10000, 0, 0, 0x10000];
#[allow(clippy::too_many_arguments)]
pub fn warp_extend(
grid: &RefmvsGrid, bx4: usize, by4: usize, bw4: usize, bh4: usize, gx_log2: u32, gy_log2: u32,
ref0: i8, fmv: Mv, drl_off: (i32, i32), sbsz: usize, iw4: usize, ih4: usize, gmv: [i32; 6],
) -> Option<[i32; 6]> {
let have_left = bx4 > 0;
let have_top = by4 > 0;
let is_sb = ((by4 & (sbsz - 1)) == 0) as i32;
let (mut x_off, mut y_off) = (0i32, 0i32);
if drl_off.0 == -1 || drl_off.1 == -1 {
x_off = drl_off.0;
y_off = drl_off.1;
let r = if is_sb == 1 && y_off == -1 {
if (bx4 & (sbsz - 1)) != 0 || x_off >= 0 {
*grid.at(by4 - 1, ((bx4 as i32 + x_off) & !1) as usize)
} else {
*grid.at(by4 - 1, (bx4 - 1) & !1)
}
} else {
*grid.at((by4 as i32 + y_off) as usize, (bx4 as i32 + x_off) as usize)
};
if r.ref_[0] == 7 {
x_off = 0;
y_off = 0;
}
if std::env::var("WEDBG").is_ok() && bx4 == 22 && by4 == 54 {
crate::dlog!("WEDBG drl_off={drl_off:?} r.ref={:?} r.mf={} -> off=({x_off},{y_off})", r.ref_, r.mf);
}
}
let refm = |b: &RefmvsBlock| b.ref_[0] == ref0 || b.ref_[1] == ref0;
if x_off == 0 && y_off == 0 {
let bml = (have_left && by4 + bh4 <= ih4).then(|| *grid.at(by4 + bh4 - 1, bx4 - 1));
let rmt = (have_top && bx4 + bw4 <= iw4).then(|| if is_sb == 1 {
*grid.at(by4 - 1, ((bx4 & !1) + bw4 - 2) & !1)
} else {
*grid.at(by4 - 1, bx4 + bw4 - 1)
});
let tml = have_left.then(|| *grid.at(by4, bx4 - 1));
let lmt = have_top.then(|| if is_sb == 1 {
*grid.at(by4 - 1, bx4 & !1)
} else {
*grid.at(by4 - 1, bx4)
});
if bml.as_ref().is_some_and(refm) {
y_off = bh4 as i32 - 1;
x_off = -1;
} else if rmt.as_ref().is_some_and(refm) {
y_off = -1;
x_off = -(bx4 as i32 & is_sb) + bw4 as i32 - (1 + is_sb);
} else if tml.as_ref().is_some_and(refm) {
y_off = 0;
x_off = -1;
} else if lmt.as_ref().is_some_and(refm) {
y_off = -1;
x_off = -(bx4 as i32 & is_sb);
}
}
if x_off == 0 && y_off == 0 {
return None; }
let r = if y_off == -1 && is_sb == 1 {
*grid.at(by4 - 1, ((bx4 as i32 + x_off) & !1) as usize)
} else {
*grid.at((by4 as i32 + y_off) as usize, (bx4 as i32 + x_off) as usize)
};
let mut m = if r.mf & 2 != 0 {
r.matrix
} else if r.mf & 1 != 0 {
gmv
} else {
let ri = (r.ref_[0] != ref0) as usize;
[r.mv[ri].x * (1 << 13), r.mv[ri].y * (1 << 13), 0x10000, 0, 0, 0x10000]
};
let sx = bx4 as i64 * 4 + 2 * bw4 as i64 - 1;
let sy = by4 as i64 * 4 + 2 * bh4 as i64 - 1;
let px = (sx << 16) + fmv.x as i64 * (1 << 13);
let py = (sy << 16) + fmv.y as i64 * (1 << 13);
if x_off >= 0 {
let ay = by4 as i64 * 4 - 1;
let sh = 1 + gy_log2;
let apx = m[2] as i64 * sx + m[3] as i64 * ay + m[0] as i64;
let apy = m[4] as i64 * sx + m[5] as i64 * ay + m[1] as i64;
let m3 = ((px - apx + bh4 as i64 - (px < apx) as i64) >> sh) as i32;
let m5 = ((py - apy + bh4 as i64 - (py < apy) as i64) >> sh) as i32;
m[3] = ((m3 + 0x20 - (m3 < 0) as i32) & !0x3f).clamp(-0x7fc0, 0x7fc0);
m[5] = ((m5 + 0x20 - (m5 < 0x10000) as i32) & !0x3f).clamp(0x8040, 0x17fc0);
} else {
let ax = bx4 as i64 * 4 - 1;
let sh = 1 + gx_log2;
let lpx = m[2] as i64 * ax + m[3] as i64 * sy + m[0] as i64;
let lpy = m[4] as i64 * ax + m[5] as i64 * sy + m[1] as i64;
let m2 = ((px - lpx + bw4 as i64 - (px < lpx) as i64) >> sh) as i32;
let m4 = ((py - lpy + bw4 as i64 - (py < lpy) as i64) >> sh) as i32;
m[2] = ((m2 + 0x20 - (m2 < 0x10000) as i32) & !0x3f).clamp(0x8040, 0x17fc0);
m[4] = ((m4 + 0x20 - (m4 < 0) as i32) & !0x3f).clamp(-0x7fc0, 0x7fc0);
}
set_affine_mv2d(bw4, bh4, fmv, &mut m, bx4, by4);
if std::env::var("WEDBG").is_ok() && bx4 == 22 && by4 == 54 {
crate::dlog!("WEDBG final off=({x_off},{y_off}) fmv=({},{}) m={m:x?}", fmv.y, fmv.x);
}
(m[2] > 0).then_some(m) }
#[inline]
fn apply_sign(v: i32, s: i32) -> i32 {
if s < 0 {
-v
} else {
v
}
}
pub fn warp_cell_mv(m: &[i32; 6], bx: i32, by: i32, cx: i32, cy: i32) -> Mv {
let mvx = (m[2] - 0x10000) * (bx + 1 + cx) + m[3] * (by + 1 + cy) + (m[0] >> 2);
let mvy = m[4] * (bx + 1 + cx) + (m[1] >> 2) + (m[5] - 0x10000) * (by + 1 + cy);
Mv {
y: apply_sign((mvy.abs() + 1024) >> 11, mvy).clamp(-0xffff, 0xffff),
x: apply_sign((mvx.abs() + 1024) >> 11, mvx).clamp(-0xffff, 0xffff),
}
}
pub struct ScanState {
pub mvstack: [Candidate; 6],
pub cnt: usize,
pub iter_cntr: i32,
pub sngl: [(i8, Mv); 4],
pub sngl_cnt: usize,
pub sngl_iter: i32,
pub dr: [[Mv; 2]; 6],
pub drvd_cnt: usize,
pub drvd_iter: i32,
pub b8x8: usize,
}
impl Default for ScanState {
fn default() -> Self {
ScanState {
mvstack: [Candidate::default(); 6], cnt: 0, iter_cntr: 0,
sngl: [(-1, Mv::default()); 4], sngl_cnt: 0, sngl_iter: 0,
dr: [[Mv::default(); 2]; 6], drvd_cnt: 0, drvd_iter: 0,
b8x8: 0,
}
}
}
fn add_candidate_c2s(st: &mut ScanState, ref_: i8, cand_mv: Mv) {
let last = st.sngl_cnt;
if st.sngl_iter < 2 {
for m in 0..last {
if !crate::av2_recon::work_tick("refmvs:766") { break; }
if st.sngl[m].1 == cand_mv && st.sngl[m].0 == ref_ {
st.sngl_iter += m as i32 + 1;
return;
}
}
st.sngl_iter += last as i32;
}
if last >= 4 {
return;
}
st.sngl[last] = (ref_, cand_mv);
st.sngl_cnt = last + 1;
}
fn add_derived_comp(st: &mut ScanState, lim: usize) {
for n in 0..st.drvd_cnt {
if !crate::av2_recon::work_tick("refmvs:784") { break; }
if st.cnt >= 6 {
break;
}
let pair = st.dr[n];
let mut cnt = st.cnt;
let mut iter = st.iter_cntr;
add_candidate_comp(&mut st.mvstack, &mut cnt, lim, 0, 8, pair, &mut iter, 16);
st.cnt = cnt;
st.iter_cntr = iter;
}
}
fn push_derived_sngl(st: &mut ScanState, cand: Mv) {
let last = st.drvd_cnt;
if st.drvd_iter < 2 {
for n in 0..last {
if !crate::av2_recon::work_tick("refmvs:801") { break; }
if st.dr[n][0] == cand {
st.drvd_iter += n as i32 + 1;
return;
}
}
st.drvd_iter += last as i32;
}
if last >= 4 {
return;
}
st.dr[last][0] = cand;
st.drvd_cnt = last + 1;
}
fn push_derived_pair(st: &mut ScanState, pair: [Mv; 2]) {
let last = st.drvd_cnt;
if st.drvd_iter < 2 {
for n in 0..last {
if !crate::av2_recon::work_tick("refmvs:820") { break; }
if st.dr[n][0] == pair[0] && st.dr[n][1] == pair[1] {
st.drvd_iter += n as i32 + 1;
return;
}
}
st.drvd_iter += last as i32;
}
if last >= 4 {
return;
}
st.dr[last] = pair;
st.drvd_cnt = last + 1;
}
pub fn add_spatial_candidate_sngl(
st: &mut ScanState,
weight: i32,
b: &RefmvsBlock,
y_off: i32,
x_off: i32,
oy8: isize,
ox8: isize,
cand_bx4: i32,
cand_by4: i32,
ref0: i8,
ref1: i8,
gmv: [Mv; 2],
) {
if st.cnt >= 6 {
return;
}
if b.mv[0].y == -0x8000 {
return; }
let rp_tmv = || -> Mv {
let (aoy8, aox8) = if b.ref_[0] == 7 {
let bd = crate::av2_decode::BLOCK_DIMENSIONS[b.bs as usize];
let (bw4n, bh4n) = (bd[0] as i32, bd[1] as i32);
let seq_refine = crate::av2_recon::SEQ_TIP.with(|c| c.get()).4;
let fm = TMVS.with(|c| c.borrow().tip_frame_mode);
let is16 = if fm == 2 { !seq_refine } else { (!seq_refine && bw4n.min(bh4n) >= 4) || bw4n.max(bh4n) >= 64 } as i32;
let ax = ((b.bx4 as i32) >> 1) + ((((cand_bx4 - b.bx4 as i32) >> 1) >> is16) << is16);
let ay = ((b.by4 as i32) >> 1) + ((((cand_by4 - b.by4 as i32) >> 1) >> is16) << is16);
(oy8 - ((cand_by4 >> 1) as isize - ay as isize), ax as isize)
} else {
(oy8, ox8)
};
let stride = TMVS.with(|c| c.borrow().stride) as isize;
let idx = 2 * stride + aoy8 * stride + aox8;
let m = RP_PROJ.with(|c| c.borrow().get(idx.max(0) as usize).map(|p| p.0))
.unwrap_or(Mv { y: INVALID_MV_I32, x: INVALID_MV_I32 });
if m.y == INVALID_MV_I32 { Mv { y: 0, x: 0 } } else { m }
};
if ref1 < 0 {
let t = TMVS.with(|c| c.borrow().clone());
let num = 1 + (ref0 >= 0) as usize;
let tip_pair = [t.tip_ref.0 as i8, t.tip_ref.1 as i8];
for n in 0..num {
if !crate::av2_recon::work_tick("refmvs:894") { break; }
let bref_n = if b.ref_[0] == 7 { tip_pair[n] } else { b.ref_[n] };
if b.ref_[n] == ref0 {
let cand_mv = if (b.mf & 1) != 0 && gmv[0].y != -0x8000 { gmv[0] } else { b.mv[n] };
add_candidate_sngl(&mut st.mvstack, &mut st.cnt, 6, weight, cand_mv, y_off, x_off, &mut st.iter_cntr, 16);
} else if b.ref_[0] == 7 && t.valid && tip_pair[n] == ref0 {
let tipmv = scale_mv(rp_tmv(), t.tip_sf[n]);
let cand_mv = Mv {
y: (tipmv.y + b.mv[0].y).clamp(-0xffff, 0xffff),
x: (tipmv.x + b.mv[0].x).clamp(-0xffff, 0xffff),
};
add_candidate_sngl(&mut st.mvstack, &mut st.cnt, 6, weight, cand_mv, y_off, x_off, &mut st.iter_cntr, 16);
} else if ref0 == 7 && t.valid && (b.ref_[0], b.ref_[1]) == (tip_pair[0], tip_pair[1]) {
let in_delta = Mv { y: b.mv[0].y - b.mv[1].y, x: b.mv[0].x - b.mv[1].x };
let out_delta = scale_mv(in_delta, t.tip_sf[0]);
let cand = Mv {
y: (b.mv[0].y - out_delta.y).clamp(-0xffff, 0xffff),
x: (b.mv[0].x - out_delta.x).clamp(-0xffff, 0xffff),
};
push_derived_sngl(st, cand);
break;
} else if t.valid && t.mv_traj && t.use_ref_frame_mvs
&& (0..7).contains(&ref0)
&& (b.ref_[0] == 7 || (0..7).contains(&b.ref_[n]))
&& RP_TRAJ.with(|c| c.borrow()[ref0 as usize][st.b8x8].y) != INVALID_MV_I32
&& RP_TRAJ.with(|c| c.borrow()[bref_n as usize][st.b8x8].y) != INVALID_MV_I32
{
let a_mv = RP_TRAJ.with(|c| c.borrow()[bref_n as usize][st.b8x8]);
let c_mv = RP_TRAJ.with(|c| c.borrow()[ref0 as usize][st.b8x8]);
let b_mv = if b.ref_[0] == 7 {
let tipmv = scale_mv(rp_tmv(), t.tip_sf[n]);
Mv {
y: (tipmv.y + b.mv[0].y).clamp(-0xffff, 0xffff),
x: (tipmv.x + b.mv[0].x).clamp(-0xffff, 0xffff),
}
} else {
b.mv[n]
};
let cand = Mv {
y: (b_mv.y + c_mv.y - a_mv.y).clamp(-0xffff, 0xffff),
x: (b_mv.x + c_mv.x - a_mv.x).clamp(-0xffff, 0xffff),
};
push_derived_sngl(st, cand);
} else if t.valid && (0..7).contains(&ref0) && b.ref_[0] >= 0 && (0..7).contains(&bref_n)
&& t.ref_sign[ref0 as usize] == t.ref_sign[bref_n as usize]
{
let (num_mv, den) = if b.ref_[0] == 7 {
let tipmv = scale_mv(rp_tmv(), t.tip_sf[n]);
(Mv {
y: (tipmv.y + b.mv[0].y).clamp(-0xffff, 0xffff),
x: (tipmv.x + b.mv[0].x).clamp(-0xffff, 0xffff),
}, t.abspocdiff[bref_n as usize])
} else {
(b.mv[n], t.abspocdiff[b.ref_[n] as usize])
};
let cand = mv_projection_t(num_mv, t.abspocdiff[ref0 as usize], den, -0xffff, 0xffff);
push_derived_sngl(st, cand);
}
if b.ref_[1] < 0 && b.ref_[0] != 7 {
break;
}
}
return;
}
{
let t = TMVS.with(|c| c.borrow().clone());
if b.ref_[0] == 7 && t.valid && (ref0, ref1) == (t.tip_ref.0 as i8, t.tip_ref.1 as i8) {
let tmv = rp_tmv();
let t0 = scale_mv(tmv, t.tip_sf[0]);
let t1 = scale_mv(tmv, t.tip_sf[1]);
let cand = [
Mv { y: (t0.y + b.mv[0].y).clamp(-0xffff, 0xffff), x: (t0.x + b.mv[0].x).clamp(-0xffff, 0xffff) },
Mv { y: (t1.y + b.mv[0].y).clamp(-0xffff, 0xffff), x: (t1.x + b.mv[0].x).clamp(-0xffff, 0xffff) },
];
add_candidate_comp(&mut st.mvstack, &mut st.cnt, 6, weight, 8, cand, &mut st.iter_cntr, 16);
return;
}
}
if !(b.ref_[0] == ref0 && b.ref_[1] == ref1) {
let t = TMVS.with(|c| c.borrow().clone());
if t.valid && t.mv_traj && t.use_ref_frame_mvs && b.ref_[0] != 7 && ref0 != ref1
&& (0..7).contains(&ref0) && (0..7).contains(&ref1)
&& RP_TRAJ.with(|c| c.borrow()[ref0 as usize][st.b8x8].y) != INVALID_MV_I32
&& RP_TRAJ.with(|c| c.borrow()[ref1 as usize][st.b8x8].y) != INVALID_MV_I32
{
let b1 = RP_TRAJ.with(|c| c.borrow()[ref0 as usize][st.b8x8]);
let b2 = RP_TRAJ.with(|c| c.borrow()[ref1 as usize][st.b8x8]);
for n in 0..2usize {
if !crate::av2_recon::work_tick("refmvs:994") { break; }
if b.ref_[n] < 0 {
break;
}
if !(0..7).contains(&b.ref_[n]) {
continue;
}
let a_mv = RP_TRAJ.with(|c| c.borrow()[b.ref_[n] as usize][st.b8x8]);
if a_mv.y == INVALID_MV_I32 {
continue;
}
let pair = [
Mv { y: (b.mv[n].y + b1.y - a_mv.y).clamp(-0xffff, 0xffff),
x: (b.mv[n].x + b1.x - a_mv.x).clamp(-0xffff, 0xffff) },
Mv { y: (b.mv[n].y + b2.y - a_mv.y).clamp(-0xffff, 0xffff),
x: (b.mv[n].x + b2.x - a_mv.x).clamp(-0xffff, 0xffff) },
];
push_derived_pair(st, pair);
}
}
}
if b.ref_[0] == ref0 && b.ref_[1] == ref1 {
let cand = [
if (b.mf & 1) != 0 && gmv[0].y != -0x8000 { gmv[0] } else { b.mv[0] },
if (b.mf & 1) != 0 && gmv[1].y != -0x8000 { gmv[1] } else { b.mv[1] },
];
add_candidate_comp(&mut st.mvstack, &mut st.cnt, 6, weight, (b.mf >> 2) as i8, cand, &mut st.iter_cntr, 16);
} else {
let refp = [ref0, ref1];
let ns: usize = if ref0 == b.ref_[0] || ref0 == b.ref_[1] {
0
} else if ref1 != b.ref_[0] && ref1 != b.ref_[1] {
return;
} else {
1
};
let nc = (refp[ns] != b.ref_[0]) as usize;
let mut oidx = st.sngl_cnt;
for i in 0..st.sngl_cnt {
if !crate::av2_recon::work_tick("refmvs:1036") { break; }
if refp[1 - ns] == st.sngl[i].0 {
oidx = i;
break;
}
}
if oidx < st.sngl_cnt {
let mut cand = [Mv::default(); 2];
cand[ns] = b.mv[nc];
cand[1 - ns] = st.sngl[oidx].1;
push_derived_pair(st, cand);
}
let cand_mv = if (b.mf & 1) != 0 && gmv[nc].y != -0x8000 { gmv[ns] } else { b.mv[nc] };
add_candidate_c2s(st, b.ref_[nc], cand_mv);
}
}
pub fn add_global_candidate(st: &mut ScanState, gmv0: Mv) {
if st.cnt >= 6 {
return;
}
let last = st.cnt;
if st.iter_cntr < 16 {
for n in 0..last {
if !crate::av2_recon::work_tick("refmvs:1062") { break; }
if st.mvstack[n].mv[0] == gmv0 {
st.iter_cntr += n as i32 + 1;
return;
}
}
st.iter_cntr += last as i32;
}
st.mvstack[last].mv[0] = gmv0;
st.mvstack[last].weight = 0;
st.mvstack[last].y_off = 0;
st.mvstack[last].x_off = 0;
st.cnt = last + 1;
}
pub const INVALID_TRAJ: u16 = 0x8080;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct TemporalBlock {
pub ref_: (i8, i8),
pub qmv: [u16; 2],
}
impl Default for TemporalBlock {
fn default() -> Self {
TemporalBlock { ref_: (-1, -1), qmv: [INVALID_TRAJ; 2] }
}
}
#[inline]
fn quantize_mv_comp(absv: u32) -> u32 {
if absv == 0 {
return 0;
}
let nbits = (31 - absv.leading_zeros()).saturating_sub(4).min(6);
(absv >> nbits) + 16 * nbits
}
pub fn quantize_mv(mv: Mv) -> u16 {
let (ay, ax) = (mv.y.unsigned_abs(), mv.x.unsigned_abs());
if ay.max(ax) >= 2048 {
return INVALID_TRAJ;
}
let qy = if mv.y < 0 { -(quantize_mv_comp(ay) as i32) } else { quantize_mv_comp(ay) as i32 } as i8;
let qx = if mv.x < 0 { -(quantize_mv_comp(ax) as i32) } else { quantize_mv_comp(ax) as i32 } as i8;
((qy as u8 as u16) << 8) | (qx as u8 as u16)
}
#[inline]
fn dequantize_mv_comp(v: i32) -> i32 {
let absv = v.unsigned_abs();
let nbits = (absv >> 4) as i32 - (absv >= 16) as i32;
let res = ((absv as i32) - nbits * 16) << nbits;
if v < 0 { -res } else { res }
}
pub fn dequantize_mv(q: u16) -> Mv {
if q == INVALID_TRAJ {
return Mv { y: -0x8000, x: 0 };
}
let qy = (q >> 8) as u8 as i8 as i32;
let qx = (q & 0xff) as u8 as i8 as i32;
Mv { y: dequantize_mv_comp(qy), x: dequantize_mv_comp(qx) }
}
#[derive(Clone, Default)]
pub struct SavedMotionField {
pub w8: usize,
pub h8: usize,
pub cells: Vec<TemporalBlock>,
pub poc: u32,
pub refpoc: [u32; 7],
pub n_ref: u32,
}
thread_local! {
pub static RP_CUR: std::cell::RefCell<(usize, usize, Vec<TemporalBlock>)> =
const { std::cell::RefCell::new((0, 0, Vec::new())) };
pub static RP_REF: std::cell::RefCell<[Option<SavedMotionField>; 8]> =
const { std::cell::RefCell::new([None, None, None, None, None, None, None, None]) };
}
pub fn rp_reset(iw4: usize, ih4: usize) {
let (w8, h8) = ((iw4 + 1) >> 1, (ih4 + 1) >> 1);
RP_CUR.with(|c| {
let mut b = c.borrow_mut();
*b = (w8, h8, vec![TemporalBlock::default(); w8 * h8]);
});
}
pub fn rp_write(bx4: usize, by4: usize, bw4: usize, bh4: usize, tb: TemporalBlock) {
RP_CUR.with(|c| {
let mut b = c.borrow_mut();
let (w8, h8, ref mut cells) = *b;
if cells.is_empty() {
return;
}
let (x0, y0) = (bx4 >> 1, by4 >> 1);
for y in 0..bh4.div_ceil(2) {
if !crate::av2_recon::work_tick("refmvs:1179") { break; }
if y0 + y >= h8 {
break;
}
for x in 0..bw4.div_ceil(2) {
if !crate::av2_recon::work_tick("refmvs:1183") { break; }
if x0 + x >= w8 {
break;
}
cells[(y0 + y) * w8 + (x0 + x)] = tb;
}
}
});
}
pub fn reset_stream_state() {
RP_REF.with(|r| *r.borrow_mut() = std::array::from_fn(|_| None));
GRID.with(|g| *g.borrow_mut() = RefmvsGrid::default());
BANK.with(|b| *b.borrow_mut() = RefmvBank::default());
}
pub fn rp_save(refresh: u32, poc: u32, refpoc: [u32; 7], n_ref: u32) {
RP_CUR.with(|c| {
let (w8, h8, ref cells) = *c.borrow();
if std::env::var("RPDBG").map_or(false, |v| v == format!("{poc}")) {
for r in 0..3usize {
if !crate::av2_recon::work_tick("refmvs:1207") { break; }
let row: Vec<String> = (0..16.min(w8)).map(|x| {
let tb = &cells[r * w8 + x];
let m0 = dequantize_mv(tb.qmv[0]);
let m1 = dequantize_mv(tb.qmv[1]);
format!("({},{};{}|{},{};{})", m0.y, m0.x, tb.ref_.0, m1.y, m1.x, tb.ref_.1)
}).collect();
crate::dlog!("[MRMVS] poc={poc} row{r}: {}", row.join(" "));
}
}
let saved = SavedMotionField { w8, h8, cells: cells.clone(), poc, refpoc, n_ref };
RP_REF.with(|r| {
let mut slots = r.borrow_mut();
for (i, slot) in slots.iter_mut().enumerate() {
if refresh & (1 << i) != 0 {
*slot = Some(saved.clone());
}
}
});
});
}
pub const INVALID_MV_I32: i32 = -0x8000;
const INVALID_REF2CUR: i32 = -32;
#[inline]
pub fn scale_mv(m: Mv, sf: i32) -> Mv {
let y = m.y as i64 * sf as i64;
let x = m.x as i64 * sf as i64;
Mv {
y: (((y + 0x2000 - (y < 0) as i64) >> 14) as i32).clamp(-0xffff, 0xffff),
x: (((x + 0x2000 - (x < 0) as i64) >> 14) as i32).clamp(-0xffff, 0xffff),
}
}
const DIV_MULT: [i32; 32] = [
0, 16384, 8192, 5461, 4096, 3276, 2730, 2340,
2048, 1820, 1638, 1489, 1365, 1260, 1170, 1092,
1024, 963, 910, 862, 819, 780, 744, 712,
682, 655, 630, 606, 585, 564, 546, 528,
];
pub fn mv_projection_t(m: Mv, num: i32, den: i32, min: i32, max: i32) -> Mv {
let frac = num * DIV_MULT[den.unsigned_abs().min(31) as usize];
let y = m.y * frac;
let x = m.x * frac;
Mv {
y: ((y + 8192 + (y >> 31)) >> 14).clamp(min, max),
x: ((x + 8192 + (x >> 31)) >> 14).clamp(min, max),
}
}
#[derive(Clone, Default)]
pub struct TmvsFrame {
pub valid: bool,
pub mv_traj: bool,
pub n_ref: usize,
pub pocdiff: [i32; 7],
pub abspocdiff: [i32; 7],
pub ref_sign: [bool; 7],
pub n_mfmvs: usize,
pub mfmv: [(i8, i8, u8); 4], pub mfmv_ref2cur: [i32; 4],
pub mfmv_ref2ref: [[i32; 7]; 4],
pub mfmv_ref2idx: [[i8; 7]; 4],
pub mfmv_ref2sf: [[[i32; 2]; 7]; 4],
pub use_ref_frame_mvs: bool,
pub tip_ref: (u8, u8),
pub tip_delta: i32,
pub tip_sf: [i32; 2],
pub tip_frame_mode: u8,
pub tip_hole_fill: bool,
pub stride: usize,
pub iw8: i32,
pub ih8: i32,
pub sample_step: i32,
}
thread_local! {
pub static TMVS: std::cell::RefCell<TmvsFrame> = std::cell::RefCell::new(TmvsFrame::default());
pub static RP_PROJ: std::cell::RefCell<Vec<(Mv, i32)>> = const { std::cell::RefCell::new(Vec::new()) };
pub static RP_TRAJ: std::cell::RefCell<[Vec<Mv>; 7]> = std::cell::RefCell::new(Default::default());
pub static RP_MAP: std::cell::RefCell<Vec<Vec<(i8, i8)>>> = const { std::cell::RefCell::new(Vec::new()) };
}
#[allow(clippy::too_many_arguments)]
fn add_temporal_candidate_t(
st: &mut ScanState, ref0: i8, ref1: i8, off_8x8: usize,
) -> bool {
let t = TMVS.with(|c| c.borrow().clone());
if !t.valid || ref0 < 0 || ref0 >= 7 {
return false;
}
let stride = t.stride;
let read_arm = |r: i8| -> Option<Mv> {
let mut m = RP_TRAJ.with(|c| c.borrow()[r as usize][off_8x8]);
if !t.mv_traj || m.y == INVALID_MV_I32 {
let (pm, pref) = RP_PROJ.with(|c| c.borrow()[2 * stride + off_8x8]);
if pm.y == INVALID_MV_I32 {
return None;
}
m = mv_projection_t(pm, t.pocdiff[r as usize], pref, -0xffff, 0xffff);
}
Some(m)
};
let mv0 = match read_arm(ref0) {
Some(m) => m,
None => return false,
};
if ref1 == -1 {
let weight = 1 + (t.abspocdiff[ref0 as usize] <= 2) as i32;
return add_candidate_sngl(&mut st.mvstack, &mut st.cnt, 6, weight, mv0, 0, 0, &mut st.iter_cntr, 16);
}
let mv1 = match read_arm(ref1) {
Some(m) => m,
None => return false,
};
add_candidate_comp(&mut st.mvstack, &mut st.cnt, 6, 1, 8, [mv0, mv1], &mut st.iter_cntr, 16)
}
fn topo_insert(cnt: usize, idx: usize, order: &mut [i8; 7], rev: &mut [i8; 7],
cnv: &[[i8; 7]; 7], walk_deps: &[bool; 7]) -> usize {
if rev[idx] != -1 {
return cnt;
}
rev[idx] = 0; let mut cnt = cnt;
if walk_deps[idx] {
for n in 0..7 {
let r = cnv[idx][n];
if r == -1 {
continue;
}
cnt = topo_insert(cnt, r as usize, order, rev, cnv, walk_deps);
}
}
order[cnt] = idx as i8;
rev[idx] = cnt as i8;
cnt + 1
}
fn abs_closest_ref(ref2ref: &[i32; 7], cur2ref: &[i32; 7], dir: bool) -> i32 {
let mut b = 0xff;
for n in 0..7 {
let a = ref2ref[n].abs();
if ((cur2ref[n] > 0 && ref2ref[n] > 0 && dir) || (cur2ref[n] < 0 && ref2ref[n] < 0 && !dir)) && a < b {
b = a;
}
}
b
}
#[allow(clippy::too_many_arguments)]
pub fn tmvs_setup(
nbits: u32, poc: u32, n_ref: usize, refidx: &[u8; 7], refpoc: &[u32; 7],
use_ref_frame_mvs_hdr: bool, mv_traj: bool, seq_tip: bool,
tip_frame_mode: u8, tip_hole_fill: bool, iw4: usize, ih4: usize,
sample_step: i32,
) {
let gpd = |a: u32, b: u32| crate::av2_recon::get_poc_diff(nbits, a, b);
let mut t = TmvsFrame {
valid: true,
mv_traj,
n_ref,
use_ref_frame_mvs: false,
tip_frame_mode,
tip_hole_fill,
sample_step: sample_step.max(1),
stride: (iw4 + 1) >> 1,
iw8: ((iw4 + 1) >> 1) as i32,
ih8: ((ih4 + 1) >> 1) as i32,
..Default::default()
};
let mut refcnt = [0u32; 7];
let mut ref_ref_poc = [[0u32; 7]; 7];
let mut have_field = [false; 7];
RP_REF.with(|r| {
let slots = r.borrow();
for i in 0..n_ref {
if !crate::av2_recon::work_tick("refmvs:1403") { break; }
if let Some(f) = slots[refidx[i] as usize].as_ref() {
if !f.cells.is_empty() {
have_field[i] = true;
refcnt[i] = f.n_ref;
ref_ref_poc[i] = f.refpoc;
}
}
}
});
let mut ref2ref = [[0i32; 7]; 7];
let mut ref2cur = [[0i32; 7]; 7];
let mut have_ref_sign = [[false; 2]; 7];
let mut refref2curref_idx = [[-1i8; 7]; 7];
for i in 0..n_ref {
if !crate::av2_recon::work_tick("refmvs:1417") { break; }
t.ref_sign[i] = gpd(refpoc[i], poc) < 0;
t.pocdiff[i] = gpd(poc, refpoc[i]).clamp(-31, 31);
t.abspocdiff[i] = t.pocdiff[i].abs();
if refcnt[i] != 0 {
for nn in 0..(refcnt[i] as usize).min(7) {
if !crate::av2_recon::work_tick("refmvs:1425") { break; }
ref2ref[i][nn] = gpd(refpoc[i], ref_ref_poc[i][nn]);
if ref2ref[i][nn] > 0 { have_ref_sign[i][0] = true; }
if ref2ref[i][nn] < 0 { have_ref_sign[i][1] = true; }
ref2cur[i][nn] = gpd(poc, ref_ref_poc[i][nn]);
let mut m = n_ref;
for mm in 0..n_ref {
if !crate::av2_recon::work_tick("refmvs:1431") { break; }
if ref_ref_poc[i][nn] == refpoc[mm] { m = mm; break; }
}
refref2curref_idx[i][nn] = if m == n_ref { -1 } else { m as i8 };
}
}
}
if seq_tip && n_ref > 1 {
let mut order = [0u8; 7];
let mut refdist = [0i32; 7];
let mut n_past = 0usize;
for n in 0..n_ref {
if !crate::av2_recon::work_tick("refmvs:1443") { break; }
let dist = gpd(refpoc[n], poc);
refdist[n] = dist;
let mut m = n;
while m > 0 && refdist[order[m - 1] as usize] > dist {
order[m] = order[m - 1];
m -= 1;
}
order[m] = n as u8;
n_past += (dist < 0) as usize;
}
t.tip_ref = if n_past == n_ref {
(order[n_ref - 1], order[n_ref - 2])
} else if n_past == 0 {
(order[0], order[1])
} else {
(order[n_past - 1], order[n_past])
};
if tip_frame_mode != 0 {
let d2 = gpd(refpoc[t.tip_ref.1 as usize], refpoc[t.tip_ref.0 as usize]);
t.tip_delta = d2.abs();
let d1 = t.pocdiff[t.tip_ref.0 as usize];
let dv = DIV_MULT[(d2.abs().min(31)) as usize];
t.tip_sf[0] = d1.abs().min(31) * dv;
if (d1 < 0) != (d2 < 0) { t.tip_sf[0] = -t.tip_sf[0]; }
let d3 = t.pocdiff[t.tip_ref.1 as usize];
t.tip_sf[1] = d3.abs().min(31) * dv;
if (d3 < 0) != (d2 < 0) { t.tip_sf[1] = -t.tip_sf[1]; }
}
}
if use_ref_frame_mvs_hdr && nbits != 0 {
let mut order = [0u8; 7];
for n in 0..n_ref {
if !crate::av2_recon::work_tick("refmvs:1476") { break; }
let pd = t.pocdiff[n];
let mut m = n;
while m > 0 && pd > t.pocdiff[order[m - 1] as usize] {
order[m] = order[m - 1];
m -= 1;
}
order[m] = n as u8;
}
let mut first_fut = 0usize;
while first_fut < n_ref && t.ref_sign[order[first_fut] as usize] {
first_fut += 1;
}
let mut topo_order = [0i8; 7];
let mut rev_topo = [-1i8; 7];
let mut topo_cnt = 0usize;
let walk_deps: [bool; 7] = {
let mut w = [false; 7];
crate::av2_recon::REF_SLOTS.with(|s| {
let slots = s.borrow();
for (i, wi) in w.iter_mut().enumerate().take(n_ref) {
let plain_inter = matches!(slots[refidx[i] as usize],
Some(r) if !r.is_key_or_intra && !r.is_sframe);
*wi = refcnt[i] != 0 && plain_inter;
}
});
w
};
for n in 0..n_ref {
if !crate::av2_recon::work_tick("refmvs:1507") { break; }
topo_cnt = topo_insert(topo_cnt, n, &mut topo_order, &mut rev_topo, &refref2curref_idx, &walk_deps);
}
if topo_cnt > 1 {
let elig: [bool; 7] = {
let mut e = [false; 7];
crate::av2_recon::REF_SLOTS.with(|s| {
let slots = s.borrow();
for (i, ei) in e.iter_mut().enumerate().take(n_ref) {
let not_key = matches!(slots[refidx[i] as usize], Some(r) if !r.is_key_or_intra);
*ei = have_field[i] && not_key;
}
});
e
};
let cur_idx: i32 = first_fut as i32 - 1;
let has_both_sides = first_fut > 0 && first_fut < n_ref;
let n_past = first_fut;
let mut checked = [[false; 2]; 7];
let mut checked_count = 0usize;
macro_rules! add_proc {
($start:expr, $tgt:expr, $side:expr, $max_check:expr) => {{
let sti: i32 = $start;
if sti >= 0 {
let s = sti as usize;
if elig[s] && t.abspocdiff[s] <= 31 && t.n_mfmvs < 4 && !checked[s][$side] {
if checked_count < $max_check {
checked[s][$side] = true;
checked_count += 1;
t.mfmv[t.n_mfmvs] = (s as i8, $tgt, $side as u8);
t.n_mfmvs += 1;
}
}
}
}};
}
if seq_tip && cur_idx >= 0 && (has_both_sides || n_past >= 2) {
let tip0 = order[cur_idx as usize] as usize;
let tip1 = if has_both_sides { order[cur_idx as usize + 1] as usize } else { order[cur_idx as usize - 1] as usize };
let (start, tgt) = if rev_topo[tip0] > rev_topo[tip1] { (tip0, tip1) } else { (tip1, tip0) };
let side = (gpd(refpoc[start], refpoc[tgt]) < 0) as usize;
add_proc!(start as i32, tgt as i8, side, 3);
}
for g in 0..2i32 {
if !crate::av2_recon::work_tick("refmvs:1563") { break; }
let mut past_idx: i32 = if cur_idx >= g { cur_idx - g } else { -1 };
if past_idx >= 0 && !have_ref_sign[order[past_idx as usize] as usize][1] { past_idx = -1; }
let mut fut_idx: i32 = if cur_idx < n_ref as i32 - g - 1 { cur_idx + 1 + g } else { -1 };
if fut_idx >= 0 && !have_ref_sign[order[fut_idx as usize] as usize][0] { fut_idx = -1; }
let pd: i32 = if past_idx >= 0 {
let r = order[past_idx as usize] as usize;
if elig[r] { abs_closest_ref(&ref2ref[r], &ref2cur[r], false) } else { 0xff }
} else { -1 };
let fd: i32 = if fut_idx >= 0 {
let r = order[fut_idx as usize] as usize;
if elig[r] { abs_closest_ref(&ref2ref[r], &ref2cur[r], true) } else { 0xff }
} else { -1 };
let pi: i32 = if past_idx >= 0 { order[past_idx as usize] as i32 } else { -1 };
let fi: i32 = if fut_idx >= 0 { order[fut_idx as usize] as i32 } else { -1 };
if fd < pd {
add_proc!(fi, -1, 0, 3);
add_proc!(pi, -1, 1, 3);
} else {
add_proc!(pi, -1, 1, 3);
add_proc!(fi, -1, 0, 3);
}
}
if cur_idx >= 0 { add_proc!(order[cur_idx as usize] as i32, -1, 0, 3); }
if cur_idx >= 1 { add_proc!(order[cur_idx as usize - 1] as i32, -1, 0, 3); }
for ri in (1..topo_cnt).rev() {
if !crate::av2_recon::work_tick("refmvs:1594") { break; }
let r = topo_order[ri] as usize;
let side = (gpd(refpoc[r], poc) < 0) as usize;
add_proc!(r as i32, -1, side, 4);
add_proc!(r as i32, -1, 1 - side, 4);
}
for n in 0..t.n_mfmvs {
if !crate::av2_recon::work_tick("refmvs:1601") { break; }
let rref = t.mfmv[n].0 as usize;
let rpoc = refpoc[rref];
let diff1 = gpd(rpoc, poc);
if diff1.abs() > 31 {
t.mfmv_ref2cur[n] = INVALID_REF2CUR;
} else {
t.mfmv_ref2cur[n] = diff1;
for m in 0..7 {
let rrpoc = ref_ref_poc[rref][m];
let diff2 = gpd(rpoc, rrpoc);
t.mfmv_ref2ref[n][m] = if (diff2 + 31) as u32 <= 62 { diff2 } else { 0 };
let mut l = 7;
for ll in 0..7 {
if rrpoc == refpoc[ll] { l = ll; break; }
}
t.mfmv_ref2idx[n][m] = if l == 7 { -1 } else { l as i8 };
let d1 = t.mfmv_ref2cur[n];
let d2 = t.mfmv_ref2ref[n][m];
let dv = DIV_MULT[(d2.abs().min(31)) as usize];
t.mfmv_ref2sf[n][m][0] = d1.abs().min(31) * dv;
if (d1 < 0) != (d2 < 0) { t.mfmv_ref2sf[n][m][0] = -t.mfmv_ref2sf[n][m][0]; }
let d3 = d1 - d2;
t.mfmv_ref2sf[n][m][1] = d3.abs().min(31) * dv;
if (d3 < 0) != (d2 > 0) { t.mfmv_ref2sf[n][m][1] = -t.mfmv_ref2sf[n][m][1]; }
}
}
}
}
}
t.use_ref_frame_mvs = t.n_mfmvs > 0;
if std::env::var("MFMVDBG").map_or(false, |v| v == format!("{}", gpd(poc, 0))) {
crate::dlog!("[MFMV] poc={} n={}", gpd(poc, 0), t.n_mfmvs);
for n in 0..t.n_mfmvs {
if !crate::av2_recon::work_tick("refmvs:1635") { break; }
crate::dlog!("[MFMV] src{n} ref={} side=0 ref2cur={} rpoc={}", t.mfmv[n].0,
t.mfmv_ref2cur[n], gpd(refpoc[t.mfmv[n].0 as usize], 0));
for m in 0..3 {
crate::dlog!("[MFMV] r2r[{n}][{m}]={} idx={} sf={},{}", t.mfmv_ref2ref[n][m],
t.mfmv_ref2idx[n][m], t.mfmv_ref2sf[n][m][0], t.mfmv_ref2sf[n][m][1]);
}
}
crate::dlog!("[MFMV] have_field={:?} refcnt={:?} tip_ref={:?} n_ref={n_ref} refidx={:?} refpoc={:?}",
&have_field[..n_ref], &refcnt[..n_ref], t.tip_ref, &refidx[..n_ref], &refpoc[..n_ref]);
crate::dlog!("[MFMV] tip_mode={} delta={} sf={:?} hole_fill={} mv_traj={} use_rfm={}",
t.tip_frame_mode, t.tip_delta, t.tip_sf, t.tip_hole_fill, t.mv_traj, t.use_ref_frame_mvs);
}
let stride = t.stride;
let sbsz8w = crate::av2_recon::sb_step4() / 2;
RP_PROJ.with(|c| {
let mut b = c.borrow_mut();
*b = vec![(Mv { y: INVALID_MV_I32, x: 0 }, 0); (sbsz8w + 2) * stride];
});
RP_TRAJ.with(|c| {
let mut b = c.borrow_mut();
for v in b.iter_mut() {
if !crate::av2_recon::work_tick("refmvs:1659") { break; }
*v = vec![Mv { y: INVALID_MV_I32, x: 0 }; sbsz8w * stride];
}
});
RP_MAP.with(|c| {
let mut b = c.borrow_mut();
*b = vec![vec![(-128i8, -128i8); sbsz8w * stride]; 21]; });
TMVS.with(|c| *c.borrow_mut() = t);
}
#[inline]
fn apply_sign_i32(v: i32, s: i32) -> i32 { if s < 0 { -v } else { v } }
pub fn load_tmvs(row_start8: i32, row_end8: i32) {
let t = TMVS.with(|c| c.borrow().clone());
if !t.valid {
return;
}
let stride = t.stride as i32;
let sbsz8 = (crate::av2_recon::sb_step4() / 2) as i32;
let step = t.sample_step.max(1);
let mfmv_sb128 = (crate::av2_recon::sb_step4() == 32 && step > 1) as i32;
let mfmv_sbsz8 = 8i32 << mfmv_sb128;
let mfmv_edge = mfmv_sbsz8 >> (step == 1) as i32;
let shift = 3i32 + mfmv_sb128;
let smask = !(step - 1);
let row_end8 = row_end8.min(t.ih8);
let col_start8 = 0i32;
let col_end8 = t.iw8;
let col_start8i = (col_start8 - mfmv_edge).max(0);
let col_end8i = (col_end8 + mfmv_edge).min(t.iw8);
RP_PROJ.with(|proj_c| {
RP_TRAJ.with(|traj_c| {
RP_MAP.with(|map_c| {
let mut proj = proj_c.borrow_mut();
let mut traj = traj_c.borrow_mut();
let mut map = map_c.borrow_mut();
let base = 2 * stride as usize; for r in 0..2usize {
if !crate::av2_recon::work_tick("refmvs:1705") { break; }
for x in col_start8 as usize..(col_end8 as usize).min(stride as usize) {
if !crate::av2_recon::work_tick("refmvs:1706") { break; }
let (di, si) = (r * stride as usize + x, base + ((sbsz8 as usize - 2 + r) * stride as usize) + x);
if di >= proj.len() || si >= proj.len() { continue; }
proj[di] = proj[si];
}
}
for y in row_start8..row_end8 {
if !crate::av2_recon::work_tick("refmvs:1714") { break; }
let py = base + ((y & (sbsz8 - 1)) * stride) as usize;
for x in col_start8 as usize..col_end8 as usize {
if !crate::av2_recon::work_tick("refmvs:1716") { break; }
if py + x >= proj.len() { continue; } proj[py + x].0 = Mv { y: INVALID_MV_I32, x: 0 };
}
}
if t.mv_traj {
for n in 0..7usize {
if !crate::av2_recon::work_tick("refmvs:1722") { break; }
for y in row_start8..row_end8 {
if !crate::av2_recon::work_tick("refmvs:1723") { break; }
let py = ((y & (sbsz8 - 1)) * stride) as usize;
for x in col_start8 as usize..col_end8 as usize {
if !crate::av2_recon::work_tick("refmvs:1725") { break; }
if py + x < traj[n].len() { traj[n][py + x] = Mv { y: INVALID_MV_I32, x: 0 }; }
}
}
let mask = mfmv_sbsz8 - 1;
for k in -1i32..=1 {
if !crate::av2_recon::work_tick("refmvs:1730") { break; }
let x_start = (col_start8 - k * mfmv_sbsz8).max(0);
let x_end = (((col_end8 + mask) & !mask) - k * mfmv_sbsz8).min(t.iw8);
let mi = ((k + 1) * 7) as usize + n;
for y in row_start8..row_end8 {
if !crate::av2_recon::work_tick("refmvs:1734") { break; }
let py = ((y & (sbsz8 - 1)) * stride) as usize;
for x in x_start..x_end {
if !crate::av2_recon::work_tick("refmvs:1736") { break; }
let o = py + x as usize;
if o >= map[mi].len() { continue; } map[mi][o] = (-128, -128);
}
}
}
}
}
let refidx = crate::av2_recon::CUR_FRAME_REFIDX.with(|c| c.get()).1;
RP_REF.with(|rr| {
let slots = rr.borrow();
let col_start8_shifted = col_start8 >> shift;
let col_end8_shifted = (col_end8 - 1) >> shift;
for n in 0..t.n_mfmvs {
if !crate::av2_recon::work_tick("refmvs:1751") { break; }
if t.mfmv_ref2cur[n] == INVALID_REF2CUR {
continue;
}
let (rref, rtgt, ref_sign) = t.mfmv[n];
let field = match slots[refidx[rref as usize] as usize].as_ref() {
Some(f) if !f.cells.is_empty() => f,
_ => continue,
};
for y in (row_start8..row_end8).step_by(step as usize) {
if !crate::av2_recon::work_tick("refmvs:1760") { break; }
for x in (col_start8i..col_end8i).step_by(step as usize) {
if !crate::av2_recon::work_tick("refmvs:1761") { break; }
if y >= field.h8 as i32 || x >= field.w8 as i32 {
continue;
}
let rb = field.cells[(y * field.w8 as i32 + x) as usize];
let b_ref = if ref_sign == 1 { rb.ref_.1 } else { rb.ref_.0 };
if b_ref == -1 {
continue;
}
let ref2idx = t.mfmv_ref2idx[n][b_ref as usize];
let b_mv = dequantize_mv(if ref_sign == 1 { rb.qmv[1] } else { rb.qmv[0] });
if b_mv.y == INVALID_MV_I32 {
continue;
}
if t.mv_traj && ref2idx != -1 {
check_traj_intersect(&t, &mut traj, &mut map, rref as usize, ref2idx as usize,
y, x, b_mv, col_start8_shifted, col_end8_shifted,
stride, sbsz8, mfmv_sbsz8, mfmv_edge, shift, smask);
}
let ref2ref = t.mfmv_ref2ref[n][b_ref as usize];
if ref2ref == 0 || ((ref2ref < 0) as u8) != ref_sign {
continue;
}
let mv1 = scale_mv(b_mv, -t.mfmv_ref2sf[n][b_ref as usize][0]);
let mut y1 = y - apply_sign_i32(mv1.y.abs() >> 6, mv1.y);
if y1 < 0 || y1 >= t.ih8 {
continue;
}
y1 &= smask;
let mut x1 = x - apply_sign_i32(mv1.x.abs() >> 6, mv1.x);
if x1 < col_start8 || x1 >= col_end8 {
continue;
}
x1 &= smask;
let y_proj_start = y1 & !(mfmv_sbsz8 - 1);
let y_proj_end = (y_proj_start + mfmv_sbsz8).min(row_end8);
if y < y_proj_start || y >= y_proj_end {
continue;
}
let x_sb_align = x1 & !(mfmv_sbsz8 - 1);
let x_proj_start = (x_sb_align - mfmv_edge).max(0);
let x_proj_end = (x_sb_align + mfmv_sbsz8 + mfmv_edge).min(t.iw8);
if x < x_proj_start || x >= x_proj_end {
continue;
}
let pos1 = (base as i32 + (y1 & (sbsz8 - 1)) * stride + x1) as usize;
if proj[pos1].0.y != INVALID_MV_I32
&& (rtgt == -1 || ref2idx != rtgt || proj[pos1].1 == ref2ref.abs())
{
continue;
}
if t.mv_traj {
let k1 = (x1 >> shift) - (x >> shift);
let pos = ((y & (sbsz8 - 1)) * stride + x) as usize;
let tpos1 = ((y1 & (sbsz8 - 1)) * stride + x1) as usize;
traj[rref as usize][tpos1] = Mv { y: mv1.y.clamp(-2047, 2047), x: mv1.x.clamp(-2047, 2047) };
if (-1..=1).contains(&k1) {
map[(((k1 + 1) * 7) + rref as i32) as usize][pos] = ((y1 - y) as i8, (x1 - x) as i8);
}
if ref2idx >= 0 {
let mv2 = scale_mv(b_mv, t.mfmv_ref2sf[n][b_ref as usize][1]);
traj[ref2idx as usize][tpos1] = Mv { y: mv2.y.clamp(-2047, 2047), x: mv2.x.clamp(-2047, 2047) };
let y2 = y + apply_sign_i32(b_mv.y.abs() >> 6, b_mv.y);
if y2 >= y_proj_start && y2 < y_proj_end {
let y2 = y2 & smask;
let x2 = x + apply_sign_i32(b_mv.x.abs() >> 6, b_mv.x);
if x2 >= x_proj_start && x2 < x_proj_end {
let x2 = x2 & smask;
let pos2 = ((y2 & (sbsz8 - 1)) * stride + x2) as usize;
let k2 = (x1 >> shift) - (x2 >> shift);
if (-1..=1).contains(&k2) {
map[(((k2 + 1) * 7) + ref2idx as i32) as usize][pos2] = ((y1 - y2) as i8, (x1 - x2) as i8);
}
}
}
}
}
let mut bm = b_mv;
if ref2ref < 0 {
bm.y = -bm.y;
bm.x = -bm.x;
}
proj[pos1] = (bm, ref2ref.abs());
}
}
}
});
let stepu = step as usize;
if t.tip_frame_mode != 0 {
for y in (row_start8..row_end8).step_by(stepu) {
if !crate::av2_recon::work_tick("refmvs:1852") { break; }
let py = base + ((y & (sbsz8 - 1)) * stride) as usize;
for x in (col_start8 as usize..col_end8 as usize).step_by(stepu) {
if !crate::av2_recon::work_tick("refmvs:1854") { break; }
let (m, r) = proj[py + x];
if m.y == INVALID_MV_I32 {
continue;
}
proj[py + x] = (mv_projection_t(m, t.tip_delta, r, -2047, 2047), t.tip_delta);
}
}
if t.tip_hole_fill {
for sx in (col_start8..col_end8).step_by(mfmv_sbsz8 as usize) {
if !crate::av2_recon::work_tick("refmvs:1864") { break; }
let xend = col_end8.min(sx + mfmv_sbsz8);
for y in (row_start8..row_end8).step_by(stepu) {
if !crate::av2_recon::work_tick("refmvs:1866") { break; }
let ystart = y & !(mfmv_sbsz8 - 1);
let yend = (ystart + mfmv_sbsz8).min(row_end8);
let pb = base as i32 + (y & (sbsz8 - 1)) * stride;
for x in (sx..xend).step_by(stepu) {
if !crate::av2_recon::work_tick("refmvs:1870") { break; }
let pos = (pb + x) as usize;
let m = proj[pos].0;
if m.y == INVALID_MV_I32 {
continue;
}
let so = stepu;
let ss = stepu * stride as usize;
if x - step >= sx && proj[pos - so].0.y == INVALID_MV_I32 {
proj[pos - so] = (m, t.tip_delta);
}
if x + step < xend && proj[pos + so].0.y == INVALID_MV_I32 {
proj[pos + so] = (m, t.tip_delta);
}
if y - step >= ystart && proj[pos - ss].0.y == INVALID_MV_I32 {
proj[pos - ss] = (m, t.tip_delta);
}
if y + step < yend && proj[pos + ss].0.y == INVALID_MV_I32 {
proj[pos + ss] = (m, t.tip_delta);
}
}
}
}
const IDIV: [i64; 5] = [65536, 32768, 21845, 16384, 13107];
for sx in (col_start8..col_end8).step_by(mfmv_sbsz8 as usize) {
if !crate::av2_recon::work_tick("refmvs:1895") { break; }
let xend = col_end8.min(sx + mfmv_sbsz8);
let mut mv_line = [Mv { y: INVALID_MV_I32, x: 0 }; 32];
let mut first_line = true;
let mut last_y = row_start8;
for y in (row_start8..row_end8).step_by(stepu) {
if !crate::av2_recon::work_tick("refmvs:1900") { break; }
let ystart = y & !(mfmv_sbsz8 - 1);
let yend = (ystart + mfmv_sbsz8).min(row_end8);
let pb = base as i32 + (y & (sbsz8 - 1)) * stride;
for x in (sx..xend).step_by(stepu) {
if !crate::av2_recon::work_tick("refmvs:1904") { break; }
let pos = (pb + x) as usize;
let mut sum_x = 0i64;
let mut sum_y = 0i64;
let mut sum_n = 0usize;
{
let so = stepu;
let ss = stepu * stride as usize;
let mut add = |p: usize| {
if proj[p].0.y != INVALID_MV_I32 {
sum_x += proj[p].0.x as i64;
sum_y += proj[p].0.y as i64;
sum_n += 1;
}
};
add(pos);
if x - step >= sx { add(pos - so); }
if x + step < xend { add(pos + so); }
if y - step >= ystart { add(pos - ss); }
if y + step < yend { add(pos + ss); }
}
if !first_line {
proj[pos - stepu * stride as usize] = (mv_line[(x - sx) as usize], t.tip_delta);
}
if sum_n > 0 {
mv_line[(x - sx) as usize] = Mv {
y: ((sum_y * IDIV[sum_n - 1] + 0x8000 - (sum_y < 0) as i64) >> 16) as i32,
x: ((sum_x * IDIV[sum_n - 1] + 0x8000 - (sum_x < 0) as i64) >> 16) as i32,
};
} else {
mv_line[(x - sx) as usize].y = INVALID_MV_I32;
}
}
first_line = false;
last_y = y;
}
if !first_line {
let pb = base as i32 + (last_y & (sbsz8 - 1)) * stride;
for x in (sx..xend).step_by(stepu) {
if !crate::av2_recon::work_tick("refmvs:1942") { break; }
proj[(pb + x) as usize] = (mv_line[(x - sx) as usize], t.tip_delta);
}
}
}
}
}
if step > 1 {
if t.mv_traj {
for n in 0..7usize {
if !crate::av2_recon::work_tick("refmvs:1954") { break; }
let tj = &mut traj[n];
for sx in (col_start8..col_end8).step_by(mfmv_sbsz8 as usize) {
if !crate::av2_recon::work_tick("refmvs:1956") { break; }
let xend = col_end8.min(sx + mfmv_sbsz8);
for y in (row_start8..row_end8).step_by(2) {
if !crate::av2_recon::work_tick("refmvs:1958") { break; }
let ystart = y & !(mfmv_sbsz8 - 1);
let yend = (ystart + mfmv_sbsz8).min(row_end8);
let pb = (y & (sbsz8 - 1)) * stride;
for x in (sx..xend).step_by(2) {
if !crate::av2_recon::work_tick("refmvs:1962") { break; }
let pos = (pb + x) as usize;
let s = stride as usize;
let m = tj[pos];
if m.y == INVALID_MV_I32 {
continue;
}
let (mut sum_y, mut sum_x, mut sum_n) = (m.y, m.x, 1i32);
let have_bottom = y + 2 < yend;
if have_bottom && tj[pos + 2 * s].y != INVALID_MV_I32 {
let bm = tj[pos + 2 * s];
sum_x += bm.x;
sum_y += bm.y;
tj[pos + s] = Mv { y: (sum_y + (sum_y > 0) as i32) >> 1, x: (sum_x + (sum_x > 0) as i32) >> 1 };
sum_n += 1;
} else {
tj[pos + s] = m;
}
let have_right = x + 2 < xend;
if have_right && tj[pos + 2].y != INVALID_MV_I32 {
let rm = tj[pos + 2];
sum_x += rm.x;
let mx = m.x + rm.x;
sum_y += rm.y;
let my = m.y + rm.y;
tj[pos + 1] = Mv { y: (my + (my > 0) as i32) >> 1, x: (mx + (mx > 0) as i32) >> 1 };
sum_n += 1;
} else {
tj[pos + 1] = m;
}
if have_right && have_bottom && tj[pos + 2 * (1 + s)].y != INVALID_MV_I32 {
let brm = tj[pos + 2 * (1 + s)];
sum_x += brm.x;
sum_y += brm.y;
sum_n += 1;
}
tj[pos + 1 + s] = match sum_n {
1 => m,
2 => Mv { y: (sum_y + (sum_y > 0) as i32) >> 1, x: (sum_x + (sum_x > 0) as i32) >> 1 },
3 => Mv { y: (sum_y * 85 + 128 - (sum_y < 0) as i32) >> 8, x: (sum_x * 85 + 128 - (sum_x < 0) as i32) >> 8 },
_ => Mv { y: (sum_y + 1 + (sum_y > 0) as i32) >> 2, x: (sum_x + 1 + (sum_x > 0) as i32) >> 2 },
};
}
}
}
}
}
for sx in (col_start8..col_end8).step_by(mfmv_sbsz8 as usize) {
if !crate::av2_recon::work_tick("refmvs:2011") { break; }
let xend = col_end8.min(sx + mfmv_sbsz8);
for y in (row_start8..row_end8).step_by(2) {
if !crate::av2_recon::work_tick("refmvs:2013") { break; }
let ystart = y & !(mfmv_sbsz8 - 1);
let yend = (ystart + mfmv_sbsz8).min(row_end8);
let pb = base as i32 + (y & (sbsz8 - 1)) * stride;
for x in (sx..xend).step_by(2) {
if !crate::av2_recon::work_tick("refmvs:2017") { break; }
let pos = (pb + x) as usize;
let s = stride as usize;
let (m, ref_off) = proj[pos];
if m.y == INVALID_MV_I32 {
continue;
}
let (mut sum_y, mut sum_x, mut sum_n) = (m.y, m.x, 1i32);
let have_right = x + 2 < xend;
if have_right && proj[pos + 2].0.y != INVALID_MV_I32 {
let (rm0, rref) = proj[pos + 2];
let rm = mv_projection_t(rm0, ref_off, rref, -2047, 2047);
sum_x += rm.x;
sum_y += rm.y;
proj[pos + 1] = (Mv { y: (sum_y + (sum_y > 0) as i32) >> 1, x: (sum_x + (sum_x > 0) as i32) >> 1 }, ref_off);
sum_n += 1;
} else {
proj[pos + 1] = proj[pos];
}
let have_bottom = y + 2 < yend;
if have_bottom && proj[pos + 2 * s].0.y != INVALID_MV_I32 {
let (bm0, bref) = proj[pos + 2 * s];
let bm = mv_projection_t(bm0, ref_off, bref, -2047, 2047);
sum_x += bm.x;
let mx = m.x + bm.x;
sum_y += bm.y;
let my = m.y + bm.y;
proj[pos + s] = (Mv { y: (my + (my > 0) as i32) >> 1, x: (mx + (mx > 0) as i32) >> 1 }, ref_off);
sum_n += 1;
} else {
proj[pos + s] = proj[pos];
}
if have_right && have_bottom && proj[pos + 2 * (1 + s)].0.y != INVALID_MV_I32 {
let (brm0, brref) = proj[pos + 2 * (1 + s)];
let brm = mv_projection_t(brm0, ref_off, brref, -2047, 2047);
sum_x += brm.x;
sum_y += brm.y;
sum_n += 1;
}
let dm = match sum_n {
1 => m,
2 => Mv { y: (sum_y + (sum_y > 0) as i32) >> 1, x: (sum_x + (sum_x > 0) as i32) >> 1 },
3 => Mv { y: (sum_y * 85 + 128 - (sum_y < 0) as i32) >> 8, x: (sum_x * 85 + 128 - (sum_x < 0) as i32) >> 8 },
_ => Mv { y: (sum_y + 1 + (sum_y > 0) as i32) >> 2, x: (sum_x + 1 + (sum_x > 0) as i32) >> 2 },
};
proj[pos + 1 + s] = (dm, ref_off);
}
}
}
}
});
});
});
}
pub fn tpl_dump_window(row_start8: i32, row_end8: i32) {
if std::env::var("TPLALL").is_err() {
return;
}
let t = TMVS.with(|c| c.borrow().clone());
if !t.valid {
return;
}
let (stride, sbsz8) = (t.stride as i32, crate::av2_recon::sb_step4() as i32 / 2);
let row_end8 = row_end8.min(t.ih8);
RP_PROJ.with(|pc| {
let p = pc.borrow();
let base = 2 * stride;
for y in row_start8..row_end8 {
for x in 0..stride {
let i = (base + (y & (sbsz8 - 1)) * stride + x) as usize;
if let Some((mv, rf)) = p.get(i) {
eprintln!("[TPLALL] r={y} c={x} {},{},{}", mv.y, mv.x, rf);
}
}
}
});
}
pub fn tmvs_dump(row_start8: i32, row_end8: i32) {
let t = TMVS.with(|c| c.borrow().clone());
if !t.valid {
return;
}
let stride = t.stride;
let row_end8 = row_end8.min(t.ih8);
RP_PROJ.with(|pc| {
RP_TRAJ.with(|tc| {
let proj = pc.borrow();
let traj = tc.borrow();
for y in row_start8..row_end8 {
if !crate::av2_recon::work_tick("refmvs:2084") { break; }
for x in 0..t.iw8 {
if !crate::av2_recon::work_tick("refmvs:2085") { break; }
let pp = ((y & 7) as usize) * stride + x as usize;
let (m, r) = proj[2 * stride + pp];
crate::dlog!("[TPROJ] y={y} x={x} mv={},{} ref={r}", if m.y == INVALID_MV_I32 { -32768 } else { m.y }, m.x);
if t.mv_traj {
for n in 0..4usize {
if !crate::av2_recon::work_tick("refmvs:2090") { break; }
let tm = traj[n][pp];
crate::dlog!("[TTRAJ{n}] y={y} x={x} mv={},{}", if tm.y == INVALID_MV_I32 { -32768 } else { tm.y }, tm.x);
}
}
}
}
})
});
}
#[allow(clippy::too_many_arguments)]
fn check_traj_intersect(
t: &TmvsFrame, traj: &mut [Vec<Mv>; 7], map: &mut [Vec<(i8, i8)>],
ref1: usize, ref2: usize, y: i32, x: i32, mv_in: Mv,
col_start8_shifted: i32, col_end8_shifted: i32,
stride: i32, sbsz8: i32, mfmv_sbsz8: i32, mfmv_edge: i32, shift: i32, smask: i32,
) {
let pos = ((y & (sbsz8 - 1)) * stride + x) as usize;
let min_k = (-1).max(col_start8_shifted - (x >> shift));
let max_k = 1.min(col_end8_shifted - (x >> shift));
for k in (min_k + 1)..=(max_k + 1) {
if !crate::av2_recon::work_tick("refmvs:2112") { break; }
let m1 = map[((k * 7) + ref1 as i32) as usize][pos];
if m1 == (-128, -128) {
continue;
}
let x1 = x + m1.1 as i32;
let k1 = (x1 >> shift) - (x >> shift);
if k1 + 1 != k {
continue;
}
let x_sb_align = x1 & !(mfmv_sbsz8 - 1);
let x_proj_start = (x_sb_align - mfmv_edge).max(0);
let x_proj_end = (x_sb_align + mfmv_sbsz8 + mfmv_edge).min(t.iw8);
if x < x_proj_start || x >= x_proj_end {
continue;
}
let y1 = y + m1.0 as i32;
let y_proj_start = y1 & !(mfmv_sbsz8 - 1);
let y_proj_end = (y_proj_start + mfmv_sbsz8).min(t.ih8);
if y < y_proj_start || y >= y_proj_end {
continue;
}
let pos1 = ((y1 & (sbsz8 - 1)) * stride + x1) as usize;
if traj[ref2][pos1].y != INVALID_MV_I32 {
continue;
}
let src = traj[ref1][pos1];
let py = (src.y + mv_in.y).clamp(-2047, 2047);
let px = (src.x + mv_in.x).clamp(-2047, 2047);
traj[ref2][pos1] = Mv { y: py, x: px };
let y2 = y1 + apply_sign_i32(py.abs() >> 6, py);
let x2 = x1 + apply_sign_i32(px.abs() >> 6, px);
if x2 < x_proj_start || x2 >= x_proj_end || y2 < y_proj_start || y2 >= y_proj_end {
continue;
}
let (y2, x2) = (y2 & smask, x2 & smask);
let pos2 = ((y2 & (sbsz8 - 1)) * stride + x2) as usize;
let k2 = (x1 >> shift) - (x2 >> shift);
if !(-1..=1).contains(&k2) {
continue;
}
map[((k2 + 1) * 7 + ref2 as i32) as usize][pos2] = ((y1 - y2) as i8, (x1 - x2) as i8);
}
let y1 = y + apply_sign_i32(mv_in.y.abs() >> 6, mv_in.y);
let x1 = x + apply_sign_i32(mv_in.x.abs() >> 6, mv_in.x);
if y1.min(x1) < 0 || y1 >= t.ih8 || x1 >= t.iw8 {
return;
}
let (y1, x1) = (y1 & smask, x1 & smask);
let min_k1 = (-1).max(col_start8_shifted - (x1 >> shift));
let max_k1 = 1.min(col_end8_shifted - (x1 >> shift));
for k in (min_k1 + 1)..=(max_k1 + 1) {
if !crate::av2_recon::work_tick("refmvs:2166") { break; }
let pos1 = ((y1 & (sbsz8 - 1)) * stride + x1) as usize;
let m1 = map[((k * 7) + ref2 as i32) as usize][pos1];
if m1 == (-128, -128) {
continue;
}
let x2 = x1 + m1.1 as i32;
let k2 = (x2 >> shift) - (x1 >> shift);
if k2 + 1 != k {
continue;
}
let x_sb_align = x2 & !(mfmv_sbsz8 - 1);
let x_proj_start = (x_sb_align - mfmv_edge).max(0);
let x_proj_end = (x_sb_align + mfmv_sbsz8 + mfmv_edge).min(t.iw8);
if x < x_proj_start || x >= x_proj_end || x1 < x_proj_start || x1 >= x_proj_end {
continue;
}
let y2 = y1 + m1.0 as i32;
let y_proj_start = y2 & !(mfmv_sbsz8 - 1);
let y_proj_end = (y_proj_start + mfmv_sbsz8).min(t.ih8);
if y < y_proj_start || y >= y_proj_end || y1 < y_proj_start || y1 >= y_proj_end {
continue;
}
let pos2 = ((y2 & (sbsz8 - 1)) * stride + x2) as usize;
if traj[ref1][pos2].y != INVALID_MV_I32 {
continue;
}
let src = traj[ref2][pos2];
let py = (src.y - mv_in.y).clamp(-0xffff, 0xffff);
let px = (src.x - mv_in.x).clamp(-0xffff, 0xffff);
traj[ref1][pos2] = Mv { y: py, x: px };
let y3 = y2 + apply_sign_i32(py.abs() >> 6, py);
let x3 = x2 + apply_sign_i32(px.abs() >> 6, px);
if x3 < x_proj_start || x3 >= x_proj_end || y3 < y_proj_start || y3 >= y_proj_end {
continue;
}
let (y3, x3) = (y3 & smask, x3 & smask);
let pos3 = ((y3 & (sbsz8 - 1)) * stride + x3) as usize;
let k3 = (x2 >> shift) - (x3 >> shift);
if !(-1..=1).contains(&k3) {
continue;
}
map[((k3 + 1) * 7 + ref1 as i32) as usize][pos3] = ((y2 - y3) as i8, (x2 - x3) as i8);
}
}
#[derive(Clone)]
pub struct RefmvBank {
pub mv: [[Mv; 4]; 9], pub mv2: [[Mv; 4]; 9],
pub ref_pair: [(i8, i8); 4],
pub cwp: [[i8; 4]; 3],
pub size: [u8; 9],
pub idx: [u8; 9],
pub avail: i32,
pub hits0: i32,
pub hits1: i32,
}
impl Default for RefmvBank {
fn default() -> Self {
RefmvBank {
mv: [[Mv::default(); 4]; 9], mv2: [[Mv::default(); 4]; 9],
ref_pair: [(-1, -1); 4], cwp: [[8; 4]; 3],
size: [0; 9], idx: [0; 9], avail: 0, hits0: 0, hits1: 0,
}
}
}
#[inline]
fn bank_class(ref0: i8) -> usize {
if (0..=5).contains(&ref0) {
ref0 as usize
} else {
8
}
}
impl RefmvBank {
fn update(&mut self, bw4: usize, bh4: usize, by4: usize, bx4: usize, sbsz: usize, sb128: usize) {
let bsh = 1 + sb128;
let bsz = 1usize << bsh;
let w = (1.max(bw4 >> bsh)) * (1.max(bh4 >> bsh));
if (by4 | bx4) & (sbsz - 1) == 0 {
self.hits1 = 0;
self.avail = (w as i32).max(4);
} else if (by4 | bx4) & (bsz - 1) == 0 {
self.hits1 = 0;
self.avail += w as i32;
}
}
pub fn add_block(&mut self, bw4: usize, bh4: usize, by4: usize, bx4: usize, sbsz: usize, sb128: usize, ref0: i8, mv: Mv) {
self.update(bw4, bh4, by4, bx4, sbsz, sb128);
if self.hits0 >= 64 || self.hits1 >= 16 || self.avail == 0 {
return;
}
self.hits0 += 1;
self.hits1 += 1;
self.avail -= 1;
self.insert_mv(ref0, mv);
}
pub fn bank_update_intra(&mut self, bw4: usize, bh4: usize, by4: usize, bx4: usize, sbsz: usize, sb128: usize) {
self.update(bw4, bh4, by4, bx4, sbsz, sb128);
}
fn add_raw(&mut self, ref0: i8, mv: Mv) {
self.hits0 += 1;
self.insert_mv(ref0, mv);
}
#[allow(clippy::too_many_arguments)]
pub fn add_block_pair(&mut self, bw4: usize, bh4: usize, by4: usize, bx4: usize, sbsz: usize, sb128: usize, ref0: i8, ref1: i8, mv: [Mv; 2], cwp: i8) {
if ref1 < 0 {
self.add_block(bw4, bh4, by4, bx4, sbsz, sb128, ref0, mv[0]);
return;
}
self.update(bw4, bh4, by4, bx4, sbsz, sb128);
if self.hits0 >= 64 || self.hits1 >= 16 || self.avail == 0 {
return;
}
self.hits0 += 1;
self.hits1 += 1;
self.avail -= 1;
self.insert_pair(ref0, ref1, mv, cwp);
}
pub fn add_raw_pair(&mut self, ref0: i8, ref1: i8, mv: [Mv; 2], cwp: i8) {
if ref1 < 0 {
self.add_raw(ref0, mv[0]);
return;
}
self.hits0 += 1;
self.insert_pair(ref0, ref1, mv, cwp);
}
fn insert_pair(&mut self, ref0: i8, ref1: i8, mv: [Mv; 2], cwp: i8) {
let c: usize = if ref0 == 0 && (0..=1).contains(&ref1) { 6 + ref1 as usize } else { 8 };
let sz = self.size[c] as usize;
let idx = self.idx[c] as usize;
let mut n = 0;
while n < sz {
let i = (idx + n) & 3;
if (c < 8 || self.ref_pair[i] == (ref0, ref1))
&& self.mv[c][i] == mv[0]
&& self.mv2[c][i] == mv[1]
{
break;
}
n += 1;
}
if n < sz {
let to = if sz == 4 { (idx + 3) & 3 } else { sz - 1 };
let from = (idx + n) & 3;
if from != to {
let bak = (self.mv[c][from], self.mv2[c][from], self.ref_pair[from], self.cwp[c - 6][from]);
let mut n1 = from;
let mut n2 = (n1 + 1) & 3;
while n1 != to {
self.mv[c][n1] = self.mv[c][n2];
self.mv2[c][n1] = self.mv2[c][n2];
self.cwp[c - 6][n1] = self.cwp[c - 6][n2];
if c == 8 {
self.ref_pair[n1] = self.ref_pair[n2];
}
n1 = n2;
n2 = (n2 + 1) & 3;
}
self.mv[c][to] = bak.0;
self.mv2[c][to] = bak.1;
self.cwp[c - 6][to] = bak.3;
if c == 8 {
self.ref_pair[to] = bak.2;
}
}
return;
}
let tgt = if sz == 4 {
let t = self.idx[c] as usize & 3;
self.idx[c] = ((self.idx[c] as usize + 1) & 3) as u8;
t
} else {
let t = self.size[c] as usize;
self.size[c] += 1;
t
};
self.mv[c][tgt] = mv[0];
self.mv2[c][tgt] = mv[1];
self.cwp[c - 6][tgt] = cwp;
if c == 8 {
self.ref_pair[tgt] = (ref0, ref1);
}
}
fn insert_mv(&mut self, ref0: i8, mv: Mv) {
let c = bank_class(ref0);
let sz = self.size[c] as usize;
let idx = self.idx[c] as usize;
let mut n = 0;
while n < sz {
let i = (idx + n) & 3;
if (c < 8 || self.ref_pair[i] == (ref0, -1)) && self.mv[c][i] == mv {
break;
}
n += 1;
}
if n < sz {
let to = if sz == 4 { (idx + 3) & 3 } else { sz - 1 };
let from = (idx + n) & 3;
if from != to {
let bak = (self.mv[c][from], self.ref_pair[from]);
let mut n1 = from;
let mut n2 = (n1 + 1) & 3;
while n1 != to {
self.mv[c][n1] = self.mv[c][n2];
if c == 8 {
self.ref_pair[n1] = self.ref_pair[n2];
}
n1 = n2;
n2 = (n2 + 1) & 3;
}
self.mv[c][to] = bak.0;
if c == 8 {
self.ref_pair[to] = bak.1;
}
}
return;
}
let tgt = if sz == 4 {
let t = self.idx[c] as usize & 3;
self.idx[c] = ((self.idx[c] as usize + 1) & 3) as u8;
t
} else {
let t = self.size[c] as usize;
self.size[c] += 1;
t
};
self.mv[c][tgt] = mv;
if c == 8 {
self.ref_pair[tgt] = (ref0, -1);
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn add_bank_candidates(st: &mut ScanState, bank: &RefmvBank, ref0: i8, ref1: i8, lim: usize, bx4: usize, by4: usize, bw4: usize, bh4: usize, iw8: usize, ih8: usize) {
let comp = ref1 >= 0;
let c: usize = if !comp {
bank_class(ref0)
} else if ref0 == 0 && (0..=1).contains(&ref1) {
6 + ref1 as usize
} else {
8
};
let sz = bank.size[c] as usize;
let idx = bank.idx[c] as usize;
let start = sz + idx;
let mut n = 0;
while n < sz && st.cnt < lim {
let bank_idx = (start.wrapping_sub(1).wrapping_sub(n)) & 3;
if c == 8 && bank.ref_pair[bank_idx] != (ref0, ref1) {
n += 1;
continue;
}
let mv = bank.mv[c][bank_idx];
let mv1 = bank.mv2[c][bank_idx];
let mut dup = false;
if st.iter_cntr < 16 {
for m in 0..st.cnt {
if !crate::av2_recon::work_tick("refmvs:2470") { break; }
if st.mvstack[m].mv[0] == mv && (!comp || st.mvstack[m].mv[1] == mv1) {
st.iter_cntr += m as i32 + 1;
dup = true;
break;
}
}
if !dup {
st.iter_cntr += st.cnt as i32;
}
}
if !dup {
let mut ok = true;
for m in [mv, mv1].iter().take(1 + comp as usize) {
if !crate::av2_recon::work_tick("refmvs:2484") { break; }
let rx = bx4 as i32 * 4 + apply_sign((m.x.abs()) >> 3, m.x);
let ry = by4 as i32 * 4 + apply_sign((m.y.abs()) >> 3, m.y);
if rx <= -(bw4 as i32) * 4 || ry <= -(bh4 as i32) * 4 || rx >= iw8 as i32 * 8 || ry >= ih8 as i32 * 8 {
ok = false;
break;
}
}
if ok {
let last = st.cnt;
st.mvstack[last].mv[0] = mv;
st.mvstack[last].mv[1] = mv1;
st.mvstack[last].weight = 0;
st.mvstack[last].cwp = if comp { bank.cwp[c.max(6) - 6][bank_idx] } else { 8 };
st.mvstack[last].y_off = 0;
st.mvstack[last].x_off = 0;
st.cnt = last + 1;
}
}
n += 1;
}
}
#[derive(Clone)]
pub struct RefmvWarpBank {
pub mat: [[[i32; 6]; 4]; 7],
pub size: [u8; 7],
pub idx: [u8; 7],
pub hits: i32,
}
impl Default for RefmvWarpBank {
fn default() -> Self {
RefmvWarpBank { mat: [[[0; 6]; 4]; 7], size: [0; 7], idx: [0; 7], hits: 0 }
}
}
impl RefmvWarpBank {
pub fn add(&mut self, ref0: i8, m: [i32; 6]) {
if self.hits >= 64 || !(0..7).contains(&(ref0 as i32)) {
return;
}
self.hits += 1;
let r = ref0 as usize;
let sz = self.size[r] as usize;
let idx = self.idx[r] as usize;
let mut n = 0;
while n < sz {
if self.mat[r][(idx + n) & 3][2..6] == m[2..6] {
break;
}
n += 1;
}
if n < sz {
let to = if sz == 4 { (idx + 3) & 3 } else { sz - 1 };
let from = (idx + n) & 3;
if from != to {
let bak = self.mat[r][from];
let mut n1 = from;
let mut n2 = (n1 + 1) & 3;
while n1 != to {
self.mat[r][n1] = self.mat[r][n2];
n1 = n2;
n2 = (n2 + 1) & 3;
}
self.mat[r][to] = bak;
}
return;
}
let tgt = if sz == 4 {
let t = self.idx[r] as usize & 3;
self.idx[r] = ((self.idx[r] as usize + 1) & 3) as u8;
t
} else {
let t = self.size[r] as usize;
self.size[r] += 1;
t
};
self.mat[r][tgt] = m;
}
}
pub struct RefmvsGrid {
pub r: Vec<RefmvsBlock>,
}
impl Default for RefmvsGrid {
fn default() -> Self {
RefmvsGrid { r: vec![RefmvsBlock::default(); crate::av2_recon::nb_len() * crate::av2_recon::nb_len()] }
}
}
impl RefmvsGrid {
#[inline]
pub(crate) fn at(&self, by4: usize, bx4: usize) -> &RefmvsBlock {
&self.r[by4.min(crate::av2_recon::nb_len() - 1) * crate::av2_recon::nb_len() + bx4.min(crate::av2_recon::nb_len() - 1)]
}
pub fn splat_intra(&mut self, bx4: usize, by4: usize, bw4: usize, bh4: usize, bs: u8) {
for r in 0..bh4 {
if !crate::av2_recon::work_tick("refmvs:2593") { break; }
for c in 0..bw4 {
if !crate::av2_recon::work_tick("refmvs:2594") { break; }
let cell = &mut self.r[(by4 + r).min(crate::av2_recon::nb_len() - 1) * crate::av2_recon::nb_len() + (bx4 + c).min(crate::av2_recon::nb_len() - 1)];
*cell = RefmvsBlock { bs, bx4: bx4 as u16, by4: by4 as u16, ..RefmvsBlock::default() };
}
}
}
pub fn splat_intrabc(&mut self, bx4: usize, by4: usize, bw4: usize, bh4: usize, bv: Mv, bs: u8) {
for r in 0..bh4 {
if !crate::av2_recon::work_tick("refmvs:2605") { break; }
for c in 0..bw4 {
if !crate::av2_recon::work_tick("refmvs:2606") { break; }
let cell = &mut self.r[(by4 + r).min(crate::av2_recon::nb_len() - 1) * crate::av2_recon::nb_len() + (bx4 + c).min(crate::av2_recon::nb_len() - 1)];
*cell = RefmvsBlock {
mv: [bv, Mv { y: -0x8000, x: -0x8000 }],
ref_: [-1, -1],
bs,
bx4: bx4 as u16,
by4: by4 as u16,
lmv: [bv, Mv { y: -0x8000, x: -0x8000 }],
..RefmvsBlock::default()
};
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn splat(&mut self, bx4: usize, by4: usize, bw4: usize, bh4: usize, mv0: Mv, ref0: i8, bs: u8, mf: u8, matrix: [i32; 6]) {
self.splat_pair(bx4, by4, bw4, bh4, [mv0, Mv { y: -0x8000, x: -0x8000 }], (ref0, -1), bs, mf, matrix);
}
#[allow(clippy::too_many_arguments)]
pub fn splat_pair(&mut self, bx4: usize, by4: usize, bw4: usize, bh4: usize, mvp: [Mv; 2], refp: (i8, i8), bs: u8, mf: u8, matrix: [i32; 6]) {
for r in 0..bh4 {
if !crate::av2_recon::work_tick("refmvs:2632") { break; }
for c in 0..bw4 {
if !crate::av2_recon::work_tick("refmvs:2633") { break; }
let mv = if mf & 2 != 0 {
warp_cell_mv(&matrix, bx4 as i32, by4 as i32, (c as i32) & !1, (r as i32) & !1)
} else {
mvp[0]
};
let cell = &mut self.r[(by4 + r).min(crate::av2_recon::nb_len() - 1) * crate::av2_recon::nb_len() + (bx4 + c).min(crate::av2_recon::nb_len() - 1)];
*cell = RefmvsBlock {
mv: [mv, mvp[1]],
ref_: [refp.0, refp.1],
bs,
mf,
bx4: bx4 as u16,
by4: by4 as u16,
lmv: mvp,
matrix,
};
}
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn refmvs_find(grid: &RefmvsGrid, bx4: usize, by4: usize, bw4: usize, bh4: usize, ref0: i8, ref1: i8, gmv0: Mv, gmv1: Mv, sbsz: usize, iw4: usize, ih4: usize, drl_reorder: u8, bank: Option<&RefmvBank>, max_drl_bits: usize, skip_mode: bool) -> (Vec<Candidate>, usize) {
let mut st = ScanState::default();
let gmv = if ref0 >= 7 { [Mv { y: 0, x: 0 }; 2] } else { [gmv0, gmv1] };
let comp = ref1 >= 0;
let tm_stride = TMVS.with(|c| c.borrow().stride);
st.b8x8 = (bx4 >> 1) + (((by4 & (sbsz - 1)) >> 1) * tm_stride.max(1));
let tms_8x8y = ((by4 & (sbsz - 1)) >> 1) as isize;
let bms_8x8y = (((by4 + bh4 - 1) & (sbsz - 1)) >> 1) as isize;
let left_8x8x = (bx4 as isize - 1) >> 1;
let top_8x8y: isize = if by4 & (sbsz - 1) != 0 { (((by4 - 1) & (sbsz - 1)) >> 1) as isize } else { -1 };
let w4 = bw4.min(iw4 - bx4);
let h4 = bh4.min(ih4 - by4);
let is_sb_boundary = (by4 & (sbsz - 1)) == 0;
let have_left = bx4 > 0;
let have_top = by4 > 0;
let mut bml_bs = None;
if have_left && bh4 == h4 {
let b = *grid.at(by4 + bh4 - 1, bx4 - 1);
bml_bs = Some(b.bs);
add_spatial_candidate_sngl(&mut st, 1, &b, bh4 as i32 - 1, -1, bms_8x8y, left_8x8x, bx4 as i32 - 1, (by4 + bh4) as i32 - 1, ref0, ref1, gmv);
}
let (x_off, abw4): (i32, usize) = if is_sb_boundary {
((bx4 & 1) as i32, (bw4 + 1) & !1)
} else {
(0, bw4)
};
let (mut rmt, mut lmt, mut tr, mut tl) = (None, None, None, None);
if have_top {
if is_sb_boundary {
if bx4 as i32 - x_off - 2 >= 0 {
tl = Some(*grid.at(by4 - 1, ((bx4 & !1) as i32 - 2) as usize));
}
if bw4 > 2 {
lmt = Some(*grid.at(by4 - 1, bx4 & !1));
}
if bw4 == w4 {
rmt = Some(*grid.at(by4 - 1, 2 * ((bx4 >> 1) + (abw4 >> 1) - 1)));
}
if (bx4 as i32 - x_off + abw4 as i32) < iw4 as i32 && bw4 <= 16 {
tr = Some(*grid.at(by4 - 1, 2 * ((bx4 >> 1) + (abw4 >> 1))));
}
} else {
if bw4 == w4 {
rmt = Some(*grid.at(by4 - 1, bx4 + bw4 - 1));
}
if bw4 > 1 {
lmt = Some(*grid.at(by4 - 1, bx4));
}
if (bx4 + bw4) & (sbsz - 1) != 0 && bx4 + bw4 < iw4 && bw4 <= 16 {
let t = *grid.at(by4 - 1, bx4 + bw4);
if t.mv[0].y != -0x8000 {
tr = Some(t);
}
}
if have_left {
tl = Some(*grid.at(by4 - 1, bx4 - 1));
}
}
}
if let Some(b) = rmt {
let xpos = abw4 as i32 - (1 << is_sb_boundary as i32) - x_off;
add_spatial_candidate_sngl(&mut st, (xpos >= 0) as i32, &b, -1, xpos, top_8x8y, (bx4 as isize + xpos as isize) >> 1, bx4 as i32 + xpos, by4 as i32 - 1, ref0, ref1, gmv);
}
let mut tml_bs = None;
if have_left && bh4 > 1 {
let b = *grid.at(by4, bx4 - 1);
tml_bs = Some(b.bs);
add_spatial_candidate_sngl(&mut st, 1, &b, 0, -1, tms_8x8y, left_8x8x, bx4 as i32 - 1, by4 as i32, ref0, ref1, gmv);
}
if let Some(b) = lmt {
let xpos = -x_off;
add_spatial_candidate_sngl(&mut st, (x_off == 0) as i32, &b, -1, xpos, top_8x8y, (bx4 as isize + xpos as isize) >> 1, bx4 as i32 + xpos, by4 as i32 - 1, ref0, ref1, gmv);
}
if have_left && bh4 <= 16 && (by4 + bh4) & (sbsz - 1) != 0 && by4 + bh4 < ih4 {
let b = *grid.at(by4 + bh4, bx4 - 1);
add_spatial_candidate_sngl(&mut st, 1, &b, bh4 as i32, -1, (((by4 + bh4) & (sbsz - 1)) >> 1) as isize, left_8x8x, bx4 as i32 - 1, (by4 + bh4) as i32, ref0, ref1, gmv);
}
if let Some(b) = tr {
let xpos = abw4 as i32 - x_off;
add_spatial_candidate_sngl(&mut st, 1, &b, -1, xpos, top_8x8y, (bx4 as isize + xpos as isize) >> 1, bx4 as i32 + xpos, by4 as i32 - 1, ref0, ref1, gmv);
}
{
let t_ok = TMVS.with(|c| {
let t = c.borrow();
t.valid && t.use_ref_frame_mvs
});
if t_ok && (ref0 != ref1 || skip_mode) && st.cnt < 6 {
let bw8 = (bw4 >> 1).min(8);
let bh8 = (bh4 >> 1).min(8);
let step_h = if bw4 >= 16 { 2usize } else { 1 };
let step_v = if bh4 >= 16 { 2usize } else { 1 };
let x_off = 2 * bw8 as i32 - 2 * step_h as i32;
let y_off = 2 * bh8 as i32 - 2 * step_v as i32;
let mut first = false;
if (x_off as usize) < w4 && (y_off as usize) < h4 && x_off >= 0 && y_off >= 0 {
let off = ((((by4 as i32 + y_off) as usize & (sbsz - 1)) >> 1) * tm_stride)
+ ((bx4 as i32 + x_off) as usize >> 1);
first = add_temporal_candidate_t(&mut st, ref0, ref1, off);
}
if !first && (bw4 > 4 || bh4 > 4) {
let off = ((((by4 + bh8) & (sbsz - 1)) >> 1) * tm_stride) + ((bx4 + bw8) >> 1);
add_temporal_candidate_t(&mut st, ref0, ref1, off);
}
}
}
if let Some(b) = tl {
let xpos = -(1 << is_sb_boundary as i32) - x_off;
add_spatial_candidate_sngl(&mut st, 0, &b, -1, xpos, top_8x8y, (bx4 as isize + xpos as isize) >> 1, bx4 as i32 + xpos, by4 as i32 - 1, ref0, ref1, gmv);
}
let nearest_refmv_count = st.cnt;
if have_left {
let adj = 3 - (bx4 & (bw4 == 1) as usize) as i32;
if bx4 as i32 - adj >= 0 {
let exbx = (bx4 as i32 - adj) as usize;
let dims = |bs: u8| crate::av2_decode::BLOCK_DIMENSIONS[bs as usize][0] as i32;
if bh4 == h4 {
let b = *grid.at(by4 + bh4 - 1, exbx);
if bml_bs.map_or(true, |bs| dims(b.bs) < adj || b.bs != bs) {
add_spatial_candidate_sngl(&mut st, 0, &b, bh4 as i32 - 1, -adj, bms_8x8y, (bx4 as isize - adj as isize) >> 1, bx4 as i32 - adj, (by4 + bh4) as i32 - 1, ref0, ref1, gmv);
}
}
if bh4 > 1 {
let b = *grid.at(by4, exbx);
if tml_bs.map_or(true, |bs| dims(b.bs) < adj || b.bs != bs) {
add_spatial_candidate_sngl(&mut st, 0, &b, 0, -adj, tms_8x8y, (bx4 as isize - adj as isize) >> 1, bx4 as i32 - adj, by4 as i32, ref0, ref1, gmv);
}
}
}
}
let reorder = (drl_reorder == 2 && nearest_refmv_count >= 2) || (drl_reorder == 1 && nearest_refmv_count >= 4);
if reorder {
let mut maxwidx = 0;
let mut maxw = st.mvstack[0].weight;
for n in 1..nearest_refmv_count {
if !crate::av2_recon::work_tick("refmvs:2826") { break; }
if st.mvstack[n].weight > maxw {
maxw = st.mvstack[n].weight;
maxwidx = n;
}
}
if maxwidx != 0 {
st.mvstack.swap(0, maxwidx);
}
}
if ref0 == -1 {
if let Some(b) = bank {
let lim = 1 + max_drl_bits;
add_bank_candidates(&mut st, b, ref0, -1, lim, bx4, by4, bw4, bh4, iw4.div_ceil(2), ih4.div_ceil(2));
}
let sbsz_px = (sbsz * 4) as i32; let lim = 1 + max_drl_bits;
let defaults = [
(-(sbsz_px * 8), 0i32),
(0i32, -(8 * (sbsz_px + 256))),
(-(bh4 as i32 * 32), 0i32),
(0i32, -(bw4 as i32 * 32)),
];
for (dy, dx) in defaults {
if st.cnt >= lim {
break;
}
st.mvstack[st.cnt].mv[0] = Mv { y: dy, x: dx };
st.mvstack[st.cnt].weight = 0;
st.cnt += 1;
}
return (st.mvstack.to_vec(), st.cnt);
}
let lim = 1 + max_drl_bits;
if comp && st.cnt < lim {
add_derived_comp(&mut st, lim);
}
if let Some(b) = bank {
add_bank_candidates(&mut st, b, ref0, ref1, lim, bx4, by4, bw4, bh4, iw4.div_ceil(2), ih4.div_ceil(2));
}
if !comp && st.cnt < lim {
for m in 0..st.drvd_cnt {
if !crate::av2_recon::work_tick("refmvs:2876") { break; }
if st.cnt >= 6 {
break;
}
let cand = st.dr[m][0];
let mut cnt2 = st.cnt;
let mut iter2 = st.iter_cntr;
add_candidate_sngl(&mut st.mvstack, &mut cnt2, lim, 0, cand, 0, 0, &mut iter2, 16);
st.cnt = cnt2;
st.iter_cntr = iter2;
}
st.drvd_cnt = 0;
}
let (minx, maxx) = (-((bx4 + bw4 + 4) as i32) * 32, (iw4 - bx4 + 4) as i32 * 32);
let (miny, maxy) = (-((by4 + bh4 + 4) as i32) * 32, (ih4 - by4 + 4) as i32 * 32);
for n in 0..st.cnt {
if !crate::av2_recon::work_tick("refmvs:2893") { break; }
st.mvstack[n].mv[0].x = st.mvstack[n].mv[0].x.clamp(minx, maxx);
st.mvstack[n].mv[0].y = st.mvstack[n].mv[0].y.clamp(miny, maxy);
if comp {
st.mvstack[n].mv[1].x = st.mvstack[n].mv[1].x.clamp(minx, maxx);
st.mvstack[n].mv[1].y = st.mvstack[n].mv[1].y.clamp(miny, maxy);
}
}
if st.cnt < 6 && ref0 >= 0 {
let mut dup = false;
if st.iter_cntr < 16 {
for n in 0..st.cnt {
if !crate::av2_recon::work_tick("refmvs:2905") { break; }
if st.mvstack[n].mv[0] == gmv[0] && (!comp || st.mvstack[n].mv[1] == gmv[1]) {
st.iter_cntr += n as i32 + 1;
dup = true;
break;
}
}
if !dup {
st.iter_cntr += st.cnt as i32;
}
}
if !dup {
let last = st.cnt;
st.mvstack[last].mv[0] = gmv[0];
st.mvstack[last].mv[1] = gmv[1];
st.mvstack[last].weight = 0;
st.mvstack[last].cwp = 8;
st.mvstack[last].y_off = 0;
st.mvstack[last].x_off = 0;
st.cnt = last + 1;
}
if bw4.min(bh4) > 8 && st.cnt >= 2 && st.cnt < 6 {
const EXT_MVP: [(usize, usize); 6] = [(0, 1), (1, 0), (0, 2), (2, 0), (1, 2), (2, 1)];
for c in 0..2usize {
if !crate::av2_recon::work_tick("refmvs:2931") { break; }
let mut n = c * 2;
while n < c * 4 + 2 {
let (yidx, xidx) = EXT_MVP[n];
st.dr[n][0] = Mv { y: st.mvstack[yidx].mv[0].y, x: st.mvstack[xidx].mv[0].x };
if comp {
st.dr[n][1] = Mv { y: st.mvstack[yidx].mv[1].y, x: st.mvstack[xidx].mv[1].x };
}
n += 1;
}
st.drvd_cnt = n;
if st.cnt == 2 {
break;
}
}
for m in 0..st.drvd_cnt {
if !crate::av2_recon::work_tick("refmvs:2946") { break; }
if st.cnt >= 6 { break; }
let pair = st.dr[m];
let mut cnt2 = st.cnt;
let mut iter2 = st.iter_cntr;
if comp {
add_candidate_comp(&mut st.mvstack, &mut cnt2, 6, 0, 8, pair, &mut iter2, 16);
} else {
add_candidate_sngl(&mut st.mvstack, &mut cnt2, 6, 0, pair[0], 0, 0, &mut iter2, 16);
}
st.cnt = cnt2;
st.iter_cntr = iter2;
}
}
}
(st.mvstack.to_vec(), st.cnt)
}
#[allow(clippy::too_many_arguments)]
pub fn refmvs_find_warp(grid: &RefmvsGrid, warp_bank: &RefmvWarpBank, bx4: usize, by4: usize, b_dim: [u8; 4], ref0: i8, gmv_matrix: [i32; 6], sbsz: usize, iw4: usize, ih4: usize) -> [[i32; 6]; 4] {
let (bw4, bh4) = (b_dim[0] as usize, b_dim[1] as usize);
let w4 = bw4.min(iw4 - bx4);
let h4 = bh4.min(ih4 - by4);
let is_sb_boundary = (by4 & (sbsz - 1)) == 0;
let have_left = bx4 > 0;
let have_top = by4 > 0;
let (minx, maxx) = (-((bx4 + bw4 + 4) as i32) * 32, (iw4 - bx4 + 4) as i32 * 32);
let (miny, maxy) = (-((by4 + bh4 + 4) as i32) * 32, (ih4 - by4 + 4) as i32 * 32);
let mut warp = [[0i32; 6]; 4];
let mut cnt1 = 0usize;
let matches = |b: &RefmvsBlock| -> Option<usize> {
if b.mv[0].y == -0x8000 {
return None;
}
if b.ref_[0] == ref0 {
Some(0)
} else if b.ref_[1] == ref0 && b.mf & 2 == 0 {
Some(1)
} else {
None
}
};
let proj = |b: &RefmvsBlock, slot: usize, x: i32, y: i32| -> Mv {
if b.mf & 2 != 0 { get_warpmv_proj(&b.matrix, x, y, minx, maxx, miny, maxy) } else { b.mv[slot] }
};
let bml = if have_left && bh4 == h4 { Some(*grid.at(by4 + bh4 - 1, bx4 - 1)) } else { None };
let (mut tl, mut lmt, mut rmt, mut tr) = (None, None, None, None);
if have_top {
if is_sb_boundary {
let x_off = (bx4 & 1) as i32;
let abw4 = (bw4 + 1) & !1;
if bx4 as i32 - x_off - 2 >= 0 {
tl = Some(*grid.at(by4 - 1, ((bx4 & !1) as i32 - 2) as usize));
}
if bw4 > 2 { lmt = Some(*grid.at(by4 - 1, bx4 & !1)); }
if bw4 == w4 { rmt = Some(*grid.at(by4 - 1, 2 * ((bx4 >> 1) + (abw4 >> 1) - 1))); }
if (bx4 as i32 - x_off + abw4 as i32) < iw4 as i32 && bw4 <= 16 {
tr = Some(*grid.at(by4 - 1, 2 * ((bx4 >> 1) + (abw4 >> 1))));
}
} else {
if have_left { tl = Some(*grid.at(by4 - 1, bx4 - 1)); }
if bw4 > 1 { lmt = Some(*grid.at(by4 - 1, bx4)); }
if bw4 == w4 { rmt = Some(*grid.at(by4 - 1, bx4 + bw4 - 1)); }
if (bx4 + bw4) & (sbsz - 1) != 0 && bx4 + bw4 < iw4 && bw4 <= 16 {
let t = *grid.at(by4 - 1, bx4 + bw4);
if t.mv[0].y != -0x8000 { tr = Some(t); }
}
}
}
let tml = if have_left && bh4 > 1 { Some(*grid.at(by4, bx4 - 1)) } else { None };
let bl = if have_left && bh4 <= 16 && (by4 + bh4) & (sbsz - 1) != 0 && by4 + bh4 < ih4 { Some(*grid.at(by4 + bh4, bx4 - 1)) } else { None };
if let Some((bmlb, bslot)) = bml.and_then(|b| matches(&b).map(|sl| (b, sl))) {
let bl_mv = proj(&bmlb, bslot, bx4 as i32 * 4, (by4 + bh4) as i32 * 4);
let build = |a: &RefmvsBlock, asl: usize, c: &RefmvsBlock, csl: usize| -> Option<[i32; 6]> {
let tl_mv = proj(a, asl, bx4 as i32 * 4, by4 as i32 * 4);
let tr_mv = proj(c, csl, (bx4 + bw4) as i32 * 4, by4 as i32 * 4);
model_from_corners(tl_mv, tr_mv, bl_mv, bx4 as i32 * 4, by4 as i32 * 4, b_dim)
};
let m1 = (
tl.and_then(|b| matches(&b).map(|sl| (b, sl))),
rmt.and_then(|b| matches(&b).map(|sl| (b, sl))),
);
if let (Some((a, asl)), Some((c, csl))) = m1 {
if let Some(m) = build(&a, asl, &c, csl) { warp[0] = m; cnt1 = 1; }
}
if cnt1 == 0 {
let m2 = (
lmt.and_then(|b| matches(&b).map(|sl| (b, sl))),
tr.and_then(|b| matches(&b).map(|sl| (b, sl))),
);
if let (Some((a, asl)), Some((c, csl))) = m2 {
if let Some(m) = build(&a, asl, &c, csl) { warp[0] = m; cnt1 = 1; }
}
}
}
for nb in [bml, rmt, tml, lmt, bl, tr, tl].into_iter().flatten() {
if !crate::av2_recon::work_tick("refmvs:3057") { break; }
if cnt1 < 4 && nb.mf & 2 != 0 && nb.ref_[0] == ref0 {
warp[cnt1] = nb.matrix;
cnt1 += 1;
}
}
if have_left {
let adj = 3 - (bx4 & (bw4 == 1) as usize) as i32;
if bx4 as i32 - adj >= 0 {
let exbx = (bx4 as i32 - adj) as usize;
let dims = |bs: u8| crate::av2_decode::BLOCK_DIMENSIONS[bs as usize][0] as i32;
if bh4 == h4 {
let b = *grid.at(by4 + bh4 - 1, exbx);
if bml.map_or(true, |bm| dims(b.bs) < adj || b.bs != bm.bs) && cnt1 < 4 && b.mf & 2 != 0 && b.ref_[0] == ref0 {
warp[cnt1] = b.matrix;
cnt1 += 1;
}
}
if bh4 > 1 {
let b = *grid.at(by4, exbx);
if tml.map_or(true, |tm| dims(b.bs) < adj || b.bs != tm.bs) && cnt1 < 4 && b.mf & 2 != 0 && b.ref_[0] == ref0 {
warp[cnt1] = b.matrix;
cnt1 += 1;
}
}
}
}
if cnt1 < 4 && (0..7).contains(&(ref0 as i32)) {
let r = ref0 as usize;
let sz = warp_bank.size[r] as usize;
let idx = warp_bank.idx[r] as usize;
let start = sz + idx;
let mut n = 0;
while n < sz && cnt1 < 4 {
warp[cnt1] = warp_bank.mat[r][(start.wrapping_sub(1).wrapping_sub(n)) & 3];
cnt1 += 1;
n += 1;
}
}
if cnt1 < 4 {
warp[cnt1] = gmv_matrix;
cnt1 += 1;
}
while cnt1 < 4 {
warp[cnt1] = IDENTITY_WARP;
cnt1 += 1;
}
warp
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn warp_bank_dedups_on_shear_not_full_matrix() {
let mut wb = RefmvWarpBank::default();
let shear = [90112, -8192, 32704, 59392];
wb.add(0, [-507904, -1689920, shear[0], shear[1], shear[2], shear[3]]);
wb.add(0, [286720, -887616, shear[0], shear[1], shear[2], shear[3]]);
wb.add(0, [286720, -889152, shear[0], shear[1], shear[2], shear[3]]);
assert_eq!(wb.size[0], 1, "same-shear adds must collapse to one entry");
assert_eq!(&wb.mat[0][0][..2], &[-507904, -1689920]);
wb.add(0, [665984, -542464, 78144, -8128, 26752, 59520]);
assert_eq!(wb.size[0], 2);
}
#[test]
fn reduce_prec_matches_oracle_block_4_0() {
let mut m = Mv { y: 25, x: 159 };
mv_reduce_prec(&mut m, 3);
assert_eq!(m, Mv { y: 24, x: 160 });
let fmv = Mv { y: m.y + -32, x: m.x + -56 };
assert_eq!(fmv, Mv { y: -8, x: 104 });
}
#[test]
fn reduce_prec_6_is_noop() {
let mut m = Mv { y: 25, x: 159 };
mv_reduce_prec(&mut m, 6);
assert_eq!(m, Mv { y: 25, x: 159 });
}
#[test]
fn gmv_identity_is_zero() {
let g = GmvModel::default();
assert_eq!(get_gmv_2d(&g, 0, 0, 4, 4, 108, 60), Mv { y: 0, x: 0 });
}
#[test]
fn warpmv_proj_identity_is_zero() {
let m = [0, 0, 1 << 16, 0, 0, 1 << 16];
assert_eq!(get_warpmv_proj(&m, 16, 0, -0xffff, 0xffff, -0xffff, 0xffff), Mv { y: 0, x: 0 });
}
#[test]
fn add_candidate_dedups_and_appends() {
let mut stack = [Candidate::default(); 6];
let mut cnt = 0usize;
let mut iter = 0i32;
assert!(add_candidate_sngl(&mut stack, &mut cnt, 6, 2, Mv { y: 17, x: 165 }, 0, -1, &mut iter, 16));
assert!(add_candidate_sngl(&mut stack, &mut cnt, 6, 1, Mv { y: 25, x: 159 }, -1, 0, &mut iter, 16));
assert_eq!(cnt, 2);
assert!(!add_candidate_sngl(&mut stack, &mut cnt, 6, 3, Mv { y: 17, x: 165 }, 0, -1, &mut iter, 16));
assert_eq!(cnt, 2);
assert_eq!(stack[0].weight, 5); assert_eq!(stack[1].weight, 1);
}
#[test]
fn refmvs_find_block_4_0_spatial_candidates() {
let mut grid = RefmvsGrid::default();
let m = [1476608, 182272, 59392, -8192, 8192, 59392];
grid.splat(0, 0, 4, 4, Mv { y: 24, x: 168 }, 0, 18, 2, m); let (stack, cnt) = refmvs_find(&grid, 4, 0, 4, 4, 0, -1, Mv { y: 0, x: 0 }, Mv { y: 0, x: 0 }, 16, 108, 60, 2, None, 3, false);
assert_eq!(cnt, 3);
assert_eq!(stack[0].mv[0], Mv { y: 25, x: 159 });
assert_eq!(stack[0].weight, 1);
assert_eq!(stack[1].mv[0], Mv { y: 31, x: 167 });
assert_eq!(stack[1].weight, 1);
assert_eq!(stack[2].mv[0], Mv { y: 0, x: 0 }); assert_eq!(stack[2].weight, 0);
}
#[test]
fn refmvs_find_block_4_0_full_stack_with_bank() {
let mut grid = RefmvsGrid::default();
let m = [1476608, 182272, 59392, -8192, 8192, 59392];
grid.splat(0, 0, 4, 4, Mv { y: 24, x: 168 }, 0, 18, 2, m);
let mut bank = RefmvBank::default();
bank.add_block(4, 4, 0, 0, 16, 0, 0, Mv { y: 24, x: 168 }); let (stack, cnt) = refmvs_find(&grid, 4, 0, 4, 4, 0, -1, Mv { y: 0, x: 0 }, Mv { y: 0, x: 0 }, 16, 108, 60, 2, Some(&bank), 3, false);
assert_eq!(cnt, 4);
assert_eq!(stack[0].mv[0], Mv { y: 25, x: 159 });
assert_eq!(stack[1].mv[0], Mv { y: 31, x: 167 });
assert_eq!(stack[2].mv[0], Mv { y: 24, x: 168 }); assert_eq!(stack[2].weight, 0);
assert_eq!(stack[3].mv[0], Mv { y: 0, x: 0 }); }
#[test]
fn refmvs_degenerate_block_0_0_is_global_zero() {
let grid = RefmvsGrid::default();
let (stack, cnt) = refmvs_find(&grid, 0, 0, 4, 4, 0, -1, Mv { y: 0, x: 0 }, Mv { y: 0, x: 0 }, 16, 108, 60, 2, None, 3, false);
assert_eq!(cnt, 1);
assert_eq!(stack[0].mv[0], Mv { y: 0, x: 0 });
assert_eq!(stack[0].weight, 0);
let mut st = ScanState::default();
add_global_candidate(&mut st, Mv { y: 0, x: 0 });
assert_eq!(st.cnt, 1);
assert_eq!(st.mvstack[0].mv[0], Mv { y: 0, x: 0 });
assert_eq!(st.mvstack[0].weight, 0);
let mut pred = st.mvstack[0].mv[0];
mv_reduce_prec(&mut pred, 3);
assert_eq!(Mv { y: pred.y + 24, x: pred.x + 168 }, Mv { y: 24, x: 168 });
}
#[test]
fn spatial_candidate_matches_ref_and_dedups() {
let mut st = ScanState::default();
let nb = RefmvsBlock { mv: [Mv { y: 24, x: 168 }, Mv::default()], ref_: [0, -1], ..Default::default() };
add_spatial_candidate_sngl(&mut st, 2, &nb, -1, 0, 0, 0, nb.bx4 as i32, nb.by4 as i32, 0, -1, [Mv { y: 0, x: 0 }; 2]);
assert_eq!(st.cnt, 1);
assert_eq!(st.mvstack[0].mv[0], Mv { y: 24, x: 168 });
assert_eq!(st.mvstack[0].weight, 2);
let intra = RefmvsBlock::default();
add_spatial_candidate_sngl(&mut st, 2, &intra, 0, -1, 0, 0, 0, 0, 0, -1, [Mv { y: 0, x: 0 }; 2]);
assert_eq!(st.cnt, 1);
let other = RefmvsBlock { mv: [Mv { y: 9, x: 9 }, Mv::default()], ref_: [1, -1], ..Default::default() };
add_spatial_candidate_sngl(&mut st, 2, &other, 0, -1, 0, 0, 0, 0, 0, -1, [Mv { y: 0, x: 0 }; 2]);
assert_eq!(st.cnt, 1);
add_global_candidate(&mut st, Mv { y: 0, x: 0 });
assert_eq!(st.cnt, 2);
assert_eq!(st.mvstack[1].mv[0], Mv { y: 0, x: 0 });
}
#[test]
fn add_candidate_respects_max_cnt() {
let mut stack = [Candidate::default(); 6];
let mut cnt = 0usize;
let mut iter = 0i32;
for i in 0..8 {
add_candidate_sngl(&mut stack, &mut cnt, 6, 1, Mv { y: i, x: 100 + i }, 0, 0, &mut iter, 16);
}
assert_eq!(cnt, 6); }
#[test]
fn find_affine_int_matches_oracle_block_6_4() {
let pts = [[[-40, 56], [48, 36]], [[-40, -72], [64, -80]]];
let m = find_affine_int(&pts, 2, 2, Mv { y: 8, x: 104 }, 6, 4);
assert_eq!(m, [665984, -542464, 78144, -8128, 26752, 59520]);
}
#[test]
fn get_warpmv_2d_matches_oracle_block_2_4() {
let warp0 = [1228800, 98304, 59392, 7168, 8192, 64512];
assert_eq!(get_warpmv_2d(&warp0, 2, 4, 2, 2, 108, 60, 6), Mv { y: 21, x: 158 });
}
#[test]
fn model_from_corners_matches_oracle_block_2_4() {
let m = model_from_corners(Mv { y: 18, x: 158 }, Mv { y: 26, x: 152 }, Mv { y: 17, x: 165 }, 8, 16, [2, 2, 1, 1]);
assert_eq!(m, Some([1228800, 98304, 59392, 7168, 8192, 64512]));
}
#[test]
fn reconstruct_warp_matrix_matches_oracle_block_0_0() {
let m = reconstruct_warp_delta_matrix(IDENTITY_WARP, [-6, -8, -0x80, -0x8000000], Mv { y: 24, x: 168 }, 4, 4, 0, 0);
assert_eq!(m, [1476608, 182272, 59392, -8192, 8192, 59392]);
}
#[test]
fn warp_cell_mv_matches_oracle_block_0_0() {
let m = [1476608, 182272, 59392, -8192, 8192, 59392];
assert_eq!(warp_cell_mv(&m, 0, 0, 2, 0), Mv { y: 31, x: 167 });
assert_eq!(warp_cell_mv(&m, 0, 0, 2, 2), Mv { y: 25, x: 159 });
}
#[test]
fn warp_cell_mv_identity_is_zero() {
let m = [0, 0, 0x10000, 0, 0, 0x10000];
assert_eq!(warp_cell_mv(&m, 0, 0, 2, 2), Mv { y: 0, x: 0 });
}
#[test]
fn warpmv_proj_matches_hand_computed() {
let m = [0x2000, 0x1000, 0x10100, 0x80, 0x40, 0x10200];
assert_eq!(get_warpmv_proj(&m, 16, 0, -0xffff, 0xffff, -0xffff, 0xffff), Mv { y: 1, x: 2 });
}
}