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