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 crate::context::*; |
11 | use crate::encoder::FrameInvariants; |
12 | use crate::lrf::*; |
13 | use crate::util::Pixel; |
14 | |
15 | use std::marker::PhantomData; |
16 | use std::ops::{Index, IndexMut}; |
17 | use std::ptr; |
18 | use std::slice; |
19 | |
20 | /// Tiled view of `RestorationUnits` |
21 | #[derive (Debug)] |
22 | pub struct TileRestorationUnits<'a> { |
23 | data: *const RestorationUnit, |
24 | // private to guarantee borrowing rules |
25 | x: usize, |
26 | y: usize, |
27 | cols: usize, |
28 | rows: usize, |
29 | /// number of cols in the underlying `FrameRestorationUnits` |
30 | stride: usize, |
31 | phantom: PhantomData<&'a RestorationUnit>, |
32 | } |
33 | |
34 | /// Mutable tiled view of `RestorationUnits` |
35 | #[derive (Debug)] |
36 | pub struct TileRestorationUnitsMut<'a> { |
37 | data: *mut RestorationUnit, |
38 | // private to guarantee borrowing rules |
39 | x: usize, |
40 | y: usize, |
41 | cols: usize, |
42 | rows: usize, |
43 | /// number of cols in the underlying `FrameRestorationUnits` |
44 | stride: usize, |
45 | phantom: PhantomData<&'a mut RestorationUnit>, |
46 | } |
47 | |
48 | // common impl for TileRestorationUnits and TileRestorationUnitsMut |
49 | macro_rules! tile_restoration_units_common { |
50 | // $name: TileRestorationUnits or TileRestorationUnitsMut |
51 | // $null: null or null_mut |
52 | // $opt_mut: nothing or mut |
53 | ($name:ident, $null:ident $(,$opt_mut:tt)?) => { |
54 | impl<'a> $name<'a> { |
55 | |
56 | #[inline(always)] |
57 | pub fn new( |
58 | frame_units: &'a $($opt_mut)? FrameRestorationUnits, |
59 | x: usize, |
60 | y: usize, |
61 | cols: usize, |
62 | rows: usize, |
63 | ) -> Self { |
64 | Self { |
65 | data: if x < frame_units.cols && y < frame_units.rows { |
66 | & $($opt_mut)? frame_units[y][x] |
67 | } else { |
68 | // on edges, a tile may contain no restoration units |
69 | ptr::$null() |
70 | }, |
71 | x, |
72 | y, |
73 | cols, |
74 | rows, |
75 | stride: frame_units.cols, |
76 | phantom: PhantomData, |
77 | } |
78 | } |
79 | |
80 | #[inline(always)] |
81 | pub const fn x(&self) -> usize { |
82 | self.x |
83 | } |
84 | |
85 | #[inline(always)] |
86 | pub const fn y(&self) -> usize { |
87 | self.y |
88 | } |
89 | |
90 | #[inline(always)] |
91 | pub const fn cols(&self) -> usize { |
92 | self.cols |
93 | } |
94 | |
95 | #[inline(always)] |
96 | pub const fn rows(&self) -> usize { |
97 | self.rows |
98 | } |
99 | } |
100 | |
101 | unsafe impl Send for $name<'_> {} |
102 | unsafe impl Sync for $name<'_> {} |
103 | |
104 | impl Index<usize> for $name<'_> { |
105 | type Output = [RestorationUnit]; |
106 | |
107 | #[inline(always)] |
108 | fn index(&self, index: usize) -> &Self::Output { |
109 | assert!(index < self.rows); |
110 | // SAFETY: The above assert ensures we do not access OOB data. |
111 | unsafe { |
112 | let ptr = self.data.add(index * self.stride); |
113 | slice::from_raw_parts(ptr, self.cols) |
114 | } |
115 | } |
116 | } |
117 | } |
118 | } |
119 | |
120 | tile_restoration_units_common!(TileRestorationUnits, null); |
121 | tile_restoration_units_common!(TileRestorationUnitsMut, null_mut, mut); |
122 | |
123 | impl TileRestorationUnitsMut<'_> { |
124 | #[inline (always)] |
125 | pub const fn as_const(&self) -> TileRestorationUnits<'_> { |
126 | TileRestorationUnits { |
127 | data: self.data, |
128 | x: self.x, |
129 | y: self.y, |
130 | cols: self.cols, |
131 | rows: self.rows, |
132 | stride: self.stride, |
133 | phantom: PhantomData, |
134 | } |
135 | } |
136 | } |
137 | |
138 | impl IndexMut<usize> for TileRestorationUnitsMut<'_> { |
139 | #[inline (always)] |
140 | fn index_mut(&mut self, index: usize) -> &mut Self::Output { |
141 | assert!(index < self.rows); |
142 | // SAFETY: The above assert ensures we do not access OOB data. |
143 | unsafe { |
144 | let ptr: *mut RestorationUnit = self.data.add(count:index * self.stride); |
145 | slice::from_raw_parts_mut(data:ptr, self.cols) |
146 | } |
147 | } |
148 | } |
149 | |
150 | /// Tiled view of `RestorationPlane` |
151 | #[derive (Debug)] |
152 | pub struct TileRestorationPlane<'a> { |
153 | pub rp_cfg: &'a RestorationPlaneConfig, |
154 | pub wiener_ref: [[i8; 3]; 2], |
155 | pub sgrproj_ref: [i8; 2], |
156 | pub units: TileRestorationUnits<'a>, |
157 | } |
158 | |
159 | /// Mutable tiled view of `RestorationPlane` |
160 | #[derive (Debug)] |
161 | pub struct TileRestorationPlaneMut<'a> { |
162 | pub rp_cfg: &'a RestorationPlaneConfig, |
163 | pub wiener_ref: [[i8; 3]; 2], |
164 | pub sgrproj_ref: [i8; 2], |
165 | pub units: TileRestorationUnitsMut<'a>, |
166 | } |
167 | |
168 | // common impl for TileRestorationPlane and TileRestorationPlaneMut |
169 | macro_rules! tile_restoration_plane_common { |
170 | // $name: TileRestorationPlane or TileRestorationPlaneMut |
171 | // $tru_type: TileRestorationUnits or TileRestorationUnitsMut |
172 | // $opt_mut: nothing or mut |
173 | ($name:ident, $tru_type:ident $(,$opt_mut:tt)?) => { |
174 | impl<'a> $name<'a> { |
175 | |
176 | #[inline(always)] |
177 | pub fn new( |
178 | rp: &'a $($opt_mut)? RestorationPlane, |
179 | units_x: usize, |
180 | units_y: usize, |
181 | units_cols: usize, |
182 | units_rows: usize, |
183 | ) -> Self { |
184 | Self { |
185 | rp_cfg: &rp.cfg, |
186 | wiener_ref: [WIENER_TAPS_MID; 2], |
187 | sgrproj_ref: SGRPROJ_XQD_MID, |
188 | units: $tru_type::new(& $($opt_mut)? rp.units, units_x, units_y, units_cols, units_rows), |
189 | } |
190 | } |
191 | |
192 | // determines the loop restoration unit row and column a |
193 | // superblock belongs to. The stretch boolean indicates if a |
194 | // superblock that belongs to a stretched LRU should return an |
195 | // index (stretch == true) or None (stretch == false). |
196 | pub const fn restoration_unit_index(&self, sbo: TileSuperBlockOffset, stretch: bool) |
197 | -> Option<(usize, usize)> { |
198 | if self.units.rows > 0 && self.units.cols > 0 { |
199 | // is this a stretch block? |
200 | let x_stretch = sbo.0.x < self.rp_cfg.sb_cols && |
201 | sbo.0.x >> self.rp_cfg.sb_h_shift >= self.units.cols; |
202 | let y_stretch = sbo.0.y < self.rp_cfg.sb_rows && |
203 | sbo.0.y >> self.rp_cfg.sb_v_shift >= self.units.rows; |
204 | if (x_stretch || y_stretch) && !stretch { |
205 | None |
206 | } else { |
207 | let x = (sbo.0.x >> self.rp_cfg.sb_h_shift) - if x_stretch { 1 } else { 0 }; |
208 | let y = (sbo.0.y >> self.rp_cfg.sb_v_shift) - if y_stretch { 1 } else { 0 }; |
209 | if x < self.units.cols && y < self.units.rows { |
210 | Some((x, y)) |
211 | } else { |
212 | None |
213 | } |
214 | } |
215 | } else { |
216 | None |
217 | } |
218 | } |
219 | |
220 | pub fn restoration_unit_offset(&self, base: TileSuperBlockOffset, |
221 | offset: TileSuperBlockOffset, stretch: bool) |
222 | -> Option<(usize, usize)> { |
223 | let base_option = self.restoration_unit_index(base, stretch); |
224 | let delta_option = self.restoration_unit_index(base + offset, stretch); |
225 | if let (Some((base_x, base_y)), Some((delta_x, delta_y))) = |
226 | (base_option, delta_option) |
227 | { |
228 | Some ((delta_x - base_x, delta_y - base_y)) |
229 | } else { |
230 | None |
231 | } |
232 | } |
233 | |
234 | pub const fn restoration_unit_countable(&self, x: usize, y: usize) -> usize { |
235 | y * self.units.cols + x |
236 | } |
237 | |
238 | // Is this the last sb (in scan order) in the restoration unit |
239 | // that we will be considering for RDO? This would be a |
240 | // straightforward calculation but for stretch; if the LRU |
241 | // stretches into a different tile, we don't consider those SBs |
242 | // in the other tile to be part of the LRU for RDO purposes. |
243 | pub fn restoration_unit_last_sb_for_rdo<T: Pixel>( |
244 | &self, |
245 | fi: &FrameInvariants<T>, |
246 | global_sbo: PlaneSuperBlockOffset, |
247 | tile_sbo: TileSuperBlockOffset, |
248 | ) -> bool { |
249 | // there is 1 restoration unit for (1 << sb_shift) super-blocks |
250 | let h_mask = (1 << self.rp_cfg.sb_h_shift) - 1; |
251 | let v_mask = (1 << self.rp_cfg.sb_v_shift) - 1; |
252 | // is this a stretch block? |
253 | let x_stretch = tile_sbo.0.x >> self.rp_cfg.sb_h_shift >= self.units.cols; |
254 | let y_stretch = tile_sbo.0.y >> self.rp_cfg.sb_v_shift >= self.units.rows; |
255 | // Need absolute superblock offsets for edge check, not local to the tile. |
256 | let sbx = global_sbo.0.x + tile_sbo.0.x; |
257 | let sby = global_sbo.0.y + tile_sbo.0.y; |
258 | // edge-of-tile check + edge-of-frame check |
259 | let last_x = (tile_sbo.0.x & h_mask == h_mask && !x_stretch) || sbx == fi.sb_width-1; |
260 | let last_y = (tile_sbo.0.y & v_mask == v_mask && !y_stretch) || sby == fi.sb_height-1; |
261 | last_x && last_y |
262 | } |
263 | |
264 | #[inline(always)] |
265 | pub fn restoration_unit(&self, sbo: TileSuperBlockOffset, stretch: bool) |
266 | -> Option<&RestorationUnit> { |
267 | self.restoration_unit_index(sbo, stretch).map(|(x, y)| &self.units[y][x]) |
268 | } |
269 | } |
270 | } |
271 | } |
272 | |
273 | tile_restoration_plane_common!(TileRestorationPlane, TileRestorationUnits); |
274 | tile_restoration_plane_common!( |
275 | TileRestorationPlaneMut, |
276 | TileRestorationUnitsMut, |
277 | mut |
278 | ); |
279 | |
280 | impl<'a> TileRestorationPlaneMut<'a> { |
281 | #[inline (always)] |
282 | pub fn restoration_unit_mut( |
283 | &mut self, sbo: TileSuperBlockOffset, |
284 | ) -> Option<&mut RestorationUnit> { |
285 | // cannot use map() due to lifetime constraints |
286 | if let Some((x: usize, y: usize)) = self.restoration_unit_index(sbo, stretch:true) { |
287 | Some(&mut self.units[y][x]) |
288 | } else { |
289 | None |
290 | } |
291 | } |
292 | |
293 | #[inline (always)] |
294 | pub const fn as_const(&self) -> TileRestorationPlane<'_> { |
295 | TileRestorationPlane { |
296 | rp_cfg: self.rp_cfg, |
297 | wiener_ref: self.wiener_ref, |
298 | sgrproj_ref: self.sgrproj_ref, |
299 | units: self.units.as_const(), |
300 | } |
301 | } |
302 | } |
303 | |
304 | /// Tiled view of `RestorationState` |
305 | #[derive (Debug)] |
306 | pub struct TileRestorationState<'a> { |
307 | pub planes: [TileRestorationPlane<'a>; MAX_PLANES], |
308 | } |
309 | |
310 | /// Mutable tiled view of `RestorationState` |
311 | #[derive (Debug)] |
312 | pub struct TileRestorationStateMut<'a> { |
313 | pub planes: [TileRestorationPlaneMut<'a>; MAX_PLANES], |
314 | } |
315 | |
316 | // common impl for TileRestorationState and TileRestorationStateMut |
317 | macro_rules! tile_restoration_state_common { |
318 | // $name: TileRestorationState or TileRestorationStateMut |
319 | // $trp_type: TileRestorationPlane or TileRestorationPlaneMut |
320 | // $iter: iter or iter_mut |
321 | // $opt_mut: nothing or mut |
322 | ($name:ident, $trp_type:ident, $iter:ident $(,$opt_mut:tt)?) => { |
323 | impl<'a> $name<'a> { |
324 | |
325 | #[inline(always)] |
326 | pub fn new( |
327 | rs: &'a $($opt_mut)? RestorationState, |
328 | sbo: PlaneSuperBlockOffset, |
329 | sb_width: usize, |
330 | sb_height: usize, |
331 | ) -> Self { |
332 | let (units_x0, units_y0, units_cols0, units_rows0) = |
333 | Self::get_units_region(rs, sbo, sb_width, sb_height, 0); |
334 | let (units_x1, units_y1, units_cols1, units_rows1) = |
335 | Self::get_units_region(rs, sbo, sb_width, sb_height, 1); |
336 | let (units_x2, units_y2, units_cols2, units_rows2) = |
337 | Self::get_units_region(rs, sbo, sb_width, sb_height, 2); |
338 | // we cannot retrieve &mut of slice items directly and safely |
339 | let mut planes_iter = rs.planes.$iter(); |
340 | Self { |
341 | planes: [ |
342 | { |
343 | let plane = planes_iter.next().unwrap(); |
344 | $trp_type::new(plane, units_x0, units_y0, units_cols0, units_rows0) |
345 | }, |
346 | { |
347 | let plane = planes_iter.next().unwrap(); |
348 | $trp_type::new(plane, units_x1, units_y1, units_cols1, units_rows1) |
349 | }, |
350 | { |
351 | let plane = planes_iter.next().unwrap(); |
352 | $trp_type::new(plane, units_x2, units_y2, units_cols2, units_rows2) |
353 | }, |
354 | ], |
355 | } |
356 | } |
357 | |
358 | #[inline(always)] |
359 | fn get_units_region( |
360 | rs: &RestorationState, |
361 | sbo: PlaneSuperBlockOffset, |
362 | sb_width: usize, |
363 | sb_height: usize, |
364 | pli: usize, |
365 | ) -> (usize, usize, usize, usize) { |
366 | let sb_h_shift = rs.planes[pli].cfg.sb_h_shift; |
367 | let sb_v_shift = rs.planes[pli].cfg.sb_v_shift; |
368 | // there may be several super-blocks per restoration unit |
369 | // the given super-block offset must match the start of a restoration unit |
370 | debug_assert!(sbo.0.x % (1 << sb_h_shift) == 0); |
371 | debug_assert!(sbo.0.y % (1 << sb_v_shift) == 0); |
372 | |
373 | let units_x = sbo.0.x >> sb_h_shift; |
374 | let units_y = sbo.0.y >> sb_v_shift; |
375 | let units_cols = sb_width + (1 << sb_h_shift) - 1 >> sb_h_shift; |
376 | let units_rows = sb_height + (1 << sb_v_shift) - 1 >> sb_v_shift; |
377 | |
378 | let FrameRestorationUnits { cols: rs_cols, rows: rs_rows, .. } = rs.planes[pli].units; |
379 | // +1 because the last super-block may use the "stretched" restoration unit |
380 | // from its neighbours |
381 | // <https://github.com/xiph/rav1e/issues/631#issuecomment-454419152> |
382 | debug_assert!(units_x < rs_cols + 1); |
383 | debug_assert!(units_y < rs_rows + 1); |
384 | debug_assert!(units_x + units_cols <= rs_cols + 1); |
385 | debug_assert!(units_y + units_rows <= rs_rows + 1); |
386 | |
387 | let units_x = units_x.min(rs_cols); |
388 | let units_y = units_y.min(rs_rows); |
389 | let units_cols = units_cols.min(rs_cols - units_x); |
390 | let units_rows = units_rows.min(rs_rows - units_y); |
391 | (units_x, units_y, units_cols, units_rows) |
392 | } |
393 | |
394 | #[inline(always)] |
395 | pub fn has_restoration_unit(&self, sbo: TileSuperBlockOffset, pli: usize, stretch: bool) |
396 | -> bool { |
397 | self.planes[pli].restoration_unit(sbo, stretch).is_some() |
398 | } |
399 | } |
400 | } |
401 | } |
402 | |
403 | tile_restoration_state_common!( |
404 | TileRestorationState, |
405 | TileRestorationPlane, |
406 | iter |
407 | ); |
408 | tile_restoration_state_common!( |
409 | TileRestorationStateMut, |
410 | TileRestorationPlaneMut, |
411 | iter_mut, |
412 | mut |
413 | ); |
414 | |
415 | impl<'a> TileRestorationStateMut<'a> { |
416 | #[inline (always)] |
417 | pub const fn as_const(&self) -> TileRestorationState { |
418 | TileRestorationState { |
419 | planes: [ |
420 | self.planes[0].as_const(), |
421 | self.planes[1].as_const(), |
422 | self.planes[2].as_const(), |
423 | ], |
424 | } |
425 | } |
426 | } |
427 | |