FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/tests/checkasm/sw_ops.c
Date: 2026-09-01 17:16:25
Exec Total Coverage
Lines: 388 410 94.6%
Functions: 29 29 100.0%
Branches: 154 199 77.4%

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/filters.h"
28 #include "libswscale/ops_dispatch.h"
29 #include "libswscale/uops.h"
30 #include "libswscale/uops_macros.h"
31
32 #include "checkasm.h"
33
34 enum {
35 MAX_UOPS = 3, /* read, op, write */
36 NB_PLANES = 4,
37 PIXELS = 64,
38 LINES = 16,
39 };
40
41 typedef struct Test {
42 const char *name;
43 SwsUOp uops[MAX_UOPS];
44 int num_uops;
45
46 /* Extra metadata for the test harness */
47 SwsPixelType type_in; /* data format to fill */
48 SwsPixelType type_out; /* data format to compare */
49 SwsCompMask planes_in; /* planes to fill */
50 SwsCompMask planes_out; /* planes to compare */
51 int pixel_bits_in; /* read pixel stride */
52 int pixel_bits_out; /* write pixel stride */
53 unsigned ranges[NB_PLANES]; /* pixel range to fill */
54 } Test;
55
56 #define FMT(fmt, ...) tprintf((char[256]) {0}, 256, fmt, __VA_ARGS__)
57 3963 static const char *tprintf(char buf[], size_t size, const char *fmt, ...)
58 {
59 va_list ap;
60 3963 va_start(ap, fmt);
61 3963 vsnprintf(buf, size, fmt, ap);
62 3963 va_end(ap);
63 3963 return buf;
64 }
65
66 4315496 static float rndf(void)
67 {
68 union { uint32_t u; float f; } x;
69 do {
70 4349562 x.u = rnd();
71
2/2
✓ Branch 0 taken 34066 times.
✓ Branch 1 taken 4315496 times.
4349562 } while (!isnormal(x.f));
72 4315496 return x.f;
73 }
74
75 3150 static SwsPixel constpx(SwsPixelType type, int val)
76 {
77
1/5
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 3150 times.
✗ Branch 4 not taken.
3150 switch (type) {
78 case SWS_PIXEL_U8: return (SwsPixel) { .u8 = val };
79 case SWS_PIXEL_U16: return (SwsPixel) { .u16 = val };
80 case SWS_PIXEL_U32: return (SwsPixel) { .u32 = val };
81 3150 case SWS_PIXEL_F32: return (SwsPixel) { .f32 = val };
82 default: return (SwsPixel) {0};
83 }
84 }
85
86 90342 static SwsPixel rndpx(SwsPixelType type)
87 {
88
4/5
✓ Branch 0 taken 798 times.
✓ Branch 1 taken 700 times.
✓ Branch 2 taken 420 times.
✓ Branch 3 taken 88424 times.
✗ Branch 4 not taken.
90342 switch (type) {
89 798 case SWS_PIXEL_U8: return (SwsPixel) { .u8 = rnd() & 0xFF };
90 700 case SWS_PIXEL_U16: return (SwsPixel) { .u16 = rnd() & 0xFFFF };
91 420 case SWS_PIXEL_U32: return (SwsPixel) { .u32 = rnd() };
92 88424 case SWS_PIXEL_F32: return (SwsPixel) { .f32 = rndf() };
93 default: return (SwsPixel) {0};
94 }
95 }
96
97 1121 static void fill32f(float *line, int num, unsigned range)
98 {
99 1121 const float scale = (float) range / UINT32_MAX;
100
2/2
✓ Branch 0 taken 4591616 times.
✓ Branch 1 taken 1121 times.
4592737 for (int i = 0; i < num; i++)
101
2/2
✓ Branch 0 taken 364544 times.
✓ Branch 1 taken 4227072 times.
4591616 line[i] = range ? scale * rnd() : rndf();
102 1121 }
103
104 2735 static void fill32(uint32_t *line, int num, unsigned range)
105 {
106
2/2
✓ Branch 0 taken 11202560 times.
✓ Branch 1 taken 2735 times.
11205295 for (int i = 0; i < num; i++)
107
4/4
✓ Branch 0 taken 425984 times.
✓ Branch 1 taken 10776576 times.
✓ Branch 2 taken 393216 times.
✓ Branch 3 taken 32768 times.
11202560 line[i] = (range && range < UINT_MAX) ? rnd() % (range + 1) : rnd();
108 2735 }
109
110 1166 static void fill16(uint16_t *line, int num, unsigned range)
111 {
112
2/2
✓ Branch 0 taken 1064 times.
✓ Branch 1 taken 102 times.
1166 if (!range) {
113 1064 fill32((uint32_t *) line, AV_CEIL_RSHIFT(num, 1), 0);
114 } else {
115
2/2
✓ Branch 0 taken 835584 times.
✓ Branch 1 taken 102 times.
835686 for (int i = 0; i < num; i++)
116 835584 line[i] = rnd() % (range + 1);
117 }
118 1166 }
119
120 1304 static void fill8(uint8_t *line, int num, unsigned range)
121 {
122
2/2
✓ Branch 0 taken 1181 times.
✓ Branch 1 taken 123 times.
1304 if (!range) {
123 1181 fill32((uint32_t *) line, AV_CEIL_RSHIFT(num, 2), 0);
124 } else {
125
2/2
✓ Branch 0 taken 2015232 times.
✓ Branch 1 taken 123 times.
2015355 for (int i = 0; i < num; i++)
126 2015232 line[i] = rnd() % (range + 1);
127 }
128 1304 }
129
130 13244 static SwsPixelType pixel_type_to_int(const SwsPixelType type)
131 {
132
3/4
✓ Branch 0 taken 2170 times.
✓ Branch 1 taken 2562 times.
✓ Branch 2 taken 8512 times.
✗ Branch 3 not taken.
13244 switch (ff_sws_pixel_type_size(type)) {
133 2170 case 1: return SWS_PIXEL_U8;
134 2562 case 2: return SWS_PIXEL_U16;
135 8512 case 4: return SWS_PIXEL_U32;
136 default: break;
137 }
138
139 av_unreachable("Invalid pixel type!");
140 return SWS_PIXEL_NONE;
141 }
142
143 10165 static void check_compiled(const Test *test,
144 const SwsCompiledOp *comp_ref,
145 const SwsCompiledOp *comp_new)
146 {
147 /**
148 * We can't use `check_func()` alone because the actual function pointer
149 * may be a wrapper or entry point shared by multiple implementations.
150 * Solve it by hashing in the active CPU flags as well.
151 */
152 10165 uintptr_t id = (uintptr_t) comp_new->func;
153 10165 id ^= (id << 6) + (id >> 2) + 0x9e3779b97f4a7c15 + comp_new->cpu_flags;
154
2/2
✓ Branch 1 taken 8810 times.
✓ Branch 2 taken 1355 times.
10165 if (!check_key(id, "%s", test->name))
155 8810 return;
156
157 1355 declare_func(void, const SwsOpExec *, const void *, int bx, int y, int bx_end, int y_end);
158
159 static DECLARE_ALIGNED_64(char, src0)[NB_PLANES][LINES][PIXELS * sizeof(uint32_t[4])];
160 static DECLARE_ALIGNED_64(char, src1)[NB_PLANES][LINES][PIXELS * sizeof(uint32_t[4])];
161 static DECLARE_ALIGNED_64(char, dst0)[NB_PLANES][LINES][PIXELS * sizeof(uint32_t[4])];
162 static DECLARE_ALIGNED_64(char, dst1)[NB_PLANES][LINES][PIXELS * sizeof(uint32_t[4])];
163
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1355 times.
1355 av_assert0(PIXELS % comp_new->block_size == 0);
165
2/2
✓ Branch 0 taken 5420 times.
✓ Branch 1 taken 1355 times.
6775 for (int p = 0; p < NB_PLANES; p++) {
166 5420 void *plane = src0[p];
167
2/2
✓ Branch 0 taken 1339 times.
✓ Branch 1 taken 4081 times.
5420 if (!SWS_COMP_TEST(test->planes_in, p)) {
168 1339 memset(plane, 0, sizeof(src0[p]));
169 1339 continue;
170 }
171
172
4/5
✓ Branch 0 taken 1304 times.
✓ Branch 1 taken 1166 times.
✓ Branch 2 taken 490 times.
✓ Branch 3 taken 1121 times.
✗ Branch 4 not taken.
4081 switch (test->type_in) {
173 1304 case SWS_PIXEL_U8:
174 1304 fill8(plane, sizeof(src0[p]) / sizeof(uint8_t), test->ranges[p]);
175 1304 break;
176 1166 case SWS_PIXEL_U16:
177 1166 fill16(plane, sizeof(src0[p]) / sizeof(uint16_t), test->ranges[p]);
178 1166 break;
179 490 case SWS_PIXEL_U32:
180 490 fill32(plane, sizeof(src0[p]) / sizeof(uint32_t), test->ranges[p]);
181 490 break;
182 1121 case SWS_PIXEL_F32:
183 1121 fill32f(plane, sizeof(src0[p]) / sizeof(uint32_t), test->ranges[p]);
184 1121 break;
185 }
186 }
187
188 1355 memcpy(src1, src0, sizeof(src0));
189 1355 memset(dst0, 0, sizeof(dst0));
190 1355 memset(dst1, 0, sizeof(dst1));
191
192 1355 const int read_size = PIXELS * test->pixel_bits_in >> 3;
193 1355 const int write_size = PIXELS * test->pixel_bits_out >> 3;
194
195 1355 SwsOpExec exec = {0};
196 1355 exec.width = PIXELS;
197 1355 exec.height = exec.slice_h = LINES;
198
2/2
✓ Branch 0 taken 5420 times.
✓ Branch 1 taken 1355 times.
6775 for (int i = 0; i < NB_PLANES; i++) {
199 5420 exec.in_stride[i] = sizeof(src0[i][0]);
200 5420 exec.out_stride[i] = sizeof(dst0[i][0]);
201 5420 exec.in_bump[i] = exec.in_stride[i] - read_size;
202 5420 exec.out_bump[i] = exec.out_stride[i] - write_size;
203 }
204
205 int32_t in_bump_y[LINES];
206 int32_t in_offset_x[PIXELS];
207 1355 const SwsUOp *read_op = &test->uops[0];
208
4/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 240 times.
✓ Branch 2 taken 336 times.
✓ Branch 3 taken 776 times.
1355 switch (read_op->uop) {
209 3 case SWS_UOP_READ_PALETTE:
210 3 exec.in_bump[1] = exec.in_stride[1] = 0;
211 static_assert(sizeof(src0[1]) >= sizeof(uint32_t[256]), "palette plane too small");
212 3 break;
213 240 case SWS_UOP_READ_PLANAR_FV: {
214 240 const int *offsets = read_op->data.kernel->offsets;
215
2/2
✓ Branch 0 taken 3600 times.
✓ Branch 1 taken 240 times.
3840 for (int y = 0; y < LINES - 1; y++)
216 3600 in_bump_y[y] = offsets[y + 1] - offsets[y] - 1;
217 240 in_bump_y[LINES - 1] = 0;
218 240 exec.in_bump_y = in_bump_y;
219 240 break;
220 }
221 336 case SWS_UOP_READ_PLANAR_FH: {
222 336 const int *offsets = read_op->data.kernel->offsets;
223
2/2
✓ Branch 0 taken 21504 times.
✓ Branch 1 taken 336 times.
21840 for (int x = 0; x < PIXELS; x++)
224 21504 in_offset_x[x] = offsets[x] * test->pixel_bits_in >> 3;
225 336 exec.in_offset_x = in_offset_x;
226 }
227 }
228
229
2/2
✓ Branch 0 taken 5420 times.
✓ Branch 1 taken 1355 times.
6775 for (int i = 0; i < NB_PLANES; i++) {
230 5420 exec.in[i] = (void *) src0[i];
231 5420 exec.out[i] = (void *) dst0[i];
232 5420 exec.block_size_in[i] = comp_ref->block_size * test->pixel_bits_in >> 3;
233 5420 exec.block_size_out[i] = comp_ref->block_size * test->pixel_bits_out >> 3;
234 }
235
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1355 times.
1355 checkasm_call(comp_ref->func, &exec, comp_ref->priv, 0, 0, PIXELS / comp_ref->block_size, LINES);
236
237
2/2
✓ Branch 0 taken 5420 times.
✓ Branch 1 taken 1355 times.
6775 for (int i = 0; i < NB_PLANES; i++) {
238 5420 exec.in[i] = (void *) src1[i];
239 5420 exec.out[i] = (void *) dst1[i];
240 5420 exec.block_size_in[i] = comp_new->block_size * test->pixel_bits_in >> 3;
241 5420 exec.block_size_out[i] = comp_new->block_size * test->pixel_bits_out >> 3;
242 }
243 1355 checkasm_call_checked(comp_new->func, &exec, comp_new->priv, 0, 0, PIXELS / comp_new->block_size, LINES);
244
245
2/2
✓ Branch 0 taken 5420 times.
✓ Branch 1 taken 1355 times.
6775 for (int i = 0; i < NB_PLANES; i++) {
246
2/2
✓ Branch 0 taken 1457 times.
✓ Branch 1 taken 3963 times.
5420 if (!SWS_COMP_TEST(test->planes_out, i))
247 1457 continue;
248
249 3963 const char *desc = FMT("%s[%d]", test->name, i);
250 3963 const int stride = sizeof(dst0[i][0]);
251
4/5
✓ Branch 0 taken 726 times.
✓ Branch 1 taken 704 times.
✓ Branch 2 taken 451 times.
✓ Branch 3 taken 2082 times.
✗ Branch 4 not taken.
3963 switch (test->type_out) {
252 726 case SWS_PIXEL_U8:
253 726 checkasm_check(uint8_t, (void *) dst0[i], stride,
254 (void *) dst1[i], stride,
255 write_size, LINES, desc);
256 726 break;
257 704 case SWS_PIXEL_U16:
258 704 checkasm_check(uint16_t, (void *) dst0[i], stride,
259 (void *) dst1[i], stride,
260 write_size >> 1, LINES, desc);
261 704 break;
262 451 case SWS_PIXEL_U32:
263 451 checkasm_check(uint32_t, (void *) dst0[i], stride,
264 (void *) dst1[i], stride,
265 write_size >> 2, LINES, desc);
266 451 break;
267 2082 case SWS_PIXEL_F32:
268 2082 checkasm_check(float_ulp, (void *) dst0[i], stride,
269 (void *) dst1[i], stride,
270 write_size >> 2, LINES, desc, 0);
271 2082 break;
272 }
273 }
274
275
1/18
✗ Branch 1 not taken.
✓ Branch 2 taken 1355 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.
1355 bench(comp_new->func, &exec, comp_new->priv, 0, 0, PIXELS / comp_new->block_size, LINES);
276 }
277
278 8960 static void run_test(const Test *test)
279 {
280 8960 SwsContext *ctx = sws_alloc_context();
281
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8960 times.
8960 if (!ctx)
282 return;
283 8960 ctx->flags = SWS_BITEXACT;
284
285 /* Generate dummy uop list on-stack */
286 8960 SwsUOpList oplist = {
287 8960 .ops = (SwsUOp *) test->uops,
288 8960 .num_ops = test->num_uops,
289
290 /* Less efficient, but works universally */
291 .planes_in = SWS_COMP_ALL,
292 .planes_out = SWS_COMP_ALL,
293 };
294
295
2/2
✓ Branch 0 taken 22204 times.
✓ Branch 1 taken 8960 times.
31164 for (int i = 0; i < test->num_uops; i++) {
296 22204 const int pixel_size = ff_sws_pixel_type_size(test->uops[i].type);
297
2/2
✓ Branch 0 taken 12026 times.
✓ Branch 1 taken 10178 times.
22204 if (pixel_size > oplist.pixel_size_max)
298 12026 oplist.pixel_size_max = pixel_size;
299 }
300
301 static const SwsOpBackend *backend_ref;
302
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8959 times.
8960 if (!backend_ref) {
303
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 for (int n = 0; ff_sws_op_backends[n]; n++) {
304
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (!strcmp(ff_sws_op_backends[n]->name, "c")) {
305 1 backend_ref = ff_sws_op_backends[n];
306 1 break;
307 }
308 }
309
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 av_assert0(backend_ref);
310 }
311
312 /* Always compile `ops` using the C backend as a reference */
313 8960 SwsCompiledOp comp_ref = {0};
314 8960 int ret = backend_ref->compile_uops(ctx, &oplist, &comp_ref);
315
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8960 times.
8960 if (ret < 0) {
316 av_assert0(ret != AVERROR(ENOTSUP));
317 fail();
318 goto done;
319 }
320
321 /* Check with the C backend to establish a reference */
322 8960 check_compiled(test, &comp_ref, &comp_ref);
323
324 /* Iterate over every other backend, and test it against the C reference */
325
2/2
✓ Branch 0 taken 26880 times.
✓ Branch 1 taken 8960 times.
35840 for (int n = 0; ff_sws_op_backends[n]; n++) {
326 26880 const SwsOpBackend *backend = ff_sws_op_backends[n];
327
3/4
✓ Branch 0 taken 26880 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 8960 times.
✓ Branch 3 taken 17920 times.
26880 if (backend->hw_format != AV_PIX_FMT_NONE || backend == backend_ref)
328 25675 continue;
329
2/2
✓ Branch 0 taken 8960 times.
✓ Branch 1 taken 8960 times.
17920 if (!backend->compile_uops)
330 8960 continue;
331
332 8960 SwsCompiledOp comp_new = {0};
333 8960 int ret = backend->compile_uops(ctx, &oplist, &comp_new);
334
2/2
✓ Branch 0 taken 7755 times.
✓ Branch 1 taken 1205 times.
8960 if (ret == AVERROR(ENOTSUP)) {
335 7755 continue;
336
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1205 times.
1205 } else if (ret < 0) {
337 fail();
338 goto done;
339 }
340
341 /* Distinguish backends from each other even with same CPU flags */
342 1205 checkasm_set_func_variant("%s_%s", backend->name, checkasm_get_cpu_suffix());
343 1205 check_compiled(test, &comp_ref, &comp_new);
344 1205 ff_sws_compiled_op_unref(&comp_new);
345 }
346
347 8960 done:
348 8960 ff_sws_compiled_op_unref(&comp_ref);
349 8960 sws_free_context(&ctx);
350 }
351
352 8960 static void check_uop(const char *name, const SwsUOp *uop,
353 SwsCompMask mask_in, SwsCompMask mask_out,
354 const unsigned ranges[NB_PLANES])
355 {
356 8960 Test t = {
357 .name = name,
358 8960 .type_in = uop->type,
359 8960 .type_out = uop->type,
360 };
361
362
4/4
✓ Branch 0 taken 7700 times.
✓ Branch 1 taken 7420 times.
✓ Branch 2 taken 6160 times.
✓ Branch 3 taken 1540 times.
15120 for (int i = 0; ranges && i < NB_PLANES; i++)
363 6160 t.ranges[i] = ranges[i];
364
365 /* uops with non-standard output type conversions */
366
7/8
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 182 times.
✓ Branch 2 taken 126 times.
✓ Branch 3 taken 266 times.
✓ Branch 4 taken 70 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 4032 times.
✓ Branch 7 taken 4186 times.
8960 switch (uop->uop) {
367 98 case SWS_UOP_TO_U8: t.type_out = SWS_PIXEL_U8; break;
368 182 case SWS_UOP_TO_U16: t.type_out = SWS_PIXEL_U16; break;
369 126 case SWS_UOP_TO_U32: t.type_out = SWS_PIXEL_U32; break;
370 266 case SWS_UOP_TO_F32: t.type_out = SWS_PIXEL_F32; break;
371 70 case SWS_UOP_EXPAND_PAIR: t.type_out = SWS_PIXEL_U16; break;
372 case SWS_UOP_EXPAND_QUAD: t.type_out = SWS_PIXEL_U32; break;
373 4032 case SWS_UOP_READ_PLANAR_FH:
374 case SWS_UOP_READ_PLANAR_FV:
375 4032 t.type_out = uop->par.filter.type;
376 4032 break;
377 }
378
379 /* Set up read/write metadata for rw uops */
380 8960 const int bits_in = ff_sws_pixel_type_size(t.type_in) * 8;
381 8960 const int bits_out = ff_sws_pixel_type_size(t.type_out) * 8;
382
10/10
✓ Branch 0 taken 4186 times.
✓ Branch 1 taken 168 times.
✓ Branch 2 taken 126 times.
✓ Branch 3 taken 126 times.
✓ Branch 4 taken 14 times.
✓ Branch 5 taken 14 times.
✓ Branch 6 taken 14 times.
✓ Branch 7 taken 14 times.
✓ Branch 8 taken 14 times.
✓ Branch 9 taken 4284 times.
8960 switch (uop->uop) {
383 4186 case SWS_UOP_READ_PLANAR:
384 case SWS_UOP_READ_PLANAR_FH:
385 case SWS_UOP_READ_PLANAR_FV:
386 4186 t.planes_in = uop->mask;
387 4186 t.pixel_bits_in = bits_in;
388 4186 break;
389 168 case SWS_UOP_WRITE_PLANAR:
390 168 t.planes_out = uop->mask;
391 168 t.pixel_bits_out = bits_out;
392 168 break;
393 126 case SWS_UOP_READ_PACKED:
394 126 t.planes_in = SWS_COMP_ELEMS(1);
395 126 t.pixel_bits_in = bits_in * SWS_COMP_COUNT(uop->mask);
396 126 break;
397 126 case SWS_UOP_WRITE_PACKED:
398 126 t.planes_out = SWS_COMP_ELEMS(1);
399 126 t.pixel_bits_out = bits_out * SWS_COMP_COUNT(uop->mask);
400 126 break;
401 14 case SWS_UOP_READ_NIBBLE:
402 14 t.planes_in = SWS_COMP_ELEMS(1);
403 14 t.pixel_bits_in = 4;
404 14 break;
405 14 case SWS_UOP_WRITE_NIBBLE:
406 14 t.planes_out = SWS_COMP_ELEMS(1);
407 14 t.pixel_bits_out = 4;
408 14 break;
409 14 case SWS_UOP_READ_BIT:
410 14 t.planes_in = SWS_COMP_ELEMS(1);
411 14 t.pixel_bits_in = 1;
412 14 break;
413 14 case SWS_UOP_WRITE_BIT:
414 14 t.planes_out = SWS_COMP_ELEMS(1);
415 14 t.pixel_bits_out = 1;
416 14 break;
417 14 case SWS_UOP_READ_PALETTE:
418 14 t.planes_in = SWS_COMP_ELEMS(2);
419 14 t.pixel_bits_in = 8; /* size of index */
420 14 break;
421 }
422
423 /* Add read uop if needed */
424
2/2
✓ Branch 0 taken 4606 times.
✓ Branch 1 taken 4354 times.
8960 if (!t.planes_in) {
425 4606 t.planes_in = mask_in;
426 4606 t.pixel_bits_in = bits_in;
427 4606 t.uops[t.num_uops++] = (SwsUOp) {
428 4606 .type = pixel_type_to_int(t.type_in),
429 .uop = SWS_UOP_READ_PLANAR,
430 .mask = SWS_COMP_ALL, /* extra planes are zero'd */
431 };
432 }
433
434 /* Add the UOp itself */
435 8960 t.uops[t.num_uops++] = *uop;
436
437 /* Add write uop if needed */
438
2/2
✓ Branch 0 taken 8638 times.
✓ Branch 1 taken 322 times.
8960 if (!t.planes_out) {
439 8638 t.planes_out = mask_out;
440 8638 t.pixel_bits_out = bits_out;
441 8638 t.uops[t.num_uops++] = (SwsUOp) {
442 8638 .type = pixel_type_to_int(t.type_out),
443 .uop = SWS_UOP_WRITE_PLANAR,
444 .mask = SWS_COMP_ALL, /* extra planes are ignored */
445 };
446 }
447
448 8960 run_test(&t);
449 8960 }
450
451 2058 static void check_range(const char *name, const SwsUOp *uop,
452 const unsigned ranges[NB_PLANES])
453 {
454 /* Test all planes to ensure data remains untouched */
455 2058 check_uop(name, uop, SWS_COMP_ALL, SWS_COMP_ALL, ranges);
456 2058 }
457
458 1834 static void check_simple(const char *name, const SwsUOp *uop)
459 {
460 1834 check_range(name, uop, NULL);
461 1834 }
462
463 112 static void check_scalar(const char *name, SwsUOp *uop)
464 {
465 112 uop->data.scalar = rndpx(uop->type);
466 112 check_simple(name, uop);
467 112 }
468
469 686 static void check_vec4(const char *name, SwsUOp *uop)
470 {
471
2/2
✓ Branch 0 taken 2744 times.
✓ Branch 1 taken 686 times.
3430 for (int i = 0; i < 4; i++)
472 2744 uop->data.vec4[i] = rndpx(uop->type);
473
474 686 check_simple(name, uop);
475 686 }
476
477 #define MK_RANGES(R) ((const unsigned[]) { R, R, R, R })
478
479 4354 static void check_read(const char *name, SwsUOp *uop)
480 {
481 4354 check_uop(name, uop, uop->mask, uop->mask, NULL);
482 4354 }
483
484 322 static void check_write(const char *name, const SwsUOp *uop)
485 {
486
2/2
✓ Branch 0 taken 308 times.
✓ Branch 1 taken 14 times.
322 const int frac = uop->uop == SWS_UOP_WRITE_BIT ? 3 :
487 308 uop->uop == SWS_UOP_WRITE_NIBBLE ? 1 : 0;
488 322 const int bits = 8 >> frac;
489 322 const unsigned range = (1 << bits) - 1;
490 322 check_uop(name, uop, uop->mask, uop->mask, MK_RANGES(range));
491 322 }
492
493 336 static void check_filter(const char *name, SwsUOp *uop)
494 {
495 336 const bool is_vert = uop->uop == SWS_UOP_READ_PLANAR_FV;
496
497 672 SwsFilterParams par = {
498 .scaler_params = { SWS_PARAM_DEFAULT, SWS_PARAM_DEFAULT },
499
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 168 times.
336 .dst_size = is_vert ? LINES : PIXELS,
500 };
501
502 336 const SwsScaler scalers[] = {
503 SWS_SCALE_POINT,
504 SWS_SCALE_SINC,
505 };
506
507
2/2
✓ Branch 0 taken 672 times.
✓ Branch 1 taken 336 times.
1008 for (int s = 0; s < FF_ARRAY_ELEMS(scalers); s++) {
508 672 par.scaler = scalers[s];
509
510
2/2
✓ Branch 0 taken 4032 times.
✓ Branch 1 taken 672 times.
4704 for (par.src_size = 1; par.src_size <= par.dst_size; par.src_size <<= 1) {
511
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4032 times.
4032 if (ff_sws_filter_generate(NULL, &par, &uop->data.kernel) < 0) {
512 fail();
513 return;
514 }
515
516 char desc[256];
517 4032 snprintf(desc, sizeof(desc), "%s_%s_%d", name,
518 4032 uop->data.kernel->name, par.src_size);
519
520 4032 check_read(desc, uop);
521 4032 av_refstruct_unref(&uop->data.kernel);
522 }
523 }
524 }
525
526 1232 static void check_swizzle(const char *name, const SwsUOp *uop)
527 {
528 /* Only check data equality in needed components; since the others
529 * could either remain untouched or contain garbage */
530 1232 check_uop(name, uop, SWS_COMP_ALL, uop->mask, NULL);
531 1232 }
532
533 28 static void check_expand_bit(const char *name, const SwsUOp *uop)
534 {
535 28 check_range(name, uop, MK_RANGES(1));
536 28 }
537
538 742 static void check_cast(const char *name, const SwsUOp *uop)
539 {
540 SwsPixelType dst;
541
5/7
✓ Branch 0 taken 98 times.
✓ Branch 1 taken 182 times.
✓ Branch 2 taken 126 times.
✓ Branch 3 taken 266 times.
✓ Branch 4 taken 70 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
742 switch (uop->uop) {
542 98 case SWS_UOP_TO_U8: dst = SWS_PIXEL_U8; break;
543 182 case SWS_UOP_TO_U16: dst = SWS_PIXEL_U16; break;
544 126 case SWS_UOP_TO_U32: dst = SWS_PIXEL_U32; break;
545 266 case SWS_UOP_TO_F32: dst = SWS_PIXEL_F32; break;
546 70 case SWS_UOP_EXPAND_PAIR: dst = SWS_PIXEL_U16; break;
547 case SWS_UOP_EXPAND_QUAD: dst = SWS_PIXEL_U32; break;
548 default: return;
549 }
550
551 742 const int isize = ff_sws_pixel_type_size(uop->type);
552 742 const int osize = ff_sws_pixel_type_size(dst);
553 742 unsigned range = UINT32_MAX >> (32 - osize * 8);
554
4/4
✓ Branch 0 taken 364 times.
✓ Branch 1 taken 378 times.
✓ Branch 2 taken 56 times.
✓ Branch 3 taken 308 times.
742 if (isize < osize || !ff_sws_pixel_type_is_int(dst))
555 434 range = 0;
556
557 742 check_uop(name, uop, uop->mask, uop->mask, MK_RANGES(range));
558 }
559
560 140 static void check_scale(const char *name, SwsUOp *uop)
561 {
562 140 const int bits = ff_sws_pixel_type_size(uop->type) * 8;
563 140 const unsigned max = UINT32_MAX >> (32 - bits);
564 140 SwsPixel scale = uop->data.scalar = rndpx(uop->type);
565 140 unsigned range = 0;
566
567 /* Ensure the result won't exceed the value range */
568
4/4
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 70 times.
140 switch (uop->type) {
569
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 case SWS_PIXEL_U8: range = max / (scale.u8 ? scale.u8 : 1); break;
570
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 case SWS_PIXEL_U16: range = max / (scale.u16 ? scale.u16 : 1); break;
571
1/2
✓ Branch 0 taken 28 times.
✗ Branch 1 not taken.
28 case SWS_PIXEL_U32: range = max / (scale.u32 ? scale.u32 : 1); break;
572 }
573
574 140 check_range(name, uop, MK_RANGES(range));
575 140 }
576
577 140 static void check_unpack(const char *name, const SwsUOp *uop)
578 {
579 140 const uint8_t *pat = uop->par.pack.pattern;
580 140 const int total = pat[0] + pat[1] + pat[2] + pat[3];
581 140 const unsigned range = UINT32_MAX >> (32 - total);
582
583 140 check_uop(name, uop, SWS_COMP(0), uop->mask, MK_RANGES(range));
584 140 }
585
586 112 static void check_pack(const char *name, const SwsUOp *uop)
587 {
588 112 const uint8_t *pat = uop->par.pack.pattern;
589 112 const unsigned ranges[4] = {
590 112 (1 << pat[0]) - 1, (1 << pat[1]) - 1,
591 112 (1 << pat[2]) - 1, (1 << pat[3]) - 1,
592 };
593
594 112 check_uop(name, uop, uop->mask, SWS_COMP(0), ranges);
595 112 }
596
597 224 static void check_linear(const char *name, SwsUOp *uop)
598 {
599 224 const SwsLinearUOp *par = &uop->par.lin;
600
2/2
✓ Branch 0 taken 896 times.
✓ Branch 1 taken 224 times.
1120 for (int i = 0; i < 4; i++) {
601
2/2
✓ Branch 0 taken 4480 times.
✓ Branch 1 taken 896 times.
5376 for (int j = 0; j < 5; j++) {
602
2/2
✓ Branch 0 taken 3108 times.
✓ Branch 1 taken 1372 times.
4480 if (par->zero & SWS_MASK(i, j))
603 3108 uop->data.mat4[i][j] = constpx(uop->type, 0);
604
2/2
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 1330 times.
1372 else if (par->one & SWS_MASK(i, j))
605 42 uop->data.mat4[i][j] = constpx(uop->type, 1);
606 else
607 1330 uop->data.mat4[i][j] = rndpx(uop->type);
608 }
609 }
610
611 224 check_simple(name, uop);
612 224 }
613
614 336 static void check_dither(const char *name, SwsUOp *uop)
615 {
616
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
336 av_assert0(!ff_sws_pixel_type_is_int(uop->type));
617
618 336 const int size = 1 << uop->par.dither.size_log2;
619 336 const int height = ff_sws_dither_height(&uop->par.dither);
620 336 SwsPixel *matrix = av_refstruct_allocz(size * height * sizeof(*matrix));
621
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 336 times.
336 if (!matrix) {
622 fail();
623 return;
624 }
625
626
2/2
✓ Branch 0 taken 86016 times.
✓ Branch 1 taken 336 times.
86352 for (int i = 0; i < size * size; i++)
627 86016 matrix[i] = rndpx(uop->type);
628 336 memcpy(matrix + size * size, matrix,
629 336 size * (height - size) * sizeof(*matrix));
630
631 336 uop->data.ptr = matrix;
632 336 check_simple(name, uop);
633
634 336 av_refstruct_unref(&matrix);
635 }
636
637 56 static void check_lut_3d(const char *name, SwsUOp *uop)
638 {
639 56 SwsLut3D *lut3d = ff_sws_lut3d_alloc();
640
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
56 if (!lut3d) {
641 fail();
642 return;
643 }
644
645 56 checkasm_init(&lut3d->input, sizeof(lut3d->input));
646
647
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 28 times.
56 if (uop->par.lut3d.dynamic) {
648 28 checkasm_init(&lut3d->tone_map, sizeof(lut3d->tone_map));
649 28 checkasm_init(&lut3d->output, sizeof(lut3d->output));
650 28 lut3d->dynamic = true;
651
652 /* Prevent out-of-bounds read from IPT values with abs(PT) > 0.5 */
653
2/2
✓ Branch 0 taken 7224 times.
✓ Branch 1 taken 28 times.
7252 for (int i = 0; i < FF_ARRAY_ELEMS(lut3d->tone_map); i++)
654 7224 lut3d->tone_map[i].y = FFMIN(lut3d->tone_map[i].y, 1 << 15);
655 }
656
657 56 uop->data.lut3d = lut3d;
658 56 check_range(name, uop, MK_RANGES(INPUT_LUT_SIZE - 1));
659
660 56 av_refstruct_unref(&lut3d);
661 }
662
663 #define CHECK_FUNCTION(CHECK, NAME, ...) \
664 CHECK(#NAME, &(SwsUOp) { __VA_ARGS__ });
665
666 #define CHECK_FOR(UOP, CHECK) \
667 SWS_FOR_STRUCT(U8, UOP, CHECK_FUNCTION, CHECK) \
668 SWS_FOR_STRUCT(U16, UOP, CHECK_FUNCTION, CHECK) \
669 SWS_FOR_STRUCT(U32, UOP, CHECK_FUNCTION, CHECK) \
670 SWS_FOR_STRUCT(F32, UOP, CHECK_FUNCTION, CHECK) \
671 report(#UOP)
672
673 14 void checkasm_check_sw_ops(void)
674 {
675 14 CHECK_FOR(READ_PLANAR, check_read);
676 14 CHECK_FOR(READ_PLANAR_FH, check_filter);
677 14 CHECK_FOR(READ_PLANAR_FV, check_filter);
678 14 CHECK_FOR(READ_PACKED, check_read);
679 14 CHECK_FOR(READ_NIBBLE, check_read);
680 14 CHECK_FOR(READ_BIT, check_read);
681 14 CHECK_FOR(READ_PALETTE, check_read);
682 14 CHECK_FOR(WRITE_PLANAR, check_write);
683 14 CHECK_FOR(WRITE_PACKED, check_write);
684 14 CHECK_FOR(WRITE_NIBBLE, check_write);
685 14 CHECK_FOR(WRITE_BIT, check_write);
686 14 CHECK_FOR(PERMUTE, check_swizzle);
687 14 CHECK_FOR(COPY, check_swizzle);
688 14 CHECK_FOR(SWAP_BYTES, check_simple);
689 14 CHECK_FOR(EXPAND_BIT, check_expand_bit);
690 14 CHECK_FOR(EXPAND_PAIR, check_cast);
691 14 CHECK_FOR(EXPAND_QUAD, check_cast);
692 14 CHECK_FOR(TO_U8, check_cast);
693 14 CHECK_FOR(TO_U16, check_cast);
694 14 CHECK_FOR(TO_U32, check_cast);
695 14 CHECK_FOR(TO_F32, check_cast);
696 14 CHECK_FOR(SCALE, check_scale);
697 14 CHECK_FOR(ADD, check_scalar);
698 14 CHECK_FOR(MIN, check_vec4);
699 14 CHECK_FOR(MAX, check_vec4);
700 14 CHECK_FOR(UNPACK, check_unpack);
701 14 CHECK_FOR(PACK, check_pack);
702 14 CHECK_FOR(LSHIFT, check_simple);
703 14 CHECK_FOR(RSHIFT, check_simple);
704 14 CHECK_FOR(CLEAR, check_vec4);
705 14 CHECK_FOR(LINEAR, check_linear);
706 14 CHECK_FOR(DITHER, check_dither);
707 14 CHECK_FOR(LUT_3D, check_lut_3d);
708 14 }
709