FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/uops_macros_gen.c
Date: 2026-09-15 09:36:26
Exec Total Coverage
Lines: 210 224 93.8%
Functions: 10 10 100.0%
Branches: 57 70 81.4%

Line Branch Exec Source
1 /**
2 * This file is part of FFmpeg.
3 *
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include <string.h>
20
21 #ifdef _WIN32
22 #include <io.h>
23 #include <fcntl.h>
24 #endif
25
26 #include "libavutil/bprint.h"
27 #include "libavutil/error.h"
28 #include "libavutil/macros.h"
29 #include "libavutil/mem.h"
30 #include "libavutil/pixfmt.h"
31 #include "libavutil/thread.h"
32 #include "libavutil/tree.h"
33 #include "ops.h"
34 #include "ops_dispatch.h"
35 #include "op_list_gen_template.c"
36 #include "swscale.h"
37 #include "uops.h"
38 #include "uops_list.h"
39
40 static const struct {
41 char full[32];
42 char abbr[32];
43 } uop_names[SWS_UOP_TYPE_NB] = {
44 #define UOP_NAME(OP, ABBR) [OP] = { #OP, ABBR },
45 UOPS_LIST(UOP_NAME)
46 };
47
48 static const struct {
49 char full[16];
50 char prefix[8];
51 } pixel_types[SWS_PIXEL_TYPE_NB] = {
52 [SWS_PIXEL_NONE] = { "SWS_PIXEL_NONE", "" },
53 [SWS_PIXEL_U8] = { "SWS_PIXEL_U8", "U8_" },
54 [SWS_PIXEL_U16] = { "SWS_PIXEL_U16", "U16_" },
55 [SWS_PIXEL_U32] = { "SWS_PIXEL_U32", "U32_" },
56 [SWS_PIXEL_F32] = { "SWS_PIXEL_F32", "F32_" },
57 };
58
59 438 static int generate_entry_struct(void *opaque, void *key)
60 {
61 438 const SwsUOp *ref = opaque;
62 438 const SwsUOp *uop = key;
63 438 AVBPrint *bp = ref->data.opaque;
64 char name[SWS_UOP_NAME_MAX];
65 438 ff_sws_uop_name(uop, name);
66 438 av_bprintf(bp, " \\\n MACRO(__VA_ARGS__, %-40s", name);
67 438 av_bprintf(bp, ", .type = %-13s, .uop = %-24s, .mask = 0x%x",
68 438 pixel_types[uop->type].full, uop_names[uop->uop].full, uop->mask);
69
70 438 const SwsUOpParams *par = &uop->par;
71
10/10
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 88 times.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 33 times.
✓ Branch 6 taken 44 times.
✓ Branch 7 taken 24 times.
✓ Branch 8 taken 4 times.
✓ Branch 9 taken 148 times.
438 switch (uop->uop) {
72 36 case SWS_UOP_READ_PLANAR_FH:
73 case SWS_UOP_READ_PLANAR_FV:
74 case SWS_UOP_READ_PLANAR_FV_FMA:
75 36 av_bprintf(bp, ", .par.filter.type = %s", pixel_types[par->filter.type].full);
76 36 break;
77 22 case SWS_UOP_RW_SHUFFLE:
78 22 av_bprintf(bp, ", .par.shuffle.clear_value = 0x%x"
79 ", .par.shuffle.read_size = %u"
80 ", .par.shuffle.write_size = %u",
81 22 par->shuffle.clear_value,
82 22 par->shuffle.read_size, par->shuffle.write_size);
83 22 break;
84 21 case SWS_UOP_LSHIFT:
85 case SWS_UOP_RSHIFT:
86 21 av_bprintf(bp, ", .par.shift.amount = %u", par->shift.amount);
87 21 break;
88 88 case SWS_UOP_PERMUTE:
89 case SWS_UOP_COPY:
90 88 av_bprintf(bp, ", .par.move.num_moves = %d", par->move.num_moves);
91 88 av_bprintf(bp, ", .par.move.dst = {%d, %d, %d, %d, %d, %d}",
92 88 par->move.dst[0], par->move.dst[1], par->move.dst[2],
93 88 par->move.dst[3], par->move.dst[4], par->move.dst[5]);
94 88 av_bprintf(bp, ", .par.move.src = {%d, %d, %d, %d, %d, %d}",
95 88 par->move.src[0], par->move.src[1], par->move.src[2],
96 88 par->move.src[3], par->move.src[4], par->move.src[5]);
97 88 break;
98 18 case SWS_UOP_PACK:
99 case SWS_UOP_UNPACK:
100 18 av_bprintf(bp, ", .par.pack.pattern = {%d, %d, %d, %d}",
101 18 par->pack.pattern[0], par->pack.pattern[1],
102 18 par->pack.pattern[2], par->pack.pattern[3]);
103 18 break;
104 33 case SWS_UOP_CLEAR:
105 33 av_bprintf(bp, ", .par.clear.one = 0x%x, .par.clear.zero = 0x%x",
106 33 par->clear.one, par->clear.zero);
107 33 break;
108 44 case SWS_UOP_LINEAR:
109 case SWS_UOP_LINEAR_FMA:
110 44 av_bprintf(bp, ", .par.lin.one = 0x%x, .par.lin.zero = 0x%x",
111 44 par->lin.one, par->lin.zero);
112
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 16 times.
44 if (uop->uop == SWS_UOP_LINEAR_FMA)
113 28 av_bprintf(bp, ", .par.lin.exact = 0x%x", par->lin.exact);
114 44 break;
115 24 case SWS_UOP_DITHER:
116 24 av_bprintf(bp, ", .par.dither = { .y_offset = {%u, %u, %u, %u}, .size_log2 = %u }",
117 24 par->dither.y_offset[0], par->dither.y_offset[1],
118 24 par->dither.y_offset[2], par->dither.y_offset[3],
119 24 par->dither.size_log2);
120 24 break;
121 4 case SWS_UOP_LUT_3D:
122 4 av_bprintf(bp, ", .par.lut3d.dynamic = %d", par->lut3d.dynamic);
123 4 break;
124 }
125
126 438 av_bprintf(bp, ")");
127 438 return 0;
128 }
129
130 438 static int generate_entry_args(void *opaque, void *key)
131 {
132 438 const SwsUOp *ref = opaque;
133 438 const SwsUOp *uop = key;
134 438 AVBPrint *bp = ref->data.opaque;
135 char name[SWS_UOP_NAME_MAX];
136 438 ff_sws_uop_name(uop, name);
137 438 av_bprintf(bp, " \\\n MACRO(__VA_ARGS__, %-40s, %-13s, %-24s, 0x%x",
138 438 name, pixel_types[uop->type].full, uop_names[uop->uop].full, uop->mask);
139
140 438 const SwsUOpParams *par = &uop->par;
141
10/10
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 22 times.
✓ Branch 2 taken 21 times.
✓ Branch 3 taken 88 times.
✓ Branch 4 taken 18 times.
✓ Branch 5 taken 33 times.
✓ Branch 6 taken 44 times.
✓ Branch 7 taken 24 times.
✓ Branch 8 taken 4 times.
✓ Branch 9 taken 148 times.
438 switch (uop->uop) {
142 36 case SWS_UOP_READ_PLANAR_FH:
143 case SWS_UOP_READ_PLANAR_FV:
144 case SWS_UOP_READ_PLANAR_FV_FMA:
145 36 av_bprintf(bp, ", %s", pixel_types[par->filter.type].full);
146 36 break;
147 22 case SWS_UOP_RW_SHUFFLE:
148 22 av_bprintf(bp, ", 0x%x, %u, %u", par->shuffle.clear_value,
149 22 par->shuffle.read_size, par->shuffle.write_size);
150 22 break;
151 21 case SWS_UOP_LSHIFT:
152 case SWS_UOP_RSHIFT:
153 21 av_bprintf(bp, ", %u", par->shift.amount);
154 21 break;
155 88 case SWS_UOP_PERMUTE:
156 case SWS_UOP_COPY:
157 88 av_bprintf(bp, ", %d", par->move.num_moves);
158 88 av_bprintf(bp, ", %d, %d, %d, %d, %d, %d",
159 88 par->move.dst[0], par->move.dst[1], par->move.dst[2],
160 88 par->move.dst[3], par->move.dst[4], par->move.dst[5]);
161 88 av_bprintf(bp, ", %d, %d, %d, %d, %d, %d",
162 88 par->move.src[0], par->move.src[1], par->move.src[2],
163 88 par->move.src[3], par->move.src[4], par->move.src[5]);
164 88 break;
165 18 case SWS_UOP_PACK:
166 case SWS_UOP_UNPACK:
167 18 av_bprintf(bp, ", %d, %d, %d, %d",
168 18 par->pack.pattern[0], par->pack.pattern[1],
169 18 par->pack.pattern[2], par->pack.pattern[3]);
170 18 break;
171 33 case SWS_UOP_CLEAR:
172 33 av_bprintf(bp, ", 0x%05x, 0x%05x", par->clear.one, par->clear.zero);
173 33 break;
174 44 case SWS_UOP_LINEAR:
175 case SWS_UOP_LINEAR_FMA:
176 44 av_bprintf(bp, ", 0x%05x, 0x%05x", par->lin.one, par->lin.zero);
177
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 16 times.
44 if (uop->uop == SWS_UOP_LINEAR_FMA)
178 28 av_bprintf(bp, ", 0x%05x", par->lin.exact);
179 44 break;
180 24 case SWS_UOP_DITHER:
181 24 av_bprintf(bp, ", %u, %u, %u, %u, %u",
182 24 par->dither.y_offset[0], par->dither.y_offset[1],
183 24 par->dither.y_offset[2], par->dither.y_offset[3],
184 24 par->dither.size_log2);
185 24 break;
186 4 case SWS_UOP_LUT_3D:
187 4 av_bprintf(bp, ", %d", par->lut3d.dynamic);
188 4 break;
189 }
190
191 438 av_bprintf(bp, ")");
192 438 return 0;
193 }
194
195 struct EnumPriv {
196 struct AVTreeNode *root;
197 AVMutex lock;
198 };
199
200 8758810 static int register_uop(struct EnumPriv *s, const SwsUOp *uop)
201 {
202 8758810 SwsUOp *key = av_memdup(uop, sizeof(*uop));
203
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8758810 times.
8758810 if (!key)
204 return AVERROR(ENOMEM);
205 8758810 memset(&key->data, 0, sizeof(key->data));
206
207 8758810 struct AVTreeNode *node = av_tree_node_alloc();
208
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8758810 times.
8758810 if (!node) {
209 av_free(key);
210 return AVERROR(ENOMEM);
211 }
212
213 8758810 ff_mutex_lock(&s->lock);
214 8758810 av_tree_insert(&s->root, key, ff_sws_uop_cmp_v, &node);
215 8758810 ff_mutex_unlock(&s->lock);
216
2/2
✓ Branch 0 taken 8758372 times.
✓ Branch 1 taken 438 times.
8758810 if (node) {
217 8758372 av_free(node);
218 8758372 av_free(key);
219 }
220 8758810 return 0;
221 }
222
223 1946516 static int register_flags(SwsContext *ctx, const SwsOpList *ops, SwsUOpFlags flags)
224 {
225 1946516 SwsUOpList *uops = ff_sws_uop_list_alloc();
226
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1946516 times.
1946516 if (!uops)
227 return AVERROR(ENOMEM);
228
229 1946516 int ret = ff_sws_ops_translate(ctx, ops, flags, uops);
230
2/2
✓ Branch 0 taken 345312 times.
✓ Branch 1 taken 1601204 times.
1946516 if (ret < 0)
231 345312 goto fail;
232
233
2/2
✓ Branch 0 taken 8758810 times.
✓ Branch 1 taken 1601204 times.
10360014 for (int i = 0; i < uops->num_ops; i++) {
234 8758810 ret = register_uop(ctx->opaque, &uops->ops[i]);
235
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8758810 times.
8758810 if (ret < 0)
236 goto fail;
237 }
238
239 1601204 fail:
240 1946516 ff_sws_uop_list_free(&uops);
241 1946516 return ret;
242 }
243
244 static const SwsUOpFlags uop_flags[] = {
245 0,
246 SWS_UOP_FLAG_PSHUFB | SWS_UOP_FLAG_FMA, /* x86 backend */
247 };
248
249 1145914 static int register_uops(SwsContext *ctx, const SwsOpList *ops,
250 SwsCompiledOp *out)
251 {
252
2/2
✓ Branch 0 taken 1946516 times.
✓ Branch 1 taken 800602 times.
2747118 for (int i = 0; i < FF_ARRAY_ELEMS(uop_flags); i++) {
253 1946516 int ret = register_flags(ctx, ops, uop_flags[i]);
254
2/2
✓ Branch 0 taken 345312 times.
✓ Branch 1 taken 1601204 times.
1946516 if (ret < 0)
255 345312 return ret;
256 }
257
258 800602 *out = (SwsCompiledOp) {0}; /* dummy value, will be immediately freed */
259 800602 return 0;
260 }
261
262 /* Dummy backend that just registers all seen uops */
263 static const SwsOpBackend backend_uops = {
264 .name = "uops_gen",
265 .compile = register_uops,
266 };
267
268 421344 static int register_all_uops(SwsContext *ctx, void *graph, SwsOpList *ops)
269 {
270 /* ff_sws_compile_pass() takes over ownership of `ops` */
271 421344 SwsOpList *copy = ff_sws_op_list_duplicate(ops);
272
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 421344 times.
421344 if (!copy)
273 return AVERROR(ENOMEM);
274
275 421344 const int flags = SWS_OP_FLAG_DRY_RUN | SWS_OP_FLAG_SPLIT_MEMCPY;
276 421344 return ff_sws_compile_pass(graph, &backend_uops, &copy, flags, NULL, NULL);
277 }
278
279 static const SwsFlags flags_list[] = {
280 0,
281 SWS_ACCURATE_RND, /* may insert extra 1x1 dither ops (for accurate rounding) */
282 SWS_BITEXACT, /* prevents some FMA optimizations */
283 SWS_ACCURATE_RND | SWS_BITEXACT,
284 };
285
286 /* Limit the range of av_tree_enumerate() to only matching uop and type */
287 3464 static int enum_type(void *opaque, void *elem)
288 {
289 3464 const SwsUOp *a = opaque, *b = elem;
290
2/2
✓ Branch 0 taken 618 times.
✓ Branch 1 taken 2846 times.
3464 if (a->type != b->type)
291 618 return (int) b->type - a->type;
292
2/2
✓ Branch 0 taken 1970 times.
✓ Branch 1 taken 876 times.
2846 if (a->uop != b->uop)
293 1970 return (int) b->uop - a->uop;
294 876 return 0;
295 }
296
297 438 static int free_uop_key(void *opaque, void *key)
298 {
299 438 av_free(key);
300 438 return 0;
301 }
302
303 /**
304 * Generate a set of boilerplate C preprocessor macros for describing and
305 * programmatically iterating over all possible SwsUOps.
306 *
307 * This function can be quite slow as it iterates over every possible
308 * combination of pixel formats and flags.
309 *
310 * Returns 0 or a negative error code. On success, an allocated string is
311 * returned via `out_str`, and must be av_free()'d by the caller.
312 */
313 1 static int sws_uops_macros_gen(char **out_str)
314 {
315 1 struct EnumPriv s = {0};
316 1 SwsLut3D *lut3d = NULL;
317 1 int ret = ff_mutex_init(&s.lock, NULL);
318
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret)
319 return AVERROR(ENOSYS);
320
321 1 AVBPrint bprint, *const bp = &bprint;
322 1 av_bprint_init(bp, 0, AV_BPRINT_SIZE_UNLIMITED);
323
324 /* Allocate dummy graph and context for ff_sws_compile_pass() */
325 1 SwsGraph *graph = ff_sws_graph_alloc();
326 1 SwsContext *ctx = sws_alloc_context();
327
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 if (!graph || !ctx) {
328 ret = AVERROR(ENOMEM);
329 goto fail;
330 }
331
332 /* Use this to plumb the enum state through all the layers of abstraction */
333 1 graph->ctx = ctx;
334 1 ctx->opaque = &s;
335 1 ctx->scaler = SWS_SCALE_BILINEAR; /* cheaper to generate filter kernels */
336 1 ctx->threads = 0; /* use slice threading to speed up tree building */
337
338 /* Allocate dummy 3DLUT to force generation of SWS_UOP_LUT_3D */
339 1 lut3d = ff_sws_lut3d_alloc();
340
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (!lut3d) {
341 ret = AVERROR(ENOMEM);
342 goto fail;
343 }
344 1 ret = ff_sws_enum_op_lists(ctx, graph, lut3d, AV_PIX_FMT_NONE,
345 AV_PIX_FMT_NONE, register_all_uops);
346
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
347 goto fail;
348
349 1 lut3d->dynamic = true;
350 1 ret = ff_sws_enum_op_lists(ctx, graph, lut3d, AV_PIX_FMT_NONE,
351 AV_PIX_FMT_NONE, register_all_uops);
352
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (ret < 0)
353 goto fail;
354
355 /* Register all unique uops over every relevant combination of flags */
356
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 for (int i = 0; i < FF_ARRAY_ELEMS(flags_list); i++) {
357 4 ctx->flags = flags_list[i];
358 4 ret = ff_sws_enum_op_lists(ctx, graph, NULL, AV_PIX_FMT_NONE,
359 AV_PIX_FMT_NONE, register_all_uops);
360
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (ret < 0)
361 goto fail;
362 }
363
364 #define BPRINT_STR(str) av_bprint_append_data(bp, str, strlen(str))
365 1 BPRINT_STR(
366 "/**\n"
367 " * This file is automatically generated. Do not edit manually.\n"
368 " * To regenerate, run: make fate-sws-uops-macros GEN=1\n"
369 " */\n"
370 "\n"
371 "#ifndef SWSCALE_UOPS_MACROS_H\n"
372 "#define SWSCALE_UOPS_MACROS_H\n"
373 "\n"
374 "/**\n"
375 " * Boilerplate helper macros, for template-based backends. These will be\n"
376 " * instantiated like this, with parameters in struct order:\n"
377 " * MACRO(__VA_ARGS__, NAME, UOP, TYPE, MASK, [PARAMS,])\n"
378 " * The _STRUCT variants pass all arguments in C struct syntax, while the\n"
379 " * plain variants give them as separate C values (e.g. for use in calls)\n"
380 " */\n"
381 "#define SWS_GLUE3(x, y, z) x ## _ ## y ## _ ## z\n"
382 "#define SWS_FOR(TYPE, UOP, MACRO, ...) \\\n"
383 " SWS_GLUE3(SWS_FOR, TYPE, UOP)(MACRO, __VA_ARGS__)\n"
384 "#define SWS_FOR_STRUCT(TYPE, UOP, MACRO, ...) \\\n"
385 " SWS_GLUE3(SWS_FOR_STRUCT, TYPE, UOP)(MACRO, __VA_ARGS__)\n"
386 "\n");
387
388 1 SwsUOp key = { .data.opaque = bp };
389
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
5 for (key.type = SWS_PIXEL_NONE + 1; key.type < SWS_PIXEL_TYPE_NB; key.type++) {
390
2/2
✓ Branch 0 taken 144 times.
✓ Branch 1 taken 4 times.
148 for (key.uop = SWS_UOP_INVALID + 1; key.uop < SWS_UOP_TYPE_NB; key.uop++) {
391 144 const char *macro = uop_names[key.uop].full + sizeof("SWS_UOP_") - 1;
392 144 const char *prefix = pixel_types[key.type].prefix;
393 144 av_bprintf(bp, "#define SWS_FOR_%s%s(MACRO, ...)", prefix, macro);
394 144 av_tree_enumerate(s.root, &key, enum_type, generate_entry_args);
395 144 av_bprintf(bp, "\n");
396 144 av_bprintf(bp, "#define SWS_FOR_STRUCT_%s%s(MACRO, ...)", prefix, macro);
397 144 av_tree_enumerate(s.root, &key, enum_type, generate_entry_struct);
398 144 av_bprintf(bp, "\n");
399 }
400 }
401
402 1 BPRINT_STR("\n#endif /* SWSCALE_UOPS_MACROS_H */");
403 1 ret = av_bprint_finalize(bp, out_str);
404
405 1 fail:
406 1 av_refstruct_unref(&lut3d);
407 1 av_bprint_finalize(bp, NULL);
408 1 av_tree_enumerate(s.root, NULL, NULL, free_uop_key);
409 1 av_tree_destroy(s.root);
410 1 ff_mutex_destroy(&s.lock);
411 1 ff_sws_graph_free(&graph);
412 1 sws_free_context(&ctx);
413 1 return ret;
414 }
415
416 1 int main(int argc, char **argv)
417 {
418 #ifdef _WIN32
419 _setmode(_fileno(stdout), _O_BINARY);
420 #endif
421
422 1 char *macros = NULL;
423 1 int ret = sws_uops_macros_gen(&macros);
424
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (ret >= 0)
425 1 puts(macros);
426 1 av_free(macros);
427 1 return ret;
428 }
429