rav1e/tiling/
tiler.rs

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