FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/ops_chain.c
Date: 2025-10-16 05:50:53
Exec Total Coverage
Lines: 120 155 77.4%
Functions: 8 9 88.9%
Branches: 106 147 72.1%

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
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (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 GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "libavutil/avassert.h"
22 #include "libavutil/mem.h"
23 #include "libavutil/rational.h"
24
25 #include "ops_chain.h"
26
27 #define Q(N) ((AVRational) { N, 1 })
28
29 8552 SwsOpChain *ff_sws_op_chain_alloc(void)
30 {
31 8552 return av_mallocz(sizeof(SwsOpChain));
32 }
33
34 8552 void ff_sws_op_chain_free_cb(void *ptr)
35 {
36
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8552 times.
8552 if (!ptr)
37 return;
38
39 8552 SwsOpChain *chain = ptr;
40
2/2
✓ Branch 0 taken 31241 times.
✓ Branch 1 taken 8552 times.
39793 for (int i = 0; i < chain->num_impl + 1; i++) {
41
2/2
✓ Branch 0 taken 790 times.
✓ Branch 1 taken 30451 times.
31241 if (chain->free[i])
42 790 chain->free[i](chain->impl[i].priv.ptr);
43 }
44
45 8552 av_free(chain);
46 }
47
48 22689 int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func,
49 void (*free)(void *), const SwsOpPriv *priv)
50 {
51 22689 const int idx = chain->num_impl;
52
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22689 times.
22689 if (idx == SWS_MAX_OPS)
53 return AVERROR(EINVAL);
54
55 av_assert1(func);
56 22689 chain->impl[idx].cont = func;
57 22689 chain->impl[idx + 1].priv = *priv;
58 22689 chain->free[idx + 1] = free;
59 22689 chain->num_impl++;
60 22689 return 0;
61 }
62
63 /**
64 * Match an operation against a reference operation. Returns a score for how
65 * well the reference matches the operation, or 0 if there is no match.
66 *
67 * If `ref->comps` has any flags set, they must be set in `op` as well.
68 * Likewise, if `ref->comps` has any components marked as unused, they must be
69 * marked as as unused in `ops` as well.
70 *
71 * For SWS_OP_LINEAR, `ref->linear.mask` must be a strict superset of
72 * `op->linear.mask`, but may not contain any columns explicitly ignored by
73 * `op->comps.unused`.
74 *
75 * For SWS_OP_READ, SWS_OP_WRITE, SWS_OP_SWAP_BYTES and SWS_OP_SWIZZLE, the
76 * exact type is not checked, just the size.
77 *
78 * Components set in `next.unused` are ignored when matching. If `flexible`
79 * is true, the op body is ignored - only the operation, pixel type, and
80 * component masks are checked.
81 */
82 6229446 static int op_match(const SwsOp *op, const SwsOpEntry *entry, const SwsComps next)
83 {
84 6229446 int score = 10;
85
2/2
✓ Branch 0 taken 5682745 times.
✓ Branch 1 taken 546701 times.
6229446 if (op->op != entry->op)
86 5682745 return 0;
87
88
2/2
✓ Branch 0 taken 454518 times.
✓ Branch 1 taken 92183 times.
546701 switch (op->op) {
89 454518 case SWS_OP_READ:
90 case SWS_OP_WRITE:
91 case SWS_OP_SWAP_BYTES:
92 case SWS_OP_SWIZZLE:
93 /* Only the size matters for these operations */
94
2/2
✓ Branch 0 taken 290030 times.
✓ Branch 1 taken 164488 times.
454518 if (ff_sws_pixel_type_size(op->type) != ff_sws_pixel_type_size(entry->type))
95 290030 return 0;
96 164488 break;
97 92183 default:
98
2/2
✓ Branch 0 taken 55588 times.
✓ Branch 1 taken 36595 times.
92183 if (op->type != entry->type)
99 55588 return 0;
100 36595 break;
101 }
102
103
2/2
✓ Branch 0 taken 785560 times.
✓ Branch 1 taken 187590 times.
973150 for (int i = 0; i < 4; i++) {
104
2/2
✓ Branch 0 taken 33373 times.
✓ Branch 1 taken 752187 times.
785560 if (entry->unused[i]) {
105
2/2
✓ Branch 0 taken 19880 times.
✓ Branch 1 taken 13493 times.
33373 if (op->comps.unused[i])
106 19880 score += 1; /* Operating on fewer components is better .. */
107 else
108 13493 return 0; /* .. but not too few! */
109 }
110 }
111
112
2/2
✓ Branch 0 taken 2169 times.
✓ Branch 1 taken 185421 times.
187590 if (op->op == SWS_OP_CLEAR) {
113 /* Clear pattern must match exactly, regardless of `entry->flexible` */
114
2/2
✓ Branch 0 taken 6136 times.
✓ Branch 1 taken 565 times.
6701 for (int i = 0; i < 4; i++) {
115
3/4
✓ Branch 0 taken 6136 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1604 times.
✓ Branch 3 taken 4532 times.
6136 if (!next.unused[i] && entry->unused[i] != !!op->c.q4[i].den)
116 1604 return 0;
117 }
118 }
119
120 /* Flexible variants always match, but lower the score to prioritize more
121 * specific implementations if they exist */
122
2/2
✓ Branch 0 taken 5524 times.
✓ Branch 1 taken 180462 times.
185986 if (entry->flexible)
123 5524 return score - 5;
124
125
8/14
✗ Branch 0 not taken.
✓ Branch 1 taken 116902 times.
✓ Branch 2 taken 244 times.
✓ Branch 3 taken 706 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 47146 times.
✓ Branch 7 taken 6398 times.
✓ Branch 8 taken 4941 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 4116 times.
✓ Branch 11 taken 9 times.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
180462 switch (op->op) {
126 case SWS_OP_INVALID:
127 return 0;
128 116902 case SWS_OP_READ:
129 case SWS_OP_WRITE:
130
2/2
✓ Branch 0 taken 29717 times.
✓ Branch 1 taken 87185 times.
116902 if (op->rw.elems != entry->rw.elems ||
131
2/2
✓ Branch 0 taken 27943 times.
✓ Branch 1 taken 1774 times.
29717 op->rw.frac != entry->rw.frac ||
132
4/4
✓ Branch 0 taken 25136 times.
✓ Branch 1 taken 2807 times.
✓ Branch 2 taken 12559 times.
✓ Branch 3 taken 12577 times.
27943 (op->rw.elems > 1 && op->rw.packed != entry->rw.packed))
133 101518 return 0;
134 15384 return score;
135 244 case SWS_OP_SWAP_BYTES:
136 244 return score;
137 706 case SWS_OP_PACK:
138 case SWS_OP_UNPACK:
139
4/4
✓ Branch 0 taken 1524 times.
✓ Branch 1 taken 56 times.
✓ Branch 2 taken 1326 times.
✓ Branch 3 taken 198 times.
1580 for (int i = 0; i < 4 && op->pack.pattern[i]; i++) {
140
2/2
✓ Branch 0 taken 452 times.
✓ Branch 1 taken 874 times.
1326 if (op->pack.pattern[i] != entry->pack.pattern[i])
141 452 return 0;
142 }
143 254 return score;
144 case SWS_OP_CLEAR:
145 for (int i = 0; i < 4; i++) {
146 if (!op->c.q4[i].den)
147 continue;
148 if (av_cmp_q(op->c.q4[i], Q(entry->clear_value)) && !next.unused[i])
149 return 0;
150 }
151 return score;
152 case SWS_OP_LSHIFT:
153 case SWS_OP_RSHIFT:
154 av_assert1(entry->flexible);
155 return score;
156 47146 case SWS_OP_SWIZZLE:
157
2/2
✓ Branch 0 taken 67200 times.
✓ Branch 1 taken 3046 times.
70246 for (int i = 0; i < 4; i++) {
158
4/4
✓ Branch 0 taken 45906 times.
✓ Branch 1 taken 21294 times.
✓ Branch 2 taken 44100 times.
✓ Branch 3 taken 1806 times.
67200 if (op->swizzle.in[i] != entry->swizzle.in[i] && !next.unused[i])
159 44100 return 0;
160 }
161 3046 return score;
162 6398 case SWS_OP_CONVERT:
163
2/2
✓ Branch 0 taken 2234 times.
✓ Branch 1 taken 4164 times.
6398 if (op->convert.to != entry->convert.to ||
164
2/2
✓ Branch 0 taken 496 times.
✓ Branch 1 taken 1738 times.
2234 op->convert.expand != entry->convert.expand)
165 4660 return 0;
166 1738 return score;
167 4941 case SWS_OP_DITHER:
168
2/2
✓ Branch 0 taken 549 times.
✓ Branch 1 taken 4392 times.
4941 return op->dither.size_log2 == entry->dither_size ? score : 0;
169 case SWS_OP_MIN:
170 case SWS_OP_MAX:
171 av_assert1(entry->flexible);
172 return score;
173 4116 case SWS_OP_LINEAR:
174 /* All required elements must be present */
175
2/2
✓ Branch 0 taken 2870 times.
✓ Branch 1 taken 1246 times.
4116 if (op->lin.mask & ~entry->linear_mask)
176 2870 return 0;
177 /* To avoid operating on possibly undefined memory, filter out
178 * implementations that operate on more input components */
179
2/2
✓ Branch 0 taken 4984 times.
✓ Branch 1 taken 1246 times.
6230 for (int i = 0; i < 4; i++) {
180
3/4
✓ Branch 0 taken 4410 times.
✓ Branch 1 taken 574 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 4410 times.
4984 if ((entry->linear_mask & SWS_MASK_COL(i)) && op->comps.unused[i])
181 return 0;
182 }
183 /* Prioritize smaller implementations */
184 1246 score += av_popcount(SWS_MASK_ALL ^ entry->linear_mask);
185 1246 return score;
186 9 case SWS_OP_SCALE:
187 9 return score;
188 case SWS_OP_TYPE_NB:
189 break;
190 }
191
192 av_unreachable("Invalid operation type!");
193 return 0;
194 }
195
196 23853 int ff_sws_op_compile_tables(const SwsOpTable *const tables[], int num_tables,
197 SwsOpList *ops, const int block_size,
198 SwsOpChain *chain)
199 {
200 static const SwsOp dummy = { .comps.unused = { true, true, true, true }};
201
2/2
✓ Branch 0 taken 16897 times.
✓ Branch 1 taken 6956 times.
23853 const SwsOp *next = ops->num_ops > 1 ? &ops->ops[1] : &dummy;
202 23853 const unsigned cpu_flags = av_get_cpu_flags();
203 23853 const SwsOpEntry *best = NULL;
204 23853 const SwsOp *op = &ops->ops[0];
205 23853 int ret, best_score = 0, best_cpu_flags;
206 23853 SwsOpPriv priv = {0};
207
208
2/2
✓ Branch 0 taken 109251 times.
✓ Branch 1 taken 23853 times.
133104 for (int n = 0; n < num_tables; n++) {
209 109251 const SwsOpTable *table = tables[n];
210
3/4
✓ Branch 0 taken 109251 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 86492 times.
✓ Branch 3 taken 22759 times.
109251 if (table->block_size && table->block_size != block_size ||
211
2/2
✓ Branch 0 taken 5230 times.
✓ Branch 1 taken 81262 times.
86492 table->cpu_flags & ~cpu_flags)
212 27989 continue;
213
214
2/2
✓ Branch 0 taken 6229446 times.
✓ Branch 1 taken 81262 times.
6310708 for (int i = 0; table->entries[i]; i++) {
215 6229446 const SwsOpEntry *entry = table->entries[i];
216 6229446 int score = op_match(op, entry, next->comps);
217
2/2
✓ Branch 0 taken 22206 times.
✓ Branch 1 taken 6207240 times.
6229446 if (score > best_score) {
218 22206 best_score = score;
219 22206 best_cpu_flags = table->cpu_flags;
220 22206 best = entry;
221 }
222 }
223 }
224
225
2/2
✓ Branch 0 taken 1647 times.
✓ Branch 1 taken 22206 times.
23853 if (!best)
226 1647 return AVERROR(ENOTSUP);
227
228
2/2
✓ Branch 0 taken 3577 times.
✓ Branch 1 taken 18629 times.
22206 if (best->setup) {
229 3577 ret = best->setup(op, &priv);
230
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3577 times.
3577 if (ret < 0)
231 return ret;
232 }
233
234 22206 chain->cpu_flags |= best_cpu_flags;
235 22206 ret = ff_sws_op_chain_append(chain, best->func, best->free, &priv);
236
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22206 times.
22206 if (ret < 0) {
237 if (best->free)
238 best->free(priv.ptr);
239 return ret;
240 }
241
242 22206 ops->ops++;
243 22206 ops->num_ops--;
244
2/2
✓ Branch 0 taken 15301 times.
✓ Branch 1 taken 6905 times.
22206 return ops->num_ops ? AVERROR(EAGAIN) : 0;
245 }
246
247 #define q2pixel(type, q) ((q).den ? (type) (q).num / (q).den : 0)
248
249 1664 int ff_sws_setup_u8(const SwsOp *op, SwsOpPriv *out)
250 {
251 1664 out->u8[0] = op->c.u;
252 1664 return 0;
253 }
254
255 int ff_sws_setup_u(const SwsOp *op, SwsOpPriv *out)
256 {
257 switch (op->type) {
258 case SWS_PIXEL_U8: out->u8[0] = op->c.u; return 0;
259 case SWS_PIXEL_U16: out->u16[0] = op->c.u; return 0;
260 case SWS_PIXEL_U32: out->u32[0] = op->c.u; return 0;
261 case SWS_PIXEL_F32: out->f32[0] = op->c.u; return 0;
262 default: return AVERROR(EINVAL);
263 }
264 }
265
266 56 int ff_sws_setup_q(const SwsOp *op, SwsOpPriv *out)
267 {
268
1/5
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 56 times.
✗ Branch 4 not taken.
56 switch (op->type) {
269 case SWS_PIXEL_U8: out->u8[0] = q2pixel(uint8_t, op->c.q); return 0;
270 case SWS_PIXEL_U16: out->u16[0] = q2pixel(uint16_t, op->c.q); return 0;
271 case SWS_PIXEL_U32: out->u32[0] = q2pixel(uint32_t, op->c.q); return 0;
272
1/2
✓ Branch 0 taken 56 times.
✗ Branch 1 not taken.
56 case SWS_PIXEL_F32: out->f32[0] = q2pixel(float, op->c.q); return 0;
273 default: return AVERROR(EINVAL);
274 }
275
276 return 0;
277 }
278
279 989 int ff_sws_setup_q4(const SwsOp *op, SwsOpPriv *out)
280 {
281
2/2
✓ Branch 0 taken 3956 times.
✓ Branch 1 taken 989 times.
4945 for (int i = 0; i < 4; i++) {
282
4/5
✓ Branch 0 taken 1352 times.
✓ Branch 1 taken 1688 times.
✓ Branch 2 taken 416 times.
✓ Branch 3 taken 500 times.
✗ Branch 4 not taken.
3956 switch (op->type) {
283
2/2
✓ Branch 0 taken 949 times.
✓ Branch 1 taken 403 times.
1352 case SWS_PIXEL_U8: out->u8[i] = q2pixel(uint8_t, op->c.q4[i]); break;
284
2/2
✓ Branch 0 taken 1171 times.
✓ Branch 1 taken 517 times.
1688 case SWS_PIXEL_U16: out->u16[i] = q2pixel(uint16_t, op->c.q4[i]); break;
285
1/2
✓ Branch 0 taken 416 times.
✗ Branch 1 not taken.
416 case SWS_PIXEL_U32: out->u32[i] = q2pixel(uint32_t, op->c.q4[i]); break;
286
2/2
✓ Branch 0 taken 461 times.
✓ Branch 1 taken 39 times.
500 case SWS_PIXEL_F32: out->f32[i] = q2pixel(float, op->c.q4[i]); break;
287 default: return AVERROR(EINVAL);
288 }
289 }
290
291 989 return 0;
292 }
293