1 // Copyright (c) 2019-2020, The rav1e contributors. All rights reserved 2 // 3 // This source code is subject to the terms of the BSD 2 Clause License and 4 // the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License 5 // was not distributed with this source code in the LICENSE file, you can 6 // obtain it at www.aomedia.org/license/software. If the Alliance for Open 7 // Media Patent License 1.0 was not distributed with this source code in the 8 // PATENTS file, you can obtain it at www.aomedia.org/license/patent. 9 10 use super::*; 11 12 use crate::context::*; 13 use crate::encoder::*; 14 use crate::util::*; 15 16 use std::iter::FusedIterator; 17 use std::marker::PhantomData; 18 19 pub const MAX_TILE_WIDTH: usize = 4096; 20 pub const MAX_TILE_AREA: usize = 4096 * 2304; 21 pub const MAX_TILE_COLS: usize = 64; 22 pub const MAX_TILE_ROWS: usize = 64; 23 pub const MAX_TILE_RATE: f64 = 4096f64 * 2176f64 * 60f64 * 1.1; 24 25 /// Tiling information 26 /// 27 /// This stores everything necessary to split a frame into tiles, and write 28 /// headers fields into the bitstream. 29 /// 30 /// The method tile_iter_mut() actually provides tiled views of FrameState 31 /// and FrameBlocks. 32 #[derive(Debug, Clone, Copy)] 33 pub struct TilingInfo { 34 pub frame_width: usize, 35 pub frame_height: usize, 36 pub tile_width_sb: usize, 37 pub tile_height_sb: usize, 38 pub cols: usize, // number of columns of tiles within the whole frame 39 pub rows: usize, // number of rows of tiles within the whole frame 40 pub tile_cols_log2: usize, 41 pub tile_rows_log2: usize, 42 pub min_tile_cols_log2: usize, 43 pub max_tile_cols_log2: usize, 44 pub min_tile_rows_log2: usize, 45 pub max_tile_rows_log2: usize, 46 pub sb_size_log2: usize, 47 pub min_tiles_log2: usize, 48 } 49 50 impl TilingInfo { 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, ) -> Self51 pub fn from_target_tiles( 52 sb_size_log2: usize, frame_width: usize, frame_height: usize, 53 frame_rate: f64, tile_cols_log2: usize, tile_rows_log2: usize, 54 is_422_p: bool, 55 ) -> Self { 56 // <https://aomediacodec.github.io/av1-spec/#tile-info-syntax> 57 58 // Frame::new() aligns to the next multiple of 8 59 let frame_width = frame_width.align_power_of_two(3); 60 let frame_height = frame_height.align_power_of_two(3); 61 let frame_width_sb = 62 frame_width.align_power_of_two_and_shift(sb_size_log2); 63 let frame_height_sb = 64 frame_height.align_power_of_two_and_shift(sb_size_log2); 65 let sb_cols = frame_width.align_power_of_two_and_shift(sb_size_log2); 66 let sb_rows = frame_height.align_power_of_two_and_shift(sb_size_log2); 67 68 // these are bitstream-defined values and must not be changed 69 let max_tile_width_sb = MAX_TILE_WIDTH >> sb_size_log2; 70 let max_tile_area_sb = MAX_TILE_AREA >> (2 * sb_size_log2); 71 let min_tile_cols_log2 = 72 Self::tile_log2(max_tile_width_sb, sb_cols).unwrap(); 73 let max_tile_cols_log2 = 74 Self::tile_log2(1, sb_cols.min(MAX_TILE_COLS)).unwrap(); 75 let max_tile_rows_log2 = 76 Self::tile_log2(1, sb_rows.min(MAX_TILE_ROWS)).unwrap(); 77 let min_tiles_log2 = min_tile_cols_log2 78 .max(Self::tile_log2(max_tile_area_sb, sb_cols * sb_rows).unwrap()); 79 80 // Implements restriction in Annex A of the spec. 81 // Unlike the other restrictions, this one does not change 82 // the header coding of the tile rows/cols. 83 let min_tiles_ratelimit_log2 = min_tiles_log2.max( 84 ((frame_width * frame_height) as f64 * frame_rate / MAX_TILE_RATE) 85 .ceil() 86 .log2() 87 .ceil() as usize, 88 ); 89 90 let tile_cols_log2 = 91 tile_cols_log2.max(min_tile_cols_log2).min(max_tile_cols_log2); 92 let tile_width_sb_pre = 93 sb_cols.align_power_of_two_and_shift(tile_cols_log2); 94 95 // If this is 4:2:2, our UV horizontal is subsampled but not our 96 // vertical. Loop Restoration Units must be square, so they 97 // will always have an even number of horizontal superblocks. For 98 // tiles and LRUs to align, tile_width_sb must be even in 4:2:2 99 // video. 100 101 // This is only relevant when doing loop restoration RDO inline 102 // with block/superblock encoding, that is, where tiles are 103 // relevant. If (when) we introduce optionally delaying loop-filter 104 // encode to after the partitioning loop, we won't need to make 105 // any 4:2:2 adjustment. 106 107 let tile_width_sb = if is_422_p { 108 (tile_width_sb_pre + 1) >> 1 << 1 109 } else { 110 tile_width_sb_pre 111 }; 112 113 let cols = (frame_width_sb + tile_width_sb - 1) / tile_width_sb; 114 115 // Adjust tile_cols_log2 in case of rounding tile_width_sb to even. 116 let tile_cols_log2 = Self::tile_log2(1, cols).unwrap(); 117 assert!(tile_cols_log2 >= min_tile_cols_log2); 118 119 let min_tile_rows_log2 = if min_tiles_log2 > tile_cols_log2 { 120 min_tiles_log2 - tile_cols_log2 121 } else { 122 0 123 }; 124 let min_tile_rows_ratelimit_log2 = 125 if min_tiles_ratelimit_log2 > tile_cols_log2 { 126 min_tiles_ratelimit_log2 - tile_cols_log2 127 } else { 128 0 129 }; 130 let tile_rows_log2 = tile_rows_log2 131 .max(min_tile_rows_log2) 132 .max(min_tile_rows_ratelimit_log2) 133 .min(max_tile_rows_log2); 134 let tile_height_sb = sb_rows.align_power_of_two_and_shift(tile_rows_log2); 135 136 let rows = (frame_height_sb + tile_height_sb - 1) / tile_height_sb; 137 138 Self { 139 frame_width, 140 frame_height, 141 tile_width_sb, 142 tile_height_sb, 143 cols, 144 rows, 145 tile_cols_log2, 146 tile_rows_log2, 147 min_tile_cols_log2, 148 max_tile_cols_log2, 149 min_tile_rows_log2, 150 max_tile_rows_log2, 151 sb_size_log2, 152 min_tiles_log2, 153 } 154 } 155 156 /// Return the smallest value for `k` such that `blkSize << k` is greater than 157 /// or equal to `target`. 158 /// 159 /// <https://aomediacodec.github.io/av1-spec/#tile-size-calculation-function> tile_log2(blk_size: usize, target: usize) -> Option<usize>160 pub fn tile_log2(blk_size: usize, target: usize) -> Option<usize> { 161 let mut k = 0; 162 while (blk_size.checked_shl(k)?) < target { 163 k += 1; 164 } 165 Some(k as usize) 166 } 167 168 #[inline(always)] tile_count(&self) -> usize169 pub const fn tile_count(&self) -> usize { 170 self.cols * self.rows 171 } 172 173 /// Split frame-level structures into tiles 174 /// 175 /// Provide mutable tiled views of frame-level structures. tile_iter_mut<'a, 'b, T: Pixel>( &self, fs: &'a mut FrameState<T>, fb: &'b mut FrameBlocks, ) -> TileContextIterMut<'a, 'b, T>176 pub fn tile_iter_mut<'a, 'b, T: Pixel>( 177 &self, fs: &'a mut FrameState<T>, fb: &'b mut FrameBlocks, 178 ) -> TileContextIterMut<'a, 'b, T> { 179 TileContextIterMut { ti: *self, fs, fb, next: 0, phantom: PhantomData } 180 } 181 } 182 183 /// Container for all tiled views 184 pub struct TileContextMut<'a, 'b, T: Pixel> { 185 pub ts: TileStateMut<'a, T>, 186 pub tb: TileBlocksMut<'b>, 187 } 188 189 /// Iterator over tiled views 190 pub struct TileContextIterMut<'a, 'b, T: Pixel> { 191 ti: TilingInfo, 192 fs: *mut FrameState<T>, 193 fb: *mut FrameBlocks, 194 next: usize, 195 phantom: PhantomData<(&'a mut FrameState<T>, &'b mut FrameBlocks)>, 196 } 197 198 impl<'a, 'b, T: Pixel> Iterator for TileContextIterMut<'a, 'b, T> { 199 type Item = TileContextMut<'a, 'b, T>; 200 next(&mut self) -> Option<Self::Item>201 fn next(&mut self) -> Option<Self::Item> { 202 if self.next < self.ti.rows * self.ti.cols { 203 let tile_col = self.next % self.ti.cols; 204 let tile_row = self.next / self.ti.cols; 205 let ctx = TileContextMut { 206 ts: { 207 let fs = unsafe { &mut *self.fs }; 208 let sbo = PlaneSuperBlockOffset(SuperBlockOffset { 209 x: tile_col * self.ti.tile_width_sb, 210 y: tile_row * self.ti.tile_height_sb, 211 }); 212 let x = sbo.0.x << self.ti.sb_size_log2; 213 let y = sbo.0.y << self.ti.sb_size_log2; 214 let tile_width = self.ti.tile_width_sb << self.ti.sb_size_log2; 215 let tile_height = self.ti.tile_height_sb << self.ti.sb_size_log2; 216 let width = tile_width.min(self.ti.frame_width - x); 217 let height = tile_height.min(self.ti.frame_height - y); 218 TileStateMut::new(fs, sbo, self.ti.sb_size_log2, width, height) 219 }, 220 tb: { 221 let fb = unsafe { &mut *self.fb }; 222 let tile_width_mi = 223 self.ti.tile_width_sb << (self.ti.sb_size_log2 - MI_SIZE_LOG2); 224 let tile_height_mi = 225 self.ti.tile_height_sb << (self.ti.sb_size_log2 - MI_SIZE_LOG2); 226 let x = tile_col * tile_width_mi; 227 let y = tile_row * tile_height_mi; 228 let cols = tile_width_mi.min(fb.cols - x); 229 let rows = tile_height_mi.min(fb.rows - y); 230 TileBlocksMut::new(fb, x, y, cols, rows) 231 }, 232 }; 233 self.next += 1; 234 Some(ctx) 235 } else { 236 None 237 } 238 } 239 size_hint(&self) -> (usize, Option<usize>)240 fn size_hint(&self) -> (usize, Option<usize>) { 241 let remaining = self.ti.cols * self.ti.rows - self.next; 242 (remaining, Some(remaining)) 243 } 244 } 245 246 impl<T: Pixel> ExactSizeIterator for TileContextIterMut<'_, '_, T> {} 247 impl<T: Pixel> FusedIterator for TileContextIterMut<'_, '_, T> {} 248 249 #[cfg(test)] 250 pub mod test { 251 use super::*; 252 use crate::api::*; 253 use crate::lrf::*; 254 use crate::mc::MotionVector; 255 use crate::predict::PredictionMode; 256 use std::sync::Arc; 257 258 #[test] test_tiling_info_from_tile_count()259 fn test_tiling_info_from_tile_count() { 260 let sb_size_log2 = 6; 261 let (width, height) = (160, 144); 262 let frame_rate = 25f64; 263 264 let ti = TilingInfo::from_target_tiles( 265 sb_size_log2, 266 width, 267 height, 268 frame_rate, 269 0, 270 0, 271 false, 272 ); 273 assert_eq!(1, ti.cols); 274 assert_eq!(1, ti.rows); 275 assert_eq!(3, ti.tile_width_sb); 276 assert_eq!(3, ti.tile_height_sb); 277 278 let ti = TilingInfo::from_target_tiles( 279 sb_size_log2, 280 width, 281 height, 282 frame_rate, 283 1, 284 1, 285 false, 286 ); 287 assert_eq!(2, ti.cols); 288 assert_eq!(2, ti.rows); 289 assert_eq!(2, ti.tile_width_sb); 290 assert_eq!(2, ti.tile_height_sb); 291 292 let ti = TilingInfo::from_target_tiles( 293 sb_size_log2, 294 width, 295 height, 296 frame_rate, 297 2, 298 2, 299 false, 300 ); 301 assert_eq!(3, ti.cols); 302 assert_eq!(3, ti.rows); 303 assert_eq!(1, ti.tile_width_sb); 304 assert_eq!(1, ti.tile_height_sb); 305 306 // cannot split more than superblocks 307 let ti = TilingInfo::from_target_tiles( 308 sb_size_log2, 309 width, 310 height, 311 frame_rate, 312 10, 313 8, 314 false, 315 ); 316 assert_eq!(3, ti.cols); 317 assert_eq!(3, ti.rows); 318 assert_eq!(1, ti.tile_width_sb); 319 assert_eq!(1, ti.tile_height_sb); 320 321 let ti = TilingInfo::from_target_tiles( 322 sb_size_log2, 323 1024, 324 1024, 325 frame_rate, 326 0, 327 0, 328 false, 329 ); 330 assert_eq!(1, ti.cols); 331 assert_eq!(1, ti.rows); 332 assert_eq!(16, ti.tile_width_sb); 333 assert_eq!(16, ti.tile_height_sb); 334 } 335 setup( width: usize, height: usize, ) -> (FrameInvariants<u16>, FrameState<u16>, FrameBlocks, f64)336 fn setup( 337 width: usize, height: usize, 338 ) -> (FrameInvariants<u16>, FrameState<u16>, FrameBlocks, f64) { 339 // FrameInvariants aligns to the next multiple of 8, so using other values could make tests confusing 340 assert!(width & 7 == 0); 341 assert!(height & 7 == 0); 342 // We test only for 420 for now 343 let chroma_sampling = ChromaSampling::Cs420; 344 let config = Arc::new(EncoderConfig { 345 width, 346 height, 347 bit_depth: 8, 348 chroma_sampling, 349 ..Default::default() 350 }); 351 let mut sequence = Sequence::new(&config); 352 // These tests are all assuming SB-sized LRUs, so set that. 353 sequence.enable_large_lru = false; 354 let frame_rate = config.frame_rate(); 355 let fi = FrameInvariants::new(config, Arc::new(sequence)); 356 let fs = FrameState::new(&fi); 357 let fb = FrameBlocks::new(fi.w_in_b, fi.h_in_b); 358 359 (fi, fs, fb, frame_rate) 360 } 361 362 #[test] test_tile_iter_len()363 fn test_tile_iter_len() { 364 // frame size 160x144, 40x36 in 4x4-blocks 365 let (fi, mut fs, mut fb, frame_rate) = setup(160, 144); 366 367 { 368 // 2x2 tiles 369 let ti = TilingInfo::from_target_tiles( 370 fi.sb_size_log2(), 371 fi.width, 372 fi.height, 373 frame_rate, 374 1, 375 1, 376 false, 377 ); 378 let mut iter = ti.tile_iter_mut(&mut fs, &mut fb); 379 assert_eq!(4, iter.len()); 380 assert!(iter.next().is_some()); 381 assert_eq!(3, iter.len()); 382 assert!(iter.next().is_some()); 383 assert_eq!(2, iter.len()); 384 assert!(iter.next().is_some()); 385 assert_eq!(1, iter.len()); 386 assert!(iter.next().is_some()); 387 assert_eq!(0, iter.len()); 388 assert!(iter.next().is_none()); 389 } 390 391 { 392 // 4x4 tiles requested, will actually get 3x3 tiles 393 let ti = TilingInfo::from_target_tiles( 394 fi.sb_size_log2(), 395 fi.width, 396 fi.height, 397 frame_rate, 398 2, 399 2, 400 false, 401 ); 402 let mut iter = ti.tile_iter_mut(&mut fs, &mut fb); 403 assert_eq!(9, iter.len()); 404 assert!(iter.next().is_some()); 405 assert_eq!(8, iter.len()); 406 assert!(iter.next().is_some()); 407 assert_eq!(7, iter.len()); 408 assert!(iter.next().is_some()); 409 assert_eq!(6, iter.len()); 410 assert!(iter.next().is_some()); 411 assert_eq!(5, iter.len()); 412 assert!(iter.next().is_some()); 413 assert_eq!(4, iter.len()); 414 assert!(iter.next().is_some()); 415 assert_eq!(3, iter.len()); 416 assert!(iter.next().is_some()); 417 assert_eq!(2, iter.len()); 418 assert!(iter.next().is_some()); 419 assert_eq!(1, iter.len()); 420 assert!(iter.next().is_some()); 421 assert_eq!(0, iter.len()); 422 assert!(iter.next().is_none()); 423 } 424 } 425 426 #[inline] rect<T: Pixel>( region: &PlaneRegionMut<'_, T>, ) -> (isize, isize, usize, usize)427 fn rect<T: Pixel>( 428 region: &PlaneRegionMut<'_, T>, 429 ) -> (isize, isize, usize, usize) { 430 let &Rect { x, y, width, height } = region.rect(); 431 (x, y, width, height) 432 } 433 434 #[test] test_tile_area()435 fn test_tile_area() { 436 let (fi, mut fs, mut fb, frame_rate) = setup(160, 144); 437 438 // 4x4 tiles requested, will actually get 3x3 tiles 439 let ti = TilingInfo::from_target_tiles( 440 fi.sb_size_log2(), 441 fi.width, 442 fi.height, 443 frame_rate, 444 2, 445 2, 446 false, 447 ); 448 let iter = ti.tile_iter_mut(&mut fs, &mut fb); 449 let tile_states = iter.map(|ctx| ctx.ts).collect::<Vec<_>>(); 450 451 // the frame must be split into 9 tiles: 452 // 453 // luma (Y) chroma (U) chroma (V) 454 // 64x64 64x64 32x64 32x32 32x32 16x32 32x32 32x32 16x32 455 // 64x64 64x64 32x64 32x32 32x32 16x32 32x32 32x32 16x32 456 // 64x16 64x16 32x16 32x 8 32x 8 16x 8 32x 8 32x 8 16x 8 457 458 assert_eq!(9, tile_states.len()); 459 460 let tile = &tile_states[0].rec; // the top-left tile 461 assert_eq!((0, 0, 64, 64), rect(&tile.planes[0])); 462 assert_eq!((0, 0, 32, 32), rect(&tile.planes[1])); 463 assert_eq!((0, 0, 32, 32), rect(&tile.planes[2])); 464 465 let tile = &tile_states[1].rec; // the top-middle tile 466 assert_eq!((64, 0, 64, 64), rect(&tile.planes[0])); 467 assert_eq!((32, 0, 32, 32), rect(&tile.planes[1])); 468 assert_eq!((32, 0, 32, 32), rect(&tile.planes[2])); 469 470 let tile = &tile_states[2].rec; // the top-right tile 471 assert_eq!((128, 0, 64, 64), rect(&tile.planes[0])); 472 assert_eq!((64, 0, 32, 32), rect(&tile.planes[1])); 473 assert_eq!((64, 0, 32, 32), rect(&tile.planes[2])); 474 475 let tile = &tile_states[3].rec; // the middle-left tile 476 assert_eq!((0, 64, 64, 64), rect(&tile.planes[0])); 477 assert_eq!((0, 32, 32, 32), rect(&tile.planes[1])); 478 assert_eq!((0, 32, 32, 32), rect(&tile.planes[2])); 479 480 let tile = &tile_states[4].rec; // the center tile 481 assert_eq!((64, 64, 64, 64), rect(&tile.planes[0])); 482 assert_eq!((32, 32, 32, 32), rect(&tile.planes[1])); 483 assert_eq!((32, 32, 32, 32), rect(&tile.planes[2])); 484 485 let tile = &tile_states[5].rec; // the middle-right tile 486 assert_eq!((128, 64, 64, 64), rect(&tile.planes[0])); 487 assert_eq!((64, 32, 32, 32), rect(&tile.planes[1])); 488 assert_eq!((64, 32, 32, 32), rect(&tile.planes[2])); 489 490 let tile = &tile_states[6].rec; // the bottom-left tile 491 assert_eq!((0, 128, 64, 64), rect(&tile.planes[0])); 492 assert_eq!((0, 64, 32, 32), rect(&tile.planes[1])); 493 assert_eq!((0, 64, 32, 32), rect(&tile.planes[2])); 494 495 let tile = &tile_states[7].rec; // the bottom-middle tile 496 assert_eq!((64, 128, 64, 64), rect(&tile.planes[0])); 497 assert_eq!((32, 64, 32, 32), rect(&tile.planes[1])); 498 assert_eq!((32, 64, 32, 32), rect(&tile.planes[2])); 499 500 let tile = &tile_states[8].rec; // the bottom-right tile 501 assert_eq!((128, 128, 64, 64), rect(&tile.planes[0])); 502 assert_eq!((64, 64, 32, 32), rect(&tile.planes[1])); 503 assert_eq!((64, 64, 32, 32), rect(&tile.planes[2])); 504 } 505 506 #[inline] b_area(region: &TileBlocksMut<'_>) -> (usize, usize, usize, usize)507 fn b_area(region: &TileBlocksMut<'_>) -> (usize, usize, usize, usize) { 508 (region.x(), region.y(), region.cols(), region.rows()) 509 } 510 511 #[test] test_tile_blocks_area()512 fn test_tile_blocks_area() { 513 let (fi, mut fs, mut fb, frame_rate) = setup(160, 144); 514 515 // 4x4 tiles requested, will actually get 3x3 tiles 516 let ti = TilingInfo::from_target_tiles( 517 fi.sb_size_log2(), 518 fi.width, 519 fi.height, 520 frame_rate, 521 2, 522 2, 523 false, 524 ); 525 let iter = ti.tile_iter_mut(&mut fs, &mut fb); 526 let tbs = iter.map(|ctx| ctx.tb).collect::<Vec<_>>(); 527 528 // the FrameBlocks must be split into 9 TileBlocks: 529 // 530 // 16x16 16x16 8x16 531 // 16x16 16x16 8x16 532 // 16x 4 16x4 8x 4 533 534 assert_eq!(9, tbs.len()); 535 536 assert_eq!((0, 0, 16, 16), b_area(&tbs[0])); 537 assert_eq!((16, 0, 16, 16), b_area(&tbs[1])); 538 assert_eq!((32, 0, 8, 16), b_area(&tbs[2])); 539 540 assert_eq!((0, 16, 16, 16), b_area(&tbs[3])); 541 assert_eq!((16, 16, 16, 16), b_area(&tbs[4])); 542 assert_eq!((32, 16, 8, 16), b_area(&tbs[5])); 543 544 assert_eq!((0, 32, 16, 4), b_area(&tbs[6])); 545 assert_eq!((16, 32, 16, 4), b_area(&tbs[7])); 546 assert_eq!((32, 32, 8, 4), b_area(&tbs[8])); 547 } 548 549 #[test] test_tile_write()550 fn test_tile_write() { 551 let (fi, mut fs, mut fb, frame_rate) = setup(160, 144); 552 553 { 554 // 4x4 tiles requested, will actually get 3x3 tiles 555 let ti = TilingInfo::from_target_tiles( 556 fi.sb_size_log2(), 557 fi.width, 558 fi.height, 559 frame_rate, 560 2, 561 2, 562 false, 563 ); 564 let iter = ti.tile_iter_mut(&mut fs, &mut fb); 565 let mut tile_states = iter.map(|ctx| ctx.ts).collect::<Vec<_>>(); 566 567 { 568 // row 12 of Y-plane of the top-left tile 569 let tile_plane = &mut tile_states[0].rec.planes[0]; 570 let row = &mut tile_plane[12]; 571 assert_eq!(64, row.len()); 572 row[35..41].copy_from_slice(&[4, 42, 12, 18, 15, 31]); 573 } 574 575 { 576 // row 8 of U-plane of the middle-right tile 577 let tile_plane = &mut tile_states[5].rec.planes[1]; 578 let row = &mut tile_plane[8]; 579 assert_eq!(32, row.len()); 580 row[..4].copy_from_slice(&[14, 121, 1, 3]); 581 } 582 583 { 584 // row 1 of V-plane of the bottom-middle tile 585 let tile_plane = &mut tile_states[7].rec.planes[2]; 586 let row = &mut tile_plane[1]; 587 assert_eq!(32, row.len()); 588 row[11..16].copy_from_slice(&[6, 5, 2, 11, 8]); 589 } 590 } 591 592 // check that writes on tiles correctly affected the underlying frame 593 594 let plane = &fs.rec.planes[0]; 595 let y = plane.cfg.yorigin + 12; 596 let x = plane.cfg.xorigin + 35; 597 let idx = y * plane.cfg.stride + x; 598 assert_eq!(&[4, 42, 12, 18, 15, 31], &plane.data[idx..idx + 6]); 599 600 let plane = &fs.rec.planes[1]; 601 let offset = (64, 32); // middle-right tile, chroma plane 602 let y = plane.cfg.yorigin + offset.1 + 8; 603 let x = plane.cfg.xorigin + offset.0; 604 let idx = y * plane.cfg.stride + x; 605 assert_eq!(&[14, 121, 1, 3], &plane.data[idx..idx + 4]); 606 607 let plane = &fs.rec.planes[2]; 608 let offset = (32, 64); // bottom-middle tile, chroma plane 609 let y = plane.cfg.yorigin + offset.1 + 1; 610 let x = plane.cfg.xorigin + offset.0 + 11; 611 let idx = y * plane.cfg.stride + x; 612 assert_eq!(&[6, 5, 2, 11, 8], &plane.data[idx..idx + 5]); 613 } 614 615 #[test] test_tile_restoration_edges()616 fn test_tile_restoration_edges() { 617 let (fi, mut fs, mut fb, frame_rate) = setup(64, 80); 618 619 let ti = TilingInfo::from_target_tiles( 620 fi.sb_size_log2(), 621 fi.width, 622 fi.height, 623 frame_rate, 624 2, 625 2, 626 false, 627 ); 628 let iter = ti.tile_iter_mut(&mut fs, &mut fb); 629 let mut tile_states = iter.map(|ctx| ctx.ts).collect::<Vec<_>>(); 630 631 assert_eq!(tile_states.len(), 2); 632 633 { 634 let trs = &mut tile_states[0].restoration; 635 let units = &trs.planes[0].units; 636 assert_eq!(units.x(), 0); 637 assert_eq!(units.y(), 0); 638 assert_eq!(units.cols(), 1); 639 assert_eq!(units.rows(), 1); 640 } 641 642 { 643 let trs = &mut tile_states[1].restoration; 644 let units = &trs.planes[0].units; 645 assert_eq!(units.x(), 0); 646 assert_eq!(units.y(), 1); 647 // no units, the tile is too small (less than 1/2 super-block) 648 assert_eq!(units.cols() * units.rows(), 0); 649 } 650 } 651 652 #[test] test_tile_restoration_write()653 fn test_tile_restoration_write() { 654 let (fi, mut fs, mut fb, frame_rate) = setup(256, 256); 655 656 { 657 // 2x2 tiles, each one containing 2×2 restoration units (1 super-block per restoration unit) 658 let ti = TilingInfo::from_target_tiles( 659 fi.sb_size_log2(), 660 fi.width, 661 fi.height, 662 frame_rate, 663 1, 664 1, 665 false, 666 ); 667 let iter = ti.tile_iter_mut(&mut fs, &mut fb); 668 let mut tile_states = iter.map(|ctx| ctx.ts).collect::<Vec<_>>(); 669 670 { 671 // unit (1, 0) of Y-plane of the top-left tile 672 let units = &mut tile_states[0].restoration.planes[0].units; 673 units[0][1].filter = 674 RestorationFilter::Wiener { coeffs: [[1, 2, 3], [4, 5, 6]] }; 675 } 676 677 { 678 // unit (0, 1) of U-plane of the bottom-right tile 679 let units = &mut tile_states[3].restoration.planes[1].units; 680 units[1][0].filter = 681 RestorationFilter::Sgrproj { set: 42, xqd: [10, 20] }; 682 } 683 684 { 685 // unit (1, 1) of V-plane of the bottom-left tile 686 let units = &mut tile_states[2].restoration.planes[2].units; 687 units[1][1].filter = 688 RestorationFilter::Sgrproj { set: 5, xqd: [1, 2] }; 689 } 690 } 691 692 // check that writes on tiles correctly affected the underlying restoration units 693 694 let units = &mut fs.restoration.planes[0].units; 695 assert_eq!( 696 units[0][1].filter, 697 RestorationFilter::Wiener { coeffs: [[1, 2, 3], [4, 5, 6]] } 698 ); 699 700 let units = &mut fs.restoration.planes[1].units; 701 assert_eq!( 702 units[3][2].filter, 703 RestorationFilter::Sgrproj { set: 42, xqd: [10, 20] } 704 ); 705 706 let units = &mut fs.restoration.planes[2].units; 707 assert_eq!( 708 units[3][1].filter, 709 RestorationFilter::Sgrproj { set: 5, xqd: [1, 2] } 710 ); 711 } 712 713 #[test] test_tile_motion_vectors_write()714 fn test_tile_motion_vectors_write() { 715 let (fi, mut fs, mut fb, frame_rate) = setup(160, 144); 716 717 { 718 // 4x4 tiles requested, will actually get 3x3 tiles 719 let ti = TilingInfo::from_target_tiles( 720 fi.sb_size_log2(), 721 fi.width, 722 fi.height, 723 frame_rate, 724 2, 725 2, 726 false, 727 ); 728 let iter = ti.tile_iter_mut(&mut fs, &mut fb); 729 let mut tile_states = iter.map(|ctx| ctx.ts).collect::<Vec<_>>(); 730 731 { 732 // block (8, 5) of the top-left tile (of the first ref frame) 733 let me_stats = &mut tile_states[0].me_stats[0]; 734 me_stats[5][8].mv = MotionVector { col: 42, row: 38 }; 735 println!("{:?}", me_stats[5][8].mv); 736 } 737 738 { 739 // block (4, 2) of the middle-right tile (of ref frame 2) 740 let me_stats = &mut tile_states[5].me_stats[2]; 741 me_stats[2][3].mv = MotionVector { col: 2, row: 14 }; 742 } 743 } 744 745 // check that writes on tiled views affected the underlying motion vectors 746 747 let me_stats = &fs.frame_me_stats[0]; 748 assert_eq!(MotionVector { col: 42, row: 38 }, me_stats[5][8].mv); 749 750 let me_stats = &fs.frame_me_stats[2]; 751 let mix = (128 >> MI_SIZE_LOG2) + 3; 752 let miy = (64 >> MI_SIZE_LOG2) + 2; 753 assert_eq!(MotionVector { col: 2, row: 14 }, me_stats[miy][mix].mv); 754 } 755 756 #[test] test_tile_blocks_write()757 fn test_tile_blocks_write() { 758 let (fi, mut fs, mut fb, frame_rate) = setup(160, 144); 759 760 { 761 // 4x4 tiles requested, will actually get 3x3 tiles 762 let ti = TilingInfo::from_target_tiles( 763 fi.sb_size_log2(), 764 fi.width, 765 fi.height, 766 frame_rate, 767 2, 768 2, 769 false, 770 ); 771 let iter = ti.tile_iter_mut(&mut fs, &mut fb); 772 let mut tbs = iter.map(|ctx| ctx.tb).collect::<Vec<_>>(); 773 774 { 775 // top-left tile 776 let tb = &mut tbs[0]; 777 // block (4, 3) 778 tb[3][4].n4_w = 42; 779 // block (8, 5) 780 tb[5][8].segmentation_idx = 14; 781 } 782 783 { 784 // middle-right tile 785 let tb = &mut tbs[5]; 786 // block (0, 1) 787 tb[1][0].n4_h = 11; 788 // block (7, 5) 789 tb[5][7].cdef_index = 3; 790 } 791 792 { 793 // bottom-middle tile 794 let tb = &mut tbs[7]; 795 // block (3, 2) 796 tb[2][3].mode = PredictionMode::PAETH_PRED; 797 // block (1, 1) 798 tb[1][1].n4_w = 8; 799 } 800 } 801 802 // check that writes on tiles correctly affected the underlying blocks 803 804 assert_eq!(42, fb[3][4].n4_w); 805 assert_eq!(14, fb[5][8].segmentation_idx); 806 807 assert_eq!(11, fb[17][32].n4_h); 808 assert_eq!(3, fb[21][39].cdef_index); 809 810 assert_eq!(PredictionMode::PAETH_PRED, fb[34][19].mode); 811 assert_eq!(8, fb[33][17].n4_w); 812 } 813 814 #[test] tile_log2_overflow()815 fn tile_log2_overflow() { 816 assert_eq!(TilingInfo::tile_log2(1, usize::max_value()), None); 817 } 818 819 #[test] from_target_tiles_422()820 fn from_target_tiles_422() { 821 let sb_size_log2 = 6; 822 let is_422_p = true; 823 let frame_rate = 60.; 824 let sb_size = 1 << sb_size_log2; 825 826 for frame_height in (sb_size..4352).step_by(sb_size) { 827 for tile_rows_log2 in 828 0..=TilingInfo::tile_log2(1, frame_height >> sb_size_log2).unwrap() 829 { 830 for frame_width in (sb_size..7680).step_by(sb_size) { 831 for tile_cols_log2 in 832 0..=TilingInfo::tile_log2(1, frame_width >> sb_size_log2).unwrap() 833 { 834 let ti = TilingInfo::from_target_tiles( 835 sb_size_log2, 836 frame_width, 837 frame_height, 838 frame_rate, 839 tile_cols_log2, 840 tile_rows_log2, 841 is_422_p, 842 ); 843 assert_eq!( 844 ti.tile_cols_log2, 845 TilingInfo::tile_log2(1, ti.cols).unwrap() 846 ); 847 assert_eq!( 848 ti.tile_rows_log2, 849 TilingInfo::tile_log2(1, ti.rows).unwrap() 850 ); 851 } 852 } 853 } 854 } 855 } 856 } 857