/
githubmirror
/
FFmpeg
Обзор
Документация
Войти
/
githubmirror
/
FFmpeg
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
libswscale/aarch64/ops.c
273 строки
10 KB
Ramiro Polla
swscale/aarch64: split ops_static.c out of ops_asmgen.c
22 июл 2026, 17:07
22 июл 2026, 17:07
c616901
Код
Авторство
О чём код?
/* * Copyright (C) 2026 Ramiro Polla * * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * FFmpeg is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "../ops_chain.h" #include "libavutil/avassert.h" #include "libavutil/avstring.h" #include "libavutil/tree.h" #include "ops_impl_conv.c" /** * Check that there is no mismatch for the SwsOpExec/SwsOpImpl offset * values used by ops_static. * NOTE: The check is performed here since this file only ever targets * aarch64, differently from ops_static which may be built on any * host. */ static_assert(offsetof_exec_in == offsetof(SwsOpExec, in), "SwsOpExec layout mismatch"); static_assert(offsetof_exec_out == offsetof(SwsOpExec, out), "SwsOpExec layout mismatch"); static_assert(offsetof_exec_in_bump == offsetof(SwsOpExec, in_bump), "SwsOpExec layout mismatch"); static_assert(offsetof_exec_out_bump == offsetof(SwsOpExec, out_bump), "SwsOpExec layout mismatch"); static_assert(offsetof_impl_cont == offsetof(SwsOpImpl, cont), "SwsOpImpl layout mismatch"); static_assert(offsetof_impl_priv == offsetof(SwsOpImpl, priv), "SwsOpImpl layout mismatch"); /*********************************************************************/ /* Forward-declare exported functions. */ #define ENTRY(fname, ...) extern void fname(void); #include "ops_entries.c" #undef ENTRY static const struct { void (*func)(void); SwsAArch64OpImplParams params; } ops_entries[] = { #define ENTRY(fname, ...) { .func = fname, .params = __VA_ARGS__ }, #include "ops_entries.c" #undef ENTRY }; /* Look up the exported function pointer for the given parameters. */ static SwsFuncPtr aarch64_lookup(const SwsAArch64OpImplParams *p) { for (int i = 0; i < FF_ARRAY_ELEMS(ops_entries); i++) if (!memcmp(p, &ops_entries[i].params, sizeof(SwsAArch64OpImplParams))) return ops_entries[i].func; return NULL; } /*********************************************************************/ static int aarch64_setup_linear(const SwsAArch64OpImplParams *p, const SwsOp *op, SwsImplResult *res) { /** * Compute number of full vector registers needed to pack all non-zero * coefficients. */ const int num_vregs = linear_num_vregs(p); av_assert0(num_vregs <= 4); float *coeffs = av_malloc(num_vregs * 4 * sizeof(float)); if (!coeffs) return AVERROR(ENOMEM); /** * Copy non-zero coefficients, packed in sequential order, offset first. * The same order must be followed in asmgen_op_linear(). */ int i_coeff = 0; for (int i = 0; i < 4; i++) { for (int j = 0; j < 5; j++) { const int jj = (j == 0) ? 4 : (j - 1); if (!(p->par.lin.zero & SWS_MASK(i, jj))) coeffs[i_coeff++] = (float) op->lin.m[i][jj].num / op->lin.m[i][jj].den; } } res->priv.ptr = coeffs; res->free = ff_op_priv_free; return 0; } /*********************************************************************/ static int aarch64_setup_dither(const SwsAArch64OpImplParams *p, const SwsOp *op, SwsImplResult *res) { /** * The input dither matrix is (1 << size_log2)² pixels large. It is * periodic, so the x and y offsets should be masked to fit inside * (1 << size_log2). * The width of the matrix is assumed to be at least 8, which matches * the maximum block_size for aarch64 asmgen when f32 operations * (i.e., dithering) are used. This guarantees that the x offset is * aligned and that reading block_size elements does not extend past * the end of the row. The x offset doesn't change between components, * so it is only required to be masked once. * The y offset, on the other hand, may change per component, and * would therefore need to be masked for every y_offset value. To * simplify the execution, we over-allocate the number of rows of * the output dither matrix by the largest y_offset value. This way, * we only need to mask y offset once, and can safely increment the * dither matrix pointer by fixed offsets for every y_offset change. */ /* Find the largest y_offset value. */ const int size = 1 << op->dither.size_log2; const int8_t *off = op->dither.y_offset; int max_offset = 0; for (int i = 0; i < 4; i++) { if (off[i] >= 0) max_offset = FFMAX(max_offset, off[i] & (size - 1)); } /* Allocate (size + max_offset) rows to allow over-reading the matrix. */ const int stride = size * sizeof(float); const int num_rows = size + max_offset; float *matrix = av_malloc(num_rows * stride); if (!matrix) return AVERROR(ENOMEM); for (int i = 0; i < size * size; i++) matrix[i] = (float) op->dither.matrix[i].num / op->dither.matrix[i].den; memcpy(&matrix[size * size], matrix, max_offset * stride); res->priv.ptr = matrix; res->free = ff_op_priv_free; return 0; } /*********************************************************************/ static int aarch64_setup(const SwsOpList *ops, int block_size, int n, const SwsAArch64OpImplParams *p, SwsImplResult *out) { const SwsOp *op = &ops->ops[n]; switch (op->op) { case SWS_OP_READ: /* Negative shift values to perform right shift using ushl. */ if (op->rw.frac == 3) { out->priv = (SwsOpPriv) { .u8 = { -7, -6, -5, -4, -3, -2, -1, 0, -7, -6, -5, -4, -3, -2, -1, 0, } }; } break; case SWS_OP_WRITE: /* Shift values for ushl. */ if (op->rw.frac == 3) { out->priv = (SwsOpPriv) { .u8 = { 7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0, } }; } break; case SWS_OP_CLEAR: ff_sws_setup_clear(&(const SwsImplParams) { .op = op }, out); break; case SWS_OP_MIN: case SWS_OP_MAX: ff_sws_setup_clamp(&(const SwsImplParams) { .op = op }, out); break; case SWS_OP_SCALE: ff_sws_setup_scale(&(const SwsImplParams) { .op = op }, out); break; case SWS_OP_LINEAR: return aarch64_setup_linear(p, op, out); case SWS_OP_DITHER: return aarch64_setup_dither(p, op, out); } return 0; } /*********************************************************************/ static int aarch64_compile(SwsContext *ctx, const SwsOpList *ops, SwsCompiledOp *out) { int ret; const int cpu_flags = av_get_cpu_flags(); if (!(cpu_flags & AV_CPU_FLAG_NEON)) return AVERROR(ENOTSUP); /* Use at most two full vregs during the widest precision section */ int block_size = (ff_sws_op_list_max_size(ops) == 4) ? 8 : 16; SwsOpChain *chain = ff_sws_op_chain_alloc(); if (!chain) return AVERROR(ENOMEM); chain->cpu_flags = AV_CPU_FLAG_NEON; *out = (SwsCompiledOp) { .priv = chain, .slice_align = 1, .free = ff_sws_op_chain_free_cb, .block_size = block_size, }; /* Look up kernel functions. */ for (int i = 0; i < ops->num_ops; i++) { SwsAArch64OpImplParams params = { 0 }; ret = convert_to_aarch64_impl(ctx, ops, i, block_size, ¶ms); if (ret < 0) goto error; SwsFuncPtr func = aarch64_lookup(¶ms); if (!func) { ret = AVERROR(ENOTSUP); goto error; } SwsImplResult res = { 0 }; ret = aarch64_setup(ops, block_size, i, ¶ms, &res); if (ret < 0) goto error; ret = ff_sws_op_chain_append(chain, func, res.free, &res.priv); if (ret < 0) goto error; } /* Look up process function. */ void ff_sws_process_0001_neon(void); void ff_sws_process_0011_neon(void); void ff_sws_process_0111_neon(void); void ff_sws_process_1111_neon(void); const SwsOp *read = ff_sws_op_list_input(ops); const SwsOp *write = ff_sws_op_list_output(ops); const int read_planes = read ? ff_sws_rw_op_planes(read) : 0; const int write_planes = ff_sws_rw_op_planes(write); SwsOpFunc process_func = NULL; switch (FFMAX(read_planes, write_planes)) { case 1: process_func = (SwsOpFunc) ff_sws_process_0001_neon; break; case 2: process_func = (SwsOpFunc) ff_sws_process_0011_neon; break; case 3: process_func = (SwsOpFunc) ff_sws_process_0111_neon; break; case 4: process_func = (SwsOpFunc) ff_sws_process_1111_neon; break; } out->func = process_func; out->cpu_flags = chain->cpu_flags; error: if (ret < 0) ff_sws_op_chain_free(chain); return ret; } /*********************************************************************/ const SwsOpBackend backend_aarch64 = { .name = "aarch64", .flags = SWS_BACKEND_AARCH64, .compile = aarch64_compile, .hw_format = AV_PIX_FMT_NONE, };