FFmpeg
Loading...
Searching...
No Matches
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 */
36static_assert(offsetof_exec_in == offsetof(SwsOpExec, in), "SwsOpExec layout mismatch");
37static_assert(offsetof_exec_out == offsetof(SwsOpExec, out), "SwsOpExec layout mismatch");
38static_assert(offsetof_exec_in_bump == offsetof(SwsOpExec, in_bump), "SwsOpExec layout mismatch");
39static_assert(offsetof_exec_out_bump == offsetof(SwsOpExec, out_bump), "SwsOpExec layout mismatch");
40static_assert(offsetof_impl_cont == offsetof(SwsOpImpl, cont), "SwsOpImpl layout mismatch");
41static_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
49static 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/*********************************************************************/
150static 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/*********************************************************************/
196static int aarch64_compile(SwsContext *ctx, const SwsOpList *ops,
198{
199 int ret;
200
201 const int cpu_flags = av_get_cpu_flags();
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);
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
261error:
262 if (ret < 0)
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};
static int aarch64_setup_dither(const SwsAArch64OpImplParams *p, const SwsOp *op, SwsImplResult *res)
Definition ops.c:101
static int aarch64_setup_linear(const SwsAArch64OpImplParams *p, const SwsOp *op, SwsImplResult *res)
Definition ops.c:68
static int aarch64_compile(SwsContext *ctx, const SwsOpList *ops, SwsCompiledOp *out)
Definition ops.c:196
static const struct @070174075374214234277303002223103136214276265127 ops_entries[]
SwsAArch64OpImplParams params
Definition ops.c:51
const SwsOpBackend backend_aarch64
Definition ops.c:268
static int aarch64_setup(const SwsOpList *ops, int block_size, int n, const SwsAArch64OpImplParams *p, SwsImplResult *out)
Definition ops.c:150
static SwsFuncPtr aarch64_lookup(const SwsAArch64OpImplParams *p)
Definition ops.c:59
static FILE * out
static AVFormatContext * ctx
simple assert() macros that are a bit more flexible than ISO C assert().
#define av_assert0(cond)
assert() equivalent, that is always enabled.
Definition avassert.h:42
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.
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
#define NULL
Definition coverity.c:32
#define AVERROR(e)
Definition error.h:45
@ SWS_BACKEND_AARCH64
Chained AArch64 NEON kernels.
Definition swscale.h:119
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
int(* func)(AVBPrint *dst, const char *in, const char *arg)
Definition jacosubdec.c:66
static atomic_int cpu_flags
Definition cpu.c:56
int av_get_cpu_flags(void)
Return the flags which specify extensions supported by the CPU.
Definition cpu.c:109
#define AV_CPU_FLAG_NEON
Definition cpu.h:73
#define FFMAX(a, b)
Definition macros.h:47
int ff_sws_op_list_max_size(const SwsOpList *ops)
Returns the size of the largest pixel type used in ops.
Definition ops.c:791
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:713
int ff_sws_rw_op_planes(const SwsOp *op)
Return the number of planes involved in a read/write operation.
Definition ops.c:133
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:722
@ SWS_OP_SCALE
Definition ops.h:56
@ SWS_OP_WRITE
Definition ops.h:41
@ SWS_OP_READ
Definition ops.h:40
@ SWS_OP_CLEAR
Definition ops.h:52
@ SWS_OP_MIN
Definition ops.h:54
@ SWS_OP_LINEAR
Definition ops.h:59
@ SWS_OP_DITHER
Definition ops.h:60
@ SWS_OP_MAX
Definition ops.h:55
int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func, void(*free)(SwsOpPriv *), const SwsOpPriv *priv)
Definition ops_chain.c:46
SwsOpChain * ff_sws_op_chain_alloc(void)
Copyright (C) 2025 Niklas Haas.
Definition ops_chain.c:27
void ff_sws_op_chain_free_cb(void *ptr)
Definition ops_chain.c:32
static void ff_sws_op_chain_free(SwsOpChain *chain)
Definition ops_chain.h:96
void(* SwsFuncPtr)(void)
Per-kernel execution context.
Definition ops_chain.h:70
int ff_sws_setup_clamp(const SwsImplParams *params, SwsImplResult *out)
int ff_sws_setup_scale(const SwsImplParams *params, SwsImplResult *out)
static void ff_op_priv_free(SwsOpPriv *priv)
Definition ops_chain.h:144
int ff_sws_setup_clear(const SwsImplParams *params, SwsImplResult *out)
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.
static int linear_num_vregs(const SwsAArch64OpImplParams *params)
Definition ops_impl.h:67
#define offsetof_exec_out_bump
Definition ops_impl.h:84
#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
#define offsetof_exec_out
Definition ops_impl.h:82
#define offsetof_exec_in_bump
Definition ops_impl.h:83
#define offsetof_impl_priv
Definition ops_impl.h:86
#define offsetof_impl_cont
Definition ops_impl.h:85
static int convert_to_aarch64_impl(SwsContext *ctx, const SwsOpList *ops, int n, int block_size, SwsAArch64OpImplParams *out)
Convert SwsOp to a SwsAArch64OpImplParams.
#define av_malloc(s)
Definition ops_static.c:52
@ AV_PIX_FMT_NONE
Definition pixfmt.h:72
#define FF_ARRAY_ELEMS(a)
SwsAArch64OpImplParams describes the parameters for an SwsUOpType operation.
Definition ops_impl.h:47
Main external API structure.
Definition swscale.h:227
SwsOpPriv priv
Definition ops_chain.h:116
void(* free)(SwsOpPriv *priv)
Definition ops_chain.h:117
Compiled "chain" of operations, which can be dispatched efficiently.
Definition ops_chain.h:84
int cpu_flags
Definition ops_chain.h:89
void(* free[SWS_MAX_OPS+1])(SwsOpPriv *)
Definition ops_chain.h:87
Copyright (C) 2026 Niklas Haas.
Helper struct for representing a list of operations.
Definition ops.h:293
SwsOp * ops
Definition ops.h:294
int num_ops
Definition ops.h:295
Definition ops.h:237
#define stride
static void error(const char *err)
A tree container.
int size
Private data for each kernel.
Definition ops_chain.h:45
void * ptr
Definition ops_chain.h:49
#define SWS_MASK(I, J)
Definition uops.h:231