/rust/registry/src/index.crates.io-1949cf8c6b5b557f/av-scenechange-0.14.1/src/data/tile.rs
Line | Count | Source |
1 | | use std::iter::FusedIterator; |
2 | | |
3 | | use v_frame::{ |
4 | | frame::Frame, |
5 | | math::Fixed, |
6 | | pixel::Pixel, |
7 | | plane::{Plane, PlaneOffset}, |
8 | | }; |
9 | | |
10 | | use crate::data::{ |
11 | | block::BlockOffset, |
12 | | frame::{FrameState, MAX_PLANES}, |
13 | | motion::{FrameMEStats, TileMEStatsMut, WriteGuardMEStats}, |
14 | | plane::{PlaneBlockOffset, PlaneRegion, Rect}, |
15 | | superblock::{PlaneSuperBlockOffset, SuperBlockOffset, MI_SIZE, MI_SIZE_LOG2, SB_SIZE_LOG2}, |
16 | | }; |
17 | | |
18 | | pub const MAX_TILE_WIDTH: usize = 4096; |
19 | | pub const MAX_TILE_AREA: usize = 4096 * 2304; |
20 | | pub const MAX_TILE_COLS: usize = 64; |
21 | | pub const MAX_TILE_ROWS: usize = 64; |
22 | | pub const MAX_TILE_RATE: f64 = 4096f64 * 2176f64 * 60f64 * 1.1; |
23 | | |
24 | | /// Tiled view of a frame |
25 | | #[derive(Debug)] |
26 | | pub struct Tile<'a, T: Pixel> { |
27 | | pub planes: [PlaneRegion<'a, T>; MAX_PLANES], |
28 | | } |
29 | | |
30 | | // common impl for Tile and TileMut |
31 | | macro_rules! tile_common { |
32 | | // $name: Tile or TileMut |
33 | | // $pr_type: PlaneRegion or PlaneRegionMut |
34 | | // $iter: iter or iter_mut |
35 | | //opt_mut: nothing or mut |
36 | | ($name:ident, $pr_type:ident, $iter:ident $(,$opt_mut:tt)?) => { |
37 | | impl<'a, T: Pixel> $name<'a, T> { |
38 | | |
39 | 0 | pub fn new( |
40 | 0 | frame: &'a $($opt_mut)? Frame<T>, |
41 | 0 | luma_rect: TileRect, |
42 | 0 | ) -> Self { |
43 | 0 | let mut planes_iter = frame.planes.$iter(); |
44 | 0 | Self { |
45 | 0 | planes: [ |
46 | 0 | { |
47 | 0 | let plane = planes_iter.next().unwrap(); |
48 | 0 | $pr_type::new(plane, luma_rect.into()) |
49 | 0 | }, |
50 | 0 | { |
51 | 0 | let plane = planes_iter.next().unwrap(); |
52 | 0 | let rect = luma_rect.decimated(plane.cfg.xdec, plane.cfg.ydec); |
53 | 0 | $pr_type::new(plane, rect.into()) |
54 | 0 | }, |
55 | 0 | { |
56 | 0 | let plane = planes_iter.next().unwrap(); |
57 | 0 | let rect = luma_rect.decimated(plane.cfg.xdec, plane.cfg.ydec); |
58 | 0 | $pr_type::new(plane, rect.into()) |
59 | 0 | }, |
60 | 0 | ], |
61 | 0 | } |
62 | 0 | } Unexecuted instantiation: <av_scenechange::data::tile::Tile<u16>>::new Unexecuted instantiation: <av_scenechange::data::tile::Tile<u8>>::new Unexecuted instantiation: <av_scenechange::data::tile::Tile<_>>::new |
63 | | } |
64 | | } |
65 | | } |
66 | | |
67 | | tile_common!(Tile, PlaneRegion, iter); |
68 | | |
69 | | /// Rectangle of a tile, in pixels |
70 | | /// |
71 | | /// This is similar to Rect, but with unsigned (x, y) for convenience. |
72 | | #[derive(Debug, Clone, Copy)] |
73 | | pub struct TileRect { |
74 | | pub x: usize, |
75 | | pub y: usize, |
76 | | pub width: usize, |
77 | | pub height: usize, |
78 | | } |
79 | | |
80 | | impl TileRect { |
81 | 0 | pub const fn decimated(self, xdec: usize, ydec: usize) -> Self { |
82 | 0 | Self { |
83 | 0 | x: self.x >> xdec, |
84 | 0 | y: self.y >> ydec, |
85 | 0 | width: self.width >> xdec, |
86 | 0 | height: self.height >> ydec, |
87 | 0 | } |
88 | 0 | } |
89 | | |
90 | 0 | pub const fn to_frame_plane_offset(self, tile_po: PlaneOffset) -> PlaneOffset { |
91 | 0 | PlaneOffset { |
92 | 0 | x: self.x as isize + tile_po.x, |
93 | 0 | y: self.y as isize + tile_po.y, |
94 | 0 | } |
95 | 0 | } |
96 | | } |
97 | | |
98 | | impl From<TileRect> for Rect { |
99 | 0 | fn from(tile_rect: TileRect) -> Rect { |
100 | 0 | Rect { |
101 | 0 | x: tile_rect.x as isize, |
102 | 0 | y: tile_rect.y as isize, |
103 | 0 | width: tile_rect.width, |
104 | 0 | height: tile_rect.height, |
105 | 0 | } |
106 | 0 | } |
107 | | } |
108 | | |
109 | | /// Tiled view of `FrameState` |
110 | | /// |
111 | | /// Contrary to `PlaneRegionMut` and `TileMut`, there is no const version: |
112 | | /// - in practice, we don't need it; |
113 | | /// - it would require to instantiate a const version of every of its inner |
114 | | /// tiled views recursively. |
115 | | /// |
116 | | /// # `TileState` fields |
117 | | /// |
118 | | /// The way the `FrameState` fields are mapped depend on how they are accessed |
119 | | /// tile-wise and frame-wise. |
120 | | /// |
121 | | /// Some fields (like `qc`) are only used during tile-encoding, so they are only |
122 | | /// stored in `TileState`. |
123 | | /// |
124 | | /// Some other fields (like `input` or `segmentation`) are not written |
125 | | /// tile-wise, so they just reference the matching field in `FrameState`. |
126 | | /// |
127 | | /// Some others (like `rec`) are written tile-wise, but must be accessible |
128 | | /// frame-wise once the tile views vanish (e.g. for deblocking). |
129 | | #[derive(Debug)] |
130 | | pub struct TileStateMut<'a, T: Pixel> { |
131 | | pub sbo: PlaneSuperBlockOffset, |
132 | | pub sb_width: usize, |
133 | | pub sb_height: usize, |
134 | | pub mi_width: usize, |
135 | | pub mi_height: usize, |
136 | | pub width: usize, |
137 | | pub height: usize, |
138 | | pub input_tile: Tile<'a, T>, // the current tile |
139 | | pub input_hres: &'a Plane<T>, |
140 | | pub input_qres: &'a Plane<T>, |
141 | | pub me_stats: Vec<TileMEStatsMut<'a>>, |
142 | | } |
143 | | |
144 | | impl<'a, T: Pixel> TileStateMut<'a, T> { |
145 | 0 | pub fn new( |
146 | 0 | fs: &'a mut FrameState<T>, |
147 | 0 | sbo: PlaneSuperBlockOffset, |
148 | 0 | width: usize, |
149 | 0 | height: usize, |
150 | 0 | frame_me_stats: &'a mut [FrameMEStats], |
151 | 0 | ) -> Self { |
152 | 0 | debug_assert!( |
153 | 0 | width % MI_SIZE == 0, |
154 | 0 | "Tile width must be a multiple of MI_SIZE" |
155 | | ); |
156 | 0 | debug_assert!( |
157 | 0 | height % MI_SIZE == 0, |
158 | 0 | "Tile width must be a multiple of MI_SIZE" |
159 | | ); |
160 | | |
161 | 0 | let sb_rounded_width = width.align_power_of_two(SB_SIZE_LOG2); |
162 | 0 | let sb_rounded_height = height.align_power_of_two(SB_SIZE_LOG2); |
163 | | |
164 | 0 | let luma_rect = TileRect { |
165 | 0 | x: sbo.0.x << SB_SIZE_LOG2, |
166 | 0 | y: sbo.0.y << SB_SIZE_LOG2, |
167 | 0 | width: sb_rounded_width, |
168 | 0 | height: sb_rounded_height, |
169 | 0 | }; |
170 | 0 | let sb_width = width.align_power_of_two_and_shift(SB_SIZE_LOG2); |
171 | 0 | let sb_height = height.align_power_of_two_and_shift(SB_SIZE_LOG2); |
172 | | |
173 | | Self { |
174 | 0 | sbo, |
175 | 0 | sb_width, |
176 | 0 | sb_height, |
177 | 0 | mi_width: width >> MI_SIZE_LOG2, |
178 | 0 | mi_height: height >> MI_SIZE_LOG2, |
179 | 0 | width, |
180 | 0 | height, |
181 | 0 | input_tile: Tile::new(&fs.input, luma_rect), |
182 | 0 | input_hres: &fs.input_hres, |
183 | 0 | input_qres: &fs.input_qres, |
184 | 0 | me_stats: frame_me_stats |
185 | 0 | .iter_mut() |
186 | 0 | .map(|fmvs| { |
187 | 0 | TileMEStatsMut::new( |
188 | 0 | fmvs, |
189 | 0 | sbo.0.x << (SB_SIZE_LOG2 - MI_SIZE_LOG2), |
190 | 0 | sbo.0.y << (SB_SIZE_LOG2 - MI_SIZE_LOG2), |
191 | 0 | width >> MI_SIZE_LOG2, |
192 | 0 | height >> MI_SIZE_LOG2, |
193 | | ) |
194 | 0 | }) Unexecuted instantiation: <av_scenechange::data::tile::TileStateMut<u16>>::new::{closure#0}Unexecuted instantiation: <av_scenechange::data::tile::TileStateMut<u8>>::new::{closure#0}Unexecuted instantiation: <av_scenechange::data::tile::TileStateMut<_>>::new::{closure#0} |
195 | 0 | .collect(), |
196 | | } |
197 | 0 | } Unexecuted instantiation: <av_scenechange::data::tile::TileStateMut<u16>>::new Unexecuted instantiation: <av_scenechange::data::tile::TileStateMut<u8>>::new Unexecuted instantiation: <av_scenechange::data::tile::TileStateMut<_>>::new |
198 | | |
199 | 0 | pub fn to_frame_block_offset(&self, tile_bo: TileBlockOffset) -> PlaneBlockOffset { |
200 | 0 | let bx = self.sbo.0.x << (SB_SIZE_LOG2 - MI_SIZE_LOG2); |
201 | 0 | let by = self.sbo.0.y << (SB_SIZE_LOG2 - MI_SIZE_LOG2); |
202 | 0 | PlaneBlockOffset(BlockOffset { |
203 | 0 | x: bx + tile_bo.0.x, |
204 | 0 | y: by + tile_bo.0.y, |
205 | 0 | }) |
206 | 0 | } Unexecuted instantiation: <av_scenechange::data::tile::TileStateMut<u16>>::to_frame_block_offset Unexecuted instantiation: <av_scenechange::data::tile::TileStateMut<u8>>::to_frame_block_offset Unexecuted instantiation: <av_scenechange::data::tile::TileStateMut<_>>::to_frame_block_offset |
207 | | } |
208 | | |
209 | | /// Absolute offset in blocks inside a tile, where a block is defined |
210 | | /// to be an `N*N` square where `N == (1 << BLOCK_TO_PLANE_SHIFT)`. |
211 | | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] |
212 | | pub struct TileBlockOffset(pub BlockOffset); |
213 | | |
214 | | impl TileBlockOffset { |
215 | | /// Convert to plane offset without decimation. |
216 | 0 | pub const fn to_luma_plane_offset(self) -> PlaneOffset { |
217 | 0 | self.0.to_luma_plane_offset() |
218 | 0 | } |
219 | | |
220 | 0 | pub fn with_offset(self, col_offset: isize, row_offset: isize) -> TileBlockOffset { |
221 | 0 | Self(self.0.with_offset(col_offset, row_offset)) |
222 | 0 | } |
223 | | } |
224 | | |
225 | | /// Tiling information |
226 | | /// |
227 | | /// This stores everything necessary to split a frame into tiles, and write |
228 | | /// headers fields into the bitstream. |
229 | | /// |
230 | | /// The method `tile_iter_mut()` actually provides tiled views of `FrameState` |
231 | | /// and `FrameBlocks`. |
232 | | #[derive(Debug, Clone, Copy)] |
233 | | pub struct TilingInfo { |
234 | | pub frame_width: usize, |
235 | | pub frame_height: usize, |
236 | | pub tile_width_sb: usize, |
237 | | pub tile_height_sb: usize, |
238 | | pub cols: usize, // number of columns of tiles within the whole frame |
239 | | pub rows: usize, // number of rows of tiles within the whole frame |
240 | | } |
241 | | |
242 | | impl TilingInfo { |
243 | | /// # Panics |
244 | | /// |
245 | | /// Panics if the resulting tile sizes would be too large. |
246 | 0 | pub fn from_target_tiles( |
247 | 0 | frame_width: usize, |
248 | 0 | frame_height: usize, |
249 | 0 | frame_rate: f64, |
250 | 0 | tile_cols_log2: usize, |
251 | 0 | tile_rows_log2: usize, |
252 | 0 | is_422_p: bool, |
253 | 0 | ) -> Self { |
254 | | // <https://aomediacodec.github.io/av1-spec/#tile-info-syntax> |
255 | | |
256 | | // Frame::new() aligns to the next multiple of 8 |
257 | 0 | let frame_width = frame_width.align_power_of_two(3); |
258 | 0 | let frame_height = frame_height.align_power_of_two(3); |
259 | 0 | let frame_width_sb = frame_width.align_power_of_two_and_shift(SB_SIZE_LOG2); |
260 | 0 | let frame_height_sb = frame_height.align_power_of_two_and_shift(SB_SIZE_LOG2); |
261 | 0 | let sb_cols = frame_width.align_power_of_two_and_shift(SB_SIZE_LOG2); |
262 | 0 | let sb_rows = frame_height.align_power_of_two_and_shift(SB_SIZE_LOG2); |
263 | | |
264 | | // these are bitstream-defined values and must not be changed |
265 | 0 | let max_tile_width_sb = MAX_TILE_WIDTH >> SB_SIZE_LOG2; |
266 | 0 | let max_tile_area_sb = MAX_TILE_AREA >> (2 * SB_SIZE_LOG2); |
267 | 0 | let min_tile_cols_log2 = Self::tile_log2(max_tile_width_sb, sb_cols).unwrap(); |
268 | 0 | let max_tile_cols_log2 = Self::tile_log2(1, sb_cols.min(MAX_TILE_COLS)).unwrap(); |
269 | 0 | let max_tile_rows_log2 = Self::tile_log2(1, sb_rows.min(MAX_TILE_ROWS)).unwrap(); |
270 | 0 | let min_tiles_log2 = |
271 | 0 | min_tile_cols_log2.max(Self::tile_log2(max_tile_area_sb, sb_cols * sb_rows).unwrap()); |
272 | | |
273 | | // Implements restriction in Annex A of the spec. |
274 | | // Unlike the other restrictions, this one does not change |
275 | | // the header coding of the tile rows/cols. |
276 | 0 | let min_tiles_ratelimit_log2 = min_tiles_log2.max( |
277 | 0 | ((frame_width * frame_height) as f64 * frame_rate / MAX_TILE_RATE) |
278 | 0 | .ceil() |
279 | 0 | .log2() |
280 | 0 | .ceil() as usize, |
281 | | ); |
282 | | |
283 | 0 | let tile_cols_log2 = tile_cols_log2.clamp(min_tile_cols_log2, max_tile_cols_log2); |
284 | 0 | let tile_width_sb_pre = sb_cols.align_power_of_two_and_shift(tile_cols_log2); |
285 | | |
286 | | // If this is 4:2:2, our UV horizontal is subsampled but not our |
287 | | // vertical. Loop Restoration Units must be square, so they |
288 | | // will always have an even number of horizontal superblocks. For |
289 | | // tiles and LRUs to align, tile_width_sb must be even in 4:2:2 |
290 | | // video. |
291 | | |
292 | | // This is only relevant when doing loop restoration RDO inline |
293 | | // with block/superblock encoding, that is, where tiles are |
294 | | // relevant. If (when) we introduce optionally delaying loop-filter |
295 | | // encode to after the partitioning loop, we won't need to make |
296 | | // any 4:2:2 adjustment. |
297 | | |
298 | 0 | let tile_width_sb = if is_422_p { |
299 | 0 | (tile_width_sb_pre + 1) >> 1 << 1 |
300 | | } else { |
301 | 0 | tile_width_sb_pre |
302 | | }; |
303 | | |
304 | 0 | let cols = frame_width_sb.div_ceil(tile_width_sb); |
305 | | |
306 | | // Adjust tile_cols_log2 in case of rounding tile_width_sb to even. |
307 | 0 | let tile_cols_log2 = Self::tile_log2(1, cols).unwrap(); |
308 | 0 | assert!(tile_cols_log2 >= min_tile_cols_log2); |
309 | | |
310 | 0 | let min_tile_rows_log2 = min_tiles_log2.saturating_sub(tile_cols_log2); |
311 | 0 | let min_tile_rows_ratelimit_log2 = min_tiles_ratelimit_log2.saturating_sub(tile_cols_log2); |
312 | 0 | let tile_rows_log2 = tile_rows_log2 |
313 | 0 | .max(min_tile_rows_log2) |
314 | 0 | .clamp(min_tile_rows_ratelimit_log2, max_tile_rows_log2); |
315 | 0 | let tile_height_sb = sb_rows.align_power_of_two_and_shift(tile_rows_log2); |
316 | | |
317 | 0 | let rows = frame_height_sb.div_ceil(tile_height_sb); |
318 | | |
319 | 0 | Self { |
320 | 0 | frame_width, |
321 | 0 | frame_height, |
322 | 0 | tile_width_sb, |
323 | 0 | tile_height_sb, |
324 | 0 | cols, |
325 | 0 | rows, |
326 | 0 | } |
327 | 0 | } |
328 | | |
329 | | /// Return the smallest value for `k` such that `blkSize << k` is greater |
330 | | /// than or equal to `target`. |
331 | | /// |
332 | | /// <https://aomediacodec.github.io/av1-spec/#tile-size-calculation-function> |
333 | 0 | pub fn tile_log2(blk_size: usize, target: usize) -> Option<usize> { |
334 | 0 | let mut k = 0; |
335 | 0 | while (blk_size.checked_shl(k)?) < target { |
336 | 0 | k += 1; |
337 | 0 | } |
338 | 0 | Some(k as usize) |
339 | 0 | } |
340 | | |
341 | | /// Split frame-level structures into tiles |
342 | | /// |
343 | | /// Provide mutable tiled views of frame-level structures. |
344 | 0 | pub fn tile_iter_mut<'a, T: Pixel>( |
345 | 0 | &self, |
346 | 0 | fs: &'a mut FrameState<T>, |
347 | 0 | ) -> TileContextIterMut<'a, T> { |
348 | 0 | let afs = fs as *mut _; |
349 | 0 | let frame_me_stats = fs.frame_me_stats.write().expect("poisoned lock"); |
350 | 0 | TileContextIterMut { |
351 | 0 | ti: *self, |
352 | 0 | fs: afs, |
353 | 0 | next: 0, |
354 | 0 | frame_me_stats, |
355 | 0 | } |
356 | 0 | } Unexecuted instantiation: <av_scenechange::data::tile::TilingInfo>::tile_iter_mut::<u16> Unexecuted instantiation: <av_scenechange::data::tile::TilingInfo>::tile_iter_mut::<u8> Unexecuted instantiation: <av_scenechange::data::tile::TilingInfo>::tile_iter_mut::<_> |
357 | | } |
358 | | |
359 | | /// Iterator over tiled views |
360 | | pub struct TileContextIterMut<'a, T: Pixel> { |
361 | | ti: TilingInfo, |
362 | | fs: *mut FrameState<T>, |
363 | | frame_me_stats: WriteGuardMEStats<'a>, |
364 | | next: usize, |
365 | | } |
366 | | |
367 | | impl<'a, T: Pixel> Iterator for TileContextIterMut<'a, T> { |
368 | | type Item = TileContextMut<'a, T>; |
369 | | |
370 | 0 | fn next(&mut self) -> Option<Self::Item> { |
371 | 0 | if self.next < self.ti.rows * self.ti.cols { |
372 | 0 | let tile_col = self.next % self.ti.cols; |
373 | 0 | let tile_row = self.next / self.ti.cols; |
374 | 0 | let ctx = TileContextMut { |
375 | 0 | ts: { |
376 | 0 | // SAFETY: Multiple tiles mutably access this struct. |
377 | 0 | // The dimensions must be configured correctly to ensure |
378 | 0 | // the tiles do not overlap. |
379 | 0 | let fs = unsafe { &mut *self.fs }; |
380 | 0 | // SAFETY: ditto |
381 | 0 | let frame_me_stats = unsafe { |
382 | 0 | let len = self.frame_me_stats.len(); |
383 | 0 | let ptr = self.frame_me_stats.as_mut_ptr(); |
384 | 0 | std::slice::from_raw_parts_mut(ptr, len) |
385 | 0 | }; |
386 | 0 | let sbo = PlaneSuperBlockOffset(SuperBlockOffset { |
387 | 0 | x: tile_col * self.ti.tile_width_sb, |
388 | 0 | y: tile_row * self.ti.tile_height_sb, |
389 | 0 | }); |
390 | 0 | let x = sbo.0.x << SB_SIZE_LOG2; |
391 | 0 | let y = sbo.0.y << SB_SIZE_LOG2; |
392 | 0 | let tile_width = self.ti.tile_width_sb << SB_SIZE_LOG2; |
393 | 0 | let tile_height = self.ti.tile_height_sb << SB_SIZE_LOG2; |
394 | 0 | let width = tile_width.min(self.ti.frame_width - x); |
395 | 0 | let height = tile_height.min(self.ti.frame_height - y); |
396 | 0 | TileStateMut::new(fs, sbo, width, height, frame_me_stats) |
397 | 0 | }, |
398 | 0 | }; |
399 | 0 | self.next += 1; |
400 | 0 | Some(ctx) |
401 | | } else { |
402 | 0 | None |
403 | | } |
404 | 0 | } Unexecuted instantiation: <av_scenechange::data::tile::TileContextIterMut<u16> as core::iter::traits::iterator::Iterator>::next Unexecuted instantiation: <av_scenechange::data::tile::TileContextIterMut<u8> as core::iter::traits::iterator::Iterator>::next Unexecuted instantiation: <av_scenechange::data::tile::TileContextIterMut<_> as core::iter::traits::iterator::Iterator>::next |
405 | | |
406 | 0 | fn size_hint(&self) -> (usize, Option<usize>) { |
407 | 0 | let remaining = self.ti.cols * self.ti.rows - self.next; |
408 | 0 | (remaining, Some(remaining)) |
409 | 0 | } Unexecuted instantiation: <av_scenechange::data::tile::TileContextIterMut<u16> as core::iter::traits::iterator::Iterator>::size_hint Unexecuted instantiation: <av_scenechange::data::tile::TileContextIterMut<u8> as core::iter::traits::iterator::Iterator>::size_hint Unexecuted instantiation: <av_scenechange::data::tile::TileContextIterMut<_> as core::iter::traits::iterator::Iterator>::size_hint |
410 | | } |
411 | | |
412 | | impl<T: Pixel> ExactSizeIterator for TileContextIterMut<'_, T> { |
413 | | } |
414 | | impl<T: Pixel> FusedIterator for TileContextIterMut<'_, T> { |
415 | | } |
416 | | |
417 | | /// Container for all tiled views |
418 | | pub struct TileContextMut<'a, T: Pixel> { |
419 | | pub ts: TileStateMut<'a, T>, |
420 | | } |