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 | 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 + tile_width_sb - 1) / 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 = if min_tiles_log2 > tile_cols_log2 { |
125 | min_tiles_log2 - tile_cols_log2 |
126 | } else { |
127 | 0 |
128 | }; |
129 | let min_tile_rows_ratelimit_log2 = |
130 | if min_tiles_ratelimit_log2 > tile_cols_log2 { |
131 | min_tiles_ratelimit_log2 - tile_cols_log2 |
132 | } else { |
133 | 0 |
134 | }; |
135 | let tile_rows_log2 = tile_rows_log2 |
136 | .max(min_tile_rows_log2) |
137 | .clamp(min_tile_rows_ratelimit_log2, max_tile_rows_log2); |
138 | let tile_height_sb = sb_rows.align_power_of_two_and_shift(tile_rows_log2); |
139 | |
140 | let rows = (frame_height_sb + tile_height_sb - 1) / tile_height_sb; |
141 | |
142 | Self { |
143 | frame_width, |
144 | frame_height, |
145 | tile_width_sb, |
146 | tile_height_sb, |
147 | cols, |
148 | rows, |
149 | tile_cols_log2, |
150 | tile_rows_log2, |
151 | min_tile_cols_log2, |
152 | max_tile_cols_log2, |
153 | min_tile_rows_log2, |
154 | max_tile_rows_log2, |
155 | sb_size_log2, |
156 | min_tiles_log2, |
157 | } |
158 | } |
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 | pub fn tile_log2(blk_size: usize, target: usize) -> Option<usize> { |
165 | let mut k = 0; |
166 | while (blk_size.checked_shl(k)?) < target { |
167 | k += 1; |
168 | } |
169 | Some(k as usize) |
170 | } |
171 | |
172 | #[inline (always)] |
173 | pub const fn tile_count(&self) -> usize { |
174 | self.cols * self.rows |
175 | } |
176 | |
177 | /// Split frame-level structures into tiles |
178 | /// |
179 | /// Provide mutable tiled views of frame-level structures. |
180 | pub fn tile_iter_mut<'a, T: Pixel>( |
181 | &self, fs: &'a mut FrameState<T>, fb: &'a mut FrameBlocks, |
182 | ) -> TileContextIterMut<'a, T> { |
183 | let afs = fs as *mut _; |
184 | let afb = fb as *mut _; |
185 | let frame_me_stats = fs.frame_me_stats.write().expect("poisoned lock" ); |
186 | TileContextIterMut { ti: *self, fs: afs, fb: afb, next: 0, frame_me_stats } |
187 | } |
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 | fn next(&mut self) -> Option<Self::Item> { |
209 | if self.next < self.ti.rows * self.ti.cols { |
210 | let tile_col = self.next % self.ti.cols; |
211 | let tile_row = self.next / self.ti.cols; |
212 | let ctx = TileContextMut { |
213 | ts: { |
214 | // SAFETY: Multiple tiles mutably access this struct. |
215 | // The dimensions must be configured correctly to ensure |
216 | // the tiles do not overlap. |
217 | let fs = unsafe { &mut *self.fs }; |
218 | // SAFETY: ditto |
219 | let frame_me_stats = unsafe { |
220 | let len = self.frame_me_stats.len(); |
221 | let ptr = self.frame_me_stats.as_mut_ptr(); |
222 | std::slice::from_raw_parts_mut(ptr, len) |
223 | }; |
224 | let sbo = PlaneSuperBlockOffset(SuperBlockOffset { |
225 | x: tile_col * self.ti.tile_width_sb, |
226 | y: tile_row * self.ti.tile_height_sb, |
227 | }); |
228 | let x = sbo.0.x << self.ti.sb_size_log2; |
229 | let y = sbo.0.y << self.ti.sb_size_log2; |
230 | let tile_width = self.ti.tile_width_sb << self.ti.sb_size_log2; |
231 | let tile_height = self.ti.tile_height_sb << self.ti.sb_size_log2; |
232 | let width = tile_width.min(self.ti.frame_width - x); |
233 | let height = tile_height.min(self.ti.frame_height - y); |
234 | TileStateMut::new( |
235 | fs, |
236 | sbo, |
237 | self.ti.sb_size_log2, |
238 | width, |
239 | height, |
240 | frame_me_stats, |
241 | ) |
242 | }, |
243 | tb: { |
244 | // SAFETY: Multiple tiles mutably access this struct. |
245 | // The dimensions must be configured correctly to ensure |
246 | // the tiles do not overlap. |
247 | let fb = unsafe { &mut *self.fb }; |
248 | let tile_width_mi = |
249 | self.ti.tile_width_sb << (self.ti.sb_size_log2 - MI_SIZE_LOG2); |
250 | let tile_height_mi = |
251 | self.ti.tile_height_sb << (self.ti.sb_size_log2 - MI_SIZE_LOG2); |
252 | let x = tile_col * tile_width_mi; |
253 | let y = tile_row * tile_height_mi; |
254 | let cols = tile_width_mi.min(fb.cols - x); |
255 | let rows = tile_height_mi.min(fb.rows - y); |
256 | TileBlocksMut::new(fb, x, y, cols, rows) |
257 | }, |
258 | }; |
259 | self.next += 1; |
260 | Some(ctx) |
261 | } else { |
262 | None |
263 | } |
264 | } |
265 | |
266 | fn size_hint(&self) -> (usize, Option<usize>) { |
267 | let remaining = self.ti.cols * self.ti.rows - self.next; |
268 | (remaining, Some(remaining)) |
269 | } |
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 | } |
883 | |