FFmpeg
ops.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2026 Ramiro Polla
3  *
4  * This file is part of FFmpeg.
5  *
6  * FFmpeg is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * FFmpeg is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with FFmpeg; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 #include "../ops_chain.h"
22 
23 #include "libavutil/avassert.h"
24 #include "libavutil/avstring.h"
25 #include "libavutil/tree.h"
26 
27 #include "ops_impl_conv.c"
28 
29 /**
30  * Check that there is no mismatch for the SwsOpExec/SwsOpImpl offset
31  * values used by ops_static.
32  * NOTE: The check is performed here since this file only ever targets
33  * aarch64, differently from ops_static which may be built on any
34  * host.
35  */
36 static_assert(offsetof_exec_in == offsetof(SwsOpExec, in), "SwsOpExec layout mismatch");
37 static_assert(offsetof_exec_out == offsetof(SwsOpExec, out), "SwsOpExec layout mismatch");
38 static_assert(offsetof_exec_in_bump == offsetof(SwsOpExec, in_bump), "SwsOpExec layout mismatch");
39 static_assert(offsetof_exec_out_bump == offsetof(SwsOpExec, out_bump), "SwsOpExec layout mismatch");
40 static_assert(offsetof_impl_cont == offsetof(SwsOpImpl, cont), "SwsOpImpl layout mismatch");
41 static_assert(offsetof_impl_priv == offsetof(SwsOpImpl, priv), "SwsOpImpl layout mismatch");
42 
43 /*********************************************************************/
44 /* Forward-declare exported functions. */
45 #define ENTRY(fname, ...) extern void fname(void);
46 #include "ops_entries.c"
47 #undef ENTRY
48 
49 static const struct {
50  void (*func)(void);
52 } ops_entries[] = {
53 #define ENTRY(fname, ...) { .func = fname, .params = __VA_ARGS__ },
54 #include "ops_entries.c"
55 #undef ENTRY
56 };
57 
58 /* Look up the exported function pointer for the given parameters. */
60 {
61  for (int i = 0; i < FF_ARRAY_ELEMS(ops_entries); i++)
62  if (!memcmp(p, &ops_entries[i].params, sizeof(SwsAArch64OpImplParams)))
63  return ops_entries[i].func;
64  return NULL;
65 }
66 
67 /*********************************************************************/
69  const SwsOp *op, SwsImplResult *res)
70 {
71  /**
72  * Compute number of full vector registers needed to pack all non-zero
73  * coefficients.
74  */
75  const int num_vregs = linear_num_vregs(p);
76  av_assert0(num_vregs <= 4);
77  float *coeffs = av_malloc(num_vregs * 4 * sizeof(float));
78  if (!coeffs)
79  return AVERROR(ENOMEM);
80 
81  /**
82  * Copy non-zero coefficients, packed in sequential order, offset first.
83  * The same order must be followed in asmgen_op_linear().
84  */
85  int i_coeff = 0;
86  for (int i = 0; i < 4; i++) {
87  for (int j = 0; j < 5; j++) {
88  const int jj = (j == 0) ? 4 : (j - 1);
89  if (!(p->par.lin.zero & SWS_MASK(i, jj)))
90  coeffs[i_coeff++] = (float) op->lin.m[i][jj].num / op->lin.m[i][jj].den;
91  }
92  }
93 
94  res->priv.ptr = coeffs;
95  res->free = ff_op_priv_free;
96 
97  return 0;
98 }
99 
100 /*********************************************************************/
102  const SwsOp *op, SwsImplResult *res)
103 {
104  /**
105  * The input dither matrix is (1 << size_log2)² pixels large. It is
106  * periodic, so the x and y offsets should be masked to fit inside
107  * (1 << size_log2).
108  * The width of the matrix is assumed to be at least 8, which matches
109  * the maximum block_size for aarch64 asmgen when f32 operations
110  * (i.e., dithering) are used. This guarantees that the x offset is
111  * aligned and that reading block_size elements does not extend past
112  * the end of the row. The x offset doesn't change between components,
113  * so it is only required to be masked once.
114  * The y offset, on the other hand, may change per component, and
115  * would therefore need to be masked for every y_offset value. To
116  * simplify the execution, we over-allocate the number of rows of
117  * the output dither matrix by the largest y_offset value. This way,
118  * we only need to mask y offset once, and can safely increment the
119  * dither matrix pointer by fixed offsets for every y_offset change.
120  */
121 
122  /* Find the largest y_offset value. */
123  const int size = 1 << op->dither.size_log2;
124  const int8_t *off = op->dither.y_offset;
125  int max_offset = 0;
126  for (int i = 0; i < 4; i++) {
127  if (off[i] >= 0)
128  max_offset = FFMAX(max_offset, off[i] & (size - 1));
129  }
130 
131  /* Allocate (size + max_offset) rows to allow over-reading the matrix. */
132  const int stride = size * sizeof(float);
133  const int num_rows = size + max_offset;
134  float *matrix = av_malloc(num_rows * stride);
135  if (!matrix)
136  return AVERROR(ENOMEM);
137 
138  for (int i = 0; i < size * size; i++)
139  matrix[i] = (float) op->dither.matrix[i].num / op->dither.matrix[i].den;
140 
141  memcpy(&matrix[size * size], matrix, max_offset * stride);
142 
143  res->priv.ptr = matrix;
144  res->free = ff_op_priv_free;
145 
146  return 0;
147 }
148 
149 /*********************************************************************/
150 static int aarch64_setup(const SwsOpList *ops, int block_size, int n,
152 {
153  const SwsOp *op = &ops->ops[n];
154  switch (op->op) {
155  case SWS_OP_READ:
156  /* Negative shift values to perform right shift using ushl. */
157  if (op->rw.frac == 3) {
158  out->priv = (SwsOpPriv) {
159  .u8 = {
160  -7, -6, -5, -4, -3, -2, -1, 0,
161  -7, -6, -5, -4, -3, -2, -1, 0,
162  }
163  };
164  }
165  break;
166  case SWS_OP_WRITE:
167  /* Shift values for ushl. */
168  if (op->rw.frac == 3) {
169  out->priv = (SwsOpPriv) {
170  .u8 = {
171  7, 6, 5, 4, 3, 2, 1, 0,
172  7, 6, 5, 4, 3, 2, 1, 0,
173  }
174  };
175  }
176  break;
177  case SWS_OP_CLEAR:
178  ff_sws_setup_clear(&(const SwsImplParams) { .op = op }, out);
179  break;
180  case SWS_OP_MIN:
181  case SWS_OP_MAX:
182  ff_sws_setup_clamp(&(const SwsImplParams) { .op = op }, out);
183  break;
184  case SWS_OP_SCALE:
185  ff_sws_setup_scale(&(const SwsImplParams) { .op = op }, out);
186  break;
187  case SWS_OP_LINEAR:
188  return aarch64_setup_linear(p, op, out);
189  case SWS_OP_DITHER:
190  return aarch64_setup_dither(p, op, out);
191  }
192  return 0;
193 }
194 
195 /*********************************************************************/
196 static int aarch64_compile(SwsContext *ctx, const SwsOpList *ops,
198 {
199  int ret;
200 
201  const int cpu_flags = av_get_cpu_flags();
202  if (!(cpu_flags & AV_CPU_FLAG_NEON))
203  return AVERROR(ENOTSUP);
204 
205  /* Use at most two full vregs during the widest precision section */
206  int block_size = (ff_sws_op_list_max_size(ops) == 4) ? 8 : 16;
207 
209  if (!chain)
210  return AVERROR(ENOMEM);
211  chain->cpu_flags = AV_CPU_FLAG_NEON;
212 
213  *out = (SwsCompiledOp) {
214  .priv = chain,
215  .slice_align = 1,
217  .block_size = block_size,
218  };
219 
220  /* Look up kernel functions. */
221  for (int i = 0; i < ops->num_ops; i++) {
223  ret = convert_to_aarch64_impl(ctx, ops, i, block_size, &params);
224  if (ret < 0)
225  goto error;
227  if (!func) {
228  ret = AVERROR(ENOTSUP);
229  goto error;
230  }
231  SwsImplResult res = { 0 };
232  ret = aarch64_setup(ops, block_size, i, &params, &res);
233  if (ret < 0)
234  goto error;
235  ret = ff_sws_op_chain_append(chain, func, res.free, &res.priv);
236  if (ret < 0)
237  goto error;
238  }
239 
240  /* Look up process function. */
241  void ff_sws_process_0001_neon(void);
242  void ff_sws_process_0011_neon(void);
243  void ff_sws_process_0111_neon(void);
244  void ff_sws_process_1111_neon(void);
245 
246  const SwsOp *read = ff_sws_op_list_input(ops);
247  const SwsOp *write = ff_sws_op_list_output(ops);
248  const int read_planes = read ? ff_sws_rw_op_planes(read) : 0;
249  const int write_planes = ff_sws_rw_op_planes(write);
250  SwsOpFunc process_func = NULL;
251  switch (FFMAX(read_planes, write_planes)) {
252  case 1: process_func = (SwsOpFunc) ff_sws_process_0001_neon; break;
253  case 2: process_func = (SwsOpFunc) ff_sws_process_0011_neon; break;
254  case 3: process_func = (SwsOpFunc) ff_sws_process_0111_neon; break;
255  case 4: process_func = (SwsOpFunc) ff_sws_process_1111_neon; break;
256  }
257 
258  out->func = process_func;
259  out->cpu_flags = chain->cpu_flags;
260 
261 error:
262  if (ret < 0)
263  ff_sws_op_chain_free(chain);
264  return ret;
265 }
266 
267 /*********************************************************************/
269  .name = "aarch64",
270  .flags = SWS_BACKEND_AARCH64,
271  .compile = aarch64_compile,
272  .hw_format = AV_PIX_FMT_NONE,
273 };
SWS_OP_READ
@ SWS_OP_READ
Definition: ops.h:39
error
static void error(const char *err)
Definition: target_bsf_fuzzer.c:32
ff_sws_rw_op_planes
int ff_sws_rw_op_planes(const SwsOp *op)
Return the number of planes involved in a read/write operation.
Definition: ops.c:132
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
out
static FILE * out
Definition: movenc.c:55
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
SWS_OP_CLEAR
@ SWS_OP_CLEAR
Definition: ops.h:51
ff_sws_op_list_max_size
int ff_sws_op_list_max_size(const SwsOpList *ops)
Returns the size of the largest pixel type used in ops.
Definition: ops.c:740
matrix
Definition: vc1dsp.c:43
ff_sws_setup_clear
int ff_sws_setup_clear(const SwsImplParams *params, SwsImplResult *out)
aarch64_lookup
static SwsFuncPtr aarch64_lookup(const SwsAArch64OpImplParams *p)
Definition: ops.c:59
SWS_OP_DITHER
@ SWS_OP_DITHER
Definition: ops.h:59
AV_CPU_FLAG_NEON
#define AV_CPU_FLAG_NEON
Definition: cpu.h:73
FFMAX
#define FFMAX(a, b)
Definition: macros.h:47
func
void(* func)(void)
Definition: ops.c:50
SwsOpBackend::name
const char * name
Definition: ops_dispatch.h:135
SwsOpChain::cpu_flags
int cpu_flags
Definition: ops_chain.h:89
SwsFuncPtr
void(* SwsFuncPtr)(void)
Per-kernel execution context.
Definition: ops_chain.h:70
SwsOpList::num_ops
int num_ops
Definition: ops.h:267
SwsOpFunc
void(* SwsOpFunc)(const SwsOpExec *exec, const void *priv, int bx_start, int y_start, int bx_end, int y_end)
Process a given range of pixel blocks.
Definition: ops_dispatch.h:95
SWS_OP_SCALE
@ SWS_OP_SCALE
Definition: ops.h:55
SwsOpChain::free
void(* free[SWS_MAX_OPS+1])(SwsOpPriv *)
Definition: ops_chain.h:87
avassert.h
FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a)
Definition: sinewin_tablegen.c:29
backend_aarch64
const SwsOpBackend backend_aarch64
Definition: ops.c:268
ff_sws_setup_scale
int ff_sws_setup_scale(const SwsImplParams *params, SwsImplResult *out)
float
float
Definition: af_crystalizer.c:122
ff_sws_op_chain_alloc
SwsOpChain * ff_sws_op_chain_alloc(void)
Copyright (C) 2025 Niklas Haas.
Definition: ops_chain.c:27
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
offsetof_impl_cont
#define offsetof_impl_cont
Definition: ops_impl.h:85
av_assert0
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition: avassert.h:42
SwsOpImpl
Definition: ops_chain.h:71
av_get_cpu_flags
int av_get_cpu_flags(void)
Return the flags which specify extensions supported by the CPU.
Definition: cpu.c:109
SWS_OP_MIN
@ SWS_OP_MIN
Definition: ops.h:53
ops_entries.c
ctx
static AVFormatContext * ctx
Definition: movenc.c:49
SWS_OP_LINEAR
@ SWS_OP_LINEAR
Definition: ops.h:58
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
params
SwsAArch64OpImplParams params
Definition: ops.c:51
SwsOpBackend
Definition: ops_dispatch.h:134
SwsOpPriv::ptr
void * ptr
Definition: ops_chain.h:49
SwsOpExec
Copyright (C) 2026 Niklas Haas.
Definition: ops_dispatch.h:36
offsetof_exec_out_bump
#define offsetof_exec_out_bump
Definition: ops_impl.h:84
SwsOpChain
Compiled "chain" of operations, which can be dispatched efficiently.
Definition: ops_chain.h:84
NULL
#define NULL
Definition: coverity.c:32
offsetof_exec_out
#define offsetof_exec_out
Definition: ops_impl.h:82
SwsImplParams
Definition: ops_chain.h:105
aarch64_compile
static int aarch64_compile(SwsContext *ctx, const SwsOpList *ops, SwsCompiledOp *out)
Definition: ops.c:196
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
size
int size
Definition: twinvq_data.h:10344
av_malloc
#define av_malloc(s)
Definition: ops_static.c:44
SWS_OP_WRITE
@ SWS_OP_WRITE
Definition: ops.h:40
tree.h
cpu_flags
CheckasmCpu cpu_flags
Definition: checkasm.c:84
ops_entries
static const struct @569 ops_entries[]
ff_sws_op_chain_free_cb
void ff_sws_op_chain_free_cb(void *ptr)
Definition: ops_chain.c:32
aarch64_setup_dither
static int aarch64_setup_dither(const SwsAArch64OpImplParams *p, const SwsOp *op, SwsImplResult *res)
Definition: ops.c:101
aarch64_setup
static int aarch64_setup(const SwsOpList *ops, int block_size, int n, const SwsAArch64OpImplParams *p, SwsImplResult *out)
Definition: ops.c:150
ops_impl_conv.c
ff_sws_op_chain_free
static void ff_sws_op_chain_free(SwsOpChain *chain)
Definition: ops_chain.h:96
SwsOpList::ops
SwsOp * ops
Definition: ops.h:266
offsetof_impl_priv
#define offsetof_impl_priv
Definition: ops_impl.h:86
aarch64_setup_linear
static int aarch64_setup_linear(const SwsAArch64OpImplParams *p, const SwsOp *op, SwsImplResult *res)
Definition: ops.c:68
SwsImplResult::free
void(* free)(SwsOpPriv *priv)
Definition: ops_chain.h:117
SwsOp
Definition: ops.h:210
ff_op_priv_free
static void ff_op_priv_free(SwsOpPriv *priv)
Definition: ops_chain.h:144
ret
ret
Definition: filter_design.txt:187
offsetof_exec_in
#define offsetof_exec_in
These values will be used by ops_asmgen to access fields inside of SwsOpExec and SwsOpImpl.
Definition: ops_impl.h:81
SWS_OP_MAX
@ SWS_OP_MAX
Definition: ops.h:54
SwsCompiledOp
Definition: ops_dispatch.h:101
convert_to_aarch64_impl
static int convert_to_aarch64_impl(SwsContext *ctx, const SwsOpList *ops, int n, int block_size, SwsAArch64OpImplParams *out)
Convert SwsOp to a SwsAArch64OpImplParams.
Definition: ops_impl_conv.c:98
SwsImplResult::priv
SwsOpPriv priv
Definition: ops_chain.h:116
AV_PIX_FMT_NONE
@ AV_PIX_FMT_NONE
Definition: pixfmt.h:72
Windows::Graphics::DirectX::Direct3D11::p
IDirect3DDxgiInterfaceAccess _COM_Outptr_ void ** p
Definition: vsrc_gfxcapture_winrt.hpp:53
SwsAArch64OpImplParams
SwsAArch64OpImplParams describes the parameters for an SwsUOpType operation.
Definition: ops_impl.h:47
offsetof_exec_in_bump
#define offsetof_exec_in_bump
Definition: ops_impl.h:83
ff_sws_op_chain_append
int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func, void(*free)(SwsOpPriv *), const SwsOpPriv *priv)
Definition: ops_chain.c:46
linear_num_vregs
static int linear_num_vregs(const SwsAArch64OpImplParams *params)
Definition: ops_impl.h:67
stride
#define stride
Definition: h264pred_template.c:536
avstring.h
SwsOpList
Helper struct for representing a list of operations.
Definition: ops.h:265
ff_sws_setup_clamp
int ff_sws_setup_clamp(const SwsImplParams *params, SwsImplResult *out)
SwsContext
Main external API structure.
Definition: swscale.h:227
SwsOpPriv
Private data for each kernel.
Definition: ops_chain.h:45
SWS_MASK
#define SWS_MASK(I, J)
Definition: uops.h:229
SwsImplResult
Definition: ops_chain.h:114
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
SWS_BACKEND_AARCH64
@ SWS_BACKEND_AARCH64
Chained AArch64 NEON kernels.
Definition: swscale.h:119