/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 | | } |