/rust/registry/src/index.crates.io-1949cf8c6b5b557f/av-scenechange-0.14.1/src/analyze/intra.rs
Line | Count | Source |
1 | | #[cfg(asm_x86_64)] |
2 | | mod simd_x86; |
3 | | |
4 | | use std::mem::{transmute, MaybeUninit}; |
5 | | |
6 | | use aligned::{Aligned, A64}; |
7 | | #[cfg(not(asm_x86_64))] |
8 | | use rust::*; |
9 | | #[cfg(asm_x86_64)] |
10 | | use simd_x86::*; |
11 | | use v_frame::{ |
12 | | frame::Frame, |
13 | | pixel::Pixel, |
14 | | plane::{Plane, PlaneOffset}, |
15 | | }; |
16 | | |
17 | | use super::importance::IMPORTANCE_BLOCK_SIZE; |
18 | | use crate::{ |
19 | | cpu::CpuFeatureLevel, |
20 | | data::{ |
21 | | block::{BlockSize, TxSize, MAX_TX_SIZE}, |
22 | | plane::{Area, AsRegion, PlaneRegion, PlaneRegionMut, Rect}, |
23 | | prediction::PredictionVariant, |
24 | | satd::get_satd, |
25 | | slice_assume_init_mut, |
26 | | superblock::MI_SIZE_LOG2, |
27 | | tile::TileRect, |
28 | | }, |
29 | | }; |
30 | | |
31 | | pub const BLOCK_TO_PLANE_SHIFT: usize = MI_SIZE_LOG2; |
32 | | |
33 | | mod rust { |
34 | | use v_frame::pixel::Pixel; |
35 | | |
36 | | use super::IntraEdge; |
37 | | use crate::{ |
38 | | cpu::CpuFeatureLevel, |
39 | | data::{block::TxSize, plane::PlaneRegionMut, prediction::PredictionVariant}, |
40 | | }; |
41 | | |
42 | | #[cfg_attr( |
43 | | all(asm_x86_64, any(target_feature = "ssse3", target_feature = "avx2")), |
44 | | cold |
45 | | )] |
46 | 0 | pub(super) fn dispatch_predict_dc_intra<T: Pixel>( |
47 | 0 | variant: PredictionVariant, |
48 | 0 | dst: &mut PlaneRegionMut<'_, T>, |
49 | 0 | tx_size: TxSize, |
50 | 0 | bit_depth: usize, |
51 | 0 | edge_buf: &IntraEdge<T>, |
52 | 0 | _cpu: CpuFeatureLevel, |
53 | 0 | ) { |
54 | 0 | let width = tx_size.width(); |
55 | 0 | let height = tx_size.height(); |
56 | | |
57 | | // left pixels are ordered from bottom to top and right-aligned |
58 | 0 | let (left, _top_left, above) = edge_buf.as_slices(); |
59 | | |
60 | 0 | let above_slice = above; |
61 | 0 | let left_slice = &left[left.len().saturating_sub(height)..]; |
62 | | |
63 | 0 | (match variant { |
64 | 0 | PredictionVariant::NONE => pred_dc_128, |
65 | 0 | PredictionVariant::LEFT => pred_dc_left, |
66 | 0 | PredictionVariant::TOP => pred_dc_top, |
67 | 0 | PredictionVariant::BOTH => pred_dc, |
68 | 0 | })(dst, above_slice, left_slice, width, height, bit_depth) |
69 | 0 | } Unexecuted instantiation: av_scenechange::analyze::intra::rust::dispatch_predict_dc_intra::<u16> Unexecuted instantiation: av_scenechange::analyze::intra::rust::dispatch_predict_dc_intra::<u8> Unexecuted instantiation: av_scenechange::analyze::intra::rust::dispatch_predict_dc_intra::<_> |
70 | | |
71 | 0 | fn pred_dc<T: Pixel>( |
72 | 0 | output: &mut PlaneRegionMut<'_, T>, |
73 | 0 | above: &[T], |
74 | 0 | left: &[T], |
75 | 0 | width: usize, |
76 | 0 | height: usize, |
77 | 0 | _bit_depth: usize, |
78 | 0 | ) { |
79 | 0 | let edges = left[..height].iter().chain(above[..width].iter()); |
80 | 0 | let len = (width + height) as u32; |
81 | 0 | let avg = (edges.fold(0u32, |acc, &v| { |
82 | 0 | let v: u32 = v.into(); |
83 | 0 | v + acc |
84 | 0 | }) + (len >> 1)) Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc::<u16>::{closure#0}Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc::<u8>::{closure#0}Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc::<_>::{closure#0} |
85 | 0 | / len; |
86 | 0 | let avg = T::cast_from(avg); |
87 | | |
88 | 0 | for line in output.rows_iter_mut().take(height) { |
89 | 0 | line[..width].fill(avg); |
90 | 0 | } |
91 | 0 | } Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc::<u16> Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc::<u8> Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc::<_> |
92 | | |
93 | 0 | fn pred_dc_128<T: Pixel>( |
94 | 0 | output: &mut PlaneRegionMut<'_, T>, |
95 | 0 | _above: &[T], |
96 | 0 | _left: &[T], |
97 | 0 | width: usize, |
98 | 0 | height: usize, |
99 | 0 | bit_depth: usize, |
100 | 0 | ) { |
101 | 0 | let v = T::cast_from(128u32 << (bit_depth - 8)); |
102 | 0 | for line in output.rows_iter_mut().take(height) { |
103 | 0 | line[..width].fill(v); |
104 | 0 | } |
105 | 0 | } Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc_128::<u16> Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc_128::<u8> Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc_128::<_> |
106 | | |
107 | 0 | fn pred_dc_left<T: Pixel>( |
108 | 0 | output: &mut PlaneRegionMut<'_, T>, |
109 | 0 | _above: &[T], |
110 | 0 | left: &[T], |
111 | 0 | width: usize, |
112 | 0 | height: usize, |
113 | 0 | _bit_depth: usize, |
114 | 0 | ) { |
115 | 0 | let sum = left[..].iter().fold(0u32, |acc, &v| { |
116 | 0 | let v: u32 = v.into(); |
117 | 0 | v + acc |
118 | 0 | }); Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc_left::<u16>::{closure#0}Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc_left::<u8>::{closure#0}Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc_left::<_>::{closure#0} |
119 | 0 | let avg = T::cast_from((sum + (height >> 1) as u32) / height as u32); |
120 | 0 | for line in output.rows_iter_mut().take(height) { |
121 | 0 | line[..width].fill(avg); |
122 | 0 | } |
123 | 0 | } Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc_left::<u16> Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc_left::<u8> Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc_left::<_> |
124 | | |
125 | 0 | fn pred_dc_top<T: Pixel>( |
126 | 0 | output: &mut PlaneRegionMut<'_, T>, |
127 | 0 | above: &[T], |
128 | 0 | _left: &[T], |
129 | 0 | width: usize, |
130 | 0 | height: usize, |
131 | 0 | _bit_depth: usize, |
132 | 0 | ) { |
133 | 0 | let sum = above[..width].iter().fold(0u32, |acc, &v| { |
134 | 0 | let v: u32 = v.into(); |
135 | 0 | v + acc |
136 | 0 | }); Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc_top::<u16>::{closure#0}Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc_top::<u8>::{closure#0}Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc_top::<_>::{closure#0} |
137 | 0 | let avg = T::cast_from((sum + (width >> 1) as u32) / width as u32); |
138 | 0 | for line in output.rows_iter_mut().take(height) { |
139 | 0 | line[..width].fill(avg); |
140 | 0 | } |
141 | 0 | } Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc_top::<u16> Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc_top::<u8> Unexecuted instantiation: av_scenechange::analyze::intra::rust::pred_dc_top::<_> |
142 | | } |
143 | | |
144 | 0 | pub(crate) fn estimate_intra_costs<T: Pixel>( |
145 | 0 | temp_plane: &mut Plane<T>, |
146 | 0 | frame: &Frame<T>, |
147 | 0 | bit_depth: usize, |
148 | 0 | cpu_feature_level: CpuFeatureLevel, |
149 | 0 | ) -> Box<[u32]> { |
150 | 0 | let plane = &frame.planes[0]; |
151 | 0 | let plane_after_prediction = temp_plane; |
152 | | |
153 | 0 | let bsize = BlockSize::from_width_and_height(IMPORTANCE_BLOCK_SIZE, IMPORTANCE_BLOCK_SIZE); |
154 | 0 | let tx_size = bsize.tx_size(); |
155 | | |
156 | 0 | let h_in_imp_b = plane.cfg.height / IMPORTANCE_BLOCK_SIZE; |
157 | 0 | let w_in_imp_b = plane.cfg.width / IMPORTANCE_BLOCK_SIZE; |
158 | 0 | let mut intra_costs = Vec::with_capacity(h_in_imp_b * w_in_imp_b); |
159 | | |
160 | 0 | for y in 0..h_in_imp_b { |
161 | 0 | for x in 0..w_in_imp_b { |
162 | 0 | let plane_org = plane.region(Area::Rect(Rect { |
163 | 0 | x: (x * IMPORTANCE_BLOCK_SIZE) as isize, |
164 | 0 | y: (y * IMPORTANCE_BLOCK_SIZE) as isize, |
165 | 0 | width: IMPORTANCE_BLOCK_SIZE, |
166 | 0 | height: IMPORTANCE_BLOCK_SIZE, |
167 | 0 | })); |
168 | 0 |
|
169 | 0 | // For scene detection, we are only going to support DC_PRED |
170 | 0 | // for simplicity and speed purposes. |
171 | 0 | let mut edge_buf = Aligned([MaybeUninit::uninit(); 4 * MAX_TX_SIZE + 1]); |
172 | 0 | let edge_buf = get_intra_edges( |
173 | 0 | &mut edge_buf, |
174 | 0 | &plane.as_region(), |
175 | 0 | PlaneOffset { |
176 | 0 | x: (x * IMPORTANCE_BLOCK_SIZE) as isize, |
177 | 0 | y: (y * IMPORTANCE_BLOCK_SIZE) as isize, |
178 | 0 | }, |
179 | 0 | bit_depth, |
180 | 0 | ); |
181 | 0 |
|
182 | 0 | let mut plane_after_prediction_region = |
183 | 0 | plane_after_prediction.region_mut(Area::Rect(Rect { |
184 | 0 | x: (x * IMPORTANCE_BLOCK_SIZE) as isize, |
185 | 0 | y: (y * IMPORTANCE_BLOCK_SIZE) as isize, |
186 | 0 | width: IMPORTANCE_BLOCK_SIZE, |
187 | 0 | height: IMPORTANCE_BLOCK_SIZE, |
188 | 0 | })); |
189 | 0 |
|
190 | 0 | predict_dc_intra( |
191 | 0 | TileRect { |
192 | 0 | x: x * IMPORTANCE_BLOCK_SIZE, |
193 | 0 | y: y * IMPORTANCE_BLOCK_SIZE, |
194 | 0 | width: IMPORTANCE_BLOCK_SIZE, |
195 | 0 | height: IMPORTANCE_BLOCK_SIZE, |
196 | 0 | }, |
197 | 0 | &mut plane_after_prediction_region, |
198 | 0 | tx_size, |
199 | 0 | bit_depth, |
200 | 0 | &edge_buf, |
201 | 0 | cpu_feature_level, |
202 | 0 | ); |
203 | 0 |
|
204 | 0 | let plane_after_prediction_region = plane_after_prediction.region(Area::Rect(Rect { |
205 | 0 | x: (x * IMPORTANCE_BLOCK_SIZE) as isize, |
206 | 0 | y: (y * IMPORTANCE_BLOCK_SIZE) as isize, |
207 | 0 | width: IMPORTANCE_BLOCK_SIZE, |
208 | 0 | height: IMPORTANCE_BLOCK_SIZE, |
209 | 0 | })); |
210 | 0 |
|
211 | 0 | let intra_cost = get_satd( |
212 | 0 | &plane_org, |
213 | 0 | &plane_after_prediction_region, |
214 | 0 | bsize.width(), |
215 | 0 | bsize.height(), |
216 | 0 | bit_depth, |
217 | 0 | cpu_feature_level, |
218 | 0 | ); |
219 | 0 |
|
220 | 0 | intra_costs.push(intra_cost); |
221 | 0 | } |
222 | | } |
223 | | |
224 | 0 | intra_costs.into_boxed_slice() |
225 | 0 | } Unexecuted instantiation: av_scenechange::analyze::intra::estimate_intra_costs::<u16> Unexecuted instantiation: av_scenechange::analyze::intra::estimate_intra_costs::<u8> Unexecuted instantiation: av_scenechange::analyze::intra::estimate_intra_costs::<_> |
226 | | |
227 | 0 | pub fn get_intra_edges<'a, T: Pixel>( |
228 | 0 | edge_buf: &'a mut IntraEdgeBuffer<T>, |
229 | 0 | dst: &PlaneRegion<'_, T>, |
230 | 0 | po: PlaneOffset, |
231 | 0 | bit_depth: usize, |
232 | 0 | ) -> IntraEdge<'a, T> { |
233 | 0 | let tx_size = TxSize::TX_8X8; |
234 | 0 | let mut init_left: usize = 0; |
235 | 0 | let mut init_above: usize = 0; |
236 | | |
237 | 0 | let base = 128u16 << (bit_depth - 8); |
238 | | |
239 | | { |
240 | | // left pixels are ordered from bottom to top and right-aligned |
241 | 0 | let (left, not_left) = edge_buf.split_at_mut(2 * MAX_TX_SIZE); |
242 | 0 | let (top_left, above) = not_left.split_at_mut(1); |
243 | | |
244 | 0 | let x = po.x as usize; |
245 | 0 | let y = po.y as usize; |
246 | | |
247 | 0 | let needs_left = x != 0; |
248 | 0 | let needs_top = y != 0; |
249 | | |
250 | 0 | let rect_w = dst |
251 | 0 | .rect() |
252 | 0 | .width |
253 | 0 | .min(dst.plane_cfg.width - dst.rect().x as usize); |
254 | 0 | let rect_h = dst |
255 | 0 | .rect() |
256 | 0 | .height |
257 | 0 | .min(dst.plane_cfg.height - dst.rect().y as usize); |
258 | | |
259 | | // Needs left |
260 | 0 | if needs_left { |
261 | 0 | let txh = if y + tx_size.height() > rect_h { |
262 | 0 | rect_h - y |
263 | | } else { |
264 | 0 | tx_size.height() |
265 | | }; |
266 | 0 | if x != 0 { |
267 | 0 | for i in 0..txh { |
268 | 0 | debug_assert!(y + i < rect_h); |
269 | 0 | left[2 * MAX_TX_SIZE - 1 - i].write(dst[y + i][x - 1]); |
270 | | } |
271 | 0 | if txh < tx_size.height() { |
272 | 0 | let val = dst[y + txh - 1][x - 1]; |
273 | 0 | for i in txh..tx_size.height() { |
274 | 0 | left[2 * MAX_TX_SIZE - 1 - i].write(val); |
275 | 0 | } |
276 | 0 | } |
277 | | } else { |
278 | 0 | let val = if y != 0 { |
279 | 0 | dst[y - 1][0] |
280 | | } else { |
281 | 0 | T::cast_from(base + 1) |
282 | | }; |
283 | 0 | for v in left[2 * MAX_TX_SIZE - tx_size.height()..].iter_mut() { |
284 | 0 | v.write(val); |
285 | 0 | } |
286 | | } |
287 | 0 | init_left += tx_size.height(); |
288 | 0 | } |
289 | | |
290 | | // Needs top |
291 | 0 | if needs_top { |
292 | 0 | let txw = if x + tx_size.width() > rect_w { |
293 | 0 | rect_w - x |
294 | | } else { |
295 | 0 | tx_size.width() |
296 | | }; |
297 | 0 | if y != 0 { |
298 | 0 | above[..txw].copy_from_slice( |
299 | | // SAFETY: &[T] and &[MaybeUninit<T>] have the same layout |
300 | 0 | unsafe { transmute::<&[T], &[MaybeUninit<T>]>(&dst[y - 1][x..x + txw]) }, |
301 | | ); |
302 | 0 | if txw < tx_size.width() { |
303 | 0 | let val = dst[y - 1][x + txw - 1]; |
304 | 0 | for v in &mut above[txw..tx_size.width()] { |
305 | 0 | v.write(val); |
306 | 0 | } |
307 | 0 | } |
308 | | } else { |
309 | 0 | let val = if x != 0 { |
310 | 0 | dst[0][x - 1] |
311 | | } else { |
312 | 0 | T::cast_from(base - 1) |
313 | | }; |
314 | 0 | for v in &mut above[..tx_size.width()] { |
315 | 0 | v.write(val); |
316 | 0 | } |
317 | | } |
318 | 0 | init_above += tx_size.width(); |
319 | 0 | } |
320 | | |
321 | 0 | top_left[0].write(T::cast_from(base)); |
322 | | } |
323 | 0 | IntraEdge::new(edge_buf, init_left, init_above) |
324 | 0 | } Unexecuted instantiation: av_scenechange::analyze::intra::get_intra_edges::<u16> Unexecuted instantiation: av_scenechange::analyze::intra::get_intra_edges::<u8> Unexecuted instantiation: av_scenechange::analyze::intra::get_intra_edges::<_> |
325 | | |
326 | 0 | pub fn predict_dc_intra<T: Pixel>( |
327 | 0 | tile_rect: TileRect, |
328 | 0 | dst: &mut PlaneRegionMut<'_, T>, |
329 | 0 | tx_size: TxSize, |
330 | 0 | bit_depth: usize, |
331 | 0 | edge_buf: &IntraEdge<T>, |
332 | 0 | cpu: CpuFeatureLevel, |
333 | 0 | ) { |
334 | | let &Rect { |
335 | 0 | x: frame_x, |
336 | 0 | y: frame_y, |
337 | | .. |
338 | 0 | } = dst.rect(); |
339 | 0 | debug_assert!(frame_x >= 0 && frame_y >= 0); |
340 | | // x and y are expressed relative to the tile |
341 | 0 | let x = frame_x as usize - tile_rect.x; |
342 | 0 | let y = frame_y as usize - tile_rect.y; |
343 | | |
344 | 0 | let variant = PredictionVariant::new(x, y); |
345 | | |
346 | 0 | dispatch_predict_dc_intra::<T>(variant, dst, tx_size, bit_depth, edge_buf, cpu); |
347 | 0 | } Unexecuted instantiation: av_scenechange::analyze::intra::predict_dc_intra::<u16> Unexecuted instantiation: av_scenechange::analyze::intra::predict_dc_intra::<u8> Unexecuted instantiation: av_scenechange::analyze::intra::predict_dc_intra::<_> |
348 | | |
349 | | type IntraEdgeBuffer<T> = Aligned<A64, [MaybeUninit<T>; 4 * MAX_TX_SIZE + 1]>; |
350 | | |
351 | | pub struct IntraEdge<'a, T: Pixel>(&'a [T], &'a [T], &'a [T]); |
352 | | |
353 | | impl<'a, T: Pixel> IntraEdge<'a, T> { |
354 | 0 | fn new(edge_buf: &'a mut IntraEdgeBuffer<T>, init_left: usize, init_above: usize) -> Self { |
355 | | // SAFETY: Initialized in `get_intra_edges`. |
356 | 0 | let left = unsafe { |
357 | 0 | let begin_left = 2 * MAX_TX_SIZE - init_left; |
358 | 0 | let end_above = 2 * MAX_TX_SIZE + 1 + init_above; |
359 | 0 | slice_assume_init_mut(&mut edge_buf[begin_left..end_above]) |
360 | | }; |
361 | 0 | let (left, top_left) = left.split_at(init_left); |
362 | 0 | let (top_left, above) = top_left.split_at(1); |
363 | 0 | Self(left, top_left, above) |
364 | 0 | } Unexecuted instantiation: <av_scenechange::analyze::intra::IntraEdge<u16>>::new Unexecuted instantiation: <av_scenechange::analyze::intra::IntraEdge<u8>>::new Unexecuted instantiation: <av_scenechange::analyze::intra::IntraEdge<_>>::new |
365 | | |
366 | 0 | pub const fn as_slices(&self) -> (&'a [T], &'a [T], &'a [T]) { |
367 | 0 | (self.0, self.1, self.2) |
368 | 0 | } Unexecuted instantiation: <av_scenechange::analyze::intra::IntraEdge<u16>>::as_slices Unexecuted instantiation: <av_scenechange::analyze::intra::IntraEdge<u8>>::as_slices Unexecuted instantiation: <av_scenechange::analyze::intra::IntraEdge<_>>::as_slices |
369 | | |
370 | | #[allow(dead_code)] |
371 | 0 | pub const fn top_left_ptr(&self) -> *const T { |
372 | 0 | self.1.as_ptr() |
373 | 0 | } |
374 | | } |