../../../ffmpeg/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
426 / 468
91.0%
Branches:
243 / 287
84.7%
Functions:
19 / 19
100.0%

libswscale/ops_dispatch.c
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/cpu.h"
23 #include "libavutil/mathematics.h"
24 #include "libavutil/mem.h"
25 #include "libavutil/mem_internal.h"
26 #include "libavutil/refstruct.h"
27
28 #include "ops.h"
29 #include "ops_internal.h"
30 #include "ops_dispatch.h"
31 #include "swscale_internal.h"
32
33 #define RET(x) \
34 do { \
35 if ((ret = (x)) < 0) \
36 goto fail; \
37 } while (0)
38
39 typedef struct SwsOpPass {
40 SwsCompiledOp comp;
41 SwsOpExec exec_base;
42 SwsOpExec exec_tail;
43 size_t num_blocks;
44 int tail_off_in;
45 int tail_off_out;
46 int tail_size_in;
47 int tail_size_out;
48 int planes_in;
49 int planes_out;
50 int pixel_bits_in;
51 int pixel_bits_out;
52 int idx_in[4];
53 int idx_out[4];
54 int palette_idx;
55 int *offsets_y;
56 int filter_size_h;
57 int filter_size_v;
58 bool memcpy_in;
59 bool memcpy_out;
60 size_t tail_blocks;
61 uint8_t *tail_buf; /* extra memory for fixing unpadded tails */
62 unsigned int tail_buf_size;
63 } SwsOpPass;
64
65 2799803 static int compile_backend(SwsContext *ctx, const SwsOpBackend *backend,
66 const SwsOpList *ops, SwsCompiledOp *out)
67 {
68 SwsOpList *copy;
69 2799803 SwsCompiledOp compiled = {0};
70 2799803 int ret = 0;
71
72 2799803 copy = ff_sws_op_list_duplicate(ops);
73
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2799803 times.
2799803 if (!copy)
74 ✗ return AVERROR(ENOMEM);
75
76 /* Ensure these are always set during compilation */
77 2799803 ff_sws_op_list_update(copy);
78
79 2799803 ret = backend->compile(ctx, copy, &compiled);
80
2/2
✓ Branch 7 → 8 taken 575370 times.
✓ Branch 7 → 14 taken 2224433 times.
2799803 if (ret < 0) {
81
1/2
✓ Branch 8 → 9 taken 575370 times.
✗ Branch 8 → 10 not taken.
575370 int msg_lev = ret == AVERROR(ENOTSUP) ? AV_LOG_TRACE : AV_LOG_ERROR;
82 1150740 av_log(ctx, msg_lev, "Backend '%s' failed to compile operations: %s\n",
83 575370 backend->name, av_err2str(ret));
84 575370 goto fail;
85 }
86
87 2224433 compiled.backend = backend;
88 2224433 *out = compiled;
89
90 2224433 av_log(ctx, AV_LOG_VERBOSE, "Compiled using backend '%s': "
91 "block size = %d, over-read = {%d %d %d %d}, over-write = {%d %d %d %d}, "
92 2224433 "cpu flags = 0x%x\n", backend->name, out->block_size,
93 out->over_read[0], out->over_read[1],
94 out->over_read[2], out->over_read[3],
95 out->over_write[0], out->over_write[1],
96 out->over_write[2], out->over_write[3],
97 out->cpu_flags);
98
99 2224433 ff_sws_op_list_print(ctx, AV_LOG_VERBOSE, AV_LOG_TRACE, ops);
100
101 2799803 fail:
102 2799803 ff_sws_op_list_free(&copy);
103 2799803 return ret;
104 }
105
106 2491367 int ff_sws_ops_compile(SwsContext *ctx, const SwsOpBackend *backend,
107 const SwsOpList *ops, SwsCompiledOp *out)
108 {
109
2/2
✓ Branch 2 → 3 taken 1756801 times.
✓ Branch 2 → 5 taken 734566 times.
2491367 if (backend)
110 1756801 return compile_backend(ctx, backend, ops, out);
111
112 734566 const SwsBackend enabled = ff_sws_enabled_backends(ctx);
113
2/2
✓ Branch 16 → 7 taken 1843552 times.
✓ Branch 16 → 17 taken 266934 times.
2110486 for (int n = 0; ff_sws_op_backends[n]; n++) {
114 1843552 const SwsOpBackend *backend = ff_sws_op_backends[n];
115
1/2
✓ Branch 7 → 8 taken 1843552 times.
✗ Branch 7 → 10 not taken.
1843552 if (ops->src.hw_format != backend->hw_format ||
116
1/2
✓ Branch 8 → 9 taken 1843552 times.
✗ Branch 8 → 10 not taken.
1843552 ops->dst.hw_format != backend->hw_format ||
117
2/2
✓ Branch 9 → 10 taken 800550 times.
✓ Branch 9 → 11 taken 1043002 times.
1843552 !(enabled & backend->flags))
118 800550 continue;
119
2/2
✓ Branch 12 → 13 taken 575370 times.
✓ Branch 12 → 14 taken 467632 times.
1043002 if (compile_backend(ctx, backend, ops, out) < 0)
120 575370 continue;
121
122 467632 return 0;
123 }
124
125 266934 return AVERROR(ENOTSUP);
126 }
127
128 2501722 void ff_sws_compiled_op_unref(SwsCompiledOp *comp)
129 {
130
2/2
✓ Branch 2 → 3 taken 477987 times.
✓ Branch 2 → 4 taken 2023735 times.
2501722 if (comp->free)
131 477987 comp->free(comp->priv);
132
133 2501722 *comp = (SwsCompiledOp) {0};
134 2501722 }
135
136 2491367 static void op_pass_free(void *ptr)
137 {
138 2491367 SwsOpPass *p = ptr;
139
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 2491367 times.
2491367 if (!p)
140 ✗ return;
141
142 2491367 ff_sws_compiled_op_unref(&p->comp);
143 2491367 av_refstruct_unref(&p->offsets_y);
144 2491367 av_free(p->exec_base.in_bump_y);
145 2491367 av_free(p->exec_base.in_offset_x);
146 2491367 av_free(p->tail_buf);
147 2491367 av_free(p);
148 }
149
150 277408 static inline void get_row_data(const SwsOpPass *p, const int y_dst,
151 const uint8_t *in[4], uint8_t *out[4])
152 {
153 277408 const SwsOpExec *base = &p->exec_base;
154
2/2
✓ Branch 2 → 3 taken 69183 times.
✓ Branch 2 → 4 taken 208225 times.
277408 const int y_src = p->offsets_y ? p->offsets_y[y_dst] : y_dst;
155
2/2
✓ Branch 7 → 6 taken 566407 times.
✓ Branch 7 → 8 taken 277408 times.
843815 for (int i = 0; i < p->planes_in; i++)
156 566407 in[i] = base->in[i] + (y_src >> base->in_sub_y[i]) * base->in_stride[i];
157
2/2
✓ Branch 10 → 9 taken 614831 times.
✓ Branch 10 → 11 taken 277408 times.
892239 for (int i = 0; i < p->planes_out; i++)
158 614831 out[i] = base->out[i] + (y_dst >> base->out_sub_y[i]) * base->out_stride[i];
159 277408 }
160
161 300295 static inline size_t pixel_bytes(size_t pixels, int pixel_bits,
162 enum AVRounding rounding)
163 {
164 300295 const uint64_t bits = (uint64_t) pixels * pixel_bits;
165
2/3
✓ Branch 2 → 3 taken 31161 times.
✓ Branch 2 → 4 taken 269134 times.
✗ Branch 2 → 5 not taken.
300295 switch (rounding) {
166 31161 case AV_ROUND_ZERO:
167 case AV_ROUND_DOWN:
168 31161 return bits >> 3;
169 269134 case AV_ROUND_INF:
170 case AV_ROUND_UP:
171 269134 return (bits + 7) >> 3;
172 ✗ default:
173 ✗ av_unreachable("Invalid rounding mode");
174 return (size_t) -1;
175 }
176 }
177
178 1181238 static size_t safe_bytes_pad(int linesize, int plane_pad)
179 {
180 av_assert1(linesize);
181 1181238 int64_t safe_bytes = FFABS((int64_t) linesize) - plane_pad;
182 1181238 return FFMAX(safe_bytes, 0);
183 }
184
185 188579 static size_t safe_blocks_offset(size_t num_blocks, unsigned block_size,
186 ptrdiff_t safe_offset,
187 const int32_t *offset_bytes)
188 {
189 188579 size_t safe_blocks = num_blocks;
190
3/4
✓ Branch 4 → 5 taken 209748 times.
✗ Branch 4 → 6 not taken.
✓ Branch 5 → 3 taken 21169 times.
✓ Branch 5 → 6 taken 188579 times.
209748 while (safe_blocks && offset_bytes[safe_blocks * block_size - 1] > safe_offset)
191 21169 safe_blocks--;
192 188579 return safe_blocks;
193 }
194
195 33160 static void copy_lines(uint8_t *dst, const ptrdiff_t dst_stride,
196 const uint8_t *src, const ptrdiff_t src_stride,
197 const int h, const size_t bytes)
198 {
199
2/2
✓ Branch 4 → 3 taken 3183360 times.
✓ Branch 4 → 5 taken 33160 times.
3216520 for (int y = 0; y < h; y++) {
200 3183360 memcpy(dst, src, bytes);
201 3183360 dst += dst_stride;
202 3183360 src += src_stride;
203 }
204 33160 }
205
206 277408 static int op_pass_setup(const SwsFrame *out, const SwsFrame *in,
207 const SwsPass *pass)
208 {
209 277408 const AVPixFmtDescriptor *indesc = av_pix_fmt_desc_get(in->format);
210 277408 const bool float_in = indesc->flags & AV_PIX_FMT_FLAG_FLOAT;
211 277408 const int width = out->width;
212
213 277408 SwsOpPass *p = pass->priv;
214 277408 SwsOpExec *exec = &p->exec_base;
215 277408 const SwsCompiledOp *comp = &p->comp;
216
217 /* Set up main loop parameters */
218 277408 const unsigned block_size = comp->block_size;
219 277408 const size_t num_blocks = p->num_blocks;
220 277408 const size_t aligned_w = num_blocks * block_size;
221 277408 p->memcpy_in = false;
222 277408 p->memcpy_out = false;
223
224 277408 size_t safe_blocks = num_blocks;
225
2/2
✓ Branch 15 → 4 taken 566407 times.
✓ Branch 15 → 16 taken 277408 times.
843815 for (int i = 0; i < p->planes_in; i++) {
226 566407 const int idx = p->idx_in[i];
227 566407 size_t input_bytes = in->linesize[idx];
228
4/4
✓ Branch 4 → 5 taken 188579 times.
✓ Branch 4 → 7 taken 377828 times.
✓ Branch 5 → 6 taken 29068 times.
✓ Branch 5 → 7 taken 159511 times.
566407 if (p->filter_size_h && float_in) {
229 /* Floating point inputs may contain NaN / Infinity in the padding */
230 29068 const int plane_w = AV_CEIL_RSHIFT(in->width, exec->in_sub_x[i]);
231 29068 input_bytes = pixel_bytes(plane_w, p->pixel_bits_in, AV_ROUND_UP);
232 }
233
234 566407 size_t safe_bytes = safe_bytes_pad(input_bytes, comp->over_read[i]);
235 size_t safe_blocks_in;
236
2/2
✓ Branch 8 → 9 taken 188579 times.
✓ Branch 8 → 11 taken 377828 times.
566407 if (exec->in_offset_x) {
237 188579 size_t filter_size = pixel_bytes(p->filter_size_h, p->pixel_bits_in,
238 AV_ROUND_UP);
239 188579 safe_blocks_in = safe_blocks_offset(num_blocks, block_size,
240 188579 safe_bytes - filter_size,
241 188579 exec->in_offset_x);
242 } else {
243 377828 safe_blocks_in = safe_bytes / exec->block_size_in[i];
244 }
245
246
2/2
✓ Branch 12 → 13 taken 31271 times.
✓ Branch 12 → 14 taken 535136 times.
566407 if (safe_blocks_in < num_blocks) {
247 31271 p->memcpy_in = true;
248 31271 safe_blocks = FFMIN(safe_blocks, safe_blocks_in);
249 }
250
251 566407 size_t loop_size = num_blocks * exec->block_size_in[i];
252 566407 exec->in[i] = in->data[idx];
253 566407 exec->in_stride[i] = in->linesize[idx];
254 566407 exec->in_bump[i] = in->linesize[idx] - loop_size;
255 }
256
257
2/2
✓ Branch 21 → 17 taken 614831 times.
✓ Branch 21 → 22 taken 277408 times.
892239 for (int i = 0; i < p->planes_out; i++) {
258 614831 const int idx = p->idx_out[i];
259 614831 size_t safe_bytes = safe_bytes_pad(out->linesize[idx], comp->over_write[i]);
260 614831 size_t safe_blocks_out = safe_bytes / exec->block_size_out[i];
261
2/2
✓ Branch 18 → 19 taken 1883 times.
✓ Branch 18 → 20 taken 612948 times.
614831 if (safe_blocks_out < num_blocks) {
262 1883 p->memcpy_out = true;
263 1883 safe_blocks = FFMIN(safe_blocks, safe_blocks_out);
264 }
265
266 614831 size_t loop_size = num_blocks * exec->block_size_out[i];
267 614831 exec->out[i] = out->data[idx];
268 614831 exec->out_stride[i] = out->linesize[idx];
269 614831 exec->out_bump[i] = out->linesize[idx] - loop_size;
270 }
271
272
1/2
✗ Branch 22 → 23 not taken.
✓ Branch 22 → 24 taken 277408 times.
277408 if (p->palette_idx >= 0) {
273 ✗ exec->in[1] = in->data[p->palette_idx];
274 ✗ exec->in_stride[1] = exec->in_bump[1] = 0;
275 }
276
277
4/4
✓ Branch 24 → 25 taken 259292 times.
✓ Branch 24 → 30 taken 18116 times.
✓ Branch 25 → 26 taken 257660 times.
✓ Branch 25 → 30 taken 1632 times.
277408 if (!p->memcpy_in && !p->memcpy_out) {
278
1/2
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 29 taken 257660 times.
257660 av_assert0(safe_blocks == num_blocks);
279 257660 return 0;
280 }
281
282 /* Set-up tail section parameters and buffers */
283 19748 SwsOpExec *tail = &p->exec_tail;
284 19748 const int align = av_cpu_max_align();
285 19748 size_t alloc_size = 0;
286 19748 *tail = *exec;
287
288 19748 const size_t safe_width = safe_blocks * block_size;
289 19748 const size_t tail_size = width - safe_width;
290 19748 p->tail_off_out = pixel_bytes(safe_width, p->pixel_bits_out, AV_ROUND_DOWN);
291 19748 p->tail_size_out = pixel_bytes(tail_size, p->pixel_bits_out, AV_ROUND_UP);
292 19748 p->tail_blocks = num_blocks - safe_blocks;
293
294
2/2
✓ Branch 33 → 34 taken 8335 times.
✓ Branch 33 → 36 taken 11413 times.
19748 if (exec->in_offset_x) {
295 8335 p->tail_off_in = exec->in_offset_x[safe_width];
296 8335 p->tail_size_in = exec->in_offset_x[width - 1] - p->tail_off_in;
297 8335 p->tail_size_in += pixel_bytes(p->filter_size_h, p->pixel_bits_in, AV_ROUND_UP);
298 } else {
299 11413 p->tail_off_in = pixel_bytes(safe_width, p->pixel_bits_in, AV_ROUND_DOWN);
300 11413 p->tail_size_in = pixel_bytes(tail_size, p->pixel_bits_in, AV_ROUND_UP);
301 }
302
303 19748 const size_t alloc_width = aligned_w - safe_width;
304
4/4
✓ Branch 44 → 45 taken 49387 times.
✓ Branch 44 → 46 taken 1632 times.
✓ Branch 45 → 40 taken 31271 times.
✓ Branch 45 → 46 taken 18116 times.
51019 for (int i = 0; p->memcpy_in && i < p->planes_in; i++) {
305 size_t needed_size;
306
2/2
✓ Branch 40 → 41 taken 21169 times.
✓ Branch 40 → 42 taken 10102 times.
31271 if (exec->in_offset_x) {
307 /* The input offset map is already padded to multiples of the block
308 * size, and clamps the input offsets to the image boundaries; so
309 * we just need to compensate for the comp->over_read */
310 21169 needed_size = p->tail_size_in;
311 } else {
312 10102 needed_size = pixel_bytes(alloc_width, p->pixel_bits_in, AV_ROUND_UP);
313 }
314 31271 size_t loop_size = p->tail_blocks * exec->block_size_in[i];
315 31271 tail->in_stride[i] = FFALIGN(needed_size + comp->over_read[i], align);
316 31271 tail->in_bump[i] = tail->in_stride[i] - loop_size;
317 31271 alloc_size += tail->in_stride[i] * in->height;
318 }
319
320
4/4
✓ Branch 49 → 50 taken 3676 times.
✓ Branch 49 → 51 taken 17961 times.
✓ Branch 50 → 47 taken 1889 times.
✓ Branch 50 → 51 taken 1787 times.
21637 for (int i = 0; p->memcpy_out && i < p->planes_out; i++) {
321 1889 size_t needed_size = pixel_bytes(alloc_width, p->pixel_bits_out, AV_ROUND_UP);
322 1889 size_t loop_size = p->tail_blocks * exec->block_size_out[i];
323 1889 tail->out_stride[i] = FFALIGN(needed_size + comp->over_write[i], align);
324 1889 tail->out_bump[i] = tail->out_stride[i] - loop_size;
325 1889 alloc_size += tail->out_stride[i] * out->height;
326 }
327
328
4/4
✓ Branch 51 → 52 taken 18116 times.
✓ Branch 51 → 54 taken 1632 times.
✓ Branch 52 → 53 taken 8099 times.
✓ Branch 52 → 54 taken 10017 times.
19748 if (p->memcpy_in && exec->in_offset_x) {
329 /* `in_offset_x` is indexed relative to the line start, not the start
330 * of the section being processed; so we need to over-allocate this
331 * array to the full width of the image, even though we will only
332 * partially fill in the offsets relevant to the tail region */
333 8099 alloc_size += aligned_w * sizeof(*exec->in_offset_x);
334 }
335
336 19748 av_fast_mallocz(&p->tail_buf, &p->tail_buf_size, alloc_size);
337
1/2
✗ Branch 55 → 56 not taken.
✓ Branch 55 → 57 taken 19748 times.
19748 if (!p->tail_buf)
338 ✗ return AVERROR(ENOMEM);
339
340 19748 uint8_t *tail_buf = p->tail_buf;
341
4/4
✓ Branch 59 → 60 taken 49387 times.
✓ Branch 59 → 61 taken 1632 times.
✓ Branch 60 → 58 taken 31271 times.
✓ Branch 60 → 61 taken 18116 times.
51019 for (int i = 0; p->memcpy_in && i < p->planes_in; i++) {
342 31271 tail->in[i] = tail_buf;
343 31271 tail_buf += tail->in_stride[i] * in->height;
344 }
345
346
4/4
✓ Branch 63 → 64 taken 3676 times.
✓ Branch 63 → 65 taken 17961 times.
✓ Branch 64 → 62 taken 1889 times.
✓ Branch 64 → 65 taken 1787 times.
21637 for (int i = 0; p->memcpy_out && i < p->planes_out; i++) {
347 1889 tail->out[i] = tail_buf;
348 1889 tail_buf += tail->out_stride[i] * out->height;
349 }
350
351
4/4
✓ Branch 65 → 66 taken 18116 times.
✓ Branch 65 → 70 taken 1632 times.
✓ Branch 66 → 67 taken 8099 times.
✓ Branch 66 → 70 taken 10017 times.
19748 if (p->memcpy_in && exec->in_offset_x) {
352 8099 tail->in_offset_x = (int32_t *) tail_buf;
353
2/2
✓ Branch 69 → 68 taken 129584 times.
✓ Branch 69 → 70 taken 8099 times.
137683 for (int i = safe_width; i < aligned_w; i++)
354 129584 tail->in_offset_x[i] = exec->in_offset_x[i] - p->tail_off_in;
355 }
356
357 /* If vertically filtering, we need to copy all lines before processing
358 * any slice, because filters may read past the current slice bounds */
359
3/6
✓ Branch 73 → 74 taken 18116 times.
✓ Branch 73 → 76 taken 1632 times.
✗ Branch 74 → 75 not taken.
✓ Branch 74 → 76 taken 18116 times.
✗ Branch 75 → 71 not taken.
✗ Branch 75 → 76 not taken.
19748 for (int i = 0; p->memcpy_in && p->offsets_y && i < p->planes_in; i++) {
360 ✗ const int lines = AV_CEIL_RSHIFT(in->height, exec->in_sub_y[i]);
361 ✗ copy_lines((uint8_t *) tail->in[i], tail->in_stride[i],
362 ✗ exec->in[i] + p->tail_off_in, exec->in_stride[i],
363 ✗ lines, p->tail_size_in);
364 }
365
366 19748 return 0;
367 }
368
369 277408 static void op_pass_run(const SwsFrame *out, const SwsFrame *in, const int y,
370 const int h, const SwsPass *pass)
371 {
372 277408 const SwsOpPass *p = pass->priv;
373 277408 const SwsCompiledOp *comp = &p->comp;
374
375 /* Fill exec metadata for this slice */
376 277408 DECLARE_ALIGNED_32(SwsOpExec, exec) = p->exec_base;
377 277408 exec.slice_y = y;
378 277408 exec.slice_h = h;
379
380 /**
381 * To ensure safety, we need to consider the following:
382 *
383 * 1. We can overread the input, unless this is the last line of an
384 * unpadded buffer. All defined operations can handle arbitrary pixel
385 * input, so overread of arbitrary data is fine. For flipped images,
386 * this condition is actually *inverted* to where the first line is
387 * the one at the end of the buffer.
388 *
389 * 2. We can overwrite the output, as long as we don't write more than the
390 * amount of pixels that fit into one linesize. So we always need to
391 * memcpy the last column on the output side if unpadded.
392 */
393
394
2/2
✓ Branch 2 → 3 taken 69183 times.
✓ Branch 2 → 4 taken 208225 times.
277408 const int y_in = p->offsets_y ? p->offsets_y[y] : y;
395 277408 const bool memcpy_in = p->memcpy_in;
396 277408 const bool memcpy_out = p->memcpy_out;
397 277408 const size_t num_blocks = p->num_blocks;
398 277408 const size_t tail_blocks = p->tail_blocks;
399
400 277408 get_row_data(p, y, exec.in, exec.out);
401
4/4
✓ Branch 6 → 7 taken 259292 times.
✓ Branch 6 → 10 taken 18116 times.
✓ Branch 7 → 8 taken 257660 times.
✓ Branch 7 → 10 taken 1632 times.
277408 if (!memcpy_in && !memcpy_out) {
402 /* Fast path (fully aligned/padded inputs and outputs) */
403 257660 comp->func(&exec, comp->priv, 0, y, num_blocks, y + h);
404 257660 return;
405 }
406
407 /* Non-aligned case (slow path); process main blocks as normal, and
408 * a separate tail (via memcpy into an appropriately padded buffer) */
409
1/2
✓ Branch 10 → 11 taken 19748 times.
✗ Branch 10 → 15 not taken.
19748 if (num_blocks > tail_blocks) {
410
2/2
✓ Branch 13 → 12 taken 78992 times.
✓ Branch 13 → 14 taken 19748 times.
98740 for (int i = 0; i < 4; i++) {
411 /* We process fewer blocks, so the in_bump needs to be increased
412 * to reflect that the plane pointers are left on the last block,
413 * not the end of the processed line, after each loop iteration */
414 78992 exec.in_bump[i] += exec.block_size_in[i] * tail_blocks;
415 78992 exec.out_bump[i] += exec.block_size_out[i] * tail_blocks;
416 }
417
418 19748 comp->func(&exec, comp->priv, 0, y, num_blocks - tail_blocks, y + h);
419 }
420
421 19748 DECLARE_ALIGNED_32(SwsOpExec, tail) = p->exec_tail;
422 19748 tail.slice_y = y;
423 19748 tail.slice_h = h;
424
425
2/2
✓ Branch 20 → 16 taken 35205 times.
✓ Branch 20 → 21 taken 19748 times.
54953 for (int i = 0; i < p->planes_in; i++) {
426 /* Input offsets are relative to the base pointer */
427
4/4
✓ Branch 16 → 17 taken 21825 times.
✓ Branch 16 → 18 taken 13380 times.
✓ Branch 17 → 18 taken 21169 times.
✓ Branch 17 → 19 taken 656 times.
35205 if (!exec.in_offset_x || memcpy_in)
428 34549 exec.in[i] += p->tail_off_in;
429 35205 tail.in[i] += (y_in >> exec.in_sub_y[i]) * tail.in_stride[i];
430 }
431
2/2
✓ Branch 23 → 22 taken 49235 times.
✓ Branch 23 → 24 taken 19748 times.
68983 for (int i = 0; i < p->planes_out; i++) {
432 49235 exec.out[i] += p->tail_off_out;
433 49235 tail.out[i] += y * tail.out_stride[i];
434 }
435
436
2/2
✓ Branch 30 → 25 taken 35205 times.
✓ Branch 30 → 31 taken 19748 times.
54953 for (int i = 0; i < p->planes_in; i++) {
437
2/2
✓ Branch 25 → 26 taken 31271 times.
✓ Branch 25 → 28 taken 3934 times.
35205 if (memcpy_in) {
438
1/2
✓ Branch 26 → 27 taken 31271 times.
✗ Branch 26 → 29 not taken.
31271 if (!p->offsets_y) { /* already copied by op_pass_setup() */
439 31271 const int lines = h >> exec.in_sub_y[i];
440 31271 copy_lines((uint8_t *) tail.in[i], tail.in_stride[i],
441 31271 exec.in[i], exec.in_stride[i], lines, p->tail_size_in);
442 }
443 } else {
444 /* Reuse input pointers directly */
445 3934 const size_t loop_size = tail_blocks * exec.block_size_in[i];
446 3934 tail.in[i] = exec.in[i];
447 3934 tail.in_stride[i] = exec.in_stride[i];
448 3934 tail.in_bump[i] = exec.in_stride[i] - loop_size;
449 }
450 }
451
452
4/4
✓ Branch 33 → 34 taken 65307 times.
✓ Branch 33 → 35 taken 1787 times.
✓ Branch 34 → 32 taken 47346 times.
✓ Branch 34 → 35 taken 17961 times.
67094 for (int i = 0; !memcpy_out && i < p->planes_out; i++) {
453 /* Reuse output pointers directly */
454 47346 const size_t loop_size = tail_blocks * exec.block_size_out[i];
455 47346 tail.out[i] = exec.out[i];
456 47346 tail.out_stride[i] = exec.out_stride[i];
457 47346 tail.out_bump[i] = exec.out_stride[i] - loop_size;
458 }
459
460 /* Dispatch kernel over tail */
461 av_assert1(tail_blocks > 0);
462 19748 comp->func(&tail, comp->priv, num_blocks - tail_blocks, y, num_blocks, y + h);
463
464
4/4
✓ Branch 39 → 40 taken 3676 times.
✓ Branch 39 → 41 taken 17961 times.
✓ Branch 40 → 37 taken 1889 times.
✓ Branch 40 → 41 taken 1787 times.
21637 for (int i = 0; memcpy_out && i < p->planes_out; i++) {
465 1889 const int lines = h >> tail.out_sub_y[i];
466 1889 copy_lines(exec.out[i], exec.out_stride[i],
467 1889 tail.out[i], tail.out_stride[i], lines, p->tail_size_out);
468 }
469 }
470
471 /* Updates the plane copy (no-op) map for this operation list */
472 467632 static void op_list_get_plane_copy(const SwsOpList *ops, SwsPass *pass)
473 {
474 467632 const SwsOp *write = ff_sws_op_list_output(ops);
475 467632 const SwsOp *read = ff_sws_op_list_input(ops);
476
5/6
✓ Branch 4 → 5 taken 467632 times.
✗ Branch 4 → 10 not taken.
✓ Branch 5 → 6 taken 328769 times.
✓ Branch 5 → 10 taken 138863 times.
✓ Branch 6 → 7 taken 286579 times.
✓ Branch 6 → 10 taken 42190 times.
467632 if (!write || write->rw.mode != SWS_RW_PLANAR ||
477
2/2
✓ Branch 7 → 8 taken 185302 times.
✓ Branch 7 → 10 taken 101277 times.
286579 !read || read->rw.mode != SWS_RW_PLANAR ||
478
2/2
✓ Branch 8 → 9 taken 83219 times.
✓ Branch 8 → 10 taken 102083 times.
185302 read->rw.type != write->rw.type ||
479
2/2
✓ Branch 9 → 10 taken 1835 times.
✓ Branch 9 → 11 taken 81384 times.
83219 read->rw.frac != write->rw.frac)
480 386248 return; /* only regular planes can be directly ref'd */
481
482 81384 const SwsOp *prev = ff_sws_op_prev(write);
483 81384 int *plane_copy = pass->output->plane_copy;
484
2/2
✓ Branch 21 → 13 taken 208880 times.
✓ Branch 21 → 22 taken 81384 times.
290264 for (int i = 0; i < write->rw.elems; i++) {
485 208880 SwsCompFlags flags = prev->outputs[i].flags;
486
2/2
✓ Branch 13 → 14 taken 207364 times.
✓ Branch 13 → 15 taken 1516 times.
208880 if (!(flags & SWS_COMP_COPY))
487 207364 continue;
488
489 1516 const int out_idx = ops->plane_dst[i];
490
3/5
✓ Branch 15 → 16 taken 1176 times.
✓ Branch 15 → 17 taken 170 times.
✓ Branch 15 → 18 taken 170 times.
✗ Branch 15 → 19 not taken.
✗ Branch 15 → 20 not taken.
1516 switch (prev->outputs[i].dep_in) {
491 1176 case SWS_COMP(0): plane_copy[out_idx] = ops->plane_src[0]; break;
492 170 case SWS_COMP(1): plane_copy[out_idx] = ops->plane_src[1]; break;
493 170 case SWS_COMP(2): plane_copy[out_idx] = ops->plane_src[2]; break;
494 ✗ case SWS_COMP(3): plane_copy[out_idx] = ops->plane_src[3]; break;
495 }
496 }
497 }
498
499 893074 static int rw_data_planes(const SwsOp *op)
500 {
501 /* Exclude the palette plane from the plane count, since it does not need
502 * to be directly processed/adjusted by the dispatch layer */
503
1/2
✓ Branch 2 → 3 taken 893074 times.
✗ Branch 2 → 4 not taken.
893074 return op->rw.mode == SWS_RW_PALETTE ? 1 : ff_sws_rw_op_planes(op);
504 }
505
506 893074 static int rw_pixel_bits(const SwsOp *op)
507 {
508
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 893074 times.
893074 if (op->rw.mode == SWS_RW_PALETTE)
509 ✗ return 8; /* index size */
510
511 893074 int elems = 0;
512
2/3
✓ Branch 4 → 5 taken 606905 times.
✓ Branch 4 → 6 taken 286169 times.
✗ Branch 4 → 7 not taken.
893074 switch (op->rw.mode) {
513 606905 case SWS_RW_PLANAR: elems = 1; break;
514 286169 case SWS_RW_PACKED: elems = op->rw.elems; break;
515 }
516
517 893074 const int size = ff_sws_pixel_type_size(op->rw.type);
518 893074 const int bits = 8 >> op->rw.frac;
519 av_assert1(bits >= 1);
520 893074 return elems * size * bits;
521 }
522
523 893074 static void align_pass(SwsPass *pass, int block_size, const int *over_rw,
524 int pixel_bits)
525 {
526
3/4
✓ Branch 2 → 3 taken 584639 times.
✓ Branch 2 → 4 taken 308435 times.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 584639 times.
893074 if (!pass || pixel_bits <= 0)
527 308435 return;
528
529 /* Add at least as many pixels as needed to cover the padding requirement */
530 584639 int pad_max = 0;
531
2/2
✓ Branch 7 → 6 taken 2338556 times.
✓ Branch 7 → 8 taken 584639 times.
2923195 for (int i = 0; i < 4; i++) {
532 2338556 const int pad = (over_rw[i] * 8 + pixel_bits - 1) / pixel_bits;
533 2338556 pad_max = FFMAX(pad_max, pad);
534 }
535
536 584639 SwsPassBuffer *buf = pass->output;
537 584639 buf->width_align = FFMAX(buf->width_align, block_size);
538 584639 buf->width_pad = FFMAX(buf->width_pad, pad_max);
539 }
540
541 /* Unchanging part of parameter list */
542 typedef struct CompileArgs {
543 const SwsOpBackend *backend;
544 SwsGraph *graph;
545 int flags;
546 } CompileArgs;
547
548 2491367 static int compile_single(const CompileArgs *args, const SwsOpList *ops,
549 SwsPass *link, SwsPass *input, SwsPass **output)
550 {
551 2491367 SwsGraph *graph = args->graph;
552 2491367 SwsContext *ctx = graph->ctx;
553 2491367 SwsOpPass *p = av_mallocz(sizeof(*p));
554
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 2491367 times.
2491367 if (!p)
555 ✗ return AVERROR(ENOMEM);
556
557 2491367 int ret = ff_sws_ops_compile(ctx, args->backend, ops, &p->comp);
558
2/2
✓ Branch 6 → 7 taken 266934 times.
✓ Branch 6 → 8 taken 2224433 times.
2491367 if (ret < 0)
559 266934 goto fail;
560
2/2
✓ Branch 8 → 9 taken 1756801 times.
✓ Branch 8 → 10 taken 467632 times.
2224433 else if (args->flags & SWS_OP_FLAG_DRY_RUN)
561 1756801 goto fail; /* nothing to do, just return */
562
563 467632 const SwsCompiledOp *comp = &p->comp;
564 467632 const SwsFormat *src = &ops->src;
565 467632 const SwsFormat *dst = &ops->dst;
566
3/4
✓ Branch 10 → 11 taken 33505 times.
✓ Branch 10 → 14 taken 434127 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 14 taken 33505 times.
467632 av_assert0(!link || link->format == dst->format);
567
1/2
✗ Branch 14 → 15 not taken.
✓ Branch 14 → 21 taken 467632 times.
467632 if (p->comp.opaque) {
568 ✗ SwsCompiledOp c = *comp;
569 ✗ av_free(p);
570 ✗ ret = ff_sws_graph_add_pass(graph, dst->format, dst->width, dst->height,
571 input, 0, c.slice_align, c.func_opaque,
572 NULL, c.priv, c.free, output);
573
0/2
✗ Branch 17 → 18 not taken.
✗ Branch 17 → 20 not taken.
✗ if (ret >= 0) {
574 ✗ (*output)->backend = c.backend->flags;
575 ✗ (*output)->cpu_flags = c.cpu_flags;
576 ✗ op_list_get_plane_copy(ops, *output);
577 ✗ ff_sws_pass_link_output(*output, link);
578 }
579 ✗ return ret;
580 }
581
582 467632 const AVPixFmtDescriptor *indesc = av_pix_fmt_desc_get(src->format);
583 467632 const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(dst->format);
584 467632 const SwsOp *write = ff_sws_op_list_output(ops);
585 467632 p->planes_out = rw_data_planes(write);
586 467632 p->pixel_bits_out = rw_pixel_bits(write);
587 467632 p->palette_idx = -1;
588 467632 p->num_blocks = (dst->width + comp->block_size - 1) / comp->block_size;
589
1/2
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 28 taken 467632 times.
467632 if (p->num_blocks * comp->block_size < (unsigned) dst->width)
590 ✗ return AVERROR(ERANGE);
591
592 467632 SwsOpExec *exec = &p->exec_base;
593 467632 *exec = (SwsOpExec) {
594 467632 .width = dst->width,
595 467632 .height = dst->height,
596 };
597
598 467632 const SwsOp *read = ff_sws_op_list_input(ops);
599
2/2
✓ Branch 29 → 30 taken 425442 times.
✓ Branch 29 → 34 taken 42190 times.
467632 if (read) {
600 425442 p->planes_in = rw_data_planes(read);
601 425442 p->pixel_bits_in = rw_pixel_bits(read);
602
1/2
✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 425442 times.
425442 if (read->rw.mode == SWS_RW_PALETTE)
603 ✗ p->palette_idx = ops->plane_src[1];
604 }
605
606 467632 const int64_t block_bits_in = (int64_t) comp->block_size * p->pixel_bits_in;
607 467632 const int64_t block_bits_out = (int64_t) comp->block_size * p->pixel_bits_out;
608
2/4
✓ Branch 34 → 35 taken 467632 times.
✗ Branch 34 → 36 not taken.
✗ Branch 35 → 36 not taken.
✓ Branch 35 → 38 taken 467632 times.
467632 if (block_bits_in & 0x7 || block_bits_out & 0x7) {
609 ✗ av_log(ctx, AV_LOG_ERROR, "Block size must be byte-aligned.\n");
610 ✗ ret = AVERROR(EINVAL);
611 ✗ goto fail;
612 }
613
614
2/2
✓ Branch 40 → 39 taken 1870528 times.
✓ Branch 40 → 41 taken 467632 times.
2338160 for (int i = 0; i < 4; i++)
615 1870528 p->idx_in[i] = p->idx_out[i] = -1;
616
617
2/2
✓ Branch 53 → 42 taken 881830 times.
✓ Branch 53 → 54 taken 467632 times.
1349462 for (int i = 0; i < p->planes_in; i++) {
618 881830 const int idx = ops->plane_src[i];
619
4/4
✓ Branch 42 → 43 taken 666685 times.
✓ Branch 42 → 44 taken 215145 times.
✓ Branch 43 → 44 taken 211578 times.
✓ Branch 43 → 45 taken 455107 times.
881830 const int chroma = idx == 1 || idx == 2;
620
2/2
✓ Branch 46 → 47 taken 426723 times.
✓ Branch 46 → 48 taken 455107 times.
881830 const int sub_x = chroma ? indesc->log2_chroma_w : 0;
621
2/2
✓ Branch 49 → 50 taken 426723 times.
✓ Branch 49 → 51 taken 455107 times.
881830 const int sub_y = chroma ? indesc->log2_chroma_h : 0;
622 881830 exec->in_sub_x[i] = sub_x;
623 881830 exec->in_sub_y[i] = sub_y;
624 881830 exec->block_size_in[i] = block_bits_in >> 3;
625 881830 p->idx_in[i] = idx;
626 }
627
628
2/2
✓ Branch 66 → 55 taken 949465 times.
✓ Branch 66 → 67 taken 467632 times.
1417097 for (int i = 0; i < p->planes_out; i++) {
629 949465 const int idx = ops->plane_dst[i];
630
4/4
✓ Branch 55 → 56 taken 717822 times.
✓ Branch 55 → 57 taken 231643 times.
✓ Branch 56 → 57 taken 227963 times.
✓ Branch 56 → 58 taken 489859 times.
949465 const int chroma = idx == 1 || idx == 2;
631
2/2
✓ Branch 59 → 60 taken 459606 times.
✓ Branch 59 → 61 taken 489859 times.
949465 const int sub_x = chroma ? outdesc->log2_chroma_w : 0;
632
2/2
✓ Branch 62 → 63 taken 459606 times.
✓ Branch 62 → 64 taken 489859 times.
949465 const int sub_y = chroma ? outdesc->log2_chroma_h : 0;
633 949465 exec->out_sub_x[i] = sub_x;
634 949465 exec->out_sub_y[i] = sub_y;
635 949465 exec->block_size_out[i] = block_bits_out >> 3;
636 949465 p->idx_out[i] = idx;
637 }
638
639
2/2
✓ Branch 67 → 68 taken 425442 times.
✓ Branch 67 → 69 taken 42190 times.
467632 const SwsFilterWeights *filter = read ? read->rw.filter.kernel : NULL;
640
4/4
✓ Branch 70 → 71 taken 425442 times.
✓ Branch 70 → 80 taken 42190 times.
✓ Branch 71 → 72 taken 68824 times.
✓ Branch 71 → 80 taken 356618 times.
467632 if (read && read->rw.filter.op == SWS_OP_FILTER_V) {
641 68824 p->offsets_y = av_refstruct_ref(filter->offsets);
642 68824 p->filter_size_v = filter->filter_size;
643
644 /* Compute relative pointer bumps for each output line */
645 68824 int32_t *bump = av_malloc_array(filter->dst_size, sizeof(*bump));
646
1/2
✗ Branch 74 → 75 not taken.
✓ Branch 74 → 76 taken 68824 times.
68824 if (!bump) {
647 ✗ ret = AVERROR(ENOMEM);
648 ✗ goto fail;
649 }
650
651 68824 int line = filter->offsets[0];
652
2/2
✓ Branch 78 → 77 taken 6526344 times.
✓ Branch 78 → 79 taken 68824 times.
6595168 for (int y = 0; y < filter->dst_size - 1; y++) {
653 6526344 int next = filter->offsets[y + 1];
654 6526344 bump[y] = next - line - 1;
655 6526344 line = next;
656 }
657 68824 bump[filter->dst_size - 1] = 0;
658 68824 exec->in_bump_y = bump;
659
4/4
✓ Branch 80 → 81 taken 356618 times.
✓ Branch 80 → 98 taken 42190 times.
✓ Branch 81 → 82 taken 70731 times.
✓ Branch 81 → 98 taken 285887 times.
398808 } else if (read && read->rw.filter.op == SWS_OP_FILTER_H) {
660 /* Compute pixel offset map for each output line */
661 70731 const int pixels = FFALIGN(filter->dst_size, comp->block_size);
662 70731 int32_t *offset = av_malloc_array(pixels, sizeof(*offset));
663
1/2
✗ Branch 83 → 84 not taken.
✓ Branch 83 → 85 taken 70731 times.
70731 if (!offset) {
664 ✗ ret = AVERROR(ENOMEM);
665 ✗ goto fail;
666 }
667 70731 exec->in_offset_x = offset;
668
669
2/2
✓ Branch 90 → 86 taken 6791168 times.
✓ Branch 90 → 91 taken 70731 times.
6861899 for (int x = 0; x < filter->dst_size; x++) {
670 /* Sanity check; if the tap would land on a half-pixel, we cannot
671 * reasonably expect the implementation to know about this. Just
672 * error out in such (theoretical) cases. */
673 6791168 int64_t bits = (int64_t) filter->offsets[x] * p->pixel_bits_in;
674
2/4
✓ Branch 86 → 87 taken 6791168 times.
✗ Branch 86 → 88 not taken.
✗ Branch 87 → 88 not taken.
✓ Branch 87 → 89 taken 6791168 times.
6791168 if ((bits & 0x7) || (bits >> 3) > INT32_MAX) {
675 ✗ ret = AVERROR(EINVAL);
676 ✗ goto fail;
677 }
678 6791168 offset[x] = bits >> 3;
679 }
680
1/2
✗ Branch 93 → 92 not taken.
✓ Branch 93 → 94 taken 70731 times.
70731 for (int x = filter->dst_size; x < pixels; x++)
681 ✗ offset[x] = offset[filter->dst_size - 1];
682
2/2
✓ Branch 96 → 95 taken 282924 times.
✓ Branch 96 → 97 taken 70731 times.
353655 for (int i = 0; i < 4; i++)
683 282924 exec->block_size_in[i] = 0; /* ptr does not advance */
684 70731 p->filter_size_h = filter->filter_size;
685 }
686
687 467632 ret = ff_sws_graph_add_pass(graph, dst->format, dst->width, dst->height,
688 467632 input, 0, comp->slice_align, op_pass_run,
689 op_pass_setup, p, op_pass_free, output);
690
1/2
✗ Branch 99 → 100 not taken.
✓ Branch 99 → 101 taken 467632 times.
467632 if (ret < 0)
691 ✗ return ret;
692
693 467632 (*output)->backend = comp->backend->flags;
694 467632 (*output)->cpu_flags = comp->cpu_flags;
695 467632 op_list_get_plane_copy(ops, *output);
696 467632 ff_sws_pass_link_output(*output, link);
697 467632 align_pass(*output, comp->block_size, comp->over_write, p->pixel_bits_out);
698
2/2
✓ Branch 104 → 105 taken 425442 times.
✓ Branch 104 → 106 taken 42190 times.
467632 if (read)
699 425442 align_pass(input, comp->block_size, comp->over_read, p->pixel_bits_in);
700 467632 return 0;
701
702 2023735 fail:
703 2023735 op_pass_free(p);
704 2023735 return ret;
705 }
706
707 /* Return a mask of all planes matching any flag in `flags` */
708 3536231 static SwsCompMask plane_mask_flags(const SwsOp *op, SwsCompFlags flags)
709 {
710 3536231 SwsCompMask planes = 0;
711
2/2
✓ Branch 6 → 3 taken 14144924 times.
✓ Branch 6 → 7 taken 3536231 times.
17681155 for (int c = 0; c < 4; c++) {
712
2/2
✓ Branch 3 → 4 taken 982263 times.
✓ Branch 3 → 5 taken 13162661 times.
14144924 if (op->outputs[c].flags & flags)
713 982263 planes |= SWS_COMP(c);
714 }
715
716 3536231 return planes;
717 }
718
719 /* Takes over ownership of *pops, even on failure */
720 3536231 static int compile_subpass(const CompileArgs *args, SwsOpList **pops,
721 SwsPass *link, SwsPass *input, SwsPass **output)
722 {
723 int ret;
724 3536231 SwsContext *ctx = args->graph->ctx;
725 3536231 SwsOpList *ops = *pops;
726 3536231 SwsOpList *rest = NULL;
727 3536231 SwsPass *tmp = NULL;
728 3536231 *pops = NULL;
729
730
1/2
✓ Branch 2 → 3 taken 3536231 times.
✗ Branch 2 → 23 not taken.
3536231 if (args->flags & SWS_OP_FLAG_SPLIT_MEMCPY) {
731 /* Split off copied and constant planes into a separate subpass,
732 * since these are likely to be handled by the memcpy backend */
733
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 3536231 times.
3536231 av_assert0(ops->num_ops >= 2);
734 3536231 const SwsOp *write = ff_sws_op_list_output(ops);
735 3536231 const SwsOp *prev = ff_sws_op_prev(write);
736 3536231 SwsCompMask planes = plane_mask_flags(prev, SWS_COMP_COPY | SWS_COMP_CONST);
737
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 12 taken 3536231 times.
3536231 RET(ff_sws_op_list_split_planes(ops, &rest, planes));
738
2/2
✓ Branch 12 → 13 taken 158163 times.
✓ Branch 12 → 23 taken 3378068 times.
3536231 if (rest) {
739 /* Parallel split: share input and link all outputs together */
740 158163 av_log(ctx, AV_LOG_DEBUG, "Splitting const/memcpy planes: %s\n",
741 158163 ff_sws_comp_mask_str(planes));
742
2/2
✓ Branch 16 → 17 taken 36326 times.
✓ Branch 16 → 18 taken 121837 times.
167058 RET(compile_subpass(args, &ops, link, input, &tmp));
743
2/2
✓ Branch 19 → 20 taken 8895 times.
✓ Branch 19 → 21 taken 112942 times.
121837 RET(compile_subpass(args, &rest, tmp, input, output));
744 112942 return 0;
745 }
746 }
747
748 /* Split off any unresolved filters, since no backend implements these */
749
2/2
✓ Branch 40 → 24 taken 10866632 times.
✓ Branch 40 → 41 taken 2491367 times.
13357999 for (int idx = 1; idx < ops->num_ops - 1; idx++) {
750 10866632 const SwsOp *op = &ops->ops[idx];
751
4/4
✓ Branch 24 → 25 taken 10453874 times.
✓ Branch 24 → 26 taken 412758 times.
✓ Branch 25 → 26 taken 473943 times.
✓ Branch 25 → 39 taken 9979931 times.
10866632 if (op->op == SWS_OP_FILTER_H || op->op == SWS_OP_FILTER_V) {
752
1/2
✗ Branch 27 → 28 not taken.
✓ Branch 27 → 29 taken 886701 times.
886701 RET(ff_sws_op_list_split_at(ops, &rest, idx));
753
1/2
✗ Branch 30 → 31 not taken.
✓ Branch 30 → 32 taken 886701 times.
886701 if (ff_sws_op_list_is_noop(ops)) {
754 /* Prevent infinite recursion by avoiding splitting in a way
755 * that does not meaningfully reduce the number of operations
756 * performed in the second part. */
757 ✗ FFSWAP(SwsOpList *, ops, rest);
758 ✗ break;
759 }
760 /* Serial split: feed first pass into second */
761
2/2
✓ Branch 33 → 34 taken 17949 times.
✓ Branch 33 → 35 taken 868752 times.
886701 RET(compile_subpass(args, &ops, NULL, input, &tmp));
762
2/2
✓ Branch 36 → 37 taken 5108 times.
✓ Branch 36 → 38 taken 863644 times.
868752 RET(compile_subpass(args, &rest, link, tmp, output));
763 863644 return 0;
764 }
765 }
766
767 2491367 ret = compile_single(args, ops, link, input, output);
768
2/2
✓ Branch 42 → 43 taken 2224433 times.
✓ Branch 42 → 44 taken 266934 times.
2491367 if (ret == AVERROR(ENOTSUP)) {
769 266934 av_log(ctx, AV_LOG_WARNING, "No backend found for operations:\n");
770 266934 ff_sws_op_list_print(ctx, AV_LOG_WARNING, AV_LOG_TRACE, ops);
771 }
772
773 2224433 fail:
774 2559645 ff_sws_op_list_free(&ops);
775 2559645 ff_sws_op_list_free(&rest);
776 2559645 return ret;
777 }
778
779 1502465 int ff_sws_compile_pass(SwsGraph *graph, const SwsOpBackend *backend,
780 SwsOpList **pops, int flags, SwsPass *input,
781 SwsPass **output)
782 {
783 1502465 const int passes_orig = graph->num_passes;
784 1502465 SwsContext *ctx = graph->ctx;
785 1502465 SwsOpList *ops = *pops;
786 1502465 int ret = 0;
787
788 1502465 const SwsOp *write = ff_sws_op_list_output(ops);
789
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 1502465 times.
1502465 if (!write) {
790 ✗ av_log(ctx, AV_LOG_ERROR, "Last operation must be SWS_OP_WRITE.\n");
791 ✗ ret = AVERROR(EINVAL);
792 ✗ goto out;
793 }
794
795
2/2
✓ Branch 6 → 7 taken 572125 times.
✓ Branch 6 → 12 taken 930340 times.
1502465 if (flags & SWS_OP_FLAG_OPTIMIZE) {
796 572125 ret = ff_sws_op_list_optimize(ops);
797
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 572125 times.
572125 if (ret < 0)
798 ✗ goto out;
799 572125 av_log(ctx, AV_LOG_DEBUG, "Operation list after optimizing:\n");
800 572125 ff_sws_op_list_print(ctx, AV_LOG_DEBUG, AV_LOG_TRACE, ops);
801 }
802
803 /* Check if the whole operation graph is an end-to-end no-op */
804
2/2
✓ Branch 13 → 14 taken 1687 times.
✓ Branch 13 → 17 taken 1500778 times.
1502465 if (ff_sws_op_list_is_noop(ops)) {
805
2/2
✓ Branch 14 → 15 taken 535 times.
✓ Branch 14 → 16 taken 1152 times.
1687 if (output)
806 535 *output = input;
807 1687 goto out;
808 }
809
810 1500778 const CompileArgs args = {
811 .backend = backend,
812 .graph = graph,
813 .flags = flags,
814 };
815
816 1500778 ret = compile_subpass(&args, &ops, NULL, input, output);
817
2/2
✓ Branch 18 → 19 taken 266934 times.
✓ Branch 18 → 20 taken 1233844 times.
1500778 if (ret < 0)
818 266934 goto out;
819
820 1233844 const int num_passes = graph->num_passes - passes_orig;
821
2/2
✓ Branch 20 → 21 taken 1133349 times.
✓ Branch 20 → 22 taken 100495 times.
1233844 if (num_passes > 1)
822 100495 av_log(ctx, AV_LOG_VERBOSE, "Using %d separate passes.\n", num_passes);
823
824 1133349 out:
825
2/2
✓ Branch 23 → 24 taken 266934 times.
✓ Branch 23 → 25 taken 1235531 times.
1502465 if (ret < 0)
826 266934 ff_sws_graph_rollback(graph, passes_orig);
827 1502465 ff_sws_op_list_free(&ops);
828 1502465 *pops = NULL;
829 1502465 return ret;
830 }
831