Coverage Report

Created: 2025-07-18 06:49

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