FFmpeg
sw_ops.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 modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #include <string.h>
22 
23 #include "libavutil/avassert.h"
24 #include "libavutil/mem_internal.h"
25 #include "libavutil/refstruct.h"
26 
27 #include "libswscale/filters.h"
29 #include "libswscale/uops.h"
30 #include "libswscale/uops_macros.h"
31 
32 #include "checkasm.h"
33 
34 enum {
35  MAX_UOPS = 3, /* read, op, write */
36  NB_PLANES = 4,
37  PIXELS = 64,
38  LINES = 16,
39 };
40 
41 typedef struct Test {
42  const char *name;
44  int num_uops;
45 
46  /* Extra metadata for the test harness */
47  SwsPixelType type_in; /* data format to fill */
48  SwsPixelType type_out; /* data format to compare */
49  SwsCompMask planes_in; /* planes to fill */
50  SwsCompMask planes_out; /* planes to compare */
51  int pixel_bits_in; /* read pixel stride */
52  int pixel_bits_out; /* write pixel stride */
53  unsigned ranges[NB_PLANES]; /* pixel range to fill */
54 } Test;
55 
56 #define FMT(fmt, ...) tprintf((char[256]) {0}, 256, fmt, __VA_ARGS__)
57 static const char *tprintf(char buf[], size_t size, const char *fmt, ...)
58 {
59  va_list ap;
60  va_start(ap, fmt);
61  vsnprintf(buf, size, fmt, ap);
62  va_end(ap);
63  return buf;
64 }
65 
66 static float rndf(void)
67 {
68  union { uint32_t u; float f; } x;
69  do {
70  x.u = rnd();
71  } while (!isnormal(x.f));
72  return x.f;
73 }
74 
76 {
77  switch (type) {
78  case SWS_PIXEL_U8: return (SwsPixel) { .u8 = val };
79  case SWS_PIXEL_U16: return (SwsPixel) { .u16 = val };
80  case SWS_PIXEL_U32: return (SwsPixel) { .u32 = val };
81  case SWS_PIXEL_F32: return (SwsPixel) { .f32 = val };
82  default: return (SwsPixel) {0};
83  }
84 }
85 
87 {
88  switch (type) {
89  case SWS_PIXEL_U8: return (SwsPixel) { .u8 = rnd() & 0xFF };
90  case SWS_PIXEL_U16: return (SwsPixel) { .u16 = rnd() & 0xFFFF };
91  case SWS_PIXEL_U32: return (SwsPixel) { .u32 = rnd() };
92  case SWS_PIXEL_F32: return (SwsPixel) { .f32 = rndf() };
93  default: return (SwsPixel) {0};
94  }
95 }
96 
97 static void fill32f(float *line, int num, unsigned range)
98 {
99  const float scale = (float) range / UINT32_MAX;
100  for (int i = 0; i < num; i++)
101  line[i] = range ? scale * rnd() : rndf();
102 }
103 
104 static void fill32(uint32_t *line, int num, unsigned range)
105 {
106  for (int i = 0; i < num; i++)
107  line[i] = (range && range < UINT_MAX) ? rnd() % (range + 1) : rnd();
108 }
109 
110 static void fill16(uint16_t *line, int num, unsigned range)
111 {
112  if (!range) {
113  fill32((uint32_t *) line, AV_CEIL_RSHIFT(num, 1), 0);
114  } else {
115  for (int i = 0; i < num; i++)
116  line[i] = rnd() % (range + 1);
117  }
118 }
119 
120 static void fill8(uint8_t *line, int num, unsigned range)
121 {
122  if (!range) {
123  fill32((uint32_t *) line, AV_CEIL_RSHIFT(num, 2), 0);
124  } else {
125  for (int i = 0; i < num; i++)
126  line[i] = rnd() % (range + 1);
127  }
128 }
129 
131 {
132  switch (ff_sws_pixel_type_size(type)) {
133  case 1: return SWS_PIXEL_U8;
134  case 2: return SWS_PIXEL_U16;
135  case 4: return SWS_PIXEL_U32;
136  default: break;
137  }
138 
139  av_unreachable("Invalid pixel type!");
140  return SWS_PIXEL_NONE;
141 }
142 
143 static void check_compiled(const Test *test,
144  const SwsCompiledOp *comp_ref,
145  const SwsCompiledOp *comp_new)
146 {
147  /**
148  * We can't use `check_func()` alone because the actual function pointer
149  * may be a wrapper or entry point shared by multiple implementations.
150  * Solve it by hashing in the active CPU flags as well.
151  */
152  uintptr_t id = (uintptr_t) comp_new->func;
153  id ^= (id << 6) + (id >> 2) + 0x9e3779b97f4a7c15 + comp_new->cpu_flags;
154  if (!check_key(id, "%s", test->name))
155  return;
156 
157  declare_func(void, const SwsOpExec *, const void *, int bx, int y, int bx_end, int y_end);
158 
159  static DECLARE_ALIGNED_64(char, src0)[NB_PLANES][LINES][PIXELS * sizeof(uint32_t[4])];
160  static DECLARE_ALIGNED_64(char, src1)[NB_PLANES][LINES][PIXELS * sizeof(uint32_t[4])];
161  static DECLARE_ALIGNED_64(char, dst0)[NB_PLANES][LINES][PIXELS * sizeof(uint32_t[4])];
162  static DECLARE_ALIGNED_64(char, dst1)[NB_PLANES][LINES][PIXELS * sizeof(uint32_t[4])];
163 
164  av_assert0(PIXELS % comp_new->block_size == 0);
165  for (int p = 0; p < NB_PLANES; p++) {
166  void *plane = src0[p];
167  if (!SWS_COMP_TEST(test->planes_in, p)) {
168  memset(plane, 0, sizeof(src0[p]));
169  continue;
170  }
171 
172  switch (test->type_in) {
173  case SWS_PIXEL_U8:
174  fill8(plane, sizeof(src0[p]) / sizeof(uint8_t), test->ranges[p]);
175  break;
176  case SWS_PIXEL_U16:
177  fill16(plane, sizeof(src0[p]) / sizeof(uint16_t), test->ranges[p]);
178  break;
179  case SWS_PIXEL_U32:
180  fill32(plane, sizeof(src0[p]) / sizeof(uint32_t), test->ranges[p]);
181  break;
182  case SWS_PIXEL_F32:
183  fill32f(plane, sizeof(src0[p]) / sizeof(uint32_t), test->ranges[p]);
184  break;
185  }
186  }
187 
188  memcpy(src1, src0, sizeof(src0));
189  memset(dst0, 0, sizeof(dst0));
190  memset(dst1, 0, sizeof(dst1));
191 
192  const int read_size = PIXELS * test->pixel_bits_in >> 3;
193  const int write_size = PIXELS * test->pixel_bits_out >> 3;
194 
195  SwsOpExec exec = {0};
196  exec.width = PIXELS;
197  exec.height = exec.slice_h = LINES;
198  for (int i = 0; i < NB_PLANES; i++) {
199  exec.in_stride[i] = sizeof(src0[i][0]);
200  exec.out_stride[i] = sizeof(dst0[i][0]);
201  exec.in_bump[i] = exec.in_stride[i] - read_size;
202  exec.out_bump[i] = exec.out_stride[i] - write_size;
203  }
204 
205  int32_t in_bump_y[LINES];
206  int32_t in_offset_x[PIXELS];
207  const SwsUOp *read_op = &test->uops[0];
208  switch (read_op->uop) {
210  exec.in_bump[1] = exec.in_stride[1] = 0;
211  static_assert(sizeof(src0[1]) >= sizeof(uint32_t[256]), "palette plane too small");
212  break;
213  case SWS_UOP_READ_PLANAR_FV: {
214  const int *offsets = read_op->data.kernel->offsets;
215  for (int y = 0; y < LINES - 1; y++)
216  in_bump_y[y] = offsets[y + 1] - offsets[y] - 1;
217  in_bump_y[LINES - 1] = 0;
218  exec.in_bump_y = in_bump_y;
219  break;
220  }
221  case SWS_UOP_READ_PLANAR_FH: {
222  const int *offsets = read_op->data.kernel->offsets;
223  for (int x = 0; x < PIXELS; x++)
224  in_offset_x[x] = offsets[x] * test->pixel_bits_in >> 3;
225  exec.in_offset_x = in_offset_x;
226  }
227  }
228 
229  for (int i = 0; i < NB_PLANES; i++) {
230  exec.in[i] = (void *) src0[i];
231  exec.out[i] = (void *) dst0[i];
232  exec.block_size_in[i] = comp_ref->block_size * test->pixel_bits_in >> 3;
233  exec.block_size_out[i] = comp_ref->block_size * test->pixel_bits_out >> 3;
234  }
235  checkasm_call(comp_ref->func, &exec, comp_ref->priv, 0, 0, PIXELS / comp_ref->block_size, LINES);
236 
237  for (int i = 0; i < NB_PLANES; i++) {
238  exec.in[i] = (void *) src1[i];
239  exec.out[i] = (void *) dst1[i];
240  exec.block_size_in[i] = comp_new->block_size * test->pixel_bits_in >> 3;
241  exec.block_size_out[i] = comp_new->block_size * test->pixel_bits_out >> 3;
242  }
243  checkasm_call_checked(comp_new->func, &exec, comp_new->priv, 0, 0, PIXELS / comp_new->block_size, LINES);
244 
245  for (int i = 0; i < NB_PLANES; i++) {
246  if (!SWS_COMP_TEST(test->planes_out, i))
247  continue;
248 
249  const char *desc = FMT("%s[%d]", test->name, i);
250  const int stride = sizeof(dst0[i][0]);
251  switch (test->type_out) {
252  case SWS_PIXEL_U8:
253  checkasm_check(uint8_t, (void *) dst0[i], stride,
254  (void *) dst1[i], stride,
255  write_size, LINES, desc);
256  break;
257  case SWS_PIXEL_U16:
258  checkasm_check(uint16_t, (void *) dst0[i], stride,
259  (void *) dst1[i], stride,
260  write_size >> 1, LINES, desc);
261  break;
262  case SWS_PIXEL_U32:
263  checkasm_check(uint32_t, (void *) dst0[i], stride,
264  (void *) dst1[i], stride,
265  write_size >> 2, LINES, desc);
266  break;
267  case SWS_PIXEL_F32:
268  checkasm_check(float_ulp, (void *) dst0[i], stride,
269  (void *) dst1[i], stride,
270  write_size >> 2, LINES, desc, 0);
271  break;
272  }
273  }
274 
275  bench(comp_new->func, &exec, comp_new->priv, 0, 0, PIXELS / comp_new->block_size, LINES);
276 }
277 
278 static void run_test(const Test *test)
279 {
281  if (!ctx)
282  return;
284 
285  /* Generate dummy uop list on-stack */
286  SwsUOpList oplist = {
287  .ops = (SwsUOp *) test->uops,
288  .num_ops = test->num_uops,
289 
290  /* Less efficient, but works universally */
291  .planes_in = SWS_COMP_ALL,
292  .planes_out = SWS_COMP_ALL,
293  };
294 
295  for (int i = 0; i < test->num_uops; i++) {
296  const int pixel_size = ff_sws_pixel_type_size(test->uops[i].type);
297  if (pixel_size > oplist.pixel_size_max)
298  oplist.pixel_size_max = pixel_size;
299  }
300 
301  static const SwsOpBackend *backend_ref;
302  if (!backend_ref) {
303  for (int n = 0; ff_sws_op_backends[n]; n++) {
304  if (!strcmp(ff_sws_op_backends[n]->name, "c")) {
305  backend_ref = ff_sws_op_backends[n];
306  break;
307  }
308  }
309  av_assert0(backend_ref);
310  }
311 
312  /* Always compile `ops` using the C backend as a reference */
313  SwsCompiledOp comp_ref = {0};
314  int ret = backend_ref->compile_uops(ctx, &oplist, &comp_ref);
315  if (ret < 0) {
316  av_assert0(ret != AVERROR(ENOTSUP));
317  fail();
318  goto done;
319  }
320 
321  /* Check with the C backend to establish a reference */
322  check_compiled(test, &comp_ref, &comp_ref);
323 
324  /* Iterate over every other backend, and test it against the C reference */
325  for (int n = 0; ff_sws_op_backends[n]; n++) {
326  const SwsOpBackend *backend = ff_sws_op_backends[n];
327  if (backend->hw_format != AV_PIX_FMT_NONE || backend == backend_ref)
328  continue;
329  if (!backend->compile_uops)
330  continue;
331 
332  SwsCompiledOp comp_new = {0};
333  int ret = backend->compile_uops(ctx, &oplist, &comp_new);
334  if (ret == AVERROR(ENOTSUP)) {
335  continue;
336  } else if (ret < 0) {
337  fail();
338  goto done;
339  }
340 
341  /* Distinguish backends from each other even with same CPU flags */
343  check_compiled(test, &comp_ref, &comp_new);
344  ff_sws_compiled_op_unref(&comp_new);
345  }
346 
347 done:
348  ff_sws_compiled_op_unref(&comp_ref);
350 }
351 
352 static void check_uop(const char *name, const SwsUOp *uop,
353  SwsCompMask mask_in, SwsCompMask mask_out,
354  const unsigned ranges[NB_PLANES])
355 {
356  Test t = {
357  .name = name,
358  .type_in = uop->type,
359  .type_out = uop->type,
360  };
361 
362  for (int i = 0; ranges && i < NB_PLANES; i++)
363  t.ranges[i] = ranges[i];
364 
365  /* uops with non-standard output type conversions */
366  switch (uop->uop) {
367  case SWS_UOP_TO_U8: t.type_out = SWS_PIXEL_U8; break;
368  case SWS_UOP_TO_U16: t.type_out = SWS_PIXEL_U16; break;
369  case SWS_UOP_TO_U32: t.type_out = SWS_PIXEL_U32; break;
370  case SWS_UOP_TO_F32: t.type_out = SWS_PIXEL_F32; break;
371  case SWS_UOP_EXPAND_PAIR: t.type_out = SWS_PIXEL_U16; break;
372  case SWS_UOP_EXPAND_QUAD: t.type_out = SWS_PIXEL_U32; break;
375  t.type_out = uop->par.filter.type;
376  break;
377  }
378 
379  /* Set up read/write metadata for rw uops */
380  const int bits_in = ff_sws_pixel_type_size(t.type_in) * 8;
381  const int bits_out = ff_sws_pixel_type_size(t.type_out) * 8;
382  switch (uop->uop) {
383  case SWS_UOP_READ_PLANAR:
386  t.planes_in = uop->mask;
387  t.pixel_bits_in = bits_in;
388  break;
390  t.planes_out = uop->mask;
391  t.pixel_bits_out = bits_out;
392  break;
393  case SWS_UOP_READ_PACKED:
394  t.planes_in = SWS_COMP_ELEMS(1);
395  t.pixel_bits_in = bits_in * SWS_COMP_COUNT(uop->mask);
396  break;
398  t.planes_out = SWS_COMP_ELEMS(1);
399  t.pixel_bits_out = bits_out * SWS_COMP_COUNT(uop->mask);
400  break;
401  case SWS_UOP_READ_NIBBLE:
402  t.planes_in = SWS_COMP_ELEMS(1);
403  t.pixel_bits_in = 4;
404  break;
406  t.planes_out = SWS_COMP_ELEMS(1);
407  t.pixel_bits_out = 4;
408  break;
409  case SWS_UOP_READ_BIT:
410  t.planes_in = SWS_COMP_ELEMS(1);
411  t.pixel_bits_in = 1;
412  break;
413  case SWS_UOP_WRITE_BIT:
414  t.planes_out = SWS_COMP_ELEMS(1);
415  t.pixel_bits_out = 1;
416  break;
418  t.planes_in = SWS_COMP_ELEMS(2);
419  t.pixel_bits_in = 8; /* size of index */
420  break;
421  }
422 
423  /* Add read uop if needed */
424  if (!t.planes_in) {
425  t.planes_in = mask_in;
426  t.pixel_bits_in = bits_in;
427  t.uops[t.num_uops++] = (SwsUOp) {
429  .uop = SWS_UOP_READ_PLANAR,
430  .mask = SWS_COMP_ALL, /* extra planes are zero'd */
431  };
432  }
433 
434  /* Add the UOp itself */
435  t.uops[t.num_uops++] = *uop;
436 
437  /* Add write uop if needed */
438  if (!t.planes_out) {
439  t.planes_out = mask_out;
440  t.pixel_bits_out = bits_out;
441  t.uops[t.num_uops++] = (SwsUOp) {
443  .uop = SWS_UOP_WRITE_PLANAR,
444  .mask = SWS_COMP_ALL, /* extra planes are ignored */
445  };
446  }
447 
448  run_test(&t);
449 }
450 
451 static void check_range(const char *name, const SwsUOp *uop,
452  const unsigned ranges[NB_PLANES])
453 {
454  /* Test all planes to ensure data remains untouched */
455  return check_uop(name, uop, SWS_COMP_ALL, SWS_COMP_ALL, ranges);
456 }
457 
458 static void check_simple(const char *name, const SwsUOp *uop)
459 {
460  return check_range(name, uop, NULL);
461 }
462 
463 static void check_scalar(const char *name, SwsUOp *uop)
464 {
465  uop->data.scalar = rndpx(uop->type);
466  check_simple(name, uop);
467 }
468 
469 static void check_vec4(const char *name, SwsUOp *uop)
470 {
471  for (int i = 0; i < 4; i++)
472  uop->data.vec4[i] = rndpx(uop->type);
473 
474  return check_simple(name, uop);
475 }
476 
477 #define MK_RANGES(R) ((const unsigned[]) { R, R, R, R })
478 
479 static void check_read(const char *name, SwsUOp *uop)
480 {
481  check_uop(name, uop, uop->mask, uop->mask, NULL);
482 }
483 
484 static void check_write(const char *name, const SwsUOp *uop)
485 {
486  const int frac = uop->uop == SWS_UOP_WRITE_BIT ? 3 :
487  uop->uop == SWS_UOP_WRITE_NIBBLE ? 1 : 0;
488  const int bits = 8 >> frac;
489  const unsigned range = (1 << bits) - 1;
490  check_uop(name, uop, uop->mask, uop->mask, MK_RANGES(range));
491 }
492 
493 static void check_filter(const char *name, SwsUOp *uop)
494 {
495  const bool is_vert = uop->uop == SWS_UOP_READ_PLANAR_FV;
496 
497  SwsFilterParams par = {
499  .dst_size = is_vert ? LINES : PIXELS,
500  };
501 
502  const SwsScaler scalers[] = {
505  };
506 
507  for (int s = 0; s < FF_ARRAY_ELEMS(scalers); s++) {
508  par.scaler = scalers[s];
509 
510  for (par.src_size = 1; par.src_size <= par.dst_size; par.src_size <<= 1) {
511  if (ff_sws_filter_generate(NULL, &par, &uop->data.kernel) < 0) {
512  fail();
513  return;
514  }
515 
516  char desc[256];
517  snprintf(desc, sizeof(desc), "%s_%s_%d", name,
518  uop->data.kernel->name, par.src_size);
519 
520  check_read(desc, uop);
522  }
523  }
524 }
525 
526 static void check_swizzle(const char *name, const SwsUOp *uop)
527 {
528  /* Only check data equality in needed components; since the others
529  * could either remain untouched or contain garbage */
530  check_uop(name, uop, SWS_COMP_ALL, uop->mask, NULL);
531 }
532 
533 static void check_expand_bit(const char *name, const SwsUOp *uop)
534 {
535  check_range(name, uop, MK_RANGES(1));
536 }
537 
538 static void check_cast(const char *name, const SwsUOp *uop)
539 {
541  switch (uop->uop) {
542  case SWS_UOP_TO_U8: dst = SWS_PIXEL_U8; break;
543  case SWS_UOP_TO_U16: dst = SWS_PIXEL_U16; break;
544  case SWS_UOP_TO_U32: dst = SWS_PIXEL_U32; break;
545  case SWS_UOP_TO_F32: dst = SWS_PIXEL_F32; break;
546  case SWS_UOP_EXPAND_PAIR: dst = SWS_PIXEL_U16; break;
547  case SWS_UOP_EXPAND_QUAD: dst = SWS_PIXEL_U32; break;
548  default: return;
549  }
550 
551  const int isize = ff_sws_pixel_type_size(uop->type);
552  const int osize = ff_sws_pixel_type_size(dst);
553  unsigned range = UINT32_MAX >> (32 - osize * 8);
554  if (isize < osize || !ff_sws_pixel_type_is_int(dst))
555  range = 0;
556 
557  check_uop(name, uop, uop->mask, uop->mask, MK_RANGES(range));
558 }
559 
560 static void check_scale(const char *name, SwsUOp *uop)
561 {
562  const int bits = ff_sws_pixel_type_size(uop->type) * 8;
563  const unsigned max = UINT32_MAX >> (32 - bits);
564  SwsPixel scale = uop->data.scalar = rndpx(uop->type);
565  unsigned range = 0;
566 
567  /* Ensure the result won't exceed the value range */
568  switch (uop->type) {
569  case SWS_PIXEL_U8: range = max / (scale.u8 ? scale.u8 : 1); break;
570  case SWS_PIXEL_U16: range = max / (scale.u16 ? scale.u16 : 1); break;
571  case SWS_PIXEL_U32: range = max / (scale.u32 ? scale.u32 : 1); break;
572  }
573 
575 }
576 
577 static void check_unpack(const char *name, const SwsUOp *uop)
578 {
579  const uint8_t *pat = uop->par.pack.pattern;
580  const int total = pat[0] + pat[1] + pat[2] + pat[3];
581  const unsigned range = UINT32_MAX >> (32 - total);
582 
583  check_uop(name, uop, SWS_COMP(0), uop->mask, MK_RANGES(range));
584 }
585 
586 static void check_pack(const char *name, const SwsUOp *uop)
587 {
588  const uint8_t *pat = uop->par.pack.pattern;
589  const unsigned ranges[4] = {
590  (1 << pat[0]) - 1, (1 << pat[1]) - 1,
591  (1 << pat[2]) - 1, (1 << pat[3]) - 1,
592  };
593 
594  check_uop(name, uop, uop->mask, SWS_COMP(0), ranges);
595 }
596 
597 static void check_linear(const char *name, SwsUOp *uop)
598 {
599  const SwsLinearUOp *par = &uop->par.lin;
600  for (int i = 0; i < 4; i++) {
601  for (int j = 0; j < 5; j++) {
602  if (par->zero & SWS_MASK(i, j))
603  uop->data.mat4[i][j] = constpx(uop->type, 0);
604  else if (par->one & SWS_MASK(i, j))
605  uop->data.mat4[i][j] = constpx(uop->type, 1);
606  else
607  uop->data.mat4[i][j] = rndpx(uop->type);
608  }
609  }
610 
611  return check_simple(name, uop);
612 }
613 
614 static void check_dither(const char *name, SwsUOp *uop)
615 {
617 
618  const int size = 1 << uop->par.dither.size_log2;
619  const int height = ff_sws_dither_height(&uop->par.dither);
621  if (!matrix) {
622  fail();
623  return;
624  }
625 
626  for (int i = 0; i < size * size; i++)
627  matrix[i] = rndpx(uop->type);
628  memcpy(matrix + size * size, matrix,
629  size * (height - size) * sizeof(*matrix));
630 
631  uop->data.ptr = matrix;
632  check_simple(name, uop);
633 
635 }
636 
637 #define CHECK_FUNCTION(CHECK, NAME, ...) \
638  CHECK(#NAME, &(SwsUOp) { __VA_ARGS__ });
639 
640 #define CHECK_FOR(UOP, CHECK) \
641  SWS_FOR_STRUCT(U8, UOP, CHECK_FUNCTION, CHECK) \
642  SWS_FOR_STRUCT(U16, UOP, CHECK_FUNCTION, CHECK) \
643  SWS_FOR_STRUCT(U32, UOP, CHECK_FUNCTION, CHECK) \
644  SWS_FOR_STRUCT(F32, UOP, CHECK_FUNCTION, CHECK) \
645  report(#UOP)
646 
648 {
649  CHECK_FOR(READ_PLANAR, check_read);
650  CHECK_FOR(READ_PLANAR_FH, check_filter);
651  CHECK_FOR(READ_PLANAR_FV, check_filter);
652  CHECK_FOR(READ_PACKED, check_read);
653  CHECK_FOR(READ_NIBBLE, check_read);
654  CHECK_FOR(READ_BIT, check_read);
655  CHECK_FOR(READ_PALETTE, check_read);
656  CHECK_FOR(WRITE_PLANAR, check_write);
657  CHECK_FOR(WRITE_PACKED, check_write);
658  CHECK_FOR(WRITE_NIBBLE, check_write);
659  CHECK_FOR(WRITE_BIT, check_write);
660  CHECK_FOR(PERMUTE, check_swizzle);
662  CHECK_FOR(SWAP_BYTES, check_simple);
663  CHECK_FOR(EXPAND_BIT, check_expand_bit);
664  CHECK_FOR(EXPAND_PAIR, check_cast);
665  CHECK_FOR(EXPAND_QUAD, check_cast);
666  CHECK_FOR(TO_U8, check_cast);
667  CHECK_FOR(TO_U16, check_cast);
668  CHECK_FOR(TO_U32, check_cast);
669  CHECK_FOR(TO_F32, check_cast);
674  CHECK_FOR(UNPACK, check_unpack);
675  CHECK_FOR(PACK, check_pack);
676  CHECK_FOR(LSHIFT, check_simple);
680  CHECK_FOR(DITHER, check_dither);
681 }
SwsUOpList::pixel_size_max
int pixel_size_max
Definition: uops.h:299
name
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 default minimum maximum flags name is the option name
Definition: writing_filters.txt:88
check_uop
static void check_uop(const char *name, const SwsUOp *uop, SwsCompMask mask_in, SwsCompMask mask_out, const unsigned ranges[NB_PLANES])
Definition: sw_ops.c:352
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
SwsCompiledOp::func
SwsOpFunc func
Definition: ops_dispatch.h:104
MAX
#define MAX
Definition: blend_modes.c:46
mem_internal.h
SwsFilterParams::src_size
int src_size
The relative sizes of the input and output images.
Definition: filters.h:57
checkasm_get_cpu_suffix
static const char * checkasm_get_cpu_suffix(void)
Get the suffix for the current CPU flag, or "c" if none.
Definition: checkasm.h:326
SWS_PIXEL_NONE
@ SWS_PIXEL_NONE
Definition: uops.h:39
SWS_COMP_ALL
@ SWS_COMP_ALL
Definition: uops.h:95
SwsOpExec::in_bump
ptrdiff_t in_bump[4]
Pointer bump, difference between stride and processed line size.
Definition: ops_dispatch.h:52
NB_PLANES
@ NB_PLANES
Definition: sw_ops.c:36
check_read
static void check_read(const char *name, SwsUOp *uop)
Definition: sw_ops.c:479
SwsOpExec::in
const uint8_t * in[4]
Definition: ops_dispatch.h:38
SwsOpExec::out_stride
ptrdiff_t out_stride[4]
Definition: ops_dispatch.h:43
checkasm_call_checked
#define checkasm_call_checked(func,...)
Definition: aarch64.h:93
matrix
Definition: vc1dsp.c:43
src1
const pixel * src1
Definition: h264pred_template.c:420
check_pack
static void check_pack(const char *name, const SwsUOp *uop)
Definition: sw_ops.c:586
ff_sws_pixel_type_is_int
static av_const bool ff_sws_pixel_type_is_int(SwsPixelType type)
Definition: uops.h:62
u
#define u(width, name, range_min, range_max)
Definition: cbs_apv.c:68
checkasm_check_sw_ops
void checkasm_check_sw_ops(void)
Definition: sw_ops.c:647
SWS_BITEXACT
@ SWS_BITEXACT
Definition: swscale.h:178
SwsOpExec::block_size_out
int32_t block_size_out[4]
Definition: ops_dispatch.h:59
test
Definition: idctdsp.c:35
check_unpack
static void check_unpack(const char *name, const SwsUOp *uop)
Definition: sw_ops.c:577
SwsFilterWeights::offsets
int * offsets
The computed source pixel positions for each row of the filter.
Definition: filters.h:105
SwsCompiledOp::cpu_flags
int cpu_flags
Definition: ops_dispatch.h:120
max
#define max(a, b)
Definition: cuda_runtime.h:33
SwsFilterParams
Definition: filters.h:45
ops_dispatch.h
SwsOpExec::in_stride
ptrdiff_t in_stride[4]
Definition: ops_dispatch.h:42
Test::name
const char * name
Definition: sw_ops.c:42
SwsLinearUOp::one
uint32_t one
Definition: uops.h:222
Test::planes_out
SwsCompMask planes_out
Definition: sw_ops.c:50
SwsOpBackend::name
const char * name
Definition: ops_dispatch.h:135
check_scale
static void check_scale(const char *name, SwsUOp *uop)
Definition: sw_ops.c:560
rndf
static float rndf(void)
Definition: sw_ops.c:66
ff_sws_filter_generate
int ff_sws_filter_generate(void *log, const SwsFilterParams *params, SwsFilterWeights **out)
Generate a filter kernel for the given parameters.
Definition: filters.c:187
DECLARE_ALIGNED_64
#define DECLARE_ALIGNED_64(t, v)
Definition: mem_internal.h:114
check_key
#define check_key
Definition: test.h:481
SWS_COMP_TEST
#define SWS_COMP_TEST(mask, X)
Definition: uops.h:97
ff_sws_op_backends
const SwsOpBackend *const ff_sws_op_backends[]
Definition: ops.c:42
SWS_UOP_TO_U16
@ SWS_UOP_TO_U16
Definition: uops.h:159
SwsFilterParams::dst_size
int dst_size
Definition: filters.h:58
SwsUOpParams::pack
SwsPackUOp pack
Definition: uops.h:251
checkasm.h
check_dither
static void check_dither(const char *name, SwsUOp *uop)
Definition: sw_ops.c:614
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
SwsOpExec::block_size_in
int32_t block_size_in[4]
Definition: ops_dispatch.h:58
SwsOpBackend::hw_format
enum AVPixelFormat hw_format
If NONE, backend only supports software frames.
Definition: ops_dispatch.h:155
SWS_COMP_ELEMS
#define SWS_COMP_ELEMS(N)
Definition: uops.h:99
SwsFilterUOp::type
SwsPixelType type
Definition: uops.h:195
refstruct.h
checkasm_call
#define checkasm_call(func,...)
Call a function with signal handling.
Definition: test.h:252
av_refstruct_allocz
static void * av_refstruct_allocz(size_t size)
Equivalent to av_refstruct_alloc_ext(size, 0, NULL, NULL)
Definition: refstruct.h:105
CHECK_FOR
#define CHECK_FOR(UOP, CHECK)
Definition: sw_ops.c:640
pixel_type_to_int
static SwsPixelType pixel_type_to_int(const SwsPixelType type)
Definition: sw_ops.c:130
avassert.h
checkasm_set_func_variant
CHECKASM_API CheckasmKey CHECKASM_API void checkasm_set_func_variant(const char *id,...) CHECKASM_PRINTF(1
Set a custom variant identifier for the next checkasm_check_func() call.
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
SWS_UOP_WRITE_NIBBLE
@ SWS_UOP_WRITE_NIBBLE
Definition: uops.h:143
SWS_UOP_READ_PALETTE
@ SWS_UOP_READ_PALETTE
Definition: uops.h:139
uops_macros.h
constpx
static SwsPixel constpx(SwsPixelType type, int val)
Definition: sw_ops.c:75
SwsUOp::kernel
SwsFilterWeights * kernel
Definition: uops.h:266
float
float
Definition: af_crystalizer.c:122
Test::planes_in
SwsCompMask planes_in
Definition: sw_ops.c:49
SwsUOp::uop
SwsUOpType uop
Definition: uops.h:260
offsets
static const int offsets[]
Definition: hevc_pel.c:34
AV_CEIL_RSHIFT
#define AV_CEIL_RSHIFT(a, b)
Definition: common.h:60
AVFormatContext::flags
int flags
Flags modifying the (de)muxer behaviour.
Definition: avformat.h:1484
SwsOpExec::in_bump_y
int32_t * in_bump_y
Line bump; determines how many additional lines to advance (after incrementing normally to the next l...
Definition: ops_dispatch.h:73
SWS_UOP_WRITE_PLANAR
@ SWS_UOP_WRITE_PLANAR
Definition: uops.h:141
SCALE
#define SCALE(c)
Definition: dcadata.c:7338
rndpx
static SwsPixel rndpx(SwsPixelType type)
Definition: sw_ops.c:86
declare_func
#define declare_func
Definition: test.h:488
bits
uint8_t bits
Definition: vp3data.h:128
LINEAR
#define LINEAR
Definition: vf_perspective.c:36
SWS_UOP_TO_F32
@ SWS_UOP_TO_F32
Definition: uops.h:161
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:42
fill32
static void fill32(uint32_t *line, int num, unsigned range)
Definition: sw_ops.c:104
MK_RANGES
#define MK_RANGES(R)
Definition: sw_ops.c:477
ctx
static AVFormatContext * ctx
Definition: movenc.c:49
bench
#define bench(...)
Definition: checkasm.h:138
SwsCompMask
uint8_t SwsCompMask
Bit-mask of components.
Definition: uops.h:87
FMT
#define FMT(fmt,...)
Definition: sw_ops.c:56
SWS_UOP_READ_PACKED
@ SWS_UOP_READ_PACKED
Definition: uops.h:136
check_linear
static void check_linear(const char *name, SwsUOp *uop)
Definition: sw_ops.c:597
COPY
#define COPY(src, name)
RSHIFT
#define RSHIFT(a, b)
Definition: common.h:56
SwsOpBackend
Definition: ops_dispatch.h:134
if
if(ret)
Definition: filter_design.txt:179
ff_sws_pixel_type_size
static av_const int ff_sws_pixel_type_size(SwsPixelType type)
Definition: uops.h:49
SwsOpExec::height
int32_t height
Definition: ops_dispatch.h:56
SwsOpExec
Copyright (C) 2026 Niklas Haas.
Definition: ops_dispatch.h:36
fill16
static void fill16(uint16_t *line, int num, unsigned range)
Definition: sw_ops.c:110
fail
#define fail
Definition: test.h:478
MAX_UOPS
@ MAX_UOPS
Definition: sw_ops.c:35
Test::uops
SwsUOp uops[MAX_UOPS]
Definition: sw_ops.c:43
NULL
#define NULL
Definition: coverity.c:32
SwsUOp::mat4
SwsPixel mat4[4][5]
Definition: uops.h:270
ADD
#define ADD(a, b)
Definition: dct32_template.c:123
filters.h
tprintf
static const char * tprintf(char buf[], size_t size, const char *fmt,...)
Definition: sw_ops.c:57
ff_sws_compiled_op_unref
void ff_sws_compiled_op_unref(SwsCompiledOp *comp)
Definition: ops_dispatch.c:128
Test::num_uops
int num_uops
Definition: sw_ops.c:44
av_unreachable
#define av_unreachable(msg)
Asserts that are used as compiler optimization hints depending upon ASSERT_LEVEL and NBDEBUG.
Definition: avassert.h:116
SwsLinearUOp
Definition: uops.h:221
check_cast
static void check_cast(const char *name, const SwsUOp *uop)
Definition: sw_ops.c:538
check_vec4
static void check_vec4(const char *name, SwsUOp *uop)
Definition: sw_ops.c:469
check_swizzle
static void check_swizzle(const char *name, const SwsUOp *uop)
Definition: sw_ops.c:526
SwsScaler
SwsScaler
Definition: swscale.h:96
Test::type_out
SwsPixelType type_out
Definition: sw_ops.c:48
SWS_UOP_READ_NIBBLE
@ SWS_UOP_READ_NIBBLE
Definition: uops.h:137
SwsOpExec::slice_h
int32_t slice_h
Definition: ops_dispatch.h:57
check_write
static void check_write(const char *name, const SwsUOp *uop)
Definition: sw_ops.c:484
test::name
const char * name
Definition: idctdsp.c:36
SwsPixelType
SwsPixelType
Definition: uops.h:38
SwsUOp::par
SwsUOpParams par
Definition: uops.h:262
SWS_UOP_TO_U32
@ SWS_UOP_TO_U32
Definition: uops.h:160
SWS_SCALE_SINC
@ SWS_SCALE_SINC
unwindowed sinc
Definition: swscale.h:103
SWS_PARAM_DEFAULT
#define SWS_PARAM_DEFAULT
Definition: swscale.h:456
SwsUOp
Definition: uops.h:257
SWS_UOP_WRITE_BIT
@ SWS_UOP_WRITE_BIT
Definition: uops.h:144
f
f
Definition: af_crystalizer.c:122
height
#define height
Definition: dsp.h:89
sws_alloc_context
SwsContext * sws_alloc_context(void)
Allocate an empty SwsContext and set its fields to default values.
Definition: utils.c:1033
dst
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition: dsp.h:87
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
Test::pixel_bits_out
int pixel_bits_out
Definition: sw_ops.c:52
SwsLinearUOp::zero
uint32_t zero
Definition: uops.h:223
SwsUOp::mask
SwsCompMask mask
Definition: uops.h:261
SwsDitherUOp::size_log2
uint8_t size_log2
Definition: uops.h:237
size
int size
Definition: twinvq_data.h:10344
rnd
#define rnd
Definition: checkasm.h:136
LINES
@ LINES
Definition: sw_ops.c:38
range
enum AVColorRange range
Definition: mediacodec_wrapper.c:2594
SwsOpBackend::compile_uops
int(* compile_uops)(SwsContext *ctx, const SwsUOpList *uops, SwsCompiledOp *out)
Alternative to compile that takes a list of micro-ops directly.
Definition: ops_dispatch.h:148
SWS_COMP
#define SWS_COMP(X)
Definition: uops.h:96
SWS_PIXEL_U32
@ SWS_PIXEL_U32
Definition: uops.h:42
SwsPixel
Definition: uops.h:77
line
Definition: graph2dot.c:48
SwsFilterWeights::name
char name[16]
Extra metadata about the filter, used to inform the optimizer / range tracker about the filter's beha...
Definition: filters.h:119
av_refstruct_unref
void av_refstruct_unref(void *objp)
Decrement the reference count of the underlying object and automatically free the object if there are...
Definition: refstruct.c:120
fill32f
static void fill32f(float *line, int num, unsigned range)
Definition: sw_ops.c:97
SwsOpExec::out
uint8_t * out[4]
Definition: ops_dispatch.h:39
SWS_SCALE_POINT
@ SWS_SCALE_POINT
nearest neighbor (point sampling)
Definition: swscale.h:100
check_range
static void check_range(const char *name, const SwsUOp *uop, const unsigned ranges[NB_PLANES])
Definition: sw_ops.c:451
SwsOpExec::in_offset_x
int32_t * in_offset_x
Pixel offset map; for horizontal scaling, in bytes.
Definition: ops_dispatch.h:81
SWS_UOP_TO_U8
@ SWS_UOP_TO_U8
Definition: uops.h:158
Test::type_in
SwsPixelType type_in
Definition: sw_ops.c:47
SWS_UOP_READ_PLANAR
@ SWS_UOP_READ_PLANAR
Definition: uops.h:132
vsnprintf
#define vsnprintf
Definition: snprintf.h:36
SWS_PIXEL_U8
@ SWS_PIXEL_U8
Definition: uops.h:40
s
uint8_t s
Definition: llvidencdsp.c:39
SwsUOp::scalar
SwsPixel scalar
Definition: uops.h:268
MIN
#define MIN(a, b)
Definition: qt-faststart.c:45
check_expand_bit
static void check_expand_bit(const char *name, const SwsUOp *uop)
Definition: sw_ops.c:533
SwsFilterParams::scaler_params
double scaler_params[SWS_NUM_SCALER_PARAMS]
Definition: filters.h:50
SwsOpExec::width
int32_t width
Definition: ops_dispatch.h:56
SwsUOpParams::lin
SwsLinearUOp lin
Definition: uops.h:253
check_simple
static void check_simple(const char *name, const SwsUOp *uop)
Definition: sw_ops.c:458
SwsCompiledOp::priv
void * priv
Definition: ops_dispatch.h:128
SwsPackUOp::pattern
uint8_t pattern[4]
Definition: uops.h:213
SwsUOp::data
union SwsUOp::@595 data
SwsUOp::type
SwsPixelType type
Definition: uops.h:259
SwsCompiledOp::block_size
int block_size
Definition: ops_dispatch.h:123
ret
ret
Definition: filter_design.txt:187
check_filter
static void check_filter(const char *name, SwsUOp *uop)
Definition: sw_ops.c:493
SWS_COMP_COUNT
#define SWS_COMP_COUNT(mask)
Definition: uops.h:100
SwsCompiledOp
Definition: ops_dispatch.h:101
run_test
static void run_test(const Test *test)
Definition: sw_ops.c:278
SwsUOp::ptr
SwsPixel * ptr
Definition: uops.h:267
AV_PIX_FMT_NONE
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:72
CLEAR
#define CLEAR(destin)
Definition: wavpackenc.c:50
SwsUOpList
Definition: uops.h:292
SwsUOp::vec4
SwsPixel vec4[4]
Definition: uops.h:269
Windows::Graphics::DirectX::Direct3D11::p
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
Definition: vsrc_gfxcapture_winrt.hpp:53
PIXELS
@ PIXELS
Definition: sw_ops.c:37
src0
const pixel *const src0
Definition: h264pred_template.c:419
Test::ranges
unsigned ranges[NB_PLANES]
Definition: sw_ops.c:53
SWS_UOP_WRITE_PACKED
@ SWS_UOP_WRITE_PACKED
Definition: uops.h:142
desc
const char * desc
Definition: libsvtav1.c:83
SwsUOpParams::dither
SwsDitherUOp dither
Definition: uops.h:254
Test::pixel_bits_in
int pixel_bits_in
Definition: sw_ops.c:51
SWS_PIXEL_F32
@ SWS_PIXEL_F32
Definition: uops.h:43
SWS_UOP_READ_PLANAR_FV
@ SWS_UOP_READ_PLANAR_FV
Definition: uops.h:134
scale
static void scale(int *out, const int *in, const int w, const int h, const int shift)
Definition: intra.c:278
uops.h
SWS_UOP_EXPAND_QUAD
@ SWS_UOP_EXPAND_QUAD
Definition: uops.h:157
check_compiled
static void check_compiled(const Test *test, const SwsCompiledOp *comp_ref, const SwsCompiledOp *comp_new)
Definition: sw_ops.c:143
SWS_UOP_READ_PLANAR_FH
@ SWS_UOP_READ_PLANAR_FH
Definition: uops.h:133
fill8
static void fill8(uint8_t *line, int num, unsigned range)
Definition: sw_ops.c:120
sws_free_context
void sws_free_context(SwsContext **ctx)
Free the context and everything associated with it, and write NULL to the provided pointer.
Definition: utils.c:2324
int32_t
int32_t
Definition: audioconvert.c:56
SwsUOpParams::filter
SwsFilterUOp filter
Definition: uops.h:248
ff_sws_dither_height
int ff_sws_dither_height(const SwsDitherUOp *dither)
Computes (1 << size_log2) + MAX(y_offset).
Definition: uops.c:226
SWS_UOP_READ_BIT
@ SWS_UOP_READ_BIT
Definition: uops.h:138
stride
#define stride
Definition: h264pred_template.c:536
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
Test
Definition: sw_ops.c:41
SwsFilterParams::scaler
SwsScaler scaler
The filter kernel and parameters to use.
Definition: filters.h:49
snprintf
#define snprintf
Definition: snprintf.h:34
SwsUOpList::ops
SwsUOp * ops
Definition: uops.h:293
SwsOpExec::out_bump
ptrdiff_t out_bump[4]
Definition: ops_dispatch.h:53
SWS_UOP_EXPAND_PAIR
@ SWS_UOP_EXPAND_PAIR
Definition: uops.h:156
check_scalar
static void check_scalar(const char *name, SwsUOp *uop)
Definition: sw_ops.c:463