FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/tests/checkasm/vc1dsp.c
Date: 2026-09-07 16:33:34
Exec Total Coverage
Lines: 192 220 87.3%
Functions: 12 12 100.0%
Branches: 158 288 54.9%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2022 Ben Avison
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
23 #include "checkasm.h"
24
25 #include "libavcodec/vc1dsp.h"
26
27 #include "libavutil/common.h"
28 #include "libavutil/internal.h"
29 #include "libavutil/intreadwrite.h"
30 #include "libavutil/mem.h"
31 #include "libavutil/mem_internal.h"
32
33 #define VC1DSP_TEST(func) { #func, offsetof(VC1DSPContext, func) },
34 #define VC1DSP_SIZED_TEST(func, width, height) { #func, offsetof(VC1DSPContext, func), width, height },
35
36 typedef struct {
37 const char *name;
38 size_t offset;
39 int width;
40 int height;
41 } test;
42
43 typedef struct matrix {
44 size_t width;
45 size_t height;
46 float d[];
47 } matrix;
48
49 static const matrix T8 = { 8, 8, {
50 12, 12, 12, 12, 12, 12, 12, 12,
51 16, 15, 9, 4, -4, -9, -15, -16,
52 16, 6, -6, -16, -16, -6, 6, 16,
53 15, -4, -16, -9, 9, 16, 4, -15,
54 12, -12, -12, 12, 12, -12, -12, 12,
55 9, -16, 4, 15, -15, -4, 16, -9,
56 6, -16, 16, -6, -6, 16, -16, 6,
57 4, -9, 15, -16, 16, -15, 9, -4
58 } };
59
60 static const matrix T4 = { 4, 4, {
61 17, 17, 17, 17,
62 22, 10, -10, -22,
63 17, -17, -17, 17,
64 10, -22, 22, -10
65 } };
66
67 static const matrix T8t = { 8, 8, {
68 12, 16, 16, 15, 12, 9, 6, 4,
69 12, 15, 6, -4, -12, -16, -16, -9,
70 12, 9, -6, -16, -12, 4, 16, 15,
71 12, 4, -16, -9, 12, 15, -6, -16,
72 12, -4, -16, 9, 12, -15, -6, 16,
73 12, -9, -6, 16, -12, -4, 16, -15,
74 12, -15, 6, 4, -12, 16, -16, 9,
75 12, -16, 16, -15, 12, -9, 6, -4
76 } };
77
78 static const matrix T4t = { 4, 4, {
79 17, 22, 17, 10,
80 17, 10, -17, -22,
81 17, -10, -17, 22,
82 17, -22, 17, -10
83 } };
84
85 60 static matrix *new_matrix(size_t width, size_t height)
86 {
87 60 matrix *out = av_mallocz(sizeof (matrix) + height * width * sizeof (float));
88
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60 times.
60 if (out == NULL) {
89 fprintf(stderr, "Memory allocation failure\n");
90 exit(EXIT_FAILURE);
91 }
92 60 out->width = width;
93 60 out->height = height;
94 60 return out;
95 }
96
97 48 static matrix *multiply(const matrix *a, const matrix *b)
98 {
99 matrix *out;
100
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 48 times.
48 if (a->width != b->height) {
101 fprintf(stderr, "Incompatible multiplication\n");
102 exit(EXIT_FAILURE);
103 }
104 48 out = new_matrix(b->width, a->height);
105
2/2
✓ Branch 0 taken 288 times.
✓ Branch 1 taken 48 times.
336 for (int j = 0; j < out->height; ++j)
106
2/2
✓ Branch 0 taken 1728 times.
✓ Branch 1 taken 288 times.
2016 for (int i = 0; i < out->width; ++i) {
107 1728 float sum = 0;
108
2/2
✓ Branch 0 taken 11520 times.
✓ Branch 1 taken 1728 times.
13248 for (int k = 0; k < a->width; ++k)
109 11520 sum += a->d[j * a->width + k] * b->d[k * b->width + i];
110 1728 out->d[j * out->width + i] = sum;
111 }
112 48 return out;
113 }
114
115 12 static void normalise(matrix *a)
116 {
117
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 12 times.
84 for (int j = 0; j < a->height; ++j)
118
2/2
✓ Branch 0 taken 432 times.
✓ Branch 1 taken 72 times.
504 for (int i = 0; i < a->width; ++i) {
119 432 float *p = a->d + j * a->width + i;
120 432 *p *= 64;
121
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 288 times.
432 if (a->height == 4)
122 144 *p /= (const unsigned[]) { 289, 292, 289, 292 } [j];
123 else
124 288 *p /= (const unsigned[]) { 288, 289, 292, 289, 288, 289, 292, 289 } [j];
125
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 288 times.
432 if (a->width == 4)
126 144 *p /= (const unsigned[]) { 289, 292, 289, 292 } [i];
127 else
128 288 *p /= (const unsigned[]) { 288, 289, 292, 289, 288, 289, 292, 289 } [i];
129 }
130 12 }
131
132 36 static void divide_and_round_nearest(matrix *a, float by)
133 {
134
2/2
✓ Branch 0 taken 216 times.
✓ Branch 1 taken 36 times.
252 for (int j = 0; j < a->height; ++j)
135
2/2
✓ Branch 0 taken 1296 times.
✓ Branch 1 taken 216 times.
1512 for (int i = 0; i < a->width; ++i) {
136 1296 float *p = a->d + j * a->width + i;
137 1296 *p = rintf(*p / by);
138 }
139 36 }
140
141 12 static void tweak(matrix *a)
142 {
143
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 12 times.
36 for (int j = 4; j < a->height; ++j)
144
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 24 times.
168 for (int i = 0; i < a->width; ++i) {
145 144 float *p = a->d + j * a->width + i;
146 144 *p += 1;
147 }
148 12 }
149
150 /* The VC-1 spec places restrictions on the values permitted at three
151 * different stages:
152 * - D: the input coefficients in frequency domain
153 * - E: the intermediate coefficients, inverse-transformed only horizontally
154 * - R: the fully inverse-transformed coefficients
155 *
156 * To fully cater for the ranges specified requires various intermediate
157 * values to be held to 17-bit precision; yet these conditions do not appear
158 * to be utilised in real-world streams. At least some assembly
159 * implementations have chosen to restrict these values to 16-bit precision,
160 * to accelerate the decoding of real-world streams at the cost of strict
161 * adherence to the spec. To avoid our test marking these as failures,
162 * reduce our random inputs.
163 */
164 #define ATTENUATION 4
165
166 12 static matrix *generate_inverse_quantized_transform_coefficients(size_t width, size_t height)
167 {
168 matrix *raw, *tmp, *D, *E, *R;
169 12 raw = new_matrix(width, height);
170
2/2
✓ Branch 0 taken 432 times.
✓ Branch 1 taken 12 times.
444 for (int i = 0; i < width * height; ++i)
171 432 raw->d[i] = (int) (rnd() % (1024/ATTENUATION)) - 512/ATTENUATION;
172
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 tmp = multiply(height == 8 ? &T8 : &T4, raw);
173
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 D = multiply(tmp, width == 8 ? &T8t : &T4t);
174 12 normalise(D);
175 12 divide_and_round_nearest(D, 1);
176
2/2
✓ Branch 0 taken 432 times.
✓ Branch 1 taken 12 times.
444 for (int i = 0; i < width * height; ++i) {
177
2/4
✓ Branch 0 taken 432 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 432 times.
432 if (D->d[i] < -2048/ATTENUATION || D->d[i] > 2048/ATTENUATION-1) {
178 /* Rare, so simply try again */
179 av_free(raw);
180 av_free(tmp);
181 av_free(D);
182 return generate_inverse_quantized_transform_coefficients(width, height);
183 }
184 }
185
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 E = multiply(D, width == 8 ? &T8 : &T4);
186 12 divide_and_round_nearest(E, 8);
187
2/2
✓ Branch 0 taken 432 times.
✓ Branch 1 taken 12 times.
444 for (int i = 0; i < width * height; ++i)
188
2/4
✓ Branch 0 taken 432 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 432 times.
432 if (E->d[i] < -4096/ATTENUATION || E->d[i] > 4096/ATTENUATION-1) {
189 /* Rare, so simply try again */
190 av_free(raw);
191 av_free(tmp);
192 av_free(D);
193 av_free(E);
194 return generate_inverse_quantized_transform_coefficients(width, height);
195 }
196
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 R = multiply(height == 8 ? &T8t : &T4t, E);
197 12 tweak(R);
198 12 divide_and_round_nearest(R, 128);
199
2/2
✓ Branch 0 taken 432 times.
✓ Branch 1 taken 12 times.
444 for (int i = 0; i < width * height; ++i)
200
2/4
✓ Branch 0 taken 432 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 432 times.
432 if (R->d[i] < -512/ATTENUATION || R->d[i] > 512/ATTENUATION-1) {
201 /* Rare, so simply try again */
202 av_free(raw);
203 av_free(tmp);
204 av_free(D);
205 av_free(E);
206 av_free(R);
207 return generate_inverse_quantized_transform_coefficients(width, height);
208 }
209 12 av_free(raw);
210 12 av_free(tmp);
211 12 av_free(E);
212 12 av_free(R);
213 12 return D;
214 }
215
216 #define RANDOMIZE_BUFFER16(name, size) \
217 do { \
218 int i; \
219 for (i = 0; i < size; ++i) { \
220 uint16_t r = rnd(); \
221 AV_WN16A(name##0 + i, r); \
222 AV_WN16A(name##1 + i, r); \
223 } \
224 } while (0)
225
226 #define RANDOMIZE_BUFFER8(name, size) \
227 do { \
228 int i; \
229 for (i = 0; i < size; ++i) { \
230 uint8_t r = rnd(); \
231 name##0[i] = r; \
232 name##1[i] = r; \
233 } \
234 } while (0)
235
236 #define RANDOMIZE_BUFFER8_MID_WEIGHTED(name, size) \
237 do { \
238 uint8_t *p##0 = name##0, *p##1 = name##1; \
239 int i = (size); \
240 while (i-- > 0) { \
241 int x = 0x80 | (rnd() & 0x7F); \
242 x >>= rnd() % 9; \
243 if (rnd() & 1) \
244 x = -x; \
245 *p##1++ = *p##0++ = 0x80 + x; \
246 } \
247 } while (0)
248
249 14 static void check_inv_trans_inplace(void)
250 {
251 /* Inverse transform input coefficients are stored in a 16-bit buffer
252 * with row stride of 8 coefficients irrespective of transform size.
253 * vc1_inv_trans_8x8 differs from the others in two ways: coefficients
254 * are stored in column-major order, and the outputs are written back
255 * to the input buffer, so we oversize it slightly to catch overruns. */
256 14 LOCAL_ALIGNED_16(int16_t, inv_trans_in0, [10 * 8]);
257 14 LOCAL_ALIGNED_16(int16_t, inv_trans_in1, [10 * 8]);
258
259 VC1DSPContext h;
260
261 14 ff_vc1dsp_init(&h);
262
263
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 13 times.
14 if (check_func(h.vc1_inv_trans_8x8, "vc1dsp.vc1_inv_trans_8x8")) {
264 matrix *coeffs;
265 1 declare_func(void, int16_t *);
266
2/2
✓ Branch 1 taken 80 times.
✓ Branch 2 taken 1 times.
81 RANDOMIZE_BUFFER16(inv_trans_in, 10 * 8);
267 1 coeffs = generate_inverse_quantized_transform_coefficients(8, 8);
268
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1 times.
9 for (int j = 0; j < 8; ++j)
269
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 8 times.
72 for (int i = 0; i < 8; ++i) {
270 64 int idx = 8 + i * 8 + j;
271 64 inv_trans_in1[idx] = inv_trans_in0[idx] = coeffs->d[j * 8 + i];
272 }
273
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 call_ref(inv_trans_in0 + 8);
274 1 call_new(inv_trans_in1 + 8);
275
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (memcmp(inv_trans_in0, inv_trans_in1, 10 * 8 * sizeof (int16_t)))
276 fail();
277
1/18
✗ Branch 1 not taken.
✓ Branch 2 taken 1 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.
1 bench_new(inv_trans_in1 + 8);
278 1 av_free(coeffs);
279 }
280 14 }
281
282 14 static void check_inv_trans_adding(void)
283 {
284 /* Inverse transform input coefficients are stored in a 16-bit buffer
285 * with row stride of 8 coefficients irrespective of transform size. */
286 14 LOCAL_ALIGNED_16(int16_t, inv_trans_in0, [8 * 8]);
287 14 LOCAL_ALIGNED_16(int16_t, inv_trans_in1, [8 * 8]);
288
289 /* For all but vc1_inv_trans_8x8, the inverse transform is narrowed and
290 * added with saturation to an array of unsigned 8-bit values. Oversize
291 * this by 8 samples left and right and one row above and below. */
292 14 LOCAL_ALIGNED_8(uint8_t, inv_trans_out0, [10 * 24]);
293 14 LOCAL_ALIGNED_8(uint8_t, inv_trans_out1, [10 * 24]);
294
295 VC1DSPContext h;
296
297 14 const test tests[] = {
298 VC1DSP_SIZED_TEST(vc1_inv_trans_8x4, 8, 4)
299 VC1DSP_SIZED_TEST(vc1_inv_trans_4x8, 4, 8)
300 VC1DSP_SIZED_TEST(vc1_inv_trans_4x4, 4, 4)
301 VC1DSP_SIZED_TEST(vc1_inv_trans_8x8_dc, 8, 8)
302 VC1DSP_SIZED_TEST(vc1_inv_trans_8x4_dc, 8, 4)
303 VC1DSP_SIZED_TEST(vc1_inv_trans_4x8_dc, 4, 8)
304 VC1DSP_SIZED_TEST(vc1_inv_trans_4x4_dc, 4, 4)
305 };
306
307 14 ff_vc1dsp_init(&h);
308
309
2/2
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 14 times.
112 for (size_t k = 0; k < FF_ARRAY_ELEMS(tests); ++k) {
310 98 void (*func)(uint8_t *, ptrdiff_t, int16_t *) = *(void **)((intptr_t) &h + tests[k].offset);
311
2/2
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 87 times.
98 if (check_func(func, "vc1dsp.%s", tests[k].name)) {
312 matrix *coeffs;
313 11 declare_func(void, uint8_t *, ptrdiff_t, int16_t *);
314
2/2
✓ Branch 1 taken 704 times.
✓ Branch 2 taken 11 times.
715 RANDOMIZE_BUFFER16(inv_trans_in, 8 * 8);
315
2/2
✓ Branch 1 taken 2640 times.
✓ Branch 2 taken 11 times.
2651 RANDOMIZE_BUFFER8(inv_trans_out, 10 * 24);
316 11 coeffs = generate_inverse_quantized_transform_coefficients(tests[k].width, tests[k].height);
317
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 11 times.
75 for (int j = 0; j < tests[k].height; ++j)
318
2/2
✓ Branch 0 taken 368 times.
✓ Branch 1 taken 64 times.
432 for (int i = 0; i < tests[k].width; ++i) {
319 368 int idx = j * 8 + i;
320 368 inv_trans_in1[idx] = inv_trans_in0[idx] = coeffs->d[j * tests[k].width + i];
321 }
322
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
11 call_ref(inv_trans_out0 + 24 + 8, 24, inv_trans_in0);
323 11 call_new(inv_trans_out1 + 24 + 8, 24, inv_trans_in1);
324
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11 times.
11 if (memcmp(inv_trans_out0, inv_trans_out1, 10 * 24))
325 fail();
326
1/18
✗ Branch 1 not taken.
✓ Branch 2 taken 11 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.
11 bench_new(inv_trans_out1 + 24 + 8, 24, inv_trans_in1);
327 11 av_free(coeffs);
328 }
329 }
330 14 }
331
332 14 static void check_loop_filter(void)
333 {
334 /* Deblocking filter buffers are big enough to hold a 16x16 block,
335 * plus 16 columns left and 4 rows above to hold filter inputs
336 * (depending on whether v or h neighbouring block edge, oversized
337 * horizontally to maintain 16-byte alignment) plus 16 columns and
338 * 4 rows below to catch write overflows */
339 14 LOCAL_ALIGNED_16(uint8_t, filter_buf0, [24 * 48]);
340 14 LOCAL_ALIGNED_16(uint8_t, filter_buf1, [24 * 48]);
341
342 VC1DSPContext h;
343
344 14 const test tests[] = {
345 VC1DSP_TEST(vc1_v_loop_filter4)
346 VC1DSP_TEST(vc1_h_loop_filter4)
347 VC1DSP_TEST(vc1_v_loop_filter8)
348 VC1DSP_TEST(vc1_h_loop_filter8)
349 VC1DSP_TEST(vc1_v_loop_filter16)
350 VC1DSP_TEST(vc1_h_loop_filter16)
351 };
352
353 14 ff_vc1dsp_init(&h);
354
355
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 14 times.
98 for (size_t k = 0; k < FF_ARRAY_ELEMS(tests); ++k) {
356 84 void (*func)(uint8_t *, ptrdiff_t, int) = *(void **)((intptr_t) &h + tests[k].offset);
357 84 declare_func(void, uint8_t *, ptrdiff_t, int);
358
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 66 times.
84 if (check_func(func, "vc1dsp.%s", tests[k].name)) {
359
2/2
✓ Branch 0 taken 18000 times.
✓ Branch 1 taken 18 times.
18018 for (int count = 1000; count > 0; --count) {
360 18000 int pq = rnd() % 31 + 1;
361
4/4
✓ Branch 3 taken 10366794 times.
✓ Branch 4 taken 10369206 times.
✓ Branch 5 taken 20736000 times.
✓ Branch 6 taken 18000 times.
20754000 RANDOMIZE_BUFFER8_MID_WEIGHTED(filter_buf, 24 * 48);
362
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 18000 times.
18000 call_ref(filter_buf0 + 4 * 48 + 16, 48, pq);
363 18000 call_new(filter_buf1 + 4 * 48 + 16, 48, pq);
364
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18000 times.
18000 if (memcmp(filter_buf0, filter_buf1, 24 * 48))
365 fail();
366 }
367 }
368
2/2
✓ Branch 0 taken 2016 times.
✓ Branch 1 taken 84 times.
2100 for (int j = 0; j < 24; ++j)
369
2/2
✓ Branch 0 taken 96768 times.
✓ Branch 1 taken 2016 times.
98784 for (int i = 0; i < 48; ++i)
370
4/4
✓ Branch 0 taken 64512 times.
✓ Branch 1 taken 32256 times.
✓ Branch 2 taken 53760 times.
✓ Branch 3 taken 10752 times.
96768 filter_buf1[j * 48 + i] = 0x60 + 0x40 * (i >= 16 && j >= 4);
371
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 66 times.
84 if (check_func(func, "vc1dsp.%s_bestcase", tests[k].name))
372
1/18
✗ Branch 1 not taken.
✓ Branch 2 taken 18 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.
18 bench_new(filter_buf1 + 4 * 48 + 16, 48, 1);
373
2/2
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 66 times.
84 if (check_func(func, "vc1dsp.%s_worstcase", tests[k].name))
374
1/18
✗ Branch 1 not taken.
✓ Branch 2 taken 18 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.
18 bench_new(filter_buf1 + 4 * 48 + 16, 48, 31);
375 }
376 14 }
377
378 #define TEST_UNESCAPE \
379 do { \
380 for (int count = 100; count > 0; --count) { \
381 escaped_offset = rnd() & 7; \
382 unescaped_offset = rnd() & 7; \
383 escaped_len = (1u << (rnd() % 8) + 3) - (rnd() & 7); \
384 RANDOMIZE_BUFFER8(unescaped, UNESCAPE_BUF_SIZE); \
385 len0 = call_ref(escaped0 + escaped_offset, escaped_len, unescaped0 + unescaped_offset); \
386 len1 = call_new(escaped1 + escaped_offset, escaped_len, unescaped1 + unescaped_offset); \
387 if (len0 != len1 || memcmp(unescaped0, unescaped1, UNESCAPE_BUF_SIZE)) \
388 fail(); \
389 } \
390 } while (0)
391
392 14 static void check_unescape(void)
393 {
394 /* This appears to be a typical length of buffer in use */
395 #define LOG2_UNESCAPE_BUF_SIZE 17
396 #define UNESCAPE_BUF_SIZE (1u<<LOG2_UNESCAPE_BUF_SIZE)
397 14 LOCAL_ALIGNED_8(uint8_t, escaped0, [UNESCAPE_BUF_SIZE]);
398 14 LOCAL_ALIGNED_8(uint8_t, escaped1, [UNESCAPE_BUF_SIZE]);
399 14 LOCAL_ALIGNED_8(uint8_t, unescaped0, [UNESCAPE_BUF_SIZE]);
400 14 LOCAL_ALIGNED_8(uint8_t, unescaped1, [UNESCAPE_BUF_SIZE]);
401
402 VC1DSPContext h;
403
404 14 ff_vc1dsp_init(&h);
405
406
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 13 times.
14 if (check_func(h.vc1_unescape_buffer, "vc1dsp.vc1_unescape_buffer")) {
407 int len0, len1, escaped_offset, unescaped_offset, escaped_len;
408 1 declare_func(int, const uint8_t *, int, uint8_t *);
409
410 /* Test data which consists of escapes sequences packed as tightly as possible */
411
2/2
✓ Branch 0 taken 131072 times.
✓ Branch 1 taken 1 times.
131073 for (int x = 0; x < UNESCAPE_BUF_SIZE; ++x)
412
2/2
✓ Branch 0 taken 43691 times.
✓ Branch 1 taken 87381 times.
131072 escaped1[x] = escaped0[x] = 3 * (x % 3 == 0);
413
7/10
✓ Branch 5 taken 13107200 times.
✓ Branch 6 taken 100 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 100 times.
✓ Branch 16 taken 100 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 100 times.
✓ Branch 21 taken 100 times.
✓ Branch 22 taken 1 times.
13107301 TEST_UNESCAPE;
414
415 /* Test random data */
416
2/2
✓ Branch 1 taken 131072 times.
✓ Branch 2 taken 1 times.
131073 RANDOMIZE_BUFFER8(escaped, UNESCAPE_BUF_SIZE);
417
7/10
✓ Branch 5 taken 13107200 times.
✓ Branch 6 taken 100 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 100 times.
✓ Branch 16 taken 100 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 100 times.
✓ Branch 21 taken 100 times.
✓ Branch 22 taken 1 times.
13107301 TEST_UNESCAPE;
418
419 /* Test data with escape sequences at random intervals */
420
2/2
✓ Branch 0 taken 1411 times.
✓ Branch 1 taken 1 times.
1412 for (int x = 0; x <= UNESCAPE_BUF_SIZE - 4;) {
421 int gap, gap_msb;
422 1411 escaped1[x+0] = escaped0[x+0] = 0;
423 1411 escaped1[x+1] = escaped0[x+1] = 0;
424 1411 escaped1[x+2] = escaped0[x+2] = 3;
425 1411 escaped1[x+3] = escaped0[x+3] = rnd() & 3;
426 1411 gap_msb = 2u << (rnd() % 8);
427 1411 gap = (rnd() &~ -gap_msb) | gap_msb;
428 1411 x += gap;
429 }
430
7/10
✓ Branch 5 taken 13107200 times.
✓ Branch 6 taken 100 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 100 times.
✓ Branch 16 taken 100 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 100 times.
✓ Branch 21 taken 100 times.
✓ Branch 22 taken 1 times.
13107301 TEST_UNESCAPE;
431
432 /* Test data which is known to contain no escape sequences */
433 1 memset(escaped0, 0xFF, UNESCAPE_BUF_SIZE);
434 1 memset(escaped1, 0xFF, UNESCAPE_BUF_SIZE);
435
7/10
✓ Branch 5 taken 13107200 times.
✓ Branch 6 taken 100 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 100 times.
✓ Branch 16 taken 100 times.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✓ Branch 19 taken 100 times.
✓ Branch 21 taken 100 times.
✓ Branch 22 taken 1 times.
13107301 TEST_UNESCAPE;
436
437 /* Benchmark the no-escape-sequences case */
438
1/18
✗ Branch 1 not taken.
✓ Branch 2 taken 1 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.
1 bench_new(escaped1, UNESCAPE_BUF_SIZE, unescaped1);
439 }
440 14 }
441
442 14 static void check_mspel_pixels(void)
443 {
444 enum {
445 MAX_BLOCK_SIZE = 16,
446 MAX_STRIDE = 64,
447 /// BUF_SIZE is bigger than necessary in order to test strides > block width.
448 BUF_SIZE = (MAX_BLOCK_SIZE - 1) * MAX_STRIDE + MAX_BLOCK_SIZE,
449 /**
450 * Due to qpel interpolation the input needs one extra line at the top
451 * and two at the bottom; horizontal interpolation also needs one pixel
452 * to the left and two to the right. At least the x86 implementation
453 * actually accesses three pixels to the right.
454 * The input is not subject to alignment requirements; making the input buffer
455 * bigger (by MAX_BLOCK_SIZE - 1) allows us to use a random misalignment.
456 */
457 INPUT_BUF_SIZE = (MAX_BLOCK_SIZE - 1) + 1 +
458 (MAX_BLOCK_SIZE + 1 + 2 - 1) * MAX_STRIDE + MAX_BLOCK_SIZE + 2 + 1,
459 };
460 DECLARE_ALIGNED(16, uint8_t, dstbuf0)[BUF_SIZE];
461 DECLARE_ALIGNED(16, uint8_t, dstbuf1)[BUF_SIZE];
462 uint8_t srcbuf0[INPUT_BUF_SIZE];
463 uint8_t srcbuf1[INPUT_BUF_SIZE];
464
465 VC1DSPContext h;
466
467 const struct MSPelTest {
468 const char *name;
469 size_t offset;
470 14 } tests[] = {
471 #define MSPEL_TEST(elem) { .name = #elem, offsetof(VC1DSPContext, elem) }
472 MSPEL_TEST(put_vc1_mspel_pixels_tab),
473 MSPEL_TEST(avg_vc1_mspel_pixels_tab),
474 };
475
476 14 ff_vc1dsp_init(&h);
477
478
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 14 times.
42 for (size_t k = 0; k < FF_ARRAY_ELEMS(tests); ++k) {
479 28 const vc1op_pixels_func (*func)[16] = (vc1op_pixels_func(*)[16])((char*)&h + tests[k].offset);
480
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 28 times.
84 for (unsigned j = 0; j < 2; ++j) {
481 56 const unsigned blocksize = 16 >> j;
482
483
2/2
✓ Branch 0 taken 896 times.
✓ Branch 1 taken 56 times.
952 for (unsigned dxy = 0; dxy < 16; ++dxy) {
484
2/2
✓ Branch 1 taken 768 times.
✓ Branch 2 taken 128 times.
896 if (!check_func(func[j][dxy], "vc1dsp.%s_mc%u%u_%u", tests[k].name, dxy & 3, dxy >> 2, blocksize))
485 768 continue;
486
2/2
✓ Branch 1 taken 64 times.
✓ Branch 2 taken 64 times.
128 declare_func_emms(AV_CPU_FLAG_MMX, void, uint8_t *, const uint8_t*, ptrdiff_t, int);
487 128 size_t dst_offset = (rnd() % (MAX_BLOCK_SIZE / blocksize)) * blocksize;
488 128 ptrdiff_t stride = (rnd() % (MAX_STRIDE / blocksize) + 1) * blocksize;
489 128 size_t src_offset = 1 + stride + rnd() % MAX_BLOCK_SIZE;
490 128 const uint8_t *src0 = srcbuf0 + src_offset, *src1 = srcbuf1 + src_offset;
491 128 uint8_t *dst0 = dstbuf0 + dst_offset, *dst1 = dstbuf1 + dst_offset;
492
493
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 128 times.
128 if (rnd() & 1) {
494 // Flip stride.
495 dst1 += (blocksize - 1) * stride;
496 dst0 += (blocksize - 1) * stride;
497 // We need one line above src and two lines below the block,
498 // hence blocksize * stride.
499 src0 += blocksize * stride;
500 src1 += blocksize * stride;
501 stride = -stride;
502 }
503
2/2
✓ Branch 1 taken 151936 times.
✓ Branch 2 taken 128 times.
152064 RANDOMIZE_BUFFER8(srcbuf, sizeof(srcbuf0));
504
2/2
✓ Branch 0 taken 256 times.
✓ Branch 1 taken 128 times.
384 for (int round = 0; round <= 1; ++round) {
505
2/2
✓ Branch 1 taken 249856 times.
✓ Branch 2 taken 256 times.
250112 RANDOMIZE_BUFFER8(dstbuf, sizeof(dstbuf0));
506
2/2
✓ Branch 2 taken 128 times.
✓ Branch 3 taken 128 times.
256 call_ref(dst0, src0, stride, round);
507 256 call_new(dst1, src1, stride, round);
508
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 256 times.
256 if (memcmp(dstbuf0, dstbuf1, sizeof(dstbuf0))) {
509 fail();
510 }
511 }
512
1/18
✗ Branch 1 not taken.
✓ Branch 2 taken 128 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.
128 bench_new(dst1, src1, stride, 0);
513 }
514 }
515 }
516 14 }
517
518 14 void checkasm_check_vc1dsp(void)
519 {
520 14 check_inv_trans_inplace();
521 14 check_inv_trans_adding();
522 14 report("inv_trans");
523
524 14 check_loop_filter();
525 14 report("loop_filter");
526
527 14 check_unescape();
528 14 report("unescape_buffer");
529
530 14 check_mspel_pixels();
531 14 report("mspel_pixels");
532 14 }
533