use super::*;
use crate::context::*;
use crate::encoder::*;
use crate::me::WriteGuardMEStats;
use crate::util::*;
use std::iter::FusedIterator;
use std::marker::PhantomData;
use std::ops::DerefMut;
pub const MAX_TILE_WIDTH: usize = 4096;
pub const MAX_TILE_AREA: usize = 4096 * 2304;
pub const MAX_TILE_COLS: usize = 64;
pub const MAX_TILE_ROWS: usize = 64;
pub const MAX_TILE_RATE: f64 = 4096f64 * 2176f64 * 60f64 * 1.1;
#[derive(Debug, Clone, Copy)]
pub struct TilingInfo {
pub frame_width: usize,
pub frame_height: usize,
pub tile_width_sb: usize,
pub tile_height_sb: usize,
pub cols: usize, pub rows: usize, pub tile_cols_log2: usize,
pub tile_rows_log2: usize,
pub min_tile_cols_log2: usize,
pub max_tile_cols_log2: usize,
pub min_tile_rows_log2: usize,
pub max_tile_rows_log2: usize,
pub sb_size_log2: usize,
pub min_tiles_log2: usize,
}
impl TilingInfo {
pub fn from_target_tiles(
sb_size_log2: usize, frame_width: usize, frame_height: usize,
frame_rate: f64, tile_cols_log2: usize, tile_rows_log2: usize,
is_422_p: bool,
) -> Self {
let frame_width = frame_width.align_power_of_two(3);
let frame_height = frame_height.align_power_of_two(3);
let frame_width_sb =
frame_width.align_power_of_two_and_shift(sb_size_log2);
let frame_height_sb =
frame_height.align_power_of_two_and_shift(sb_size_log2);
let sb_cols = frame_width.align_power_of_two_and_shift(sb_size_log2);
let sb_rows = frame_height.align_power_of_two_and_shift(sb_size_log2);
let max_tile_width_sb = MAX_TILE_WIDTH >> sb_size_log2;
let max_tile_area_sb = MAX_TILE_AREA >> (2 * sb_size_log2);
let min_tile_cols_log2 =
Self::tile_log2(max_tile_width_sb, sb_cols).unwrap();
let max_tile_cols_log2 =
Self::tile_log2(1, sb_cols.min(MAX_TILE_COLS)).unwrap();
let max_tile_rows_log2 =
Self::tile_log2(1, sb_rows.min(MAX_TILE_ROWS)).unwrap();
let min_tiles_log2 = min_tile_cols_log2
.max(Self::tile_log2(max_tile_area_sb, sb_cols * sb_rows).unwrap());
let min_tiles_ratelimit_log2 = min_tiles_log2.max(
((frame_width * frame_height) as f64 * frame_rate / MAX_TILE_RATE)
.ceil()
.log2()
.ceil() as usize,
);
let tile_cols_log2 =
tile_cols_log2.clamp(min_tile_cols_log2, max_tile_cols_log2);
let tile_width_sb_pre =
sb_cols.align_power_of_two_and_shift(tile_cols_log2);
let tile_width_sb = if is_422_p {
(tile_width_sb_pre + 1) >> 1 << 1
} else {
tile_width_sb_pre
};
let cols = (frame_width_sb + tile_width_sb - 1) / tile_width_sb;
let tile_cols_log2 = Self::tile_log2(1, cols).unwrap();
assert!(tile_cols_log2 >= min_tile_cols_log2);
let min_tile_rows_log2 = if min_tiles_log2 > tile_cols_log2 {
min_tiles_log2 - tile_cols_log2
} else {
0
};
let min_tile_rows_ratelimit_log2 =
if min_tiles_ratelimit_log2 > tile_cols_log2 {
min_tiles_ratelimit_log2 - tile_cols_log2
} else {
0
};
let tile_rows_log2 = tile_rows_log2
.max(min_tile_rows_log2)
.clamp(min_tile_rows_ratelimit_log2, max_tile_rows_log2);
let tile_height_sb = sb_rows.align_power_of_two_and_shift(tile_rows_log2);
let rows = (frame_height_sb + tile_height_sb - 1) / tile_height_sb;
Self {
frame_width,
frame_height,
tile_width_sb,
tile_height_sb,
cols,
rows,
tile_cols_log2,
tile_rows_log2,
min_tile_cols_log2,
max_tile_cols_log2,
min_tile_rows_log2,
max_tile_rows_log2,
sb_size_log2,
min_tiles_log2,
}
}
pub fn tile_log2(blk_size: usize, target: usize) -> Option<usize> {
let mut k = 0;
while (blk_size.checked_shl(k)?) < target {
k += 1;
}
Some(k as usize)
}
#[inline(always)]
pub const fn tile_count(&self) -> usize {
self.cols * self.rows
}
pub fn tile_iter_mut<'a, T: Pixel>(
&self, fs: &'a mut FrameState<T>, fb: &'a mut FrameBlocks,
) -> TileContextIterMut<'a, T> {
let afs = fs as *mut _;
let afb = fb as *mut _;
let frame_me_stats = fs.frame_me_stats.write().expect("poisoned lock");
TileContextIterMut { ti: *self, fs: afs, fb: afb, next: 0, frame_me_stats }
}
}
pub struct TileContextMut<'a, T: Pixel> {
pub ts: TileStateMut<'a, T>,
pub tb: TileBlocksMut<'a>,
}
pub struct TileContextIterMut<'a, T: Pixel> {
ti: TilingInfo,
fs: *mut FrameState<T>,
fb: *mut FrameBlocks,
frame_me_stats: WriteGuardMEStats<'a>,
next: usize,
}
impl<'a, T: Pixel> Iterator for TileContextIterMut<'a, T> {
type Item = TileContextMut<'a, T>;
fn next(&mut self) -> Option<Self::Item> {
if self.next < self.ti.rows * self.ti.cols {
let tile_col = self.next % self.ti.cols;
let tile_row = self.next / self.ti.cols;
let ctx = TileContextMut {
ts: {
let fs = unsafe { &mut *self.fs };
let frame_me_stats = unsafe {
let len = self.frame_me_stats.len();
let ptr = self.frame_me_stats.as_mut_ptr();
std::slice::from_raw_parts_mut(ptr, len)
};
let sbo = PlaneSuperBlockOffset(SuperBlockOffset {
x: tile_col * self.ti.tile_width_sb,
y: tile_row * self.ti.tile_height_sb,
});
let x = sbo.0.x << self.ti.sb_size_log2;
let y = sbo.0.y << self.ti.sb_size_log2;
let tile_width = self.ti.tile_width_sb << self.ti.sb_size_log2;
let tile_height = self.ti.tile_height_sb << self.ti.sb_size_log2;
let width = tile_width.min(self.ti.frame_width - x);
let height = tile_height.min(self.ti.frame_height - y);
TileStateMut::new(
fs,
sbo,
self.ti.sb_size_log2,
width,
height,
frame_me_stats,
)
},
tb: {
let fb = unsafe { &mut *self.fb };
let tile_width_mi =
self.ti.tile_width_sb << (self.ti.sb_size_log2 - MI_SIZE_LOG2);
let tile_height_mi =
self.ti.tile_height_sb << (self.ti.sb_size_log2 - MI_SIZE_LOG2);
let x = tile_col * tile_width_mi;
let y = tile_row * tile_height_mi;
let cols = tile_width_mi.min(fb.cols - x);
let rows = tile_height_mi.min(fb.rows - y);
TileBlocksMut::new(fb, x, y, cols, rows)
},
};
self.next += 1;
Some(ctx)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.ti.cols * self.ti.rows - self.next;
(remaining, Some(remaining))
}
}
impl<T: Pixel> ExactSizeIterator for TileContextIterMut<'_, T> {}
impl<T: Pixel> FusedIterator for TileContextIterMut<'_, T> {}
#[cfg(test)]
pub mod test {
use super::*;
use crate::api::*;
use crate::lrf::*;
use crate::mc::MotionVector;
use crate::predict::PredictionMode;
use std::sync::Arc;
#[test]
fn test_tiling_info_from_tile_count() {
let sb_size_log2 = 6;
let (width, height) = (160, 144);
let frame_rate = 25f64;
let ti = TilingInfo::from_target_tiles(
sb_size_log2,
width,
height,
frame_rate,
0,
0,
false,
);
assert_eq!(1, ti.cols);
assert_eq!(1, ti.rows);
assert_eq!(3, ti.tile_width_sb);
assert_eq!(3, ti.tile_height_sb);
let ti = TilingInfo::from_target_tiles(
sb_size_log2,
width,
height,
frame_rate,
1,
1,
false,
);
assert_eq!(2, ti.cols);
assert_eq!(2, ti.rows);
assert_eq!(2, ti.tile_width_sb);
assert_eq!(2, ti.tile_height_sb);
let ti = TilingInfo::from_target_tiles(
sb_size_log2,
width,
height,
frame_rate,
2,
2,
false,
);
assert_eq!(3, ti.cols);
assert_eq!(3, ti.rows);
assert_eq!(1, ti.tile_width_sb);
assert_eq!(1, ti.tile_height_sb);
let ti = TilingInfo::from_target_tiles(
sb_size_log2,
width,
height,
frame_rate,
10,
8,
false,
);
assert_eq!(3, ti.cols);
assert_eq!(3, ti.rows);
assert_eq!(1, ti.tile_width_sb);
assert_eq!(1, ti.tile_height_sb);
let ti = TilingInfo::from_target_tiles(
sb_size_log2,
1024,
1024,
frame_rate,
0,
0,
false,
);
assert_eq!(1, ti.cols);
assert_eq!(1, ti.rows);
assert_eq!(16, ti.tile_width_sb);
assert_eq!(16, ti.tile_height_sb);
}
fn setup(
width: usize, height: usize,
) -> (FrameInvariants<u16>, FrameState<u16>, FrameBlocks, f64) {
assert!(width & 7 == 0);
assert!(height & 7 == 0);
let chroma_sampling = ChromaSampling::Cs420;
let config = Arc::new(EncoderConfig {
width,
height,
bit_depth: 8,
chroma_sampling,
..Default::default()
});
let mut sequence = Sequence::new(&config);
sequence.enable_large_lru = false;
let frame_rate = config.frame_rate();
let fi = FrameInvariants::new(config, Arc::new(sequence));
let fs = FrameState::new(&fi);
let fb = FrameBlocks::new(fi.w_in_b, fi.h_in_b);
(fi, fs, fb, frame_rate)
}
#[test]
fn test_tile_iter_len() {
let (fi, mut fs, mut fb, frame_rate) = setup(160, 144);
{
let ti = TilingInfo::from_target_tiles(
fi.sb_size_log2(),
fi.width,
fi.height,
frame_rate,
1,
1,
false,
);
let mut iter = ti.tile_iter_mut(&mut fs, &mut fb);
assert_eq!(4, iter.len());
assert!(iter.next().is_some());
assert_eq!(3, iter.len());
assert!(iter.next().is_some());
assert_eq!(2, iter.len());
assert!(iter.next().is_some());
assert_eq!(1, iter.len());
assert!(iter.next().is_some());
assert_eq!(0, iter.len());
assert!(iter.next().is_none());
}
{
let ti = TilingInfo::from_target_tiles(
fi.sb_size_log2(),
fi.width,
fi.height,
frame_rate,
2,
2,
false,
);
let mut iter = ti.tile_iter_mut(&mut fs, &mut fb);
assert_eq!(9, iter.len());
assert!(iter.next().is_some());
assert_eq!(8, iter.len());
assert!(iter.next().is_some());
assert_eq!(7, iter.len());
assert!(iter.next().is_some());
assert_eq!(6, iter.len());
assert!(iter.next().is_some());
assert_eq!(5, iter.len());
assert!(iter.next().is_some());
assert_eq!(4, iter.len());
assert!(iter.next().is_some());
assert_eq!(3, iter.len());
assert!(iter.next().is_some());
assert_eq!(2, iter.len());
assert!(iter.next().is_some());
assert_eq!(1, iter.len());
assert!(iter.next().is_some());
assert_eq!(0, iter.len());
assert!(iter.next().is_none());
}
}
#[inline]
fn rect<T: Pixel>(
region: &PlaneRegionMut<'_, T>,
) -> (isize, isize, usize, usize) {
let &Rect { x, y, width, height } = region.rect();
(x, y, width, height)
}
#[test]
fn test_tile_area() {
let (fi, mut fs, mut fb, frame_rate) = setup(160, 144);
let ti = TilingInfo::from_target_tiles(
fi.sb_size_log2(),
fi.width,
fi.height,
frame_rate,
2,
2,
false,
);
let iter = ti.tile_iter_mut(&mut fs, &mut fb);
let tile_states = iter.map(|ctx| ctx.ts).collect::<Vec<_>>();
assert_eq!(9, tile_states.len());
let tile = &tile_states[0].rec; assert_eq!((0, 0, 64, 64), rect(&tile.planes[0]));
assert_eq!((0, 0, 32, 32), rect(&tile.planes[1]));
assert_eq!((0, 0, 32, 32), rect(&tile.planes[2]));
let tile = &tile_states[1].rec; assert_eq!((64, 0, 64, 64), rect(&tile.planes[0]));
assert_eq!((32, 0, 32, 32), rect(&tile.planes[1]));
assert_eq!((32, 0, 32, 32), rect(&tile.planes[2]));
let tile = &tile_states[2].rec; assert_eq!((128, 0, 64, 64), rect(&tile.planes[0]));
assert_eq!((64, 0, 32, 32), rect(&tile.planes[1]));
assert_eq!((64, 0, 32, 32), rect(&tile.planes[2]));
let tile = &tile_states[3].rec; assert_eq!((0, 64, 64, 64), rect(&tile.planes[0]));
assert_eq!((0, 32, 32, 32), rect(&tile.planes[1]));
assert_eq!((0, 32, 32, 32), rect(&tile.planes[2]));
let tile = &tile_states[4].rec; assert_eq!((64, 64, 64, 64), rect(&tile.planes[0]));
assert_eq!((32, 32, 32, 32), rect(&tile.planes[1]));
assert_eq!((32, 32, 32, 32), rect(&tile.planes[2]));
let tile = &tile_states[5].rec; assert_eq!((128, 64, 64, 64), rect(&tile.planes[0]));
assert_eq!((64, 32, 32, 32), rect(&tile.planes[1]));
assert_eq!((64, 32, 32, 32), rect(&tile.planes[2]));
let tile = &tile_states[6].rec; assert_eq!((0, 128, 64, 64), rect(&tile.planes[0]));
assert_eq!((0, 64, 32, 32), rect(&tile.planes[1]));
assert_eq!((0, 64, 32, 32), rect(&tile.planes[2]));
let tile = &tile_states[7].rec; assert_eq!((64, 128, 64, 64), rect(&tile.planes[0]));
assert_eq!((32, 64, 32, 32), rect(&tile.planes[1]));
assert_eq!((32, 64, 32, 32), rect(&tile.planes[2]));
let tile = &tile_states[8].rec; assert_eq!((128, 128, 64, 64), rect(&tile.planes[0]));
assert_eq!((64, 64, 32, 32), rect(&tile.planes[1]));
assert_eq!((64, 64, 32, 32), rect(&tile.planes[2]));
}
#[inline]
const fn b_area(region: &TileBlocksMut<'_>) -> (usize, usize, usize, usize) {
(region.x(), region.y(), region.cols(), region.rows())
}
#[test]
fn test_tile_blocks_area() {
let (fi, mut fs, mut fb, frame_rate) = setup(160, 144);
let ti = TilingInfo::from_target_tiles(
fi.sb_size_log2(),
fi.width,
fi.height,
frame_rate,
2,
2,
false,
);
let iter = ti.tile_iter_mut(&mut fs, &mut fb);
let tbs = iter.map(|ctx| ctx.tb).collect::<Vec<_>>();
assert_eq!(9, tbs.len());
assert_eq!((0, 0, 16, 16), b_area(&tbs[0]));
assert_eq!((16, 0, 16, 16), b_area(&tbs[1]));
assert_eq!((32, 0, 8, 16), b_area(&tbs[2]));
assert_eq!((0, 16, 16, 16), b_area(&tbs[3]));
assert_eq!((16, 16, 16, 16), b_area(&tbs[4]));
assert_eq!((32, 16, 8, 16), b_area(&tbs[5]));
assert_eq!((0, 32, 16, 4), b_area(&tbs[6]));
assert_eq!((16, 32, 16, 4), b_area(&tbs[7]));
assert_eq!((32, 32, 8, 4), b_area(&tbs[8]));
}
#[test]
fn test_tile_write() {
let (fi, mut fs, mut fb, frame_rate) = setup(160, 144);
{
let ti = TilingInfo::from_target_tiles(
fi.sb_size_log2(),
fi.width,
fi.height,
frame_rate,
2,
2,
false,
);
let iter = ti.tile_iter_mut(&mut fs, &mut fb);
let mut tile_states = iter.map(|ctx| ctx.ts).collect::<Vec<_>>();
{
let tile_plane = &mut tile_states[0].rec.planes[0];
let row = &mut tile_plane[12];
assert_eq!(64, row.len());
row[35..41].copy_from_slice(&[4, 42, 12, 18, 15, 31]);
}
{
let tile_plane = &mut tile_states[5].rec.planes[1];
let row = &mut tile_plane[8];
assert_eq!(32, row.len());
row[..4].copy_from_slice(&[14, 121, 1, 3]);
}
{
let tile_plane = &mut tile_states[7].rec.planes[2];
let row = &mut tile_plane[1];
assert_eq!(32, row.len());
row[11..16].copy_from_slice(&[6, 5, 2, 11, 8]);
}
}
let plane = &fs.rec.planes[0];
let y = plane.cfg.yorigin + 12;
let x = plane.cfg.xorigin + 35;
let idx = y * plane.cfg.stride + x;
assert_eq!(&[4, 42, 12, 18, 15, 31], &plane.data[idx..idx + 6]);
let plane = &fs.rec.planes[1];
let offset = (64, 32); let y = plane.cfg.yorigin + offset.1 + 8;
let x = plane.cfg.xorigin + offset.0;
let idx = y * plane.cfg.stride + x;
assert_eq!(&[14, 121, 1, 3], &plane.data[idx..idx + 4]);
let plane = &fs.rec.planes[2];
let offset = (32, 64); let y = plane.cfg.yorigin + offset.1 + 1;
let x = plane.cfg.xorigin + offset.0 + 11;
let idx = y * plane.cfg.stride + x;
assert_eq!(&[6, 5, 2, 11, 8], &plane.data[idx..idx + 5]);
}
#[test]
fn test_tile_restoration_edges() {
let (fi, mut fs, mut fb, frame_rate) = setup(64, 80);
let ti = TilingInfo::from_target_tiles(
fi.sb_size_log2(),
fi.width,
fi.height,
frame_rate,
2,
2,
false,
);
let iter = ti.tile_iter_mut(&mut fs, &mut fb);
let mut tile_states = iter.map(|ctx| ctx.ts).collect::<Vec<_>>();
assert_eq!(tile_states.len(), 2);
{
let trs = &mut tile_states[0].restoration;
let units = &trs.planes[0].units;
assert_eq!(units.x(), 0);
assert_eq!(units.y(), 0);
assert_eq!(units.cols(), 1);
assert_eq!(units.rows(), 1);
}
{
let trs = &mut tile_states[1].restoration;
let units = &trs.planes[0].units;
assert_eq!(units.x(), 0);
assert_eq!(units.y(), 1);
assert_eq!(units.cols() * units.rows(), 0);
}
}
#[test]
fn test_tile_restoration_write() {
let (fi, mut fs, mut fb, frame_rate) = setup(256, 256);
{
let ti = TilingInfo::from_target_tiles(
fi.sb_size_log2(),
fi.width,
fi.height,
frame_rate,
1,
1,
false,
);
let iter = ti.tile_iter_mut(&mut fs, &mut fb);
let mut tile_states = iter.map(|ctx| ctx.ts).collect::<Vec<_>>();
{
let units = &mut tile_states[0].restoration.planes[0].units;
units[0][1].filter =
RestorationFilter::Wiener { coeffs: [[1, 2, 3], [4, 5, 6]] };
}
{
let units = &mut tile_states[3].restoration.planes[1].units;
units[1][0].filter =
RestorationFilter::Sgrproj { set: 42, xqd: [10, 20] };
}
{
let units = &mut tile_states[2].restoration.planes[2].units;
units[1][1].filter =
RestorationFilter::Sgrproj { set: 5, xqd: [1, 2] };
}
}
let units = &mut fs.restoration.planes[0].units;
assert_eq!(
units[0][1].filter,
RestorationFilter::Wiener { coeffs: [[1, 2, 3], [4, 5, 6]] }
);
let units = &mut fs.restoration.planes[1].units;
assert_eq!(
units[3][2].filter,
RestorationFilter::Sgrproj { set: 42, xqd: [10, 20] }
);
let units = &mut fs.restoration.planes[2].units;
assert_eq!(
units[3][1].filter,
RestorationFilter::Sgrproj { set: 5, xqd: [1, 2] }
);
}
#[test]
fn test_tile_motion_vectors_write() {
let (fi, mut fs, mut fb, frame_rate) = setup(160, 144);
{
let ti = TilingInfo::from_target_tiles(
fi.sb_size_log2(),
fi.width,
fi.height,
frame_rate,
2,
2,
false,
);
let iter = ti.tile_iter_mut(&mut fs, &mut fb);
let mut tile_states = iter.map(|ctx| ctx.ts).collect::<Vec<_>>();
{
let me_stats = &mut tile_states[0].me_stats[0];
me_stats[5][8].mv = MotionVector { col: 42, row: 38 };
println!("{:?}", me_stats[5][8].mv);
}
{
let me_stats = &mut tile_states[5].me_stats[2];
me_stats[2][3].mv = MotionVector { col: 2, row: 14 };
}
}
let me_stats = &fs.frame_me_stats.read().unwrap()[0];
assert_eq!(MotionVector { col: 42, row: 38 }, me_stats[5][8].mv);
let me_stats = &fs.frame_me_stats.read().unwrap()[2];
let mix = (128 >> MI_SIZE_LOG2) + 3;
let miy = (64 >> MI_SIZE_LOG2) + 2;
assert_eq!(MotionVector { col: 2, row: 14 }, me_stats[miy][mix].mv);
}
#[test]
fn test_tile_blocks_write() {
let (fi, mut fs, mut fb, frame_rate) = setup(160, 144);
{
let ti = TilingInfo::from_target_tiles(
fi.sb_size_log2(),
fi.width,
fi.height,
frame_rate,
2,
2,
false,
);
let iter = ti.tile_iter_mut(&mut fs, &mut fb);
let mut tbs = iter.map(|ctx| ctx.tb).collect::<Vec<_>>();
{
let tb = &mut tbs[0];
tb[3][4].n4_w = 42;
tb[5][8].segmentation_idx = 14;
}
{
let tb = &mut tbs[5];
tb[1][0].n4_h = 11;
tb[5][7].cdef_index = 3;
}
{
let tb = &mut tbs[7];
tb[2][3].mode = PredictionMode::PAETH_PRED;
tb[1][1].n4_w = 8;
}
}
assert_eq!(42, fb[3][4].n4_w);
assert_eq!(14, fb[5][8].segmentation_idx);
assert_eq!(11, fb[17][32].n4_h);
assert_eq!(3, fb[21][39].cdef_index);
assert_eq!(PredictionMode::PAETH_PRED, fb[34][19].mode);
assert_eq!(8, fb[33][17].n4_w);
}
#[test]
fn tile_log2_overflow() {
assert_eq!(TilingInfo::tile_log2(1, usize::MAX), None);
}
#[test]
fn from_target_tiles_422() {
let sb_size_log2 = 6;
let is_422_p = true;
let frame_rate = 60.;
let sb_size = 1 << sb_size_log2;
for frame_height in (sb_size..4352).step_by(sb_size) {
for tile_rows_log2 in
0..=TilingInfo::tile_log2(1, frame_height >> sb_size_log2).unwrap()
{
for frame_width in (sb_size..7680).step_by(sb_size) {
for tile_cols_log2 in
0..=TilingInfo::tile_log2(1, frame_width >> sb_size_log2).unwrap()
{
let ti = TilingInfo::from_target_tiles(
sb_size_log2,
frame_width,
frame_height,
frame_rate,
tile_cols_log2,
tile_rows_log2,
is_422_p,
);
assert_eq!(
ti.tile_cols_log2,
TilingInfo::tile_log2(1, ti.cols).unwrap()
);
assert_eq!(
ti.tile_rows_log2,
TilingInfo::tile_log2(1, ti.rows).unwrap()
);
}
}
}
}
}
}