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_lookup.h"
28 
29 #include "ops_impl_conv.c"
30 
31 /**
32  * Check that there is no mismatch for the SwsOpExec/SwsOpImpl offset
33  * values used by ops_asmgen.
34  * NOTE: The check is performed here since this file only ever targets
35  * aarch64, differently from ops_asmgen which may be built on any
36  * host.
37  */
38 static_assert(offsetof_exec_in == offsetof(SwsOpExec, in), "SwsOpExec layout mismatch");
39 static_assert(offsetof_exec_out == offsetof(SwsOpExec, out), "SwsOpExec layout mismatch");
40 static_assert(offsetof_exec_in_bump == offsetof(SwsOpExec, in_bump), "SwsOpExec layout mismatch");
41 static_assert(offsetof_exec_out_bump == offsetof(SwsOpExec, out_bump), "SwsOpExec layout mismatch");
42 static_assert(offsetof_impl_cont == offsetof(SwsOpImpl, cont), "SwsOpImpl layout mismatch");
43 static_assert(offsetof_impl_priv == offsetof(SwsOpImpl, priv), "SwsOpImpl layout mismatch");
44 
45 /*********************************************************************/
47  const SwsOp *op, SwsImplResult *res)
48 {
49  /**
50  * Compute number of full vector registers needed to pack all non-zero
51  * coefficients.
52  */
53  const int num_vregs = linear_num_vregs(p);
54  av_assert0(num_vregs <= 4);
55  float *coeffs = av_malloc(num_vregs * 4 * sizeof(float));
56  if (!coeffs)
57  return AVERROR(ENOMEM);
58 
59  /**
60  * Copy non-zero coefficients, reordered to match SwsAArch64LinearOpMask.
61  * The coefficients are packed in sequential order. The same order must
62  * be followed in asmgen_op_linear().
63  */
64  int i_coeff = 0;
65  LOOP_LINEAR_MASK(p, i, j) {
66  const int jj = linear_index_to_sws_op(j);
67  coeffs[i_coeff++] = (float) op->lin.m[i][jj].num / op->lin.m[i][jj].den;
68  }
69 
70  res->priv.ptr = coeffs;
71  res->free = ff_op_priv_free;
72 
73  return 0;
74 }
75 
76 /*********************************************************************/
78  const SwsOp *op, SwsImplResult *res)
79 {
80  /**
81  * The input dither matrix is (1 << size_log2)² pixels large. It is
82  * periodic, so the x and y offsets should be masked to fit inside
83  * (1 << size_log2).
84  * The width of the matrix is assumed to be at least 8, which matches
85  * the maximum block_size for aarch64 asmgen when f32 operations
86  * (i.e., dithering) are used. This guarantees that the x offset is
87  * aligned and that reading block_size elements does not extend past
88  * the end of the row. The x offset doesn't change between components,
89  * so it is only required to be masked once.
90  * The y offset, on the other hand, may change per component, and
91  * would therefore need to be masked for every y_offset value. To
92  * simplify the execution, we over-allocate the number of rows of
93  * the output dither matrix by the largest y_offset value. This way,
94  * we only need to mask y offset once, and can safely increment the
95  * dither matrix pointer by fixed offsets for every y_offset change.
96  */
97 
98  /* Find the largest y_offset value. */
99  const int size = 1 << op->dither.size_log2;
100  const int8_t *off = op->dither.y_offset;
101  int max_offset = 0;
102  for (int i = 0; i < 4; i++) {
103  if (off[i] >= 0)
104  max_offset = FFMAX(max_offset, off[i] & (size - 1));
105  }
106 
107  /* Allocate (size + max_offset) rows to allow over-reading the matrix. */
108  const int stride = size * sizeof(float);
109  const int num_rows = size + max_offset;
110  float *matrix = av_malloc(num_rows * stride);
111  if (!matrix)
112  return AVERROR(ENOMEM);
113 
114  for (int i = 0; i < size * size; i++)
115  matrix[i] = (float) op->dither.matrix[i].num / op->dither.matrix[i].den;
116 
117  memcpy(&matrix[size * size], matrix, max_offset * stride);
118 
119  res->priv.ptr = matrix;
120  res->free = ff_op_priv_free;
121 
122  return 0;
123 }
124 
125 /*********************************************************************/
126 static int aarch64_setup(const SwsOpList *ops, int block_size, int n,
128 {
129  const SwsOp *op = &ops->ops[n];
130  switch (op->op) {
131  case SWS_OP_READ:
132  /* Negative shift values to perform right shift using ushl. */
133  if (op->rw.frac == 3) {
134  out->priv = (SwsOpPriv) {
135  .u8 = {
136  -7, -6, -5, -4, -3, -2, -1, 0,
137  -7, -6, -5, -4, -3, -2, -1, 0,
138  }
139  };
140  }
141  break;
142  case SWS_OP_WRITE:
143  /* Shift values for ushl. */
144  if (op->rw.frac == 3) {
145  out->priv = (SwsOpPriv) {
146  .u8 = {
147  7, 6, 5, 4, 3, 2, 1, 0,
148  7, 6, 5, 4, 3, 2, 1, 0,
149  }
150  };
151  }
152  break;
153  case SWS_OP_CLEAR:
154  ff_sws_setup_clear(&(const SwsImplParams) { .op = op }, out);
155  break;
156  case SWS_OP_MIN:
157  case SWS_OP_MAX:
158  ff_sws_setup_clamp(&(const SwsImplParams) { .op = op }, out);
159  break;
160  case SWS_OP_SCALE:
161  ff_sws_setup_scale(&(const SwsImplParams) { .op = op }, out);
162  break;
163  case SWS_OP_LINEAR:
164  return aarch64_setup_linear(p, op, out);
165  case SWS_OP_DITHER:
166  return aarch64_setup_dither(p, op, out);
167  }
168  return 0;
169 }
170 
171 /*********************************************************************/
172 static int aarch64_compile(SwsContext *ctx, const SwsOpList *ops,
174 {
175  int ret;
176 
177  const int cpu_flags = av_get_cpu_flags();
178  if (!(cpu_flags & AV_CPU_FLAG_NEON))
179  return AVERROR(ENOTSUP);
180 
181  /* Use at most two full vregs during the widest precision section */
182  int block_size = (ff_sws_op_list_max_size(ops) == 4) ? 8 : 16;
183 
185  if (!chain)
186  return AVERROR(ENOMEM);
187  chain->cpu_flags = AV_CPU_FLAG_NEON;
188 
189  *out = (SwsCompiledOp) {
190  .priv = chain,
191  .slice_align = 1,
193  .block_size = block_size,
194  };
195 
196  /* Look up kernel functions. */
197  for (int i = 0; i < ops->num_ops; i++) {
198  SwsAArch64OpImplParams params = { 0 };
199  ret = convert_to_aarch64_impl(ctx, ops, i, block_size, &params);
200  if (ret < 0)
201  goto error;
203  if (!func) {
204  ret = AVERROR(ENOTSUP);
205  goto error;
206  }
207  SwsImplResult res = { 0 };
208  ret = aarch64_setup(ops, block_size, i, &params, &res);
209  if (ret < 0)
210  goto error;
211  ret = ff_sws_op_chain_append(chain, func, res.free, &res.priv);
212  if (ret < 0)
213  goto error;
214  }
215 
216  /* Look up process function. */
217  void ff_sws_process_0001_neon(void);
218  void ff_sws_process_0011_neon(void);
219  void ff_sws_process_0111_neon(void);
220  void ff_sws_process_1111_neon(void);
221 
222  const SwsOp *read = ff_sws_op_list_input(ops);
223  const SwsOp *write = ff_sws_op_list_output(ops);
224  const int read_planes = read ? ff_sws_rw_op_planes(read) : 0;
225  const int write_planes = ff_sws_rw_op_planes(write);
226  SwsOpFunc process_func = NULL;
227  switch (FFMAX(read_planes, write_planes)) {
228  case 1: process_func = (SwsOpFunc) ff_sws_process_0001_neon; break;
229  case 2: process_func = (SwsOpFunc) ff_sws_process_0011_neon; break;
230  case 3: process_func = (SwsOpFunc) ff_sws_process_0111_neon; break;
231  case 4: process_func = (SwsOpFunc) ff_sws_process_1111_neon; break;
232  }
233 
234  out->func = process_func;
235  out->cpu_flags = chain->cpu_flags;
236 
237 error:
238  if (ret < 0)
239  ff_sws_op_chain_free(chain);
240  return ret;
241 }
242 
243 /*********************************************************************/
245  .name = "aarch64",
246  .flags = SWS_BACKEND_AARCH64,
247  .compile = aarch64_compile,
248  .hw_format = AV_PIX_FMT_NONE,
249 };
SWS_OP_READ
@ SWS_OP_READ
Definition: ops.h:39
error
static void error(const char *err)
Definition: target_bsf_fuzzer.c:32
func
int(* func)(AVBPrint *dst, const char *in, const char *arg)
Definition: jacosubdec.c:66
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
linear_index_to_sws_op
static int linear_index_to_sws_op(int idx)
Definition: ops_impl.h:144
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)
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
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
backend_aarch64
const SwsOpBackend backend_aarch64
Definition: ops.c:244
ff_sws_setup_scale
int ff_sws_setup_scale(const SwsImplParams *params, SwsImplResult *out)
ff_sws_aarch64_lookup
SwsFuncPtr ff_sws_aarch64_lookup(const SwsAArch64OpImplParams *p)
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:173
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
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
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:172
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:170
SwsImplParams
Definition: ops_chain.h:105
aarch64_compile
static int aarch64_compile(SwsContext *ctx, const SwsOpList *ops, SwsCompiledOp *out)
Definition: ops.c:172
i
#define i(width, name, range_min, range_max)
Definition: cbs_h264.c:63
size
int size
Definition: twinvq_data.h:10344
SWS_OP_WRITE
@ SWS_OP_WRITE
Definition: ops.h:40
tree.h
cpu_flags
CheckasmCpu cpu_flags
Definition: checkasm.c:84
ops_lookup.h
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:77
aarch64_setup
static int aarch64_setup(const SwsOpList *ops, int block_size, int n, const SwsAArch64OpImplParams *p, SwsImplResult *out)
Definition: ops.c:126
ops_impl_conv.c
av_malloc
#define av_malloc(s)
Definition: ops_asmgen.c:44
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:174
aarch64_setup_linear
static int aarch64_setup_linear(const SwsAArch64OpImplParams *p, const SwsOp *op, SwsImplResult *res)
Check that there is no mismatch for the SwsOpExec/SwsOpImpl offset values used by ops_asmgen.
Definition: ops.c:46
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:169
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:59
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
LOOP_LINEAR_MASK
#define LOOP_LINEAR_MASK(p, idx, jdx)
Definition: ops_impl.h:130
SwsAArch64OpImplParams
SwsAArch64OpImplParams describes the parameters for an SwsAArch64OpType operation.
Definition: ops_impl.h:92
offsetof_exec_in_bump
#define offsetof_exec_in_bump
Definition: ops_impl.h:171
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:136
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
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