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