FFmpeg
ops_optimizer.c
Go to the documentation of this file.
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/attributes.h"
22 #include "libavutil/avassert.h"
23 #include "libavutil/bswap.h"
24 #include "libavutil/rational.h"
25 
26 #include "ops.h"
27 #include "ops_internal.h"
28 
29 #define RET(x) \
30  do { \
31  if ((ret = (x)) < 0) \
32  return ret; \
33  } while (0)
34 
35 /**
36  * Try to commute a clear op with the next operation. Makes any adjustments
37  * to the operations as needed, but does not perform the actual commutation.
38  *
39  * Returns whether successful.
40  */
41 static bool op_commute_clear(SwsOp *op, SwsOp *next)
42 {
43  av_assert1(op->op == SWS_OP_CLEAR);
44  switch (next->op) {
45  case SWS_OP_CONVERT:
46  op->type = next->convert.to;
48  case SWS_OP_LSHIFT:
49  case SWS_OP_RSHIFT:
50  case SWS_OP_DITHER:
51  case SWS_OP_MIN:
52  case SWS_OP_MAX:
53  case SWS_OP_SCALE:
54  case SWS_OP_READ:
55  ff_sws_apply_op_q(next, op->clear.value);
56  return true;
57  case SWS_OP_FILTER_H:
58  case SWS_OP_FILTER_V:
59  op->type = next->filter.type;
60  return true;
61  case SWS_OP_SWIZZLE:
62  ff_sws_comp_mask_swizzle(&op->clear.mask, &next->swizzle);
63  ff_sws_apply_op_q(next, op->clear.value);
64  return true;
65  case SWS_OP_SWAP_BYTES:
66  switch (next->type) {
67  case SWS_PIXEL_U16:
68  case SWS_PIXEL_U32:
69  ff_sws_apply_op_q(next, op->clear.value); /* always representable */
70  return true;
71  default:
72  return false;
73  }
74  case SWS_OP_INVALID:
75  case SWS_OP_WRITE:
76  case SWS_OP_LINEAR:
77  case SWS_OP_PACK:
78  case SWS_OP_UNPACK:
79  case SWS_OP_CLEAR:
80  return false;
81  case SWS_OP_TYPE_NB:
82  break;
83  }
84 
85  av_unreachable("Invalid operation type!");
86  return false;
87 }
88 
89  /**
90  * Try to commute a swizzle op with the next operation. Makes any adjustments
91  * to the operations as needed, but does not perform the actual commutation.
92  *
93  * Returns whether successful.
94  */
95 static bool op_commute_swizzle(SwsOp *op, SwsOp *next)
96 {
97  bool seen[4] = {0};
98 
100  switch (next->op) {
101  case SWS_OP_CONVERT:
102  op->type = next->convert.to;
104  case SWS_OP_SWAP_BYTES:
105  case SWS_OP_LSHIFT:
106  case SWS_OP_RSHIFT:
107  case SWS_OP_SCALE:
108  return true;
109  case SWS_OP_FILTER_H:
110  case SWS_OP_FILTER_V:
111  op->type = next->filter.type;
112  return true;
113 
114  /**
115  * We can commute per-channel ops only if the per-channel constants are the
116  * same for all duplicated channels; e.g.:
117  * SWIZZLE {0, 0, 0, 3}
118  * NEXT {x, x, x, w}
119  * ->
120  * NEXT {x, _, _, w}
121  * SWIZZLE {0, 0, 0, 3}
122  */
123  case SWS_OP_MIN:
124  case SWS_OP_MAX: {
125  const SwsClampOp c = next->clamp;
126  for (int i = 0; i < 4; i++) {
127  if (!SWS_OP_NEEDED(op, i))
128  continue;
129  const int j = op->swizzle.in[i];
130  if (seen[j] && av_cmp_q64(next->clamp.limit[j], c.limit[i]))
131  return false;
132  next->clamp.limit[j] = c.limit[i];
133  seen[j] = true;
134  }
135  return true;
136  }
137 
138  case SWS_OP_DITHER: {
139  const SwsDitherOp d = next->dither;
140  for (int i = 0; i < 4; i++) {
141  if (!SWS_OP_NEEDED(op, i))
142  continue;
143  const int j = op->swizzle.in[i];
144  if (seen[j] && next->dither.y_offset[j] != d.y_offset[i])
145  return false;
146  next->dither.y_offset[j] = d.y_offset[i];
147  seen[j] = true;
148  }
149  return true;
150  }
151 
152  case SWS_OP_INVALID:
153  case SWS_OP_READ:
154  case SWS_OP_WRITE:
155  case SWS_OP_SWIZZLE:
156  case SWS_OP_CLEAR:
157  case SWS_OP_LINEAR:
158  case SWS_OP_PACK:
159  case SWS_OP_UNPACK:
160  return false;
161  case SWS_OP_TYPE_NB:
162  break;
163  }
164 
165  av_unreachable("Invalid operation type!");
166  return false;
167 }
168 
169 /**
170  * Try to commute a filter op with the previous operation. Makes any
171  * adjustments to the operations as needed, but does not perform the actual
172  * commutation.
173  *
174  * Returns whether successful.
175  */
176 static bool op_commute_filter(SwsOp *op, SwsOp *prev)
177 {
178  av_assert0(!ff_sws_pixel_type_is_int(op->filter.type));
179 
180  switch (prev->op) {
181  case SWS_OP_SWIZZLE:
182  case SWS_OP_SCALE:
183  case SWS_OP_LINEAR:
184  case SWS_OP_DITHER:
185  prev->type = op->filter.type;
186  return true;
187  case SWS_OP_CONVERT:
188  case SWS_OP_INVALID:
189  case SWS_OP_READ:
190  case SWS_OP_WRITE:
191  case SWS_OP_SWAP_BYTES:
192  case SWS_OP_UNPACK:
193  case SWS_OP_PACK:
194  case SWS_OP_LSHIFT:
195  case SWS_OP_RSHIFT:
196  case SWS_OP_CLEAR:
197  case SWS_OP_MIN:
198  case SWS_OP_MAX:
199  case SWS_OP_FILTER_H:
200  case SWS_OP_FILTER_V:
201  return false;
202  case SWS_OP_TYPE_NB:
203  break;
204  }
205 
206  av_unreachable("Invalid operation type!");
207  return false;
208 }
209 
210 /* returns log2(x) only if x is a power of two, or 0 otherwise */
211 static int exact_log2(const int x)
212 {
213  int p;
214  if (x <= 0)
215  return 0;
216  p = av_log2(x);
217  return (1 << p) == x ? p : 0;
218 }
219 
220 static int exact_log2_q64(const AVRational64 x)
221 {
222  if (x.den == 1)
223  return exact_log2(x.num);
224  else if (x.num == 1)
225  return -exact_log2(x.den);
226  else
227  return 0;
228 }
229 
230 /**
231  * If a linear operation can be reduced to a scalar multiplication, returns
232  * the corresponding scaling factor, or 0 otherwise.
233  */
234 static bool extract_scalar(const SwsLinearOp *c,
235  const SwsComps *comps, const SwsComps *prev,
236  SwsScaleOp *out_scale)
237 {
238  SwsScaleOp scale = {0};
239 
240  /* There are components not on the main diagonal */
241  if (c->mask & ~SWS_MASK_DIAG4)
242  return false;
243 
244  for (int i = 0; i < 4; i++) {
245  const AVRational64 s = c->m[i][i];
246  if ((prev->flags[i] & SWS_COMP_ZERO) ||
247  (comps->flags[i] & SWS_COMP_GARBAGE))
248  continue;
249  if (scale.factor.den && av_cmp_q64(s, scale.factor))
250  return false;
251  scale.factor = s;
252  }
253 
254  if (scale.factor.den)
255  *out_scale = scale;
256  return scale.factor.den;
257 }
258 
259 /* Extracts an integer clear operation (subset) from the given linear op. */
260 static bool extract_constant_rows(SwsLinearOp *c, const SwsComps *prev,
261  SwsClearOp *out_clear)
262 {
263  SwsClearOp clear = {0};
264  bool ret = false;
265 
266  for (int i = 0; i < 4; i++) {
267  bool const_row = c->m[i][4].den == 1; /* offset is integer */
268  for (int j = 0; j < 4; j++) {
269  const_row &= c->m[i][j].num == 0 || /* scalar is zero */
270  (prev->flags[j] & SWS_COMP_ZERO); /* input is zero */
271  }
272  if (const_row && (c->mask & SWS_MASK_ROW(i))) {
273  clear.mask |= SWS_COMP(i);
274  clear.value[i] = c->m[i][4];
275  for (int j = 0; j < 5; j++)
276  c->m[i][j] = Q(i == j);
277  c->mask &= ~SWS_MASK_ROW(i);
278  ret = true;
279  }
280  }
281 
282  if (ret)
283  *out_clear = clear;
284  return ret;
285 }
286 
287 /* Unswizzle a linear operation by aligning single-input rows with
288  * their corresponding diagonal */
289 static bool extract_swizzle(SwsLinearOp *op, const SwsComps *prev,
290  SwsSwizzleOp *out_swiz)
291 {
292  SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
293  SwsLinearOp c = *op;
294 
295  /* Find non-zero coefficients in the main 4x4 matrix */
296  uint32_t nonzero = 0;
297  for (int i = 0; i < 4; i++) {
298  for (int j = 0; j < 4; j++) {
299  if (!c.m[i][j].num || (prev->flags[j] & SWS_COMP_ZERO))
300  continue;
301  nonzero |= SWS_MASK(i, j);
302  }
303  }
304 
305  /* If a value is unique in its row and the target column is
306  * empty, move it there and update the input swizzle */
307  for (int i = 0; i < 4; i++) {
308  if (nonzero & SWS_MASK_COL(i))
309  continue; /* target column is not empty */
310  for (int j = 0; j < 4; j++) {
311  if ((nonzero & SWS_MASK_ROW(i)) == SWS_MASK(i, j)) {
312  /* Move coefficient to the diagonal */
313  c.m[i][i] = c.m[i][j];
314  c.m[i][j] = Q(0);
315  swiz.in[i] = j;
316  break;
317  }
318  }
319  }
320 
321  if (swiz.mask == SWS_SWIZZLE(0, 1, 2, 3).mask)
322  return false; /* no swizzle was identified */
323 
324  c.mask = ff_sws_linear_mask(&c);
325  *out_swiz = swiz;
326  *op = c;
327  return true;
328 }
329 
330 static int op_result_is_exact(const SwsOp *op)
331 {
332  for (int i = 0; i < 4; i++) {
333  if (SWS_OP_NEEDED(op, i) && !(op->comps.flags[i] & SWS_COMP_EXACT))
334  return false;
335  }
336 
337  return true;
338 }
339 
341 {
342  int ret;
343 
344 retry:
346 
347  /* Try to push filters towards the input; do this first to unblock
348  * in-place optimizations like linear op fusion */
349  for (int n = 1; n < ops->num_ops; n++) {
350  SwsOp *op = &ops->ops[n];
351  SwsOp *prev = &ops->ops[n - 1];
352 
353  switch (op->op) {
354  case SWS_OP_FILTER_H:
355  case SWS_OP_FILTER_V:
356  if (op_commute_filter(op, prev)) {
357  FFSWAP(SwsOp, *op, *prev);
358  goto retry;
359  }
360 
361  /* Merge filter with prior conversion */
362  if (prev->op == SWS_OP_CONVERT && !prev->convert.expand) {
363  int size_from = ff_sws_pixel_type_size(prev->type);
364  int size_to = ff_sws_pixel_type_size(op->type);
365  av_assert1(prev->convert.to == op->type);
366  if (size_from < size_to) {
367  op->type = prev->type;
368  ff_sws_op_list_remove_at(ops, n - 1, 1);
369  goto retry;
370  }
371  }
372  break;
373  }
374  }
375 
376  /* Apply all in-place optimizations (that do not re-order the list) */
377  for (int n = 0; n < ops->num_ops; n++) {
378  SwsOp dummy = {0};
379  SwsOp *op = &ops->ops[n];
380  SwsOp *prev = n ? &ops->ops[n - 1] : &dummy;
381  SwsOp *next = n + 1 < ops->num_ops ? &ops->ops[n + 1] : &dummy;
382 
383  /* common helper variable */
385  bool noop = true;
386 
387  if (!needed && op->op != SWS_OP_WRITE) {
388  /* Remove any operation whose output is not needed */
389  ff_sws_op_list_remove_at(ops, n, 1);
390  goto retry;
391  }
392 
393  switch (op->op) {
394  case SWS_OP_READ:
395  /* "Compress" planar reads where not all components are needed */
396  if (op->rw.mode == SWS_RW_PLANAR) {
397  SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
398  int nb_planes = 0;
399  for (int i = 0; i < op->rw.elems; i++) {
400  if (!SWS_OP_NEEDED(op, i)) {
401  swiz.in[i] = 3 - (i - nb_planes); /* map to unused plane */
402  continue;
403  }
404 
405  const int idx = nb_planes++;
406  av_assert1(idx <= i);
407  ops->plane_src[idx] = ops->plane_src[i];
408  swiz.in[i] = idx;
409  }
410 
411  if (nb_planes < op->rw.elems) {
412  op->rw.elems = nb_planes;
413  RET(ff_sws_op_list_insert_at(ops, n + 1, &(SwsOp) {
414  .op = SWS_OP_SWIZZLE,
415  .type = op->rw.filter.op ? op->rw.filter.type : op->type,
416  .swizzle = swiz,
417  }));
418  goto retry;
419  }
420  }
421  break;
422 
423  case SWS_OP_SWAP_BYTES:
424  /* Redundant (double) swap */
425  if (next->op == SWS_OP_SWAP_BYTES) {
426  ff_sws_op_list_remove_at(ops, n, 2);
427  goto retry;
428  }
429  break;
430 
431  case SWS_OP_UNPACK:
432  /* Redundant unpack+pack */
433  if (next->op == SWS_OP_PACK && next->type == op->type &&
434  next->pack.pattern[0] == op->pack.pattern[0] &&
435  next->pack.pattern[1] == op->pack.pattern[1] &&
436  next->pack.pattern[2] == op->pack.pattern[2] &&
437  next->pack.pattern[3] == op->pack.pattern[3])
438  {
439  ff_sws_op_list_remove_at(ops, n, 2);
440  goto retry;
441  }
442  break;
443 
444  case SWS_OP_LSHIFT:
445  case SWS_OP_RSHIFT:
446  /* Two shifts in the same direction */
447  if (next->op == op->op) {
448  op->shift.amount += next->shift.amount;
449  ff_sws_op_list_remove_at(ops, n + 1, 1);
450  goto retry;
451  }
452 
453  /* No-op shift */
454  if (!op->shift.amount) {
455  ff_sws_op_list_remove_at(ops, n, 1);
456  goto retry;
457  }
458  break;
459 
460  case SWS_OP_CLEAR:
461  for (int i = 0; i < 4; i++) {
462  if (!SWS_COMP_TEST(op->clear.mask, i))
463  continue;
464 
465  if ((prev->comps.flags[i] & SWS_COMP_ZERO) &&
466  !(prev->comps.flags[i] & SWS_COMP_GARBAGE) &&
467  op->clear.value[i].num == 0)
468  {
469  /* Redundant clear-to-zero of zero component */
470  op->clear.mask ^= SWS_COMP(i);
471  } else if (!SWS_OP_NEEDED(op, i)) {
472  /* Unnecessary clear of unused component */
473  op->clear.mask ^= SWS_COMP(i);
474  } else {
475  noop = false;
476  }
477  }
478 
479  if (noop) {
480  ff_sws_op_list_remove_at(ops, n, 1);
481  goto retry;
482  }
483 
484  /* Transitive clear */
485  if (next->op == SWS_OP_CLEAR) {
486  for (int i = 0; i < 4; i++) {
487  if (SWS_COMP_TEST(next->clear.mask, i))
488  op->clear.value[i] = next->clear.value[i];
489  }
490  op->clear.mask |= next->clear.mask;
491  ff_sws_op_list_remove_at(ops, n + 1, 1);
492  goto retry;
493  }
494  break;
495 
496  case SWS_OP_SWIZZLE:
497  for (int i = 0; i < 4; i++) {
498  if (!SWS_OP_NEEDED(op, i))
499  continue;
500  if (op->swizzle.in[i] != i)
501  noop = false;
502  }
503 
504  /* Identity swizzle */
505  if (noop) {
506  ff_sws_op_list_remove_at(ops, n, 1);
507  goto retry;
508  }
509 
510  /* Transitive swizzle */
511  if (next->op == SWS_OP_SWIZZLE) {
512  const SwsSwizzleOp orig = op->swizzle;
513  for (int i = 0; i < 4; i++)
514  op->swizzle.in[i] = orig.in[next->swizzle.in[i]];
515  ff_sws_op_list_remove_at(ops, n + 1, 1);
516  goto retry;
517  }
518 
519  /* Swizzle planes instead of components, if possible */
520  if (prev->op == SWS_OP_READ && prev->rw.mode == SWS_RW_PLANAR) {
521  for (int dst = 0; dst < prev->rw.elems; dst++) {
522  const int src = op->swizzle.in[dst];
523  if (src > dst && src < prev->rw.elems) {
524  FFSWAP(int, ops->plane_src[dst], ops->plane_src[src]);
525  for (int i = dst; i < 4; i++) {
526  if (op->swizzle.in[i] == dst)
527  op->swizzle.in[i] = src;
528  else if (op->swizzle.in[i] == src)
529  op->swizzle.in[i] = dst;
530  }
531  goto retry;
532  }
533  }
534  }
535 
536  if (next->op == SWS_OP_WRITE && next->rw.mode == SWS_RW_PLANAR) {
537  for (int dst = 0; dst < next->rw.elems; dst++) {
538  const int src = op->swizzle.in[dst];
539  if (src > dst && src < next->rw.elems) {
540  FFSWAP(int, ops->plane_dst[dst], ops->plane_dst[src]);
541  FFSWAP(int, op->swizzle.in[dst], op->swizzle.in[src]);
542  goto retry;
543  }
544  }
545  }
546  break;
547 
548  case SWS_OP_CONVERT:
549  /* No-op conversion */
550  if (op->type == op->convert.to) {
551  ff_sws_op_list_remove_at(ops, n, 1);
552  goto retry;
553  }
554 
555  /* Transitive conversion */
556  if (next->op == SWS_OP_CONVERT &&
557  op->convert.expand == next->convert.expand)
558  {
559  av_assert1(op->convert.to == next->type);
560  op->convert.to = next->convert.to;
561  ff_sws_op_list_remove_at(ops, n + 1, 1);
562  goto retry;
563  }
564 
565  /* Conversion followed by integer expansion */
566  if (next->op == SWS_OP_SCALE && !op->convert.expand &&
567  ff_sws_pixel_type_is_int(op->type) &&
568  ff_sws_pixel_type_is_int(op->convert.to) &&
569  !av_cmp_q64(next->scale.factor,
570  ff_sws_pixel_expand(op->type, op->convert.to)))
571  {
572  op->convert.expand = true;
573  ff_sws_op_list_remove_at(ops, n + 1, 1);
574  goto retry;
575  }
576  break;
577 
578  case SWS_OP_MIN:
579  for (int i = 0; i < 4; i++) {
580  if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
581  continue;
582  if (av_cmp_q64(op->clamp.limit[i], prev->comps.max[i]) < 0)
583  noop = false;
584  }
585 
586  if (noop) {
587  ff_sws_op_list_remove_at(ops, n, 1);
588  goto retry;
589  }
590  break;
591 
592  case SWS_OP_MAX:
593  for (int i = 0; i < 4; i++) {
594  if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
595  continue;
596  if (av_cmp_q64(prev->comps.min[i], op->clamp.limit[i]) < 0)
597  noop = false;
598  }
599 
600  if (noop) {
601  ff_sws_op_list_remove_at(ops, n, 1);
602  goto retry;
603  }
604  break;
605 
606  case SWS_OP_DITHER:
607  for (int i = 0; i < 4; i++) {
608  if (op->dither.y_offset[i] < 0)
609  continue;
610  if (!SWS_OP_NEEDED(op, i) || (prev->comps.flags[i] & SWS_COMP_EXACT)) {
611  op->dither.y_offset[i] = -1; /* unnecessary dither */
612  goto retry;
613  } else {
614  noop = false;
615  }
616  }
617 
618  if (noop) {
619  ff_sws_op_list_remove_at(ops, n, 1);
620  goto retry;
621  }
622  break;
623 
624  case SWS_OP_LINEAR: {
625  SwsSwizzleOp swizzle;
626  SwsClearOp clear;
628 
629  /* No-op (identity) linear operation */
630  if (!op->lin.mask) {
631  ff_sws_op_list_remove_at(ops, n, 1);
632  goto retry;
633  }
634 
635  if (next->op == SWS_OP_LINEAR) {
636  /* 5x5 matrix multiplication after appending [ 0 0 0 0 1 ] */
637  const SwsLinearOp m1 = op->lin;
638  const SwsLinearOp m2 = next->lin;
639  for (int i = 0; i < 4; i++) {
640  for (int j = 0; j < 5; j++) {
641  AVRational64 sum = Q(0);
642  for (int k = 0; k < 4; k++)
643  sum = av_add_q64(sum, av_mul_q64(m2.m[i][k], m1.m[k][j]));
644  if (j == 4) /* m1.m[4][j] == 1 */
645  sum = av_add_q64(sum, m2.m[i][4]);
646  op->lin.m[i][j] = sum;
647  }
648  }
649  op->lin.mask = ff_sws_linear_mask(&op->lin);
650  ff_sws_op_list_remove_at(ops, n + 1, 1);
651  goto retry;
652  }
653 
654  /* Optimize away zero columns */
655  for (int j = 0; j < 4; j++) {
656  const uint32_t col = SWS_MASK_COL(j);
657  if (!(prev->comps.flags[j] & SWS_COMP_ZERO) || !(op->lin.mask & col))
658  continue;
659  for (int i = 0; i < 4; i++)
660  op->lin.m[i][j] = Q(i == j);
661  op->lin.mask &= ~col;
662  goto retry;
663  }
664 
665  /* Optimize away unused rows */
666  for (int i = 0; i < 4; i++) {
667  const uint32_t row = SWS_MASK_ROW(i);
668  if (SWS_OP_NEEDED(op, i) || !(op->lin.mask & row))
669  continue;
670  for (int j = 0; j < 5; j++)
671  op->lin.m[i][j] = Q(i == j);
672  op->lin.mask &= ~row;
673  goto retry;
674  }
675 
676  /* Convert constant rows to explicit clear instruction */
677  if (extract_constant_rows(&op->lin, &prev->comps, &clear)) {
678  RET(ff_sws_op_list_insert_at(ops, n + 1, &(SwsOp) {
679  .op = SWS_OP_CLEAR,
680  .type = op->type,
681  .comps = op->comps,
682  .clear = clear,
683  }));
684  goto retry;
685  }
686 
687  /* Multiplication by scalar constant */
688  if (extract_scalar(&op->lin, &op->comps, &prev->comps, &scale)) {
689  op->op = SWS_OP_SCALE;
690  op->scale = scale;
691  goto retry;
692  }
693 
694  /* Swizzle by fixed pattern */
695  if (extract_swizzle(&op->lin, &prev->comps, &swizzle)) {
696  RET(ff_sws_op_list_insert_at(ops, n, &(SwsOp) {
697  .op = SWS_OP_SWIZZLE,
698  .type = op->type,
699  .swizzle = swizzle,
700  }));
701  goto retry;
702  }
703  break;
704  }
705 
706  case SWS_OP_SCALE: {
707  const int factor2 = exact_log2_q64(op->scale.factor);
708 
709  /* No-op scaling */
710  if (op->scale.factor.num == 1 && op->scale.factor.den == 1) {
711  ff_sws_op_list_remove_at(ops, n, 1);
712  goto retry;
713  }
714 
715  /* Merge consecutive scaling operations */
716  if (next->op == SWS_OP_SCALE) {
717  op->scale.factor = av_mul_q64(op->scale.factor, next->scale.factor);
718  ff_sws_op_list_remove_at(ops, n + 1, 1);
719  goto retry;
720  }
721 
722  /* Scaling by exact power of two */
723  if (factor2 && ff_sws_pixel_type_is_int(op->type)) {
724  op->op = factor2 > 0 ? SWS_OP_LSHIFT : SWS_OP_RSHIFT;
725  op->shift.amount = FFABS(factor2);
726  goto retry;
727  }
728  break;
729  }
730 
731  case SWS_OP_FILTER_H:
732  case SWS_OP_FILTER_V:
733  /* Merge with prior simple planar read */
734  if (prev->op == SWS_OP_READ && !prev->rw.filter.op &&
735  prev->rw.mode == SWS_RW_PLANAR && !prev->rw.frac) {
736  prev->rw.filter.op = op->op;
737  prev->rw.filter.kernel = av_refstruct_ref(op->filter.kernel);
738  prev->rw.filter.type = op->filter.type;
739  ff_sws_op_list_remove_at(ops, n, 1);
740  goto retry;
741  }
742  break;
743  }
744  }
745 
746  /* Push clears to the back to void any unused components */
747  for (int n = 0; n < ops->num_ops - 1; n++) {
748  SwsOp *op = &ops->ops[n];
749  SwsOp *next = &ops->ops[n + 1];
750 
751  switch (op->op) {
752  case SWS_OP_CLEAR:
753  if (op_commute_clear(op, next)) {
754  FFSWAP(SwsOp, *op, *next);
755  goto retry;
756  }
757  break;
758  }
759  }
760 
761  /* Apply any remaining preferential re-ordering optimizations; do these
762  * last because they are more likely to block other optimizations if done
763  * too aggressively */
764  for (int n = 0; n < ops->num_ops - 1; n++) {
765  SwsOp *op = &ops->ops[n];
766  SwsOp *next = &ops->ops[n + 1];
767 
768  switch (op->op) {
769  case SWS_OP_SWIZZLE: {
770  /* Try to push swizzles towards the output */
771  if (op_commute_swizzle(op, next)) {
772  FFSWAP(SwsOp, *op, *next);
773  goto retry;
774  }
775  break;
776  }
777 
778  case SWS_OP_SCALE:
779  /* Exact integer multiplication */
780  if (op->scale.factor.den == 1 && next->op == SWS_OP_CONVERT &&
783  {
784  op->type = next->convert.to;
785  FFSWAP(SwsOp, *op, *next);
786  goto retry;
787  }
788  break;
789  }
790  }
791 
792  return 0;
793 }
794 
796 {
797  SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
798  SwsOp *write = &ops->ops[ops->num_ops - 1];
799  av_assert0(write->op == SWS_OP_WRITE);
800 
801  write->rw.elems = 0;
802  for (int src = 0; src < 4; src++) {
803  if (!SWS_COMP_TEST(planes, src))
804  continue; /* plane not selected */
805  const int dst = write->rw.elems++;
806  av_assert2(src >= dst);
807  swiz.in[dst] = src;
808  FFSWAP(int, ops->plane_dst[dst], ops->plane_dst[src]);
809  }
810 
811  /* Insert swizzle to select desired planes */
812  int ret = ff_sws_op_list_insert_at(ops, ops->num_ops - 1, &(SwsOp) {
813  .op = SWS_OP_SWIZZLE,
814  .type = write->type,
815  .swizzle = swiz,
816  });
817  if (ret < 0)
818  return ret;
819 
820  /* The optimizer will take care of the rest */
821  return ff_sws_op_list_optimize(ops);
822 }
823 
825 {
826  const SwsOp *write = ff_sws_op_list_output(ops1);
827  if (!write || write->rw.mode != SWS_RW_PLANAR) {
828  *out_ops2 = NULL;
829  return 0;
830  }
831 
832  const SwsCompMask full = SWS_COMP_ELEMS(write->rw.elems);
833  const SwsCompMask mask1 = planes & full;
834  const SwsCompMask mask2 = full ^ mask1;
835  if (!mask1 || !mask2) {
836  /* Nothing to filter */
837  *out_ops2 = NULL;
838  return 0;
839  }
840 
841  SwsOpList *ops2 = ff_sws_op_list_duplicate(ops1);
842  if (!ops2)
843  return AVERROR(ENOMEM);
844 
845  int ret;
846  if ((ret = select_planes(ops1, mask1)) < 0 ||
847  (ret = select_planes(ops2, mask2)) < 0)
848  {
849  ff_sws_op_list_free(&ops2);
850  return ret;
851  }
852 
853  *out_ops2 = ops2;
854  return 0;
855 }
856 
857 int ff_sws_shuffle_mask(const SwsUOp *uop, int8_t shuffle[], int size)
858 {
859  const SwsShuffleUOp *par = &uop->par.shuffle;
861  av_assert1(par->write_size <= sizeof(uop->data.shuffle.mask));
862  av_assert1(size <= INT8_MAX);
863 
864  const int num_groups = size / FFMAX(par->read_size, par->write_size);
865  if (!num_groups)
866  return AVERROR(EINVAL);
867 
868  memset(shuffle, 0, size);
869  for (int n = 0; n < num_groups; n++) {
870  const int base_in = n * par->read_size;
871  const int base_out = n * par->write_size;
872  for (int i = 0; i < par->write_size; i++) {
873  const int8_t idx = uop->data.shuffle.mask[i];
874  shuffle[base_out + i] = idx + (idx >= 0) * base_in;
875  }
876  }
877 
878  return num_groups;
879 }
880 
882 {
883  switch (ff_sws_pixel_type_size(type)) {
884  case 1: return true;
885  case 2: return val.u16 == val.u8 * 0x101ul;
886  case 4: return val.u32 == val.u8 * 0x1010101ul;
887  default: break;
888  }
889 
890  av_unreachable("Invalid pixel type!");
891  return false;
892 }
893 
894 static int solve_shuffle(const SwsUOpList *const uops, SwsUOp *out)
895 {
896  if (!uops->num_ops)
897  return AVERROR(EINVAL);
898  const SwsUOp *read = &uops->ops[0];
899  switch (read->uop) {
900  case SWS_UOP_READ_PACKED:
901  break;
902  case SWS_UOP_READ_PLANAR:
903  if (read->mask != SWS_COMP_ELEMS(1))
904  return AVERROR(ENOTSUP);
905  break;
906  default:
907  return AVERROR(ENOTSUP);
908  }
909 
910  const int read_size = ff_sws_pixel_type_size(read->type);
911  uint32_t mask[4] = {0};
912  int clear_val = -1;
913  int read_elems = 0;
914  for (int i = 0; i < 4; i++) {
915  if (SWS_COMP_TEST(read->mask, i)) {
916  mask[i] = 0x01010101 * i * read_size + 0x03020100;
917  read_elems++;
918  }
919  }
920 
921  for (int opidx = 1; opidx < uops->num_ops; opidx++) {
922  const SwsUOp *uop = &uops->ops[opidx];
923  const SwsUOpParams *par = &uop->par;
924  switch (uop->uop) {
925  case SWS_UOP_COPY:
926  case SWS_UOP_PERMUTE: {
927  uint32_t tmp;
928  for (int i = 0; i < par->move.num_moves; i++) {
929  const int dst_idx = par->move.dst[i];
930  const int src_idx = par->move.src[i];
931  uint32_t *src = src_idx < 0 ? &tmp : &mask[src_idx];
932  uint32_t *dst = dst_idx < 0 ? &tmp : &mask[dst_idx];
933  *dst = *src;
934  }
935  break;
936  }
937 
938  case SWS_UOP_SWAP_BYTES:
939  for (int i = 0; i < 4; i++) {
940  switch (ff_sws_pixel_type_size(uop->type)) {
941  case 2: mask[i] = av_bswap16(mask[i]); break;
942  case 4: mask[i] = av_bswap32(mask[i]); break;
943  }
944  }
945  break;
946 
947  case SWS_UOP_CLEAR:
948  for (int i = 0; i < 4; i++) {
949  if (!SWS_COMP_TEST(uop->mask, i))
950  continue;
951  SwsPixel val = uop->data.vec4[i];
952  if (!pixel_is_repeating(uop->type, val) ||
953  (clear_val >= 0 && clear_val != val.u8))
954  return AVERROR(ENOTSUP); /* would require different bytes */
955  mask[i] = 0xFFFFFFFFul; /* (uint8_t[4]) { -1, -1, -1, -1 } */
956  clear_val = val.u8;
957  }
958  break;
959 
960  case SWS_UOP_EXPAND_PAIR:
961  case SWS_UOP_EXPAND_QUAD:
962  for (int i = 0; i < 4; i++)
963  mask[i] = 0x01010101 * (mask[i] & 0xFF);
964  break;
965 
967  if (uop->mask != SWS_COMP_ELEMS(1))
968  return AVERROR(ENOTSUP);
970  case SWS_UOP_WRITE_PACKED: {
971  const int write_elems = av_popcount(uop->mask);
972  const int write_size = ff_sws_pixel_type_size(uop->type);
973  *out = (SwsUOp) {
974  .uop = SWS_UOP_RW_SHUFFLE,
975  .type = SWS_PIXEL_U8,
976  .mask = SWS_COMP_ELEMS(1), /* single plane for now */
977  };
978 
979  SwsShuffleUOp *par = &out->par.shuffle;
980  SwsShuffleMask *data = &out->data.shuffle;
981  *par = (SwsShuffleUOp) {
982  .read_size = read_elems * read_size,
983  .write_size = write_elems * write_size,
984  .clear_value = clear_val >= 0 ? clear_val : 0,
985  };
986 
987  /* Generate baseline shuffle for a single pixel */
988  data->pixels = 1;
989  for (int i = 0; i < write_elems; i++) {
990  const int offset = i * write_size;
991  for (int b = 0; b < write_size; b++)
992  data->mask[offset + b] = mask[i] >> (b * 8);
993  }
994 
995  /* Expand as many times as needed to round up to the size of the
996  * shuffle uop data mask */
997  int8_t tmp[FF_ARRAY_ELEMS(data->mask)];
998  const int num_groups = ff_sws_shuffle_mask(out, tmp, sizeof(tmp));
999  if (num_groups < 0)
1000  return num_groups;
1001  memcpy(data->mask, tmp, sizeof(tmp));
1002  par->read_size *= num_groups;
1003  par->write_size *= num_groups;
1004  data->pixels = num_groups;
1005  return 0;
1006  }
1007 
1008  default:
1009  return AVERROR(ENOTSUP);
1010  }
1011  }
1012 
1013  return AVERROR(EINVAL);
1014 }
1015 
1017 {
1018  /* Try promoting the entire uop list to a packed shuffle operation */
1019  if (flags & SWS_UOP_FLAG_PSHUFB) {
1020  SwsUOp shuffle;
1021  int ret = solve_shuffle(uops, &shuffle);
1022  if (ret >= 0) {
1023  ff_sws_uop_list_remove_at(uops, 0, uops->num_ops);
1024  return ff_sws_uop_list_append(uops, &shuffle);
1025  } else if (ret < 0 && ret != AVERROR(ENOTSUP)) {
1026  return ret;
1027  }
1028  }
1029 
1030 #if 0
1031  static const SwsUOp dummy = {0};
1032 
1033 retry:
1034  for (int i = 0; i < uops->num_ops; i++) {
1035  const SwsUOp *next = i < uops->num_ops - 1 ? &uops->ops[i + 1] : &dummy;
1036  SwsUOp *op = &uops->ops[i];
1037 
1038  switch (op->uop) {
1039  /* placeholder */
1040  }
1041  }
1042 #endif
1043 
1044  return 0;
1045 }
1046 
1047 /**
1048  * Determine a suitable intermediate buffer format for a given combination
1049  * of pixel types and number of planes. The exact interpretation of these
1050  * formats does not matter at all; since they will only ever be used as
1051  * temporary intermediate buffers. We still need to pick *some* format as
1052  * a consequence of ff_sws_graph_add_pass() taking an AVPixelFormat for the
1053  * output buffer.
1054  */
1055 static enum AVPixelFormat get_planar_fmt(SwsPixelType type, int nb_planes)
1056 {
1057  switch (ff_sws_pixel_type_size(type)) {
1058  case 1:
1059  switch (nb_planes) {
1060  case 1: return AV_PIX_FMT_GRAY8;
1061  case 2: return AV_PIX_FMT_YUV444P; // FIXME: no 2-plane planar fmt
1062  case 3: return AV_PIX_FMT_YUV444P;
1063  case 4: return AV_PIX_FMT_YUVA444P;
1064  }
1065  break;
1066  case 2:
1067  switch (nb_planes) {
1068  case 1: return AV_PIX_FMT_GRAY16;
1069  case 2: return AV_PIX_FMT_YUV444P16; // FIXME: no 2-plane planar fmt
1070  case 3: return AV_PIX_FMT_YUV444P16;
1071  case 4: return AV_PIX_FMT_YUVA444P16;
1072  }
1073  break;
1074  case 4:
1075  switch (nb_planes) {
1076  case 1: return AV_PIX_FMT_GRAYF32;
1077  case 2: return AV_PIX_FMT_GBRPF32; // FIXME: no 2-plane planar fmt
1078  case 3: return AV_PIX_FMT_GBRPF32;
1079  case 4: return AV_PIX_FMT_GBRAPF32;
1080  }
1081  break;
1082  }
1083 
1084  av_unreachable("Invalid pixel type or number of planes?");
1085  return AV_PIX_FMT_NONE;
1086 }
1087 
1088 static void get_input_size(const SwsOpList *ops, SwsFormat *fmt)
1089 {
1090  fmt->width = ops->src.width;
1091  fmt->height = ops->src.height;
1092 
1093  const SwsOp *read = ff_sws_op_list_input(ops);
1094  if (read && read->rw.filter.op == SWS_OP_FILTER_V) {
1095  fmt->height = read->rw.filter.kernel->dst_size;
1096  } else if (read && read->rw.filter.op == SWS_OP_FILTER_H) {
1097  fmt->width = read->rw.filter.kernel->dst_size;
1098  }
1099 }
1100 
1102 {
1103  int ret;
1104  if (index <= 0 || index >= ops1->num_ops) {
1105  *out_ops2 = NULL;
1106  return 0;
1107  }
1108 
1109  const SwsOp *op = &ops1->ops[index];
1110  const SwsOp *prev = &ops1->ops[index - 1];
1111 
1112  SwsOpList *ops2 = ff_sws_op_list_duplicate(ops1);
1113  if (!ops2)
1114  return AVERROR(ENOMEM);
1115 
1116  /**
1117  * Not all components may be needed; but we need the ones that *are*
1118  * used to be contiguous for the write/read operations. So, first
1119  * compress them into a linearly ascending list of components
1120  */
1121  int nb_planes = 0;
1122  SwsSwizzleOp swiz_wr = SWS_SWIZZLE(0, 1, 2, 3);
1123  SwsSwizzleOp swiz_rd = SWS_SWIZZLE(0, 1, 2, 3);
1124  for (int i = 0; i < 4; i++) {
1125  if (SWS_OP_NEEDED(prev, i)) {
1126  const int o = nb_planes++;
1127  swiz_wr.in[o] = i;
1128  swiz_rd.in[i] = o;
1129  }
1130  }
1131 
1132  /* Determine metadata for the intermediate format */
1133  const SwsPixelType type = op->type;
1134  ops2->src.format = get_planar_fmt(type, nb_planes);
1135  ops2->src.desc = av_pix_fmt_desc_get(ops2->src.format);
1136  get_input_size(ops1, &ops2->src);
1137  ops1->dst = ops2->src;
1138 
1139  for (int i = 0; i < nb_planes; i++) {
1140  const int idx = swiz_wr.in[i];
1141  ops1->plane_dst[i] = ops2->plane_src[i] = i;
1142  ops2->comps_src.flags[i] = prev->comps.flags[idx];
1143  ops2->comps_src.min[i] = prev->comps.min[idx];
1144  ops2->comps_src.max[i] = prev->comps.max[idx];
1145  }
1146 
1147  ff_sws_op_list_remove_at(ops1, index, ops1->num_ops - index);
1148  ff_sws_op_list_remove_at(ops2, 0, index);
1149  op = NULL; /* the above command may invalidate op */
1150 
1151  if (swiz_wr.mask != SWS_SWIZZLE(0, 1, 2, 3).mask) {
1152  ret = ff_sws_op_list_append(ops1, &(SwsOp) {
1153  .op = SWS_OP_SWIZZLE,
1154  .type = type,
1155  .swizzle = swiz_wr,
1156  });
1157  if (ret < 0)
1158  goto fail;
1159  }
1160 
1161  ret = ff_sws_op_list_append(ops1, &(SwsOp) {
1162  .op = SWS_OP_WRITE,
1163  .type = type,
1164  .rw.elems = nb_planes,
1165  });
1166  if (ret < 0)
1167  goto fail;
1168 
1169  ret = ff_sws_op_list_insert_at(ops2, 0, &(SwsOp) {
1170  .op = SWS_OP_READ,
1171  .type = type,
1172  .rw.elems = nb_planes,
1173  });
1174  if (ret < 0)
1175  goto fail;
1176 
1177  if (swiz_rd.mask != SWS_SWIZZLE(0, 1, 2, 3).mask) {
1178  ret = ff_sws_op_list_insert_at(ops2, 1, &(SwsOp) {
1179  .op = SWS_OP_SWIZZLE,
1180  .type = type,
1181  .swizzle = swiz_rd,
1182  });
1183  if (ret < 0)
1184  goto fail;
1185  }
1186 
1187  ret = ff_sws_op_list_optimize(ops1);
1188  if (ret < 0)
1189  goto fail;
1190 
1191  ret = ff_sws_op_list_optimize(ops2);
1192  if (ret < 0)
1193  goto fail;
1194 
1195  *out_ops2 = ops2;
1196  return 0;
1197 
1198 fail:
1199  ff_sws_op_list_free(&ops2);
1200  return ret;
1201 }
SWS_OP_READ
@ SWS_OP_READ
Definition: ops.h:39
SWS_MASK_COL
#define SWS_MASK_COL(J)
Definition: uops.h:232
SWS_UOP_RW_SHUFFLE
@ SWS_UOP_RW_SHUFFLE
Definition: uops.h:147
ff_sws_op_list_free
void ff_sws_op_list_free(SwsOpList **p_ops)
Definition: ops.c:611
select_planes
static int select_planes(SwsOpList *ops, SwsCompMask planes)
Definition: ops_optimizer.c:795
AVPixelFormat
AVPixelFormat
Pixel format.
Definition: pixfmt.h:71
SwsClearOp::value
AVRational64 value[4]
Definition: ops.h:162
SWS_OP_SWIZZLE
@ SWS_OP_SWIZZLE
Definition: ops.h:42
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
SwsUOpParams::move
SwsMoveUOp move
Definition: uops.h:250
SWS_OP_LSHIFT
@ SWS_OP_LSHIFT
Definition: ops.h:47
SWS_OP_UNPACK
@ SWS_OP_UNPACK
Definition: ops.h:45
ff_sws_op_list_duplicate
SwsOpList * ff_sws_op_list_duplicate(const SwsOpList *ops)
Returns a duplicate of ops, or NULL on OOM.
Definition: ops.c:625
SwsClearOp
Definition: ops.h:160
SWS_RW_PLANAR
@ SWS_RW_PLANAR
Note: 1-component reads are either SWS_RW_PLANAR or SWS_RW_PACKED, depending on the underlying interp...
Definition: ops.h:100
extract_scalar
static bool extract_scalar(const SwsLinearOp *c, const SwsComps *comps, const SwsComps *prev, SwsScaleOp *out_scale)
If a linear operation can be reduced to a scalar multiplication, returns the corresponding scaling fa...
Definition: ops_optimizer.c:234
out
static FILE * out
Definition: movenc.c:55
SwsSwizzleOp::mask
uint32_t mask
Definition: ops.h:147
extract_constant_rows
static bool extract_constant_rows(SwsLinearOp *c, const SwsComps *prev, SwsClearOp *out_clear)
Definition: ops_optimizer.c:260
SwsOpList::comps_src
SwsComps comps_src
Source component metadata associated with pixel values from each corresponding component (in plane/me...
Definition: ops.h:284
ff_sws_op_list_input
const SwsOp * ff_sws_op_list_input(const SwsOpList *ops)
Returns the input operation for a given op list, or NULL if there is none (e.g.
Definition: ops.c:662
av_pix_fmt_desc_get
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition: pixdesc.c:3460
SWS_COMP_ZERO
@ SWS_COMP_ZERO
Definition: ops.h:76
SWS_OP_CLEAR
@ SWS_OP_CLEAR
Definition: ops.h:51
SwsOp::swizzle
SwsSwizzleOp swizzle
Definition: ops.h:217
SwsOp::convert
SwsConvertOp convert
Definition: ops.h:220
rational.h
ff_sws_op_list_append
int ff_sws_op_list_append(SwsOpList *ops, SwsOp *op)
These will take over ownership of op and set it to {0}, even on failure.
Definition: ops.c:705
mask
int mask
Definition: mediacodecdec_common.c:154
SwsOp::rw
SwsReadWriteOp rw
Definition: ops.h:215
ops.h
ff_sws_pixel_type_is_int
static av_const bool ff_sws_pixel_type_is_int(SwsPixelType type)
Definition: uops.h:62
SWS_OP_DITHER
@ SWS_OP_DITHER
Definition: ops.h:59
b
#define b
Definition: input.c:43
data
const char data[16]
Definition: mxf.c:149
get_input_size
static void get_input_size(const SwsOpList *ops, SwsFormat *fmt)
Definition: ops_optimizer.c:1088
exact_log2_q64
static int exact_log2_q64(const AVRational64 x)
Definition: ops_optimizer.c:220
ff_sws_op_list_optimize
int ff_sws_op_list_optimize(SwsOpList *ops)
Fuse compatible and eliminate redundant operations, as well as replacing some operations with more ef...
Definition: ops_optimizer.c:340
SWS_OP_TYPE_NB
@ SWS_OP_TYPE_NB
Definition: ops.h:65
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
av_popcount
#define av_popcount
Definition: common.h:154
solve_shuffle
static int solve_shuffle(const SwsUOpList *const uops, SwsUOp *out)
Definition: ops_optimizer.c:894
SwsScaleOp::factor
AVRational64 factor
Definition: ops.h:175
dummy
static int dummy
Definition: ffplay.c:3751
ff_sws_comp_mask_needed
SwsCompMask ff_sws_comp_mask_needed(const SwsOp *op)
Definition: ops.c:122
SwsMoveUOp::num_moves
int num_moves
Definition: uops.h:205
ff_sws_shuffle_mask
int ff_sws_shuffle_mask(const SwsUOp *uop, int8_t shuffle[], int size)
Compute a shuffle mask for pshufb-style ASM functions, by repeating the shuffle pattern for as many g...
Definition: ops_optimizer.c:857
SwsOpList::plane_dst
uint8_t plane_dst[4]
Definition: ops.h:273
SwsClearOp::mask
SwsCompMask mask
Definition: ops.h:161
SWS_COMP_TEST
#define SWS_COMP_TEST(mask, X)
Definition: uops.h:97
SwsOpList::num_ops
int num_ops
Definition: ops.h:267
SwsDitherOp
Definition: ops.h:178
AV_PIX_FMT_YUVA444P16
#define AV_PIX_FMT_YUVA444P16
Definition: pixfmt.h:603
SWS_UOP_PERMUTE
@ SWS_UOP_PERMUTE
Definition: uops.h:150
SwsSwizzleOp
Definition: ops.h:141
val
static double val(void *priv, double ch)
Definition: aeval.c:77
type
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf type
Definition: writing_filters.txt:86
SwsUOpParams
Definition: uops.h:246
AV_PIX_FMT_GRAY16
#define AV_PIX_FMT_GRAY16
Definition: pixfmt.h:528
ff_sws_op_list_split_at
int ff_sws_op_list_split_at(SwsOpList *ops1, SwsOpList **out_ops2, int index)
Split an op list into two at the given index.
Definition: ops_optimizer.c:1101
SWS_COMP_ELEMS
#define SWS_COMP_ELEMS(N)
Definition: uops.h:99
SWS_UOP_COPY
@ SWS_UOP_COPY
Definition: uops.h:151
SwsOp::op
SwsOpType op
Definition: ops.h:211
Q
#define Q(q)
SWS_OP_SCALE
@ SWS_OP_SCALE
Definition: ops.h:55
avassert.h
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
SwsOp::clear
SwsClearOp clear
Definition: ops.h:219
SwsFormat::height
int height
Definition: format.h:78
SWS_OP_NEEDED
#define SWS_OP_NEEDED(op, idx)
Definition: ops.h:237
SwsReadWriteOp::filter
struct SwsReadWriteOp::@581 filter
Filter kernel to apply to each plane while sampling.
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:504
SwsShuffleUOp::write_size
uint8_t write_size
Definition: uops.h:186
planes
static const struct @604 planes[]
SwsUOp::uop
SwsUOpType uop
Definition: uops.h:260
SWS_UOP_FLAG_PSHUFB
@ SWS_UOP_FLAG_PSHUFB
Definition: uops.h:125
AV_PIX_FMT_YUV444P16
#define AV_PIX_FMT_YUV444P16
Definition: pixfmt.h:558
SWS_SWIZZLE
#define SWS_SWIZZLE(X, Y, Z, W)
Definition: ops.h:153
SWS_UOP_WRITE_PLANAR
@ SWS_UOP_WRITE_PLANAR
Definition: uops.h:141
SwsShuffleMask::mask
int8_t mask[16]
Definition: uops.h:190
ff_sws_pixel_expand
static AVRational64 ff_sws_pixel_expand(SwsPixelType from, SwsPixelType to)
Definition: ops_internal.h:31
op
static int op(uint8_t **dst, const uint8_t *dst_end, GetByteContext *gb, int pixel, int count, int *x, int width, int linesize)
Perform decode operation.
Definition: anm.c:76
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:42
av_cmp_q64
int av_cmp_q64(AVRational64 a, AVRational64 b)
Compare two 64-bit rationals.
Definition: rational64.c:108
SWS_OP_MIN
@ SWS_OP_MIN
Definition: ops.h:53
extract_swizzle
static bool extract_swizzle(SwsLinearOp *op, const SwsComps *prev, SwsSwizzleOp *out_swiz)
Definition: ops_optimizer.c:289
ctx
static AVFormatContext * ctx
Definition: movenc.c:49
SwsCompMask
uint8_t SwsCompMask
Bit-mask of components.
Definition: uops.h:87
SWS_UOP_READ_PACKED
@ SWS_UOP_READ_PACKED
Definition: uops.h:136
SWS_OP_LINEAR
@ SWS_OP_LINEAR
Definition: ops.h:58
op_commute_filter
static bool op_commute_filter(SwsOp *op, SwsOp *prev)
Try to commute a filter op with the previous operation.
Definition: ops_optimizer.c:176
ff_sws_op_list_output
const SwsOp * ff_sws_op_list_output(const SwsOpList *ops)
Returns the output operation for a given op list, or NULL if there is none.
Definition: ops.c:671
SWS_OP_FILTER_H
@ SWS_OP_FILTER_H
Definition: ops.h:62
av_mul_q64
AVRational64 av_mul_q64(AVRational64 b, AVRational64 c)
Multiply two 64-bit rationals.
Definition: rational64.c:124
AV_PIX_FMT_GRAYF32
#define AV_PIX_FMT_GRAYF32
Definition: pixfmt.h:588
tmp
static uint8_t tmp[40]
Definition: aes_ctr.c:52
FFABS
#define FFABS(a)
Absolute value, Note, INT_MIN / INT64_MIN result in undefined behavior as they are not representable ...
Definition: common.h:74
ff_sws_pixel_type_size
static av_const int ff_sws_pixel_type_size(SwsPixelType type)
Definition: uops.h:49
SWS_OP_PACK
@ SWS_OP_PACK
Definition: ops.h:46
SwsOp::dither
SwsDitherOp dither
Definition: ops.h:223
SwsReadWriteOp::kernel
SwsFilterWeights * kernel
Definition: ops.h:128
fail
#define fail
Definition: test.h:478
NULL
#define NULL
Definition: coverity.c:32
av_unreachable
#define av_unreachable(msg)
Asserts that are used as compiler optimization hints depending upon ASSERT_LEVEL and NBDEBUG.
Definition: avassert.h:116
SwsShuffleUOp::read_size
uint8_t read_size
Definition: uops.h:185
SwsReadWriteOp::frac
uint8_t frac
Definition: ops.h:117
SwsMoveUOp::dst
int8_t dst[SWS_UOP_MOVE_MAX]
Definition: uops.h:208
av_fallthrough
#define av_fallthrough
Definition: attributes.h:67
SWS_COMP_GARBAGE
@ SWS_COMP_GARBAGE
Definition: ops.h:74
SwsConvertOp::to
SwsPixelType to
Definition: ops.h:166
SWS_OP_FILTER_V
@ SWS_OP_FILTER_V
Definition: ops.h:63
SwsOp::clamp
SwsClampOp clamp
Definition: ops.h:221
ff_sws_op_list_remove_at
void ff_sws_op_list_remove_at(SwsOpList *ops, int index, int count)
Definition: ops.c:680
attributes.h
RET
#define RET(x)
Copyright (C) 2025 Niklas Haas.
Definition: ops_optimizer.c:29
ff_sws_apply_op_q
void ff_sws_apply_op_q(const SwsOp *op, AVRational64 x[4])
Apply an operation to an AVRational64.
Definition: ops.c:156
AV_PIX_FMT_GRAY8
@ AV_PIX_FMT_GRAY8
Y , 8bpp.
Definition: pixfmt.h:81
SwsPixelType
SwsPixelType
Definition: uops.h:38
index
int index
Definition: gxfenc.c:90
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
SwsUOp::par
SwsUOpParams par
Definition: uops.h:262
SwsConvertOp::expand
bool expand
Definition: ops.h:167
ff_sws_uop_list_remove_at
void ff_sws_uop_list_remove_at(SwsUOpList *uops, int index, int count)
Definition: uops.c:215
SwsUOp
Definition: uops.h:257
SwsPackOp::pattern
uint8_t pattern[4]
Packed bits are assumed to be LSB-aligned within the underlying integer type; i.e.
Definition: ops.h:138
ff_sws_comp_mask_swizzle
void ff_sws_comp_mask_swizzle(SwsCompMask *mask, const SwsSwizzleOp *swiz)
Definition: ops.c:109
SwsUOp::shuffle
SwsShuffleMask shuffle
Definition: uops.h:271
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:87
SwsClampOp
Definition: ops.h:170
av_bswap32
#define av_bswap32
Definition: bswap.h:47
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
SwsOp::type
SwsPixelType type
Definition: ops.h:212
AV_PIX_FMT_GBRPF32
#define AV_PIX_FMT_GBRPF32
Definition: pixfmt.h:584
SwsUOp::mask
SwsCompMask mask
Definition: uops.h:261
ff_sws_op_list_insert_at
int ff_sws_op_list_insert_at(SwsOpList *ops, int index, SwsOp *op)
Definition: ops.c:691
ff_sws_linear_mask
uint32_t ff_sws_linear_mask(const SwsLinearOp *c)
Definition: ops.c:751
size
int size
Definition: twinvq_data.h:10344
SWS_OP_RSHIFT
@ SWS_OP_RSHIFT
Definition: ops.h:48
SwsOp::lin
SwsLinearOp lin
Definition: ops.h:214
SwsOpList::src
SwsFormat src
Definition: ops.h:270
SWS_OP_INVALID
@ SWS_OP_INVALID
Definition: ops.h:36
ff_sws_op_list_update_comps
void ff_sws_op_list_update_comps(SwsOpList *ops)
Infer + propagate known information about components.
Definition: ops.c:308
SwsFormat
Definition: format.h:77
SwsShiftOp::amount
uint8_t amount
Definition: ops.h:157
AVRational64
64-bit Rational number (pair of numerator and denominator).
Definition: rational64.h:52
SWS_OP_WRITE
@ SWS_OP_WRITE
Definition: ops.h:40
SWS_COMP
#define SWS_COMP(X)
Definition: uops.h:96
SWS_PIXEL_U32
@ SWS_PIXEL_U32
Definition: uops.h:42
SwsClampOp::limit
AVRational64 limit[4]
Definition: ops.h:171
av_refstruct_ref
void * av_refstruct_ref(void *obj)
Create a new reference to an object managed via this API, i.e.
Definition: refstruct.c:140
AV_PIX_FMT_YUVA444P
@ AV_PIX_FMT_YUVA444P
planar YUV 4:4:4 32bpp, (1 Cr & Cb sample per 1x1 Y & A samples)
Definition: pixfmt.h:174
SWS_MASK_ROW
#define SWS_MASK_ROW(I)
Definition: uops.h:231
SwsPixel
Definition: uops.h:77
offset
it s the only field you need to keep assuming you have a context There is some magic you don t need to care about around this just let it vf offset
Definition: writing_filters.txt:86
SwsOp::comps
SwsComps comps
Metadata about the operation's input/output components.
Definition: ops.h:234
SwsScaleOp
Definition: ops.h:174
SwsLinearOp
Definition: ops.h:185
SWS_MASK_DIAG4
#define SWS_MASK_DIAG4
Definition: uops.h:233
get_planar_fmt
static enum AVPixelFormat get_planar_fmt(SwsPixelType type, int nb_planes)
Determine a suitable intermediate buffer format for a given combination of pixel types and number of ...
Definition: ops_optimizer.c:1055
SwsShuffleUOp
Definition: uops.h:183
noop
#define noop(a)
Definition: h264chroma_template.c:71
SwsReadWriteOp::op
SwsOpType op
Definition: ops.h:127
ff_sws_uop_list_optimize
int ff_sws_uop_list_optimize(SwsContext *ctx, SwsUOpFlags flags, SwsUOpList *uops)
Called internally by ff_sws_ops_translate().
Definition: ops_optimizer.c:1016
av_assert2
#define av_assert2(cond)
assert() equivalent, that does lie in speed critical code.
Definition: avassert.h:68
SwsFormat::format
enum AVPixelFormat format
Definition: format.h:81
SwsOp::filter
SwsFilterOp filter
Definition: ops.h:224
SWS_UOP_READ_PLANAR
@ SWS_UOP_READ_PLANAR
Definition: uops.h:132
SwsOpList::ops
SwsOp * ops
Definition: ops.h:266
SwsFilterOp::type
SwsPixelType type
Definition: ops.h:207
SwsReadWriteOp::type
SwsPixelType type
Definition: ops.h:129
SwsFormat::desc
const AVPixFmtDescriptor * desc
Definition: format.h:86
SWS_PIXEL_U8
@ SWS_PIXEL_U8
Definition: uops.h:40
av_assert1
#define av_assert1(cond)
assert() equivalent, that does not lie in speed critical code.
Definition: avassert.h:58
needed
The exact code depends on how similar the blocks are and how related they are to the and needs to apply these operations to the correct inlink or outlink if there are several Macros are available to factor that when no extra processing is needed
Definition: filter_design.txt:212
s
uint8_t s
Definition: llvidencdsp.c:39
SWS_UOP_SWAP_BYTES
@ SWS_UOP_SWAP_BYTES
Definition: uops.h:154
ops_internal.h
SwsShuffleMask
Definition: uops.h:189
SwsFormat::width
int width
Definition: format.h:78
SwsOp
Definition: ops.h:210
SwsUOp::data
union SwsUOp::@595 data
SwsUOp::type
SwsPixelType type
Definition: uops.h:259
AVRational64::den
int64_t den
Denominator.
Definition: rational64.h:54
SwsComps::flags
SwsCompFlags flags[4]
Definition: ops.h:83
ret
ret
Definition: filter_design.txt:187
bswap.h
SwsComps::min
AVRational64 min[4]
Definition: ops.h:87
FFSWAP
#define FFSWAP(type, a, b)
Definition: macros.h:52
SwsUOpList::num_ops
int num_ops
Definition: uops.h:294
SwsOpList::dst
SwsFormat dst
Definition: ops.h:270
SWS_OP_MAX
@ SWS_OP_MAX
Definition: ops.h:54
op_commute_swizzle
static bool op_commute_swizzle(SwsOp *op, SwsOp *next)
Try to commute a swizzle op with the next operation.
Definition: ops_optimizer.c:95
full
char full[32]
Definition: uops_macros_gen.c:40
SwsComps
Definition: ops.h:82
AV_PIX_FMT_NONE
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:72
AV_PIX_FMT_GBRAPF32
#define AV_PIX_FMT_GBRAPF32
Definition: pixfmt.h:585
SWS_OP_SWAP_BYTES
@ SWS_OP_SWAP_BYTES
Definition: ops.h:41
pixel_is_repeating
static bool pixel_is_repeating(SwsPixelType type, SwsPixel val)
Definition: ops_optimizer.c:881
SwsUOpList
Definition: uops.h:292
op_result_is_exact
static int op_result_is_exact(const SwsOp *op)
Definition: ops_optimizer.c:330
SwsUOp::vec4
SwsPixel vec4[4]
Definition: uops.h:269
SwsOp::shift
SwsShiftOp shift
Definition: ops.h:218
Windows::Graphics::DirectX::Direct3D11::p
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
Definition: vsrc_gfxcapture_winrt.hpp:53
ff_sws_uop_list_append
int ff_sws_uop_list_append(SwsUOpList *uops, SwsUOp *uop)
Definition: uops.c:202
AV_PIX_FMT_YUV444P
@ AV_PIX_FMT_YUV444P
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition: pixfmt.h:78
SWS_COMP_EXACT
@ SWS_COMP_EXACT
Definition: ops.h:75
SWS_UOP_WRITE_PACKED
@ SWS_UOP_WRITE_PACKED
Definition: uops.h:142
SwsReadWriteOp::elems
uint8_t elems
Definition: ops.h:116
SwsDitherOp::y_offset
int8_t y_offset[4]
Definition: ops.h:182
scale
static void scale(int *out, const int *in, const int w, const int h, const int shift)
Definition: intra.c:278
SWS_UOP_EXPAND_QUAD
@ SWS_UOP_EXPAND_QUAD
Definition: uops.h:157
SwsUOpFlags
uint32_t SwsUOpFlags
Definition: uops.h:121
AVRational64::num
int64_t num
Numerator.
Definition: rational64.h:53
SwsComps::max
AVRational64 max[4]
Definition: ops.h:87
SwsSwizzleOp::in
uint8_t in[4]
Definition: ops.h:148
SWS_OP_CONVERT
@ SWS_OP_CONVERT
Definition: ops.h:52
SwsMoveUOp::src
int8_t src[SWS_UOP_MOVE_MAX]
Definition: uops.h:209
SwsOp::scale
SwsScaleOp scale
Definition: ops.h:222
op_commute_clear
static bool op_commute_clear(SwsOp *op, SwsOp *next)
Try to commute a clear op with the next operation.
Definition: ops_optimizer.c:41
SwsReadWriteOp::mode
SwsReadWriteMode mode
Examples: rgba = 4x u8 packed yuv444p = 3x u8 rgb565 = 1x u16 <- use SWS_OP_UNPACK to unpack monow = ...
Definition: ops.h:115
SwsOpList::plane_src
uint8_t plane_src[4]
Definition: ops.h:273
SWS_UOP_CLEAR
@ SWS_UOP_CLEAR
Definition: uops.h:174
SwsOpList
Helper struct for representing a list of operations.
Definition: ops.h:265
av_bswap16
#define av_bswap16
Definition: bswap.h:28
SwsOp::pack
SwsPackOp pack
Definition: ops.h:216
SwsContext
Main external API structure.
Definition: swscale.h:227
SWS_PIXEL_U16
@ SWS_PIXEL_U16
Definition: uops.h:41
SWS_MASK
#define SWS_MASK(I, J)
Definition: uops.h:229
shuffle
static uint64_t shuffle(uint64_t in, const uint8_t *shuffle, int shuffle_len)
Definition: des.c:179
av_log2
int av_log2(unsigned v)
Definition: intmath.c:26
SwsUOpList::ops
SwsUOp * ops
Definition: uops.h:293
ff_sws_op_list_split_planes
int ff_sws_op_list_split_planes(SwsOpList *ops1, SwsOpList **out_ops2, SwsCompMask planes)
Reduce an op list into a reduced subset that operates only on a given subset of planes.
Definition: ops_optimizer.c:824
src
#define src
Definition: vp8dsp.c:248
SWS_UOP_EXPAND_PAIR
@ SWS_UOP_EXPAND_PAIR
Definition: uops.h:156
read
static uint32_t BS_FUNC() read(BSCTX *bc, unsigned int n)
Return n bits from the buffer, n has to be in the 0-32 range.
Definition: bitstream_template.h:239
exact_log2
static int exact_log2(const int x)
Definition: ops_optimizer.c:211
SwsLinearOp::m
AVRational64 m[4][5]
Generalized 5x5 affine transformation: [ Out.x ] = [ A B C D E ] [ Out.y ] = [ F G H I J ] * [ x y z ...
Definition: ops.h:198
SwsUOpParams::shuffle
SwsShuffleUOp shuffle
Definition: uops.h:247
av_add_q64
AVRational64 av_add_q64(AVRational64 b, AVRational64 c)
Add two 64-bit rationals.
Definition: rational64.c:135