FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/aarch64/ops_impl_conv.c
Date: 2026-08-28 05:02:41
Exec Total Coverage
Lines: 181 187 96.8%
Functions: 3 3 100.0%
Branches: 151 161 93.8%

Line Branch Exec Source
1 /*
2 * Copyright (C) 2026 Ramiro Polla
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 /**
22 * NOTE: This file is #include'd directly by both the NEON backend and
23 * the sws_ops_aarch64 tool.
24 */
25
26 #include "libavutil/error.h"
27 #include "libavutil/rational.h"
28 #include "libswscale/ops.h"
29
30 #include "ops_impl.h"
31
32 35292 static void swizzle_emit(SwsAArch64OpImplParams *out, uint8_t dst, uint8_t src)
33 {
34 35292 int idx = out->par.move.num_moves++;
35 35292 out->par.move.dst[idx] = dst;
36 35292 out->par.move.src[idx] = src;
37 35292 }
38
39 11778 static void convert_swizzle_to_moves(const SwsOp *op, SwsAArch64OpImplParams *out)
40 {
41 11778 SwsSwizzleOp swizzle = {
42 .in = {
43 11778 op->swizzle.in[0],
44 11778 op->swizzle.in[1],
45 11778 op->swizzle.in[2],
46 11778 op->swizzle.in[3],
47 }
48 };
49
50 /* Compute used vectors (src and dst) */
51 11778 uint8_t src_used[4] = { 0 };
52 11778 bool done[4] = { true, true, true, true };
53
4/4
✓ Branch 0 taken 27448 times.
✓ Branch 1 taken 19664 times.
✓ Branch 2 taken 47112 times.
✓ Branch 3 taken 11778 times.
58890 LOOP(out->mask, dst) {
54 27448 uint8_t src = swizzle.in[dst];
55 27448 src_used[src]++;
56 27448 done[dst] = false;
57 }
58
59 /* First perform unobstructed copies. */
60
2/2
✓ Branch 0 taken 18036 times.
✓ Branch 1 taken 11778 times.
29814 for (bool progress = true; progress; ) {
61 18036 progress = false;
62
2/2
✓ Branch 0 taken 72144 times.
✓ Branch 1 taken 18036 times.
90180 for (int dst = 0; dst < 4; dst++) {
63
4/4
✓ Branch 0 taken 30953 times.
✓ Branch 1 taken 41191 times.
✓ Branch 2 taken 21790 times.
✓ Branch 3 taken 9163 times.
72144 if (done[dst] || src_used[dst])
64 62981 continue;
65 9163 uint8_t src = swizzle.in[dst];
66 9163 swizzle_emit(out, dst, src);
67 9163 src_used[src]--;
68 9163 done[dst] = true;
69 9163 progress = true;
70 }
71 }
72
73 /* Then swap and rotate remaining operations. */
74
2/2
✓ Branch 0 taken 47112 times.
✓ Branch 1 taken 11778 times.
58890 for (int dst = 0; dst < 4; dst++) {
75
2/2
✓ Branch 0 taken 39268 times.
✓ Branch 1 taken 7844 times.
47112 if (done[dst])
76 39268 continue;
77
78 7844 swizzle_emit(out, -1, dst);
79
80 7844 uint8_t cur_dst = dst;
81 7844 uint8_t src = swizzle.in[cur_dst];
82
2/2
✓ Branch 0 taken 10441 times.
✓ Branch 1 taken 7844 times.
18285 while (src != dst) {
83 10441 swizzle_emit(out, cur_dst, src);
84 10441 done[cur_dst] = true;
85 10441 cur_dst = src;
86 10441 src = swizzle.in[cur_dst];
87 }
88
89 7844 swizzle_emit(out, cur_dst, -1);
90 7844 done[cur_dst] = true;
91 }
92 11778 }
93
94 /**
95 * Convert SwsOp to a SwsAArch64OpImplParams. Read the comments regarding
96 * SwsAArch64OpImplParams in ops_impl.h for more information.
97 */
98 144578 static int convert_to_aarch64_impl(SwsContext *ctx, const SwsOpList *ops, int n,
99 int block_size, SwsAArch64OpImplParams *out)
100 {
101 144578 const SwsOp *op = &ops->ops[n];
102
103 144578 out->block_size = block_size;
104
105 /**
106 * Most SwsOp work on fields described by SWS_OP_NEEDED().
107 * The few that don't will override this field later.
108 */
109 144578 out->mask = 0;
110
2/2
✓ Branch 0 taken 578312 times.
✓ Branch 1 taken 144578 times.
722890 for (int i = 0; i < 4; i++) {
111
2/2
✓ Branch 0 taken 321644 times.
✓ Branch 1 taken 256668 times.
578312 if (SWS_OP_NEEDED(op, i))
112 321644 out->mask |= SWS_COMP(i);
113 }
114
115 144578 out->type = op->type;
116
117 /* Map SwsOpType to SwsUOpType */
118
14/15
✓ Branch 0 taken 17436 times.
✓ Branch 1 taken 19519 times.
✓ Branch 2 taken 13251 times.
✓ Branch 3 taken 11778 times.
✓ Branch 4 taken 3398 times.
✓ Branch 5 taken 3424 times.
✓ Branch 6 taken 1736 times.
✓ Branch 7 taken 1320 times.
✓ Branch 8 taken 6043 times.
✓ Branch 9 taken 28794 times.
✓ Branch 10 taken 13531 times.
✓ Branch 11 taken 4930 times.
✓ Branch 12 taken 10830 times.
✓ Branch 13 taken 8588 times.
✗ Branch 14 not taken.
144578 switch (op->op) {
119 17436 case SWS_OP_READ:
120
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17436 times.
17436 if (op->rw.filter.op)
121 return AVERROR(ENOTSUP);
122 /**
123 * The different types of read operations have been split into
124 * their own SwsUOpType to simplify the implementation.
125 */
126
2/2
✓ Branch 0 taken 262 times.
✓ Branch 1 taken 17174 times.
17436 if (op->rw.frac == 1)
127 262 out->uop = SWS_UOP_READ_NIBBLE;
128
2/2
✓ Branch 0 taken 262 times.
✓ Branch 1 taken 16912 times.
17174 else if (op->rw.frac == 3)
129 262 out->uop = SWS_UOP_READ_BIT;
130
4/4
✓ Branch 0 taken 8139 times.
✓ Branch 1 taken 8773 times.
✓ Branch 2 taken 4987 times.
✓ Branch 3 taken 3152 times.
16912 else if (op->rw.mode == SWS_RW_PACKED && op->rw.elems > 1)
131 4987 out->uop = SWS_UOP_READ_PACKED;
132
4/4
✓ Branch 0 taken 8773 times.
✓ Branch 1 taken 3152 times.
✓ Branch 2 taken 8641 times.
✓ Branch 3 taken 132 times.
11925 else if (op->rw.mode == SWS_RW_PACKED || op->rw.mode == SWS_RW_PLANAR)
133 11793 out->uop = SWS_UOP_READ_PLANAR;
134 else
135 132 return AVERROR(ENOTSUP);
136 17304 break;
137 19519 case SWS_OP_WRITE:
138
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19519 times.
19519 if (op->rw.filter.op)
139 return AVERROR(ENOTSUP);
140 /**
141 * The different types of write operations have been split into
142 * their own SwsUOpType to simplify the implementation.
143 */
144
2/2
✓ Branch 0 taken 264 times.
✓ Branch 1 taken 19255 times.
19519 if (op->rw.frac == 1)
145 264 out->uop = SWS_UOP_WRITE_NIBBLE;
146
2/2
✓ Branch 0 taken 264 times.
✓ Branch 1 taken 18991 times.
19255 else if (op->rw.frac == 3)
147 264 out->uop = SWS_UOP_WRITE_BIT;
148
4/4
✓ Branch 0 taken 8201 times.
✓ Branch 1 taken 10790 times.
✓ Branch 2 taken 5025 times.
✓ Branch 3 taken 3176 times.
18991 else if (op->rw.mode == SWS_RW_PACKED && op->rw.elems > 1)
149 5025 out->uop = SWS_UOP_WRITE_PACKED;
150
3/4
✓ Branch 0 taken 10790 times.
✓ Branch 1 taken 3176 times.
✓ Branch 2 taken 10790 times.
✗ Branch 3 not taken.
13966 else if (op->rw.mode == SWS_RW_PACKED || op->rw.mode == SWS_RW_PLANAR)
151 13966 out->uop = SWS_UOP_WRITE_PLANAR;
152 else
153 return AVERROR(ENOTSUP);
154 19519 break;
155 13251 case SWS_OP_SWAP_BYTES: out->uop = SWS_UOP_SWAP_BYTES; break;
156 11778 case SWS_OP_SWIZZLE: {
157 /**
158 * Detect whether copies are needed or if a simple permute is
159 * enough.
160 */
161 11778 out->uop = SWS_UOP_PERMUTE;
162 11778 SwsCompMask seen = 0;
163
4/4
✓ Branch 0 taken 33594 times.
✓ Branch 1 taken 10770 times.
✓ Branch 2 taken 44364 times.
✓ Branch 3 taken 10338 times.
54702 LOOP(out->mask, i) {
164 33594 uint8_t src = op->swizzle.in[i];
165
2/2
✓ Branch 0 taken 1440 times.
✓ Branch 1 taken 32154 times.
33594 if (seen & SWS_COMP(src)) {
166 1440 out->uop = SWS_UOP_COPY;
167 1440 break;
168 }
169 32154 seen |= SWS_COMP(src);
170 }
171 11778 break;
172 }
173 3398 case SWS_OP_UNPACK: out->uop = SWS_UOP_UNPACK; break;
174 3424 case SWS_OP_PACK: out->uop = SWS_UOP_PACK; break;
175 1736 case SWS_OP_LSHIFT: out->uop = SWS_UOP_LSHIFT; break;
176 1320 case SWS_OP_RSHIFT: out->uop = SWS_UOP_RSHIFT; break;
177 6043 case SWS_OP_CLEAR: out->uop = SWS_UOP_CLEAR; break;
178 28794 case SWS_OP_CONVERT:
179
2/2
✓ Branch 0 taken 192 times.
✓ Branch 1 taken 28602 times.
28794 if (op->convert.expand) {
180
1/3
✓ Branch 0 taken 192 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
192 switch (op->convert.to) {
181 192 case SWS_PIXEL_U16: out->uop = SWS_UOP_EXPAND_PAIR; break;
182 case SWS_PIXEL_U32: out->uop = SWS_UOP_EXPAND_QUAD; break;
183 }
184 } else {
185
4/5
✓ Branch 0 taken 3715 times.
✓ Branch 1 taken 9780 times.
✓ Branch 2 taken 1032 times.
✓ Branch 3 taken 14075 times.
✗ Branch 4 not taken.
28602 switch (op->convert.to) {
186 3715 case SWS_PIXEL_U8: out->uop = SWS_UOP_TO_U8; break;
187 9780 case SWS_PIXEL_U16: out->uop = SWS_UOP_TO_U16; break;
188 1032 case SWS_PIXEL_U32: out->uop = SWS_UOP_TO_U32; break;
189 14075 case SWS_PIXEL_F32: out->uop = SWS_UOP_TO_F32; break;
190 }
191 }
192 28794 break;
193 13531 case SWS_OP_MIN:
194 case SWS_OP_MAX:
195
2/2
✓ Branch 0 taken 8868 times.
✓ Branch 1 taken 4663 times.
13531 out->uop = (op->op == SWS_OP_MIN) ? SWS_UOP_MIN : SWS_UOP_MAX;
196 13531 out->mask &= ff_sws_comp_mask_q4(op->clamp.limit);
197 13531 break;
198 4930 case SWS_OP_SCALE: out->uop = SWS_UOP_SCALE; break;
199 10830 case SWS_OP_LINEAR:
200 21660 out->uop = (ctx->flags & SWS_BITEXACT)
201 ? SWS_UOP_LINEAR
202
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10830 times.
10830 : SWS_UOP_LINEAR_FMA;
203 10830 break;
204 8588 case SWS_OP_DITHER: out->uop = SWS_UOP_DITHER; break;
205 default:
206 return AVERROR(ENOTSUP);
207 }
208
209
9/9
✓ Branch 0 taken 36823 times.
✓ Branch 1 taken 11778 times.
✓ Branch 2 taken 3398 times.
✓ Branch 3 taken 3424 times.
✓ Branch 4 taken 3056 times.
✓ Branch 5 taken 6043 times.
✓ Branch 6 taken 10830 times.
✓ Branch 7 taken 8588 times.
✓ Branch 8 taken 60506 times.
144446 switch (out->uop) {
210 36823 case SWS_UOP_READ_BIT:
211 case SWS_UOP_READ_NIBBLE:
212 case SWS_UOP_READ_PACKED:
213 case SWS_UOP_READ_PLANAR:
214 case SWS_UOP_WRITE_BIT:
215 case SWS_UOP_WRITE_NIBBLE:
216 case SWS_UOP_WRITE_PACKED:
217 case SWS_UOP_WRITE_PLANAR:
218
4/5
✓ Branch 0 taken 13306 times.
✓ Branch 1 taken 1769 times.
✓ Branch 2 taken 13663 times.
✓ Branch 3 taken 8085 times.
✗ Branch 4 not taken.
36823 switch (op->rw.elems) {
219 13306 case 1: out->mask = SWS_COMP_ELEMS(1); break;
220 1769 case 2: out->mask = SWS_COMP_ELEMS(2); break;
221 13663 case 3: out->mask = SWS_COMP_ELEMS(3); break;
222 8085 case 4: out->mask = SWS_COMP_ELEMS(4); break;
223 };
224 36823 break;
225 11778 case SWS_UOP_PERMUTE:
226 case SWS_UOP_COPY:
227 /* Recompute mask taking identity swizzle into account */
228 11778 out->mask = 0;
229
2/2
✓ Branch 0 taken 47112 times.
✓ Branch 1 taken 11778 times.
58890 for (int i = 0; i < 4; i++) {
230
4/4
✓ Branch 0 taken 35149 times.
✓ Branch 1 taken 11963 times.
✓ Branch 2 taken 27448 times.
✓ Branch 3 taken 7701 times.
47112 if (SWS_OP_NEEDED(op, i) && op->swizzle.in[i] != i)
231 27448 out->mask |= SWS_COMP(i);
232 }
233 11778 convert_swizzle_to_moves(op, out);
234 /* The element size and type don't matter. */
235 11778 out->block_size = block_size * ff_sws_pixel_type_size(op->type);
236 11778 out->type = SWS_PIXEL_U8;
237 11778 break;
238 3398 case SWS_UOP_UNPACK:
239
2/2
✓ Branch 0 taken 13592 times.
✓ Branch 1 taken 3398 times.
16990 for (int i = 0; i < 4; i++)
240 13592 out->par.pack.pattern[i] = op->pack.pattern[i];
241 3398 break;
242 3424 case SWS_UOP_PACK:
243 3424 out->mask = 0;
244
4/4
✓ Branch 0 taken 13696 times.
✓ Branch 1 taken 1064 times.
✓ Branch 2 taken 11336 times.
✓ Branch 3 taken 2360 times.
14760 for (int i = 0; i < 4 && op->pack.pattern[i]; i++)
245 11336 out->mask |= SWS_COMP(i);
246
2/2
✓ Branch 0 taken 13696 times.
✓ Branch 1 taken 3424 times.
17120 for (int i = 0; i < 4; i++)
247 13696 out->par.pack.pattern[i] = op->pack.pattern[i];
248 3424 break;
249 3056 case SWS_UOP_LSHIFT:
250 case SWS_UOP_RSHIFT:
251 3056 out->par.shift.amount = op->shift.amount;
252 3056 break;
253 6043 case SWS_UOP_CLEAR:
254 6043 out->mask = 0;
255
2/2
✓ Branch 0 taken 24172 times.
✓ Branch 1 taken 6043 times.
30215 for (int i = 0; i < 4; i++) {
256
2/2
✓ Branch 0 taken 7213 times.
✓ Branch 1 taken 16959 times.
24172 if (op->clear.mask & SWS_COMP(i)) {
257 7213 out->mask |= SWS_COMP(i);
258
2/2
✓ Branch 0 taken 1197 times.
✓ Branch 1 taken 6016 times.
7213 if (op->clear.value[i].num == 0) {
259 1197 out->par.clear.zero |= SWS_COMP(i);
260 } else {
261 6016 uint32_t val = op->clear.value[i].num / op->clear.value[i].den;
262
4/4
✓ Branch 0 taken 1240 times.
✓ Branch 1 taken 4776 times.
✓ Branch 2 taken 320 times.
✓ Branch 3 taken 920 times.
6016 if ((op->type == SWS_PIXEL_U8 && val == UINT8_MAX) ||
263
4/4
✓ Branch 0 taken 3328 times.
✓ Branch 1 taken 1768 times.
✓ Branch 2 taken 2224 times.
✓ Branch 3 taken 1104 times.
5096 (op->type == SWS_PIXEL_U16 && val == UINT16_MAX) ||
264
3/4
✓ Branch 0 taken 896 times.
✓ Branch 1 taken 3096 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 896 times.
3992 (op->type == SWS_PIXEL_U32 && val == UINT32_MAX))
265 2024 out->par.clear.one |= SWS_COMP(i);
266 }
267 }
268 }
269 6043 break;
270 10830 case SWS_UOP_LINEAR:
271 case SWS_UOP_LINEAR_FMA:
272 10830 out->mask = 0;
273 10830 const uint32_t lin_mask = ff_sws_linear_mask(&op->lin);
274
2/2
✓ Branch 0 taken 43320 times.
✓ Branch 1 taken 10830 times.
54150 for (int i = 0; i < 4; i++) {
275
4/4
✓ Branch 0 taken 27694 times.
✓ Branch 1 taken 15626 times.
✓ Branch 2 taken 478 times.
✓ Branch 3 taken 27216 times.
43320 if (!SWS_OP_NEEDED(op, i) || !(lin_mask & SWS_MASK_ROW(i))) {
276
2/2
✓ Branch 0 taken 80520 times.
✓ Branch 1 taken 16104 times.
96624 for (int j = 0; j < 5; j++)
277 80520 out->par.lin.zero |= SWS_MASK(i, j);
278 16104 continue;
279 }
280 27216 out->mask |= SWS_COMP(i);
281
2/2
✓ Branch 0 taken 136080 times.
✓ Branch 1 taken 27216 times.
163296 for (int j = 0; j < 5; j++) {
282 136080 const AVRational64 k = op->lin.m[i][j];
283
4/4
✓ Branch 0 taken 108864 times.
✓ Branch 1 taken 27216 times.
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 108828 times.
136080 if (j < 4 && k.num == k.den)
284 36 out->par.lin.one |= SWS_MASK(i, j);
285
2/2
✓ Branch 0 taken 57454 times.
✓ Branch 1 taken 78590 times.
136044 else if (k.num == 0)
286 57454 out->par.lin.zero |= SWS_MASK(i, j);
287 }
288 }
289 10830 break;
290 8588 case SWS_UOP_DITHER:
291
6/6
✓ Branch 0 taken 6758 times.
✓ Branch 1 taken 1830 times.
✓ Branch 2 taken 6580 times.
✓ Branch 3 taken 2008 times.
✓ Branch 4 taken 1016 times.
✓ Branch 5 taken 7572 times.
8588 out->mask = SWS_COMP_MASK(op->dither.y_offset[0] >= 0,
292 op->dither.y_offset[1] >= 0,
293 op->dither.y_offset[2] >= 0,
294 op->dither.y_offset[3] >= 0);
295
4/4
✓ Branch 0 taken 22578 times.
✓ Branch 1 taken 11774 times.
✓ Branch 2 taken 34352 times.
✓ Branch 3 taken 8588 times.
42940 LOOP(out->mask, i) {
296 22578 out->par.dither.y_offset[i] = op->dither.y_offset[i];
297 }
298 8588 out->par.dither.size_log2 = op->dither.size_log2;
299 8588 break;
300 }
301
302
2/2
✓ Branch 0 taken 56117 times.
✓ Branch 1 taken 88329 times.
144446 switch (out->uop) {
303 56117 case SWS_UOP_READ_BIT:
304 case SWS_UOP_READ_NIBBLE:
305 case SWS_UOP_READ_PACKED:
306 case SWS_UOP_READ_PLANAR:
307 case SWS_UOP_WRITE_BIT:
308 case SWS_UOP_WRITE_NIBBLE:
309 case SWS_UOP_WRITE_PACKED:
310 case SWS_UOP_WRITE_PLANAR:
311 case SWS_UOP_SWAP_BYTES:
312 case SWS_UOP_CLEAR:
313 /* Only the element size matters, not the type. */
314
2/2
✓ Branch 0 taken 5342 times.
✓ Branch 1 taken 50775 times.
56117 if (out->type == SWS_PIXEL_F32)
315 5342 out->type = SWS_PIXEL_U32;
316 56117 break;
317 }
318
319 144446 return 0;
320 }
321