Coverage Report

Created: 2025-11-24 07:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}