FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/tests/checkasm/sw_ops.c
Date: 2026-04-24 19:58:39
Exec Total Coverage
Lines: 369 383 96.3%
Functions: 24 24 100.0%
Branches: 183 211 86.7%

Line Branch Exec Source
1 /**
2 * Copyright (C) 2025 Niklas Haas
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 "libavutil/avassert.h"
24 #include "libavutil/mem_internal.h"
25 #include "libavutil/refstruct.h"
26
27 #include "libswscale/ops.h"
28 #include "libswscale/ops_internal.h"
29
30 #include "checkasm.h"
31
32 enum {
33 NB_PLANES = 4,
34 PIXELS = 64,
35 LINES = 16,
36 };
37
38 enum {
39 U8 = SWS_PIXEL_U8,
40 U16 = SWS_PIXEL_U16,
41 U32 = SWS_PIXEL_U32,
42 F32 = SWS_PIXEL_F32,
43 };
44
45 #define FMT(fmt, ...) tprintf((char[256]) {0}, 256, fmt, __VA_ARGS__)
46 26683 static const char *tprintf(char buf[], size_t size, const char *fmt, ...)
47 {
48 va_list ap;
49 26683 va_start(ap, fmt);
50 26683 vsnprintf(buf, size, fmt, ap);
51 26683 va_end(ap);
52 26683 return buf;
53 }
54
55 22170 static int rw_pixel_bits(const SwsOp *op)
56 {
57
2/2
✓ Branch 0 taken 264 times.
✓ Branch 1 taken 21906 times.
22170 const int elems = op->rw.packed ? op->rw.elems : 1;
58 22170 const int size = ff_sws_pixel_type_size(op->type);
59 22170 const int bits = 8 >> op->rw.frac;
60 av_assert1(bits >= 1);
61 22170 return elems * size * bits;
62 }
63
64 19431424 static float rndf(void)
65 {
66 union { uint32_t u; float f; } x;
67 do {
68 19584313 x.u = rnd();
69
2/2
✓ Branch 0 taken 152889 times.
✓ Branch 1 taken 19431424 times.
19584313 } while (!isnormal(x.f));
70 19431424 return x.f;
71 }
72
73 4872 static void fill32f(float *line, int num, unsigned range)
74 {
75 4872 const float scale = (float) range / UINT32_MAX;
76
2/2
✓ Branch 0 taken 19955712 times.
✓ Branch 1 taken 4872 times.
19960584 for (int i = 0; i < num; i++)
77
2/2
✓ Branch 0 taken 524288 times.
✓ Branch 1 taken 19431424 times.
19955712 line[i] = range ? scale * rnd() : rndf();
78 4872 }
79
80 15860 static void fill32(uint32_t *line, int num, unsigned range)
81 {
82
2/2
✓ Branch 0 taken 64962560 times.
✓ Branch 1 taken 15860 times.
64978420 for (int i = 0; i < num; i++)
83
4/4
✓ Branch 0 taken 851968 times.
✓ Branch 1 taken 64110592 times.
✓ Branch 2 taken 753664 times.
✓ Branch 3 taken 98304 times.
64962560 line[i] = (range && range < UINT_MAX) ? rnd() % (range + 1) : rnd();
84 15860 }
85
86 5832 static void fill16(uint16_t *line, int num, unsigned range)
87 {
88
2/2
✓ Branch 0 taken 5657 times.
✓ Branch 1 taken 175 times.
5832 if (!range) {
89 5657 fill32((uint32_t *) line, AV_CEIL_RSHIFT(num, 1), 0);
90 } else {
91
2/2
✓ Branch 0 taken 1433600 times.
✓ Branch 1 taken 175 times.
1433775 for (int i = 0; i < num; i++)
92 1433600 line[i] = rnd() % (range + 1);
93 }
94 5832 }
95
96 6076 static void fill8(uint8_t *line, int num, unsigned range)
97 {
98
2/2
✓ Branch 0 taken 5779 times.
✓ Branch 1 taken 297 times.
6076 if (!range) {
99 5779 fill32((uint32_t *) line, AV_CEIL_RSHIFT(num, 2), 0);
100 } else {
101
2/2
✓ Branch 0 taken 4866048 times.
✓ Branch 1 taken 297 times.
4866345 for (int i = 0; i < num; i++)
102 4866048 line[i] = rnd() % (range + 1);
103 }
104 6076 }
105
106 37968 static void set_range(AVRational *rangeq, unsigned range, unsigned range_def)
107 {
108
2/2
✓ Branch 0 taken 35028 times.
✓ Branch 1 taken 2940 times.
37968 if (!range)
109 35028 range = range_def;
110
3/4
✓ Branch 0 taken 37968 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 25872 times.
✓ Branch 3 taken 12096 times.
37968 if (range && range <= INT_MAX)
111 25872 *rangeq = (AVRational) { range, 1 };
112 37968 }
113
114 5301 static void check_compiled(const char *name, const SwsOpBackend *backend,
115 const SwsOp *read_op, const SwsOp *write_op,
116 const int ranges[NB_PLANES],
117 const SwsCompiledOp *comp_ref,
118 const SwsCompiledOp *comp_new)
119 {
120 5301 declare_func(void, const SwsOpExec *, const void *, int bx, int y, int bx_end, int y_end);
121
122 static DECLARE_ALIGNED_64(char, src0)[NB_PLANES][LINES][PIXELS * sizeof(uint32_t[4])];
123 static DECLARE_ALIGNED_64(char, src1)[NB_PLANES][LINES][PIXELS * sizeof(uint32_t[4])];
124 static DECLARE_ALIGNED_64(char, dst0)[NB_PLANES][LINES][PIXELS * sizeof(uint32_t[4])];
125 static DECLARE_ALIGNED_64(char, dst1)[NB_PLANES][LINES][PIXELS * sizeof(uint32_t[4])];
126
127
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5301 times.
5301 av_assert0(PIXELS % comp_new->block_size == 0);
128
2/2
✓ Branch 0 taken 21204 times.
✓ Branch 1 taken 5301 times.
26505 for (int p = 0; p < NB_PLANES; p++) {
129 21204 void *plane = src0[p];
130
4/5
✓ Branch 0 taken 6076 times.
✓ Branch 1 taken 5832 times.
✓ Branch 2 taken 4424 times.
✓ Branch 3 taken 4872 times.
✗ Branch 4 not taken.
21204 switch (read_op->type) {
131 6076 case U8:
132 6076 fill8(plane, sizeof(src0[p]) / sizeof(uint8_t), ranges[p]);
133 6076 break;
134 5832 case U16:
135 5832 fill16(plane, sizeof(src0[p]) / sizeof(uint16_t), ranges[p]);
136 5832 break;
137 4424 case U32:
138 4424 fill32(plane, sizeof(src0[p]) / sizeof(uint32_t), ranges[p]);
139 4424 break;
140 4872 case F32:
141 4872 fill32f(plane, sizeof(src0[p]) / sizeof(uint32_t), ranges[p]);
142 4872 break;
143 }
144 }
145
146 5301 memcpy(src1, src0, sizeof(src0));
147 5301 memset(dst0, 0, sizeof(dst0));
148 5301 memset(dst1, 0, sizeof(dst1));
149
150 5301 const int read_size = PIXELS * rw_pixel_bits(read_op) >> 3;
151 5301 const int write_size = PIXELS * rw_pixel_bits(write_op) >> 3;
152
153 5301 SwsOpExec exec = {0};
154 5301 exec.width = PIXELS;
155 5301 exec.height = exec.slice_h = LINES;
156
2/2
✓ Branch 0 taken 21204 times.
✓ Branch 1 taken 5301 times.
26505 for (int i = 0; i < NB_PLANES; i++) {
157 21204 exec.in_stride[i] = sizeof(src0[i][0]);
158 21204 exec.out_stride[i] = sizeof(dst0[i][0]);
159 21204 exec.in_bump[i] = exec.in_stride[i] - read_size;
160 21204 exec.out_bump[i] = exec.out_stride[i] - write_size;
161 }
162
163 int32_t in_bump_y[LINES];
164
2/2
✓ Branch 0 taken 440 times.
✓ Branch 1 taken 4861 times.
5301 if (read_op->rw.filter == SWS_OP_FILTER_V) {
165 440 const int *offsets = read_op->rw.kernel->offsets;
166
2/2
✓ Branch 0 taken 6600 times.
✓ Branch 1 taken 440 times.
7040 for (int y = 0; y < LINES - 1; y++)
167 6600 in_bump_y[y] = offsets[y + 1] - offsets[y] - 1;
168 440 in_bump_y[LINES - 1] = 0;
169 440 exec.in_bump_y = in_bump_y;
170 }
171
172 int32_t in_offset_x[PIXELS];
173
2/2
✓ Branch 0 taken 616 times.
✓ Branch 1 taken 4685 times.
5301 if (read_op->rw.filter == SWS_OP_FILTER_H) {
174 616 const int *offsets = read_op->rw.kernel->offsets;
175 616 const int rw_bits = rw_pixel_bits(read_op);
176
2/2
✓ Branch 0 taken 39424 times.
✓ Branch 1 taken 616 times.
40040 for (int x = 0; x < PIXELS; x++)
177 39424 in_offset_x[x] = offsets[x] * rw_bits >> 3;
178 616 exec.in_offset_x = in_offset_x;
179 }
180
181 /**
182 * We can't use `check_func()` alone because the actual function pointer
183 * may be a wrapper or entry point shared by multiple implementations.
184 * Solve it by hashing in the active CPU flags as well.
185 */
186 5301 uintptr_t id = (uintptr_t) comp_new->func;
187 5301 id ^= (id << 6) + (id >> 2) + 0x9e3779b97f4a7c15 + comp_new->cpu_flags;
188
189
2/2
✓ Branch 3 taken 2738 times.
✓ Branch 4 taken 2563 times.
5301 if (check_key((void*) id, "%s/%s", name, backend->name)) {
190 2738 exec.block_size_in = comp_ref->block_size * rw_pixel_bits(read_op) >> 3;
191 2738 exec.block_size_out = comp_ref->block_size * rw_pixel_bits(write_op) >> 3;
192
2/2
✓ Branch 0 taken 10952 times.
✓ Branch 1 taken 2738 times.
13690 for (int i = 0; i < NB_PLANES; i++) {
193 10952 exec.in[i] = (void *) src0[i];
194 10952 exec.out[i] = (void *) dst0[i];
195 }
196 2738 checkasm_call(comp_ref->func, &exec, comp_ref->priv, 0, 0, PIXELS / comp_ref->block_size, LINES);
197
198 2738 exec.block_size_in = comp_new->block_size * rw_pixel_bits(read_op) >> 3;
199 2738 exec.block_size_out = comp_new->block_size * rw_pixel_bits(write_op) >> 3;
200
2/2
✓ Branch 0 taken 10952 times.
✓ Branch 1 taken 2738 times.
13690 for (int i = 0; i < NB_PLANES; i++) {
201 10952 exec.in[i] = (void *) src1[i];
202 10952 exec.out[i] = (void *) dst1[i];
203 }
204 2738 checkasm_call_checked(comp_new->func, &exec, comp_new->priv, 0, 0, PIXELS / comp_new->block_size, LINES);
205
206
2/2
✓ Branch 0 taken 10835 times.
✓ Branch 1 taken 2699 times.
13534 for (int i = 0; i < NB_PLANES; i++) {
207 10835 const char *desc = FMT("%s[%d]", name, i);
208 10835 const int stride = sizeof(dst0[i][0]);
209
210
4/5
✓ Branch 0 taken 1388 times.
✓ Branch 1 taken 2117 times.
✓ Branch 2 taken 1581 times.
✓ Branch 3 taken 5749 times.
✗ Branch 4 not taken.
10835 switch (write_op->type) {
211 1388 case U8:
212 1388 checkasm_check(uint8_t, (void *) dst0[i], stride,
213 (void *) dst1[i], stride,
214 write_size, LINES, desc);
215 1388 break;
216 2117 case U16:
217 2117 checkasm_check(uint16_t, (void *) dst0[i], stride,
218 (void *) dst1[i], stride,
219 write_size >> 1, LINES, desc);
220 2117 break;
221 1581 case U32:
222 1581 checkasm_check(uint32_t, (void *) dst0[i], stride,
223 (void *) dst1[i], stride,
224 write_size >> 2, LINES, desc);
225 1581 break;
226 5749 case F32:
227 5749 checkasm_check(float_ulp, (void *) dst0[i], stride,
228 (void *) dst1[i], stride,
229 write_size >> 2, LINES, desc, 0);
230 5749 break;
231 }
232
233
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 10796 times.
10835 if (write_op->rw.packed)
234 39 break;
235 }
236
237
1/8
✗ Branch 1 not taken.
✓ Branch 2 taken 2738 times.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 41 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 44 not taken.
2738 bench(comp_new->func, &exec, comp_new->priv, 0, 0, PIXELS / comp_new->block_size, LINES);
238 }
239 5301 }
240
241 12376 static void check_ops(const char *name, const unsigned ranges[NB_PLANES],
242 const SwsOp *ops)
243 {
244 12376 SwsContext *ctx = sws_alloc_context();
245
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12376 times.
12376 if (!ctx)
246 return;
247 12376 ctx->flags = SWS_BITEXACT;
248
249 static const unsigned def_ranges[4] = {0};
250
2/2
✓ Branch 0 taken 6300 times.
✓ Branch 1 taken 6076 times.
12376 if (!ranges)
251 6300 ranges = def_ranges;
252
253 const SwsOp *read_op, *write_op;
254 12376 SwsOpList oplist = {
255 .ops = (SwsOp *) ops,
256 .plane_src = {0, 1, 2, 3},
257 .plane_dst = {0, 1, 2, 3},
258 };
259
260 12376 read_op = &ops[0];
261
2/2
✓ Branch 0 taken 31808 times.
✓ Branch 1 taken 12376 times.
44184 for (oplist.num_ops = 0; ops[oplist.num_ops].op; oplist.num_ops++)
262 31808 write_op = &ops[oplist.num_ops];
263
264
2/2
✓ Branch 0 taken 49504 times.
✓ Branch 1 taken 12376 times.
61880 for (int p = 0; p < NB_PLANES; p++) {
265
4/5
✓ Branch 0 taken 10864 times.
✓ Branch 1 taken 14000 times.
✓ Branch 2 taken 13104 times.
✓ Branch 3 taken 11536 times.
✗ Branch 4 not taken.
49504 switch (read_op->type) {
266 10864 case U8:
267 10864 set_range(&oplist.comps_src.max[p], ranges[p], UINT8_MAX);
268 10864 oplist.comps_src.min[p] = (AVRational) { 0, 1 };
269 10864 break;
270 14000 case U16:
271 14000 set_range(&oplist.comps_src.max[p], ranges[p], UINT16_MAX);
272 14000 oplist.comps_src.min[p] = (AVRational) { 0, 1 };
273 14000 break;
274 13104 case U32:
275 13104 set_range(&oplist.comps_src.max[p], ranges[p], UINT32_MAX);
276 13104 oplist.comps_src.min[p] = (AVRational) { 0, 1 };
277 13104 break;
278 11536 case F32:
279
4/4
✓ Branch 0 taken 672 times.
✓ Branch 1 taken 10864 times.
✓ Branch 2 taken 448 times.
✓ Branch 3 taken 224 times.
11536 if (ranges[p] && ranges[p] <= INT_MAX) {
280 448 oplist.comps_src.max[p] = (AVRational) { ranges[p], 1 };
281 448 oplist.comps_src.min[p] = (AVRational) { 0, 1 };
282 }
283 11536 break;
284 }
285 }
286
287 static const SwsOpBackend *backend_ref;
288
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 12375 times.
12376 if (!backend_ref) {
289
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 for (int n = 0; ff_sws_op_backends[n]; n++) {
290
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (!strcmp(ff_sws_op_backends[n]->name, "c")) {
291 1 backend_ref = ff_sws_op_backends[n];
292 1 break;
293 }
294 }
295
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 av_assert0(backend_ref);
296 }
297
298 /* Always compile `ops` using the C backend as a reference */
299 12376 SwsCompiledOp comp_ref = {0};
300 12376 int ret = ff_sws_ops_compile_backend(ctx, backend_ref, &oplist, &comp_ref);
301
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12376 times.
12376 if (ret < 0) {
302 av_assert0(ret != AVERROR(ENOTSUP));
303 fail();
304 goto done;
305 }
306
307 /* Iterate over every other backend, and test it against the C reference */
308
2/2
✓ Branch 0 taken 37128 times.
✓ Branch 1 taken 12376 times.
49504 for (int n = 0; ff_sws_op_backends[n]; n++) {
309 37128 const SwsOpBackend *backend = ff_sws_op_backends[n];
310
3/4
✓ Branch 0 taken 37128 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 12376 times.
✓ Branch 3 taken 24752 times.
37128 if (backend->hw_format != AV_PIX_FMT_NONE || backend == backend_ref)
311 33595 continue;
312
313
2/2
✓ Branch 1 taken 1768 times.
✓ Branch 2 taken 22984 times.
24752 if (!av_get_cpu_flags()) {
314 /* Also test once with the existing C reference to set the baseline */
315 1768 check_compiled(name, backend, read_op, write_op, ranges, &comp_ref, &comp_ref);
316 }
317
318 24752 SwsCompiledOp comp_new = {0};
319 24752 int ret = ff_sws_ops_compile_backend(ctx, backend, &oplist, &comp_new);
320
2/2
✓ Branch 0 taken 21219 times.
✓ Branch 1 taken 3533 times.
24752 if (ret == AVERROR(ENOTSUP)) {
321 21219 continue;
322
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3533 times.
3533 } else if (ret < 0) {
323 fail();
324 goto done;
325 }
326
327 3533 check_compiled(name, backend, read_op, write_op, ranges, &comp_ref, &comp_new);
328 3533 ff_sws_compiled_op_unref(&comp_new);
329 }
330
331 12376 done:
332 12376 ff_sws_compiled_op_unref(&comp_ref);
333 12376 sws_free_context(&ctx);
334 }
335
336 #define CHECK_RANGES(NAME, RANGES, N_IN, N_OUT, IN, OUT, ...) \
337 do { \
338 check_ops(NAME, RANGES, (SwsOp[]) { \
339 { \
340 .op = SWS_OP_READ, \
341 .type = IN, \
342 .rw.elems = N_IN, \
343 }, \
344 __VA_ARGS__, \
345 { \
346 .op = SWS_OP_WRITE, \
347 .type = OUT, \
348 .rw.elems = N_OUT, \
349 }, {0} \
350 }); \
351 } while (0)
352
353 #define MK_RANGES(R) ((const unsigned[]) { R, R, R, R })
354 #define CHECK_RANGE(NAME, RANGE, N_IN, N_OUT, IN, OUT, ...) \
355 CHECK_RANGES(NAME, MK_RANGES(RANGE), N_IN, N_OUT, IN, OUT, __VA_ARGS__)
356
357 #define CHECK_COMMON_RANGE(NAME, RANGE, IN, OUT, ...) \
358 CHECK_RANGE(FMT("%s_p1000", NAME), RANGE, 4, 4, IN, OUT, __VA_ARGS__); \
359 CHECK_RANGE(FMT("%s_p1110", NAME), RANGE, 4, 4, IN, OUT, __VA_ARGS__); \
360 CHECK_RANGE(FMT("%s_p1111", NAME), RANGE, 4, 4, IN, OUT, __VA_ARGS__); \
361 CHECK_RANGE(FMT("%s_p1001", NAME), RANGE, 4, 4, IN, OUT, __VA_ARGS__, { \
362 .op = SWS_OP_SWIZZLE, \
363 .type = OUT, \
364 .swizzle = SWS_SWIZZLE(0, 3, 1, 2), \
365 })
366
367 #define CHECK(NAME, N_IN, N_OUT, IN, OUT, ...) \
368 CHECK_RANGE(NAME, 0, N_IN, N_OUT, IN, OUT, __VA_ARGS__)
369
370 #define CHECK_COMMON(NAME, IN, OUT, ...) \
371 CHECK_COMMON_RANGE(NAME, 0, IN, OUT, __VA_ARGS__)
372
373 14 static void check_read_write(void)
374 {
375
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 14 times.
70 for (SwsPixelType t = U8; t < SWS_PIXEL_TYPE_NB; t++) {
376 56 const char *type = ff_sws_pixel_type_name(t);
377
2/2
✓ Branch 0 taken 224 times.
✓ Branch 1 taken 56 times.
280 for (int i = 1; i <= 4; i++) {
378 /* Test N->N planar read/write */
379
2/2
✓ Branch 0 taken 560 times.
✓ Branch 1 taken 224 times.
784 for (int o = 1; o <= i; o++) {
380 560 check_ops(FMT("rw_%d_%d_%s", i, o, type), NULL, (SwsOp[]) {
381 {
382 .op = SWS_OP_READ,
383 .type = t,
384 .rw.elems = i,
385 }, {
386 .op = SWS_OP_WRITE,
387 .type = t,
388 .rw.elems = o,
389 }, {0}
390 });
391 }
392
393 /* Test packed read/write */
394
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 168 times.
224 if (i == 1)
395 56 continue;
396
397 168 check_ops(FMT("read_packed%d_%s", i, type), NULL, (SwsOp[]) {
398 {
399 .op = SWS_OP_READ,
400 .type = t,
401 .rw.elems = i,
402 .rw.packed = true,
403 }, {
404 .op = SWS_OP_WRITE,
405 .type = t,
406 .rw.elems = i,
407 }, {0}
408 });
409
410 168 check_ops(FMT("write_packed%d_%s", i, type), NULL, (SwsOp[]) {
411 {
412 .op = SWS_OP_READ,
413 .type = t,
414 .rw.elems = i,
415 }, {
416 .op = SWS_OP_WRITE,
417 .type = t,
418 .rw.elems = i,
419 .rw.packed = true,
420 }, {0}
421 });
422 }
423 }
424
425 /* Test fractional reads/writes */
426
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 14 times.
56 for (int frac = 1; frac <= 3; frac++) {
427 42 const int bits = 8 >> frac;
428 42 const int range = (1 << bits) - 1;
429
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 28 times.
42 if (bits == 2)
430 14 continue; /* no 2 bit packed formats currently exist */
431
432 28 check_ops(FMT("read_frac%d", frac), NULL, (SwsOp[]) {
433 {
434 .op = SWS_OP_READ,
435 .type = U8,
436 .rw.elems = 1,
437 .rw.frac = frac,
438 }, {
439 .op = SWS_OP_WRITE,
440 .type = U8,
441 .rw.elems = 1,
442 }, {0}
443 });
444
445 28 check_ops(FMT("write_frac%d", frac), MK_RANGES(range), (SwsOp[]) {
446 {
447 .op = SWS_OP_READ,
448 .type = U8,
449 .rw.elems = 1,
450 }, {
451 .op = SWS_OP_WRITE,
452 .type = U8,
453 .rw.elems = 1,
454 .rw.frac = frac,
455 }, {0}
456 });
457 }
458 14 }
459
460 14 static void check_swap_bytes(void)
461 {
462 14 CHECK_COMMON("swap_bytes_16", U16, U16, {
463 .op = SWS_OP_SWAP_BYTES,
464 .type = U16,
465 });
466
467 14 CHECK_COMMON("swap_bytes_32", U32, U32, {
468 .op = SWS_OP_SWAP_BYTES,
469 .type = U32,
470 });
471 14 }
472
473 14 static void check_pack_unpack(void)
474 {
475 const struct {
476 SwsPixelType type;
477 SwsPackOp op;
478 14 } patterns[] = {
479 { U8, {{ 3, 3, 2 }}},
480 { U8, {{ 2, 3, 3 }}},
481 { U8, {{ 1, 2, 1 }}},
482 {U16, {{ 5, 6, 5 }}},
483 {U16, {{ 5, 5, 5 }}},
484 {U16, {{ 4, 4, 4 }}},
485 {U32, {{ 2, 10, 10, 10 }}},
486 {U32, {{10, 10, 10, 2 }}},
487 };
488
489
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 14 times.
126 for (int i = 0; i < FF_ARRAY_ELEMS(patterns); i++) {
490 112 const SwsPixelType type = patterns[i].type;
491 112 const SwsPackOp pack = patterns[i].op;
492
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 84 times.
112 const int num = pack.pattern[3] ? 4 : 3;
493 112 const char *pat = FMT("%d%d%d%d", pack.pattern[0], pack.pattern[1],
494 pack.pattern[2], pack.pattern[3]);
495 112 const int total = pack.pattern[0] + pack.pattern[1] +
496 112 pack.pattern[2] + pack.pattern[3];
497 112 const unsigned ranges[4] = {
498 112 (1 << pack.pattern[0]) - 1,
499 112 (1 << pack.pattern[1]) - 1,
500 112 (1 << pack.pattern[2]) - 1,
501 112 (1 << pack.pattern[3]) - 1,
502 };
503
504 112 CHECK_RANGES(FMT("pack_%s", pat), ranges, num, 1, type, type, {
505 .op = SWS_OP_PACK,
506 .type = type,
507 .pack = pack,
508 });
509
510 112 CHECK_RANGE(FMT("unpack_%s", pat), UINT32_MAX >> (32 - total), 1, num, type, type, {
511 .op = SWS_OP_UNPACK,
512 .type = type,
513 .pack = pack,
514 });
515 }
516 14 }
517
518 1225938 static AVRational rndq(SwsPixelType t)
519 {
520 1225938 const unsigned num = rnd();
521
2/2
✓ Branch 0 taken 1344 times.
✓ Branch 1 taken 1224594 times.
1225938 if (ff_sws_pixel_type_is_int(t)) {
522 1344 const unsigned mask = UINT_MAX >> (32 - ff_sws_pixel_type_size(t) * 8);
523 1344 return (AVRational) { num & mask, 1 };
524 } else {
525 1224594 const unsigned den = rnd();
526
1/2
✓ Branch 0 taken 1224594 times.
✗ Branch 1 not taken.
1224594 return (AVRational) { num, den ? den : 1 };
527 }
528 }
529
530 14 static void check_clear(void)
531 {
532
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 14 times.
70 for (SwsPixelType t = U8; t < SWS_PIXEL_TYPE_NB; t++) {
533 56 const char *type = ff_sws_pixel_type_name(t);
534 56 const int bits = ff_sws_pixel_type_size(t) * 8;
535
536 /* TODO: AVRational can't fit 32 bit constants */
537
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 28 times.
56 if (bits < 32) {
538 28 const AVRational chroma = (AVRational) { 1 << (bits - 1), 1};
539 28 const AVRational alpha = (AVRational) { (1 << bits) - 1, 1};
540 28 const AVRational zero = (AVRational) { 0, 1};
541 28 const AVRational none = {0};
542
543 28 const AVRational patterns[][4] = {
544 /* Zero only */
545 { none, none, none, zero },
546 { zero, none, none, none },
547 /* Alpha only */
548 { none, none, none, alpha },
549 { alpha, none, none, none },
550 /* Chroma only */
551 { chroma, chroma, none, none },
552 { none, chroma, chroma, none },
553 { none, none, chroma, chroma },
554 { chroma, none, chroma, none },
555 { none, chroma, none, chroma },
556 /* Alpha+chroma */
557 { chroma, chroma, none, alpha },
558 { none, chroma, chroma, alpha },
559 { alpha, none, chroma, chroma },
560 { chroma, none, chroma, alpha },
561 { alpha, chroma, none, chroma },
562 };
563
564
2/2
✓ Branch 0 taken 392 times.
✓ Branch 1 taken 28 times.
420 for (int i = 0; i < FF_ARRAY_ELEMS(patterns); i++) {
565 392 SwsClearOp clear = { .mask = ff_sws_comp_mask_q4(patterns[i]) };
566 392 memcpy(clear.value, patterns[i], sizeof(clear.value));
567 392 CHECK(FMT("clear_pattern_%s[%d]", type, i), 4, 4, t, t, {
568 .op = SWS_OP_CLEAR,
569 .type = t,
570 .clear = clear,
571 });
572 }
573
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 14 times.
28 } else if (!ff_sws_pixel_type_is_int(t)) {
574 /* Floating point YUV doesn't exist, only alpha needs to be cleared */
575 14 CHECK(FMT("clear_alpha_%s", type), 4, 4, t, t, {
576 .op = SWS_OP_CLEAR,
577 .type = t,
578 .clear.value[3] = { 0, 1 },
579 .clear.mask = SWS_COMP(3),
580 });
581 }
582 }
583 14 }
584
585 14 static void check_shift(void)
586 {
587
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 14 times.
56 for (SwsPixelType t = U16; t < SWS_PIXEL_TYPE_NB; t++) {
588 42 const char *type = ff_sws_pixel_type_name(t);
589
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 28 times.
42 if (!ff_sws_pixel_type_is_int(t))
590 14 continue;
591
592
2/2
✓ Branch 0 taken 224 times.
✓ Branch 1 taken 28 times.
252 for (int shift = 1; shift <= 8; shift++) {
593 224 CHECK_COMMON(FMT("lshift%d_%s", shift, type), t, t, {
594 .op = SWS_OP_LSHIFT,
595 .type = t,
596 .shift = { shift },
597 });
598
599 224 CHECK_COMMON(FMT("rshift%d_%s", shift, type), t, t, {
600 .op = SWS_OP_RSHIFT,
601 .type = t,
602 .shift = { shift },
603 });
604 }
605 }
606 14 }
607
608 14 static void check_swizzle(void)
609 {
610
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 14 times.
70 for (SwsPixelType t = U8; t < SWS_PIXEL_TYPE_NB; t++) {
611 56 const char *type = ff_sws_pixel_type_name(t);
612 static const int patterns[][4] = {
613 /* Pure swizzle */
614 {3, 0, 1, 2},
615 {3, 0, 2, 1},
616 {2, 1, 0, 3},
617 {3, 2, 1, 0},
618 {3, 1, 0, 2},
619 {3, 2, 0, 1},
620 {1, 2, 0, 3},
621 {1, 0, 2, 3},
622 {2, 0, 1, 3},
623 {2, 3, 1, 0},
624 {2, 1, 3, 0},
625 {1, 2, 3, 0},
626 {1, 3, 2, 0},
627 {0, 2, 1, 3},
628 {0, 2, 3, 1},
629 {0, 3, 1, 2},
630 {3, 1, 2, 0},
631 {0, 3, 2, 1},
632 /* Luma expansion */
633 {0, 0, 0, 3},
634 {3, 0, 0, 0},
635 {0, 0, 0, 1},
636 {1, 0, 0, 0},
637 };
638
639
2/2
✓ Branch 0 taken 1232 times.
✓ Branch 1 taken 56 times.
1288 for (int i = 0; i < FF_ARRAY_ELEMS(patterns); i++) {
640 1232 const int x = patterns[i][0], y = patterns[i][1],
641 1232 z = patterns[i][2], w = patterns[i][3];
642 1232 CHECK(FMT("swizzle_%d%d%d%d_%s", x, y, z, w, type), 4, 4, t, t, {
643 .op = SWS_OP_SWIZZLE,
644 .type = t,
645 .swizzle = SWS_SWIZZLE(x, y, z, w),
646 });
647 }
648 }
649 14 }
650
651 14 static void check_convert(void)
652 {
653
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 14 times.
70 for (SwsPixelType i = U8; i < SWS_PIXEL_TYPE_NB; i++) {
654 56 const char *itype = ff_sws_pixel_type_name(i);
655 56 const int isize = ff_sws_pixel_type_size(i);
656
2/2
✓ Branch 0 taken 224 times.
✓ Branch 1 taken 56 times.
280 for (SwsPixelType o = U8; o < SWS_PIXEL_TYPE_NB; o++) {
657 224 const char *otype = ff_sws_pixel_type_name(o);
658 224 const int osize = ff_sws_pixel_type_size(o);
659 224 const char *name = FMT("convert_%s_%s", itype, otype);
660
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 168 times.
224 if (i == o)
661 56 continue;
662
663
4/4
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 70 times.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 84 times.
168 if (isize < osize || !ff_sws_pixel_type_is_int(o)) {
664 84 CHECK_COMMON(name, i, o, {
665 .op = SWS_OP_CONVERT,
666 .type = i,
667 .convert.to = o,
668 });
669
3/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 70 times.
✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
84 } else if (isize > osize || !ff_sws_pixel_type_is_int(i)) {
670 84 uint32_t range = UINT32_MAX >> (32 - osize * 8);
671 84 CHECK_COMMON_RANGE(name, range, i, o, {
672 .op = SWS_OP_CONVERT,
673 .type = i,
674 .convert.to = o,
675 });
676 }
677 }
678 }
679
680 /* Check expanding conversions */
681 14 CHECK_COMMON("expand16", U8, U16, {
682 .op = SWS_OP_CONVERT,
683 .type = U8,
684 .convert.to = U16,
685 .convert.expand = true,
686 });
687
688 14 CHECK_COMMON("expand32", U8, U32, {
689 .op = SWS_OP_CONVERT,
690 .type = U8,
691 .convert.to = U32,
692 .convert.expand = true,
693 });
694 14 }
695
696 14 static void check_dither(void)
697 {
698
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 14 times.
28 for (SwsPixelType t = F32; t < SWS_PIXEL_TYPE_NB; t++) {
699 14 const char *type = ff_sws_pixel_type_name(t);
700
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (ff_sws_pixel_type_is_int(t))
701 continue;
702
703 /* Test all sizes up to 256x256 */
704
2/2
✓ Branch 0 taken 126 times.
✓ Branch 1 taken 14 times.
140 for (int size_log2 = 0; size_log2 <= 8; size_log2++) {
705 126 const int size = 1 << size_log2;
706 126 const int mask = size - 1;
707 126 AVRational *matrix = av_refstruct_allocz(size * size * sizeof(*matrix));
708
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
126 if (!matrix) {
709 fail();
710 return;
711 }
712
713
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 112 times.
126 if (size == 1) {
714 14 matrix[0] = (AVRational) { 1, 2 };
715 } else {
716
2/2
✓ Branch 0 taken 1223320 times.
✓ Branch 1 taken 112 times.
1223432 for (int i = 0; i < size * size; i++)
717 1223320 matrix[i] = rndq(t);
718 }
719
720 126 CHECK_COMMON(FMT("dither_%dx%d_%s", size, size, type), t, t, {
721 .op = SWS_OP_DITHER,
722 .type = t,
723 .dither.size_log2 = size_log2,
724 .dither.matrix = matrix,
725 .dither.y_offset = {0, 3 & mask, 2 & mask, 5 & mask},
726 });
727
728 126 av_refstruct_unref(&matrix);
729 }
730 }
731 }
732
733 14 static void check_min_max(void)
734 {
735
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 14 times.
70 for (SwsPixelType t = U8; t < SWS_PIXEL_TYPE_NB; t++) {
736 56 const char *type = ff_sws_pixel_type_name(t);
737 56 CHECK_COMMON(FMT("min_%s", type), t, t, {
738 .op = SWS_OP_MIN,
739 .type = t,
740 .clamp = {{ rndq(t), rndq(t), rndq(t), rndq(t) }},
741 });
742
743 56 CHECK_COMMON(FMT("max_%s", type), t, t, {
744 .op = SWS_OP_MAX,
745 .type = t,
746 .clamp = {{ rndq(t), rndq(t), rndq(t), rndq(t) }},
747 });
748 }
749 14 }
750
751 14 static void check_linear(void)
752 {
753 static const struct {
754 const char *name;
755 uint32_t mask;
756 } patterns[] = {
757 { "luma", SWS_MASK_LUMA },
758 { "alpha", SWS_MASK_ALPHA },
759 { "luma+alpha", SWS_MASK_LUMA | SWS_MASK_ALPHA },
760 { "dot3", 0x7 },
761 { "row0", SWS_MASK_ROW(0) ^ SWS_MASK(0, 3) },
762 { "diag3", SWS_MASK_DIAG3 },
763 { "diag4", SWS_MASK_DIAG4 },
764 { "diag3+alpha", SWS_MASK_DIAG3 | SWS_MASK_ALPHA },
765 { "diag3+off3", SWS_MASK_DIAG3 | SWS_MASK_OFF3 },
766 { "matrix3+off3", SWS_MASK_MAT3 | SWS_MASK_OFF3 },
767 { "matrix3+off3+alpha", SWS_MASK_MAT3 | SWS_MASK_OFF3 | SWS_MASK_ALPHA },
768 };
769
770
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 14 times.
28 for (SwsPixelType t = F32; t < SWS_PIXEL_TYPE_NB; t++) {
771 14 const char *type = ff_sws_pixel_type_name(t);
772
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (ff_sws_pixel_type_is_int(t))
773 continue;
774
775
2/2
✓ Branch 0 taken 154 times.
✓ Branch 1 taken 14 times.
168 for (int p = 0; p < FF_ARRAY_ELEMS(patterns); p++) {
776 154 const uint32_t mask = patterns[p].mask;
777 154 SwsLinearOp lin = { .mask = mask };
778
779
2/2
✓ Branch 0 taken 616 times.
✓ Branch 1 taken 154 times.
770 for (int i = 0; i < 4; i++) {
780
2/2
✓ Branch 0 taken 3080 times.
✓ Branch 1 taken 616 times.
3696 for (int j = 0; j < 5; j++) {
781
2/2
✓ Branch 0 taken 770 times.
✓ Branch 1 taken 2310 times.
3080 if (mask & SWS_MASK(i, j)) {
782 770 lin.m[i][j] = rndq(t);
783 } else {
784 2310 lin.m[i][j] = (AVRational) { i == j, 1 };
785 }
786 }
787 }
788
789 154 CHECK(FMT("linear_%s_%s", patterns[p].name, type), 4, 4, t, t, {
790 .op = SWS_OP_LINEAR,
791 .type = t,
792 .lin = lin,
793 });
794 }
795 }
796 14 }
797
798 14 static void check_scale(void)
799 {
800
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 14 times.
70 for (SwsPixelType t = U8; t < SWS_PIXEL_TYPE_NB; t++) {
801 56 const char *type = ff_sws_pixel_type_name(t);
802 56 const int bits = ff_sws_pixel_type_size(t) * 8;
803
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 14 times.
56 if (ff_sws_pixel_type_is_int(t)) {
804 42 const unsigned max = UINT32_MAX >> (32 - bits);
805
806 /* Test fixed fast path for expansion from bits to full range */
807 42 CHECK_COMMON_RANGE(FMT("scale_full_%s", type), 1, t, t, {
808 .op = SWS_OP_SCALE,
809 .type = t,
810 .scale = {{ max, 1 }},
811 });
812
813 /* Ensure the result won't exceed the value range */
814 42 const unsigned scale = rnd() & (max >> 1);
815
1/2
✓ Branch 0 taken 42 times.
✗ Branch 1 not taken.
42 const unsigned range = max / (scale ? scale : 1);
816 42 CHECK_COMMON_RANGE(FMT("scale_%s", type), range, t, t, {
817 .op = SWS_OP_SCALE,
818 .type = t,
819 .scale = {{ scale, 1 }},
820 });
821 } else {
822 14 CHECK_COMMON(FMT("scale_%s", type), t, t, {
823 .op = SWS_OP_SCALE,
824 .type = t,
825 .scale = { rndq(t) },
826 });
827 }
828 }
829 14 }
830
831 14 static void check_filter(void)
832 {
833 14 SwsFilterParams params = {
834 .scaler_params = { SWS_PARAM_DEFAULT, SWS_PARAM_DEFAULT },
835 };
836
837 14 const SwsScaler scalers[] = {
838 SWS_SCALE_POINT,
839 SWS_SCALE_SINC,
840 };
841
842 SwsFilterWeights *filter;
843
844
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 14 times.
70 for (SwsPixelType t = U8; t < SWS_PIXEL_TYPE_NB; t++) {
845 56 const char *type = ff_sws_pixel_type_name(t);
846
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 56 times.
168 for (int s = 0; s < FF_ARRAY_ELEMS(scalers); s++) {
847 112 params.scaler = scalers[s];
848 112 params.dst_size = LINES;
849
2/2
✓ Branch 0 taken 560 times.
✓ Branch 1 taken 112 times.
672 for (int h = 1; h <= LINES; h += h) {
850 560 params.src_size = h;
851 560 int ret = ff_sws_filter_generate(NULL, &params, &filter);
852
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 560 times.
560 if (ret < 0) {
853 fail();
854 return;
855 }
856
857 560 const char *name = filter->name;
858
2/2
✓ Branch 0 taken 2240 times.
✓ Branch 1 taken 560 times.
2800 for (int n = 1; n <= 4; n++) {
859 2240 check_ops(FMT("%s_filter%d_v_%dx%d_%s", name, n, PIXELS, h, type), NULL, (SwsOp[]) {
860 {
861 .op = SWS_OP_READ,
862 .type = t,
863 .rw.elems = n,
864 .rw.filter = SWS_OP_FILTER_V,
865 .rw.kernel = filter,
866 }, {
867 .op = SWS_OP_WRITE,
868 .type = SWS_PIXEL_F32,
869 .rw.elems = n,
870 }, {0}
871 });
872 }
873
874 560 av_refstruct_unref(&filter);
875 }
876
877 112 params.dst_size = PIXELS;
878
2/2
✓ Branch 0 taken 784 times.
✓ Branch 1 taken 112 times.
896 for (int w = 1; w <= PIXELS; w += w) {
879 784 params.src_size = w;
880 784 int ret = ff_sws_filter_generate(NULL, &params, &filter);
881
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 784 times.
784 if (ret < 0) {
882 fail();
883 return;
884 }
885
886 784 const char *name = filter->name;
887
2/2
✓ Branch 0 taken 3136 times.
✓ Branch 1 taken 784 times.
3920 for (int n = 1; n <= 4; n++) {
888 3136 check_ops(FMT("%s_filter%d_h_%dx%d_%s", name, n, w, LINES, type), NULL, (SwsOp[]) {
889 {
890 .op = SWS_OP_READ,
891 .type = t,
892 .rw.elems = n,
893 .rw.filter = SWS_OP_FILTER_H,
894 .rw.kernel = filter,
895 }, {
896 .op = SWS_OP_WRITE,
897 .type = SWS_PIXEL_F32,
898 .rw.elems = n,
899 }, {0}
900 });
901 }
902
903 784 av_refstruct_unref(&filter);
904 }
905 }
906 }
907 }
908
909 14 void checkasm_check_sw_ops(void)
910 {
911 14 check_read_write();
912 14 report("read_write");
913 14 check_swap_bytes();
914 14 report("swap_bytes");
915 14 check_pack_unpack();
916 14 report("pack_unpack");
917 14 check_clear();
918 14 report("clear");
919 14 check_shift();
920 14 report("shift");
921 14 check_swizzle();
922 14 report("swizzle");
923 14 check_convert();
924 14 report("convert");
925 14 check_dither();
926 14 report("dither");
927 14 check_min_max();
928 14 report("min_max");
929 14 check_linear();
930 14 report("linear");
931 14 check_scale();
932 14 report("scale");
933 14 check_filter();
934 14 report("filter");
935 14 }
936