FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/tests/checkasm/hevc_pred.c
Date: 2026-08-15 14:54:27
Exec Total Coverage
Lines: 155 159 97.5%
Functions: 6 6 100.0%
Branches: 87 186 46.8%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2026 Jun Zhao <barryjzhao@tencent.com>
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include <string.h>
22 #include "checkasm.h"
23 #include "libavcodec/hevc/pred.h"
24 #include "libavutil/intreadwrite.h"
25 #include "libavutil/mem_internal.h"
26
27 static const uint32_t pixel_mask[3] = { 0xffffffff, 0x01ff01ff, 0x03ff03ff };
28
29 #define SIZEOF_PIXEL ((bit_depth + 7) / 8)
30 #define PRED_SIZE 128 /* Increased to 4 * MAX_TB_SIZE to accommodate C code reads */
31
32 #define randomize_ref_buffers() \
33 do { \
34 uint32_t mask = pixel_mask[bit_depth - 8]; \
35 for (int i = -4; i < PRED_SIZE; i += 4) { \
36 uint32_t r = rnd() & mask; \
37 AV_WN32A(top + i, r); \
38 AV_WN32A(left + i, r); \
39 } \
40 } while (0)
41
42 28 static void check_pred_dc(HEVCPredContext *h,
43 uint8_t *top, uint8_t *left, int bit_depth)
44 {
45 28 const char *const block_name[] = { "4x4", "8x8", "16x16", "32x32" };
46 28 const int block_size[] = { 4, 8, 16, 32 };
47 int log2_size;
48
49 28 PIXEL_RECT(buf0, 64, 64);
50 28 PIXEL_RECT(buf1, 64, 64);
51
52 28 declare_func(void, uint8_t *src, const uint8_t *top,
53 const uint8_t *left, ptrdiff_t stride,
54 int log2_size, int c_idx);
55
56 /* Test all 4 sizes: 4x4, 8x8, 16x16, 32x32 */
57
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 28 times.
140 for (log2_size = 2; log2_size <= 5; log2_size++) {
58 112 int size = block_size[log2_size - 2];
59
60
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 104 times.
112 if (check_func(h->pred_dc, "hevc_pred_dc_%s_%d",
61 block_name[log2_size - 2], bit_depth)) {
62 /* Test with c_idx=0 (luma, with edge smoothing for size < 32) */
63
2/2
✓ Branch 1 taken 264 times.
✓ Branch 2 taken 8 times.
272 randomize_ref_buffers();
64 8 CLEAR_PIXEL_RECT(buf0);
65 8 CLEAR_PIXEL_RECT(buf1);
66
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 call_ref(buf0, top, left, buf0_stride, log2_size, 0);
67 8 call_new(buf1, top, left, buf1_stride, log2_size, 0);
68
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 checkasm_check_pixel_padded(buf0, buf0_stride,
69 buf1, buf1_stride,
70 size, size, "dst");
71
72 /* Test with c_idx=1 (chroma, no edge smoothing) */
73
2/2
✓ Branch 1 taken 264 times.
✓ Branch 2 taken 8 times.
272 randomize_ref_buffers();
74 8 CLEAR_PIXEL_RECT(buf0);
75 8 CLEAR_PIXEL_RECT(buf1);
76
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 call_ref(buf0, top, left, buf0_stride, log2_size, 1);
77 8 call_new(buf1, top, left, buf1_stride, log2_size, 1);
78
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 checkasm_check_pixel_padded(buf0, buf0_stride,
79 buf1, buf1_stride,
80 size, size, "dst");
81
82
1/18
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
8 bench_new(buf1, top, left, buf1_stride, log2_size, 0);
83 }
84 }
85 28 }
86
87 28 static void check_pred_planar(HEVCPredContext *h,
88 uint8_t *top, uint8_t *left, int bit_depth)
89 {
90 28 const char *const block_name[] = { "4x4", "8x8", "16x16", "32x32" };
91 28 const int block_size[] = { 4, 8, 16, 32 };
92 int i;
93
94 28 PIXEL_RECT(buf0, 64, 64);
95 28 PIXEL_RECT(buf1, 64, 64);
96
97 28 declare_func(void, uint8_t *src, const uint8_t *top,
98 const uint8_t *left, ptrdiff_t stride);
99
100 /* Test all 4 sizes: 4x4, 8x8, 16x16, 32x32 */
101
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 28 times.
140 for (i = 0; i < 4; i++) {
102 112 int size = block_size[i];
103
104
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 104 times.
112 if (check_func(h->pred_planar[i], "hevc_pred_planar_%s_%d",
105 block_name[i], bit_depth)) {
106
2/2
✓ Branch 1 taken 264 times.
✓ Branch 2 taken 8 times.
272 randomize_ref_buffers();
107 8 CLEAR_PIXEL_RECT(buf0);
108 8 CLEAR_PIXEL_RECT(buf1);
109
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
8 call_ref(buf0, top, left, buf0_stride);
110 8 call_new(buf1, top, left, buf1_stride);
111
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 checkasm_check_pixel_padded(buf0, buf0_stride,
112 buf1, buf1_stride,
113 size, size, "dst");
114
115
1/18
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
8 bench_new(buf1, top, left, buf1_stride);
116 }
117 }
118 28 }
119
120 /*
121 * Angular prediction modes are divided into categories:
122 *
123 * Mode 10: Horizontal pure copy (H pure)
124 * Mode 26: Vertical pure copy (V pure)
125 * Modes 2-9: Horizontal positive angle (H pos) - uses left reference
126 * Modes 11-17: Horizontal negative angle (H neg) - needs reference extension
127 * Modes 18-25: Vertical negative angle (V neg) - needs reference extension
128 * Modes 27-34: Vertical positive angle (V pos) - uses top reference
129 *
130 * Each category has 4 NEON functions for 4x4, 8x8, 16x16, 32x32 sizes.
131 */
132 28 static void check_pred_angular(HEVCPredContext *h,
133 uint8_t *top, uint8_t *left, int bit_depth)
134 {
135 28 const char *const block_name[] = { "4x4", "8x8", "16x16", "32x32" };
136 28 const int block_size[] = { 4, 8, 16, 32 };
137 int i, mode;
138
139 28 PIXEL_RECT(buf0, 64, 64);
140 28 PIXEL_RECT(buf1, 64, 64);
141
142 28 declare_func(void, uint8_t *src, const uint8_t *top,
143 const uint8_t *left, ptrdiff_t stride, int c_idx, int mode);
144
145 /* Test all 4 sizes */
146
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 28 times.
140 for (i = 0; i < 4; i++) {
147 112 int size = block_size[i];
148
149 /* Test all 33 angular modes (2-34) */
150
2/2
✓ Branch 0 taken 3696 times.
✓ Branch 1 taken 112 times.
3808 for (mode = 2; mode <= 34; mode++) {
151 const char *mode_category;
152
153 /* Determine mode category for descriptive test name */
154
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 3584 times.
3696 if (mode == 10)
155 112 mode_category = "Hpure";
156
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 3472 times.
3584 else if (mode == 26)
157 112 mode_category = "Vpure";
158
3/4
✓ Branch 0 taken 3472 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 896 times.
✓ Branch 3 taken 2576 times.
3472 else if (mode >= 2 && mode <= 9)
159 896 mode_category = "Hpos";
160
3/4
✓ Branch 0 taken 2576 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 784 times.
✓ Branch 3 taken 1792 times.
2576 else if (mode >= 11 && mode <= 17)
161 784 mode_category = "Hneg";
162
3/4
✓ Branch 0 taken 1792 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 896 times.
✓ Branch 3 taken 896 times.
1792 else if (mode >= 18 && mode <= 25)
163 896 mode_category = "Vneg";
164 else /* mode >= 27 && mode <= 34 */
165 896 mode_category = "Vpos";
166
167
2/2
✓ Branch 1 taken 264 times.
✓ Branch 2 taken 3432 times.
3696 if (check_func(h->pred_angular[i],
168 "hevc_pred_angular_%s_%s_mode%d_%d",
169 block_name[i], mode_category, mode, bit_depth)) {
170 /* Test with c_idx=0 (luma) */
171
2/2
✓ Branch 1 taken 8712 times.
✓ Branch 2 taken 264 times.
8976 randomize_ref_buffers();
172 264 CLEAR_PIXEL_RECT(buf0);
173 264 CLEAR_PIXEL_RECT(buf1);
174
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 264 times.
264 call_ref(buf0, top, left, buf0_stride, 0, mode);
175 264 call_new(buf1, top, left, buf1_stride, 0, mode);
176
2/2
✓ Branch 0 taken 132 times.
✓ Branch 1 taken 132 times.
264 checkasm_check_pixel_padded(buf0, buf0_stride,
177 buf1, buf1_stride,
178 size, size, "dst");
179
180 /* Test with c_idx=1 (chroma) for modes 10/26 to cover
181 * the edge filtering skip path */
182
4/4
✓ Branch 0 taken 256 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 248 times.
264 if (mode == 10 || mode == 26) {
183
2/2
✓ Branch 1 taken 528 times.
✓ Branch 2 taken 16 times.
544 randomize_ref_buffers();
184 16 CLEAR_PIXEL_RECT(buf0);
185 16 CLEAR_PIXEL_RECT(buf1);
186
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 16 times.
16 call_ref(buf0, top, left, buf0_stride, 1, mode);
187 16 call_new(buf1, top, left, buf1_stride, 1, mode);
188
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
16 checkasm_check_pixel_padded(buf0, buf0_stride,
189 buf1, buf1_stride,
190 size, size, "dst");
191 }
192
193
1/18
✗ Branch 1 not taken.
✓ Branch 2 taken 264 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
264 bench_new(buf1, top, left, buf1_stride, 0, mode);
194 }
195 }
196 }
197 28 }
198
199 28 static void check_ref_filter_3tap(HEVCPredContext *h,
200 uint8_t *top, uint8_t *left, int bit_depth)
201 {
202 28 const char *const block_name[] = { "8x8", "16x16", "32x32" };
203 28 const int block_size[] = { 8, 16, 32 };
204 int i;
205
206 /* 3-tap filter: out[i] = (in[i+1] + 2*in[i] + in[i-1] + 2) >> 2
207 * Filters 2*size-1 samples (indices 0..2*size-2) plus corner [-1].
208 * Output: filtered_left[-1..2*size-1] and filtered_top[-1..2*size-1] */
209 28 declare_func(void, uint8_t *filtered_left, uint8_t *filtered_top,
210 const uint8_t *left, const uint8_t *top, int size);
211
212
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 28 times.
112 for (i = 0; i < 3; i++) {
213 84 int size = block_size[i];
214 84 int n = 2 * size;
215
216
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 78 times.
84 if (check_func(h->ref_filter_3tap[i],
217 "hevc_ref_filter_3tap_%s_%d",
218 block_name[i], bit_depth)) {
219 /* Allocate output buffers with space for [-1] indexing.
220 * Need n+1 elements: indices [-1..n-1] = n+1 pixels.
221 * Use (n+1)*SIZEOF_PIXEL bytes starting at offset SIZEOF_PIXEL. */
222 6 LOCAL_ALIGNED_32(uint8_t, fl_ref_buf, [PRED_SIZE + 16]);
223 6 LOCAL_ALIGNED_32(uint8_t, fl_new_buf, [PRED_SIZE + 16]);
224 6 LOCAL_ALIGNED_32(uint8_t, ft_ref_buf, [PRED_SIZE + 16]);
225 6 LOCAL_ALIGNED_32(uint8_t, ft_new_buf, [PRED_SIZE + 16]);
226 6 uint8_t *fl_ref = fl_ref_buf + 8;
227 6 uint8_t *fl_new = fl_new_buf + 8;
228 6 uint8_t *ft_ref = ft_ref_buf + 8;
229 6 uint8_t *ft_new = ft_new_buf + 8;
230
231
2/2
✓ Branch 1 taken 198 times.
✓ Branch 2 taken 6 times.
204 randomize_ref_buffers();
232 /* Clear output buffers so comparison is clean */
233 6 memset(fl_ref_buf, 0, PRED_SIZE + 16);
234 6 memset(fl_new_buf, 0, PRED_SIZE + 16);
235 6 memset(ft_ref_buf, 0, PRED_SIZE + 16);
236 6 memset(ft_new_buf, 0, PRED_SIZE + 16);
237
238
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 6 times.
6 call_ref(fl_ref, ft_ref, left, top, size);
239 6 call_new(fl_new, ft_new, left, top, size);
240
241 /* Compare filtered_left[-1..2*size-1] and filtered_top[-1..2*size-1] */
242 6 if (memcmp(fl_ref - SIZEOF_PIXEL, fl_new - SIZEOF_PIXEL,
243
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 (n + 1) * SIZEOF_PIXEL))
244 fail();
245 6 if (memcmp(ft_ref - SIZEOF_PIXEL, ft_new - SIZEOF_PIXEL,
246
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 (n + 1) * SIZEOF_PIXEL))
247 fail();
248
249
1/18
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
6 bench_new(fl_new, ft_new, left, top, size);
250 }
251 }
252 28 }
253
254 28 static void check_ref_filter_strong(HEVCPredContext *h,
255 uint8_t *top, uint8_t *left,
256 int bit_depth)
257 {
258 /* Strong intra smoothing: only 32x32 luma.
259 * Interpolates top into filtered_top[0..62], sets filtered_top[-1] and [63].
260 * Modifies left[0..62] in-place. */
261 28 declare_func(void, uint8_t *filtered_top, uint8_t *left,
262 const uint8_t *top);
263
264
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 26 times.
28 if (check_func(h->ref_filter_strong,
265 "hevc_ref_filter_strong_%d", bit_depth)) {
266 2 LOCAL_ALIGNED_32(uint8_t, ft_ref_buf, [PRED_SIZE + 16]);
267 2 LOCAL_ALIGNED_32(uint8_t, ft_new_buf, [PRED_SIZE + 16]);
268 2 LOCAL_ALIGNED_32(uint8_t, left_ref_buf, [PRED_SIZE + 16]);
269 2 LOCAL_ALIGNED_32(uint8_t, left_new_buf, [PRED_SIZE + 16]);
270 2 uint8_t *ft_ref = ft_ref_buf + 8;
271 2 uint8_t *ft_new = ft_new_buf + 8;
272 2 uint8_t *left_ref = left_ref_buf + 8;
273 2 uint8_t *left_new = left_new_buf + 8;
274
275
2/2
✓ Branch 1 taken 66 times.
✓ Branch 2 taken 2 times.
68 randomize_ref_buffers();
276 2 memset(ft_ref_buf, 0, PRED_SIZE + 16);
277 2 memset(ft_new_buf, 0, PRED_SIZE + 16);
278
279 /* Copy left so both ref and new start with the same input
280 * (left is modified in-place) */
281 2 memcpy(left_ref_buf, left - 8, PRED_SIZE + 16);
282 2 memcpy(left_new_buf, left - 8, PRED_SIZE + 16);
283
284
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 call_ref(ft_ref, left_ref, top);
285 2 call_new(ft_new, left_new, top);
286
287 /* Compare filtered_top[-1..63] = 65 pixels */
288 2 if (memcmp(ft_ref - SIZEOF_PIXEL, ft_new - SIZEOF_PIXEL,
289
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 65 * SIZEOF_PIXEL))
290 fail();
291
292 /* Compare left[-1..63] = 65 pixels (left[-1] is unchanged,
293 * left[0..62] are modified, left[63] is unchanged) */
294 2 if (memcmp(left_ref - SIZEOF_PIXEL, left_new - SIZEOF_PIXEL,
295
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 65 * SIZEOF_PIXEL))
296 fail();
297
298
1/18
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 47 not taken.
✗ Branch 48 not taken.
✗ Branch 51 not taken.
✗ Branch 52 not taken.
✗ Branch 54 not taken.
✗ Branch 55 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
2 bench_new(ft_new, left_new, top);
299 }
300 28 }
301
302 14 void checkasm_check_hevc_pred(void)
303 {
304 14 LOCAL_ALIGNED_32(uint8_t, top_buf, [PRED_SIZE + 16]);
305 14 LOCAL_ALIGNED_32(uint8_t, left_buf, [PRED_SIZE + 16]);
306 /* Add offset of 8 bytes to allow negative indexing (top[-1], left[-1]) */
307 14 uint8_t *top = top_buf + 8;
308 14 uint8_t *left = left_buf + 8;
309 int bit_depth;
310
311
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 14 times.
42 for (bit_depth = 8; bit_depth <= 10; bit_depth += 2) {
312 HEVCPredContext h;
313
314 28 ff_hevc_pred_init(&h, bit_depth);
315 28 check_pred_dc(&h, top, left, bit_depth);
316 }
317 14 report("pred_dc");
318
319
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 14 times.
42 for (bit_depth = 8; bit_depth <= 10; bit_depth += 2) {
320 HEVCPredContext h;
321
322 28 ff_hevc_pred_init(&h, bit_depth);
323 28 check_pred_planar(&h, top, left, bit_depth);
324 }
325 14 report("pred_planar");
326
327
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 14 times.
42 for (bit_depth = 8; bit_depth <= 10; bit_depth += 2) {
328 HEVCPredContext h;
329
330 28 ff_hevc_pred_init(&h, bit_depth);
331 28 check_pred_angular(&h, top, left, bit_depth);
332 }
333 14 report("pred_angular");
334
335
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 14 times.
42 for (bit_depth = 8; bit_depth <= 10; bit_depth += 2) {
336 HEVCPredContext h;
337
338 28 ff_hevc_pred_init(&h, bit_depth);
339 28 check_ref_filter_3tap(&h, top, left, bit_depth);
340 }
341 14 report("ref_filter_3tap");
342
343
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 14 times.
42 for (bit_depth = 8; bit_depth <= 10; bit_depth += 2) {
344 HEVCPredContext h;
345
346 28 ff_hevc_pred_init(&h, bit_depth);
347 28 check_ref_filter_strong(&h, top, left, bit_depth);
348 }
349 14 report("ref_filter_strong");
350 14 }
351