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