FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libswscale/x86/ops.c
Date: 2026-10-19 08:11:58
Exec Total Coverage
Lines: 256 298 85.9%
Functions: 19 19 100.0%
Branches: 191 350 54.6%

Line Branch Exec Source
1 /**
2 * Copyright (C) 2025-2026 Niklas Haas
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 <float.h>
22
23 #include "libavutil/avassert.h"
24 #include "libavutil/mem.h"
25 #include "libavutil/x86/cpu.h"
26
27 #include "../ops_chain.h"
28 #include "../uops.h"
29 #include "../uops_macros.h"
30
31 23935 static int setup_rw_packed(const SwsImplParams *params, SwsImplResult *out)
32 {
33 23935 const SwsUOp *uop = params->uop;
34
35 /* 3-component packed reads/writes process one extra garbage word */
36
2/2
✓ Branch 0 taken 6376 times.
✓ Branch 1 taken 17559 times.
23935 if (uop->mask == SWS_COMP_ELEMS(3)) {
37
2/3
✓ Branch 0 taken 3732 times.
✓ Branch 1 taken 2644 times.
✗ Branch 2 not taken.
6376 switch (uop->uop) {
38 3732 case SWS_UOP_READ_PACKED: out->over_read[0] = sizeof(uint32_t); break;
39 2644 case SWS_UOP_WRITE_PACKED: out->over_write[0] = sizeof(uint32_t); break;
40 }
41 }
42
43 23935 return 0;
44 }
45
46 19120 static int setup_filter_v(const SwsImplParams *params, SwsImplResult *out)
47 {
48 19120 const SwsFilterWeights *filter = params->uop->data.kernel;
49 static_assert(sizeof(out->priv.ptr) <= sizeof(int32_t[2]),
50 ">8 byte pointers not supported");
51
52 /* Pre-convert weights to float */
53 19120 float *weights = av_calloc(filter->num_weights, sizeof(float));
54
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19120 times.
19120 if (!weights)
55 ✗ return AVERROR(ENOMEM);
56
57
2/2
✓ Branch 0 taken 6148832 times.
✓ Branch 1 taken 19120 times.
6167952 for (int i = 0; i < filter->num_weights; i++)
58 6148832 weights[i] = (float) filter->weights[i] / SWS_FILTER_SCALE;
59
60 19120 out->priv.ptr = weights;
61 19120 out->priv.uptr[1] = filter->filter_size;
62 19120 out->free = ff_op_priv_free;
63 19120 return 0;
64 }
65
66 19254 static int hscale_sizeof_weight(const SwsUOp *uop)
67 {
68
3/4
✓ Branch 0 taken 5413 times.
✓ Branch 1 taken 9934 times.
✓ Branch 2 taken 3907 times.
✗ Branch 3 not taken.
19254 switch (uop->type) {
69 5413 case SWS_PIXEL_U8: return sizeof(int16_t);
70 9934 case SWS_PIXEL_U16: return sizeof(int16_t);
71 3907 case SWS_PIXEL_F32: return sizeof(float);
72 ✗ default: return 0;
73 }
74 }
75
76 2635 static int setup_filter_h(const SwsImplParams *params, SwsImplResult *out)
77 {
78 2635 const SwsUOp *uop = params->uop;
79 2635 const SwsFilterWeights *filter = uop->data.kernel;
80
81 /**
82 * `vpgatherdd` gathers 32 bits at a time; so if we're filtering a smaller
83 * size, we need to gather 2/4 taps simultaneously and unroll the inner
84 * loop over several packed samples.
85 */
86 2635 const int pixel_size = ff_sws_pixel_type_size(uop->type);
87 2635 const int taps_align = sizeof(int32_t) / pixel_size;
88 2635 const int filter_size = filter->filter_size;
89 2635 const int block_size = params->table->block_size;
90 2635 const size_t aligned_size = FFALIGN(filter_size, taps_align);
91 2635 const size_t line_size = FFALIGN(filter->dst_size, block_size);
92 av_assert1(FFALIGN(line_size, taps_align) == line_size);
93
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2635 times.
2635 if (aligned_size > INT_MAX)
94 ✗ return AVERROR(EINVAL);
95
96 union {
97 void *ptr;
98 int16_t *i16;
99 float *f32;
100 } weights;
101
102 2635 const int sizeof_weight = hscale_sizeof_weight(uop);
103 2635 weights.ptr = av_calloc(line_size, sizeof_weight * aligned_size);
104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2635 times.
2635 if (!weights.ptr)
105 ✗ return AVERROR(ENOMEM);
106
107 /**
108 * Transpose filter weights to group (aligned) taps by block
109 */
110 2635 const int mmsize = block_size * 2;
111 2635 const int gather_size = mmsize / sizeof(int32_t); /* pixels per vpgatherdd */
112
2/2
✓ Branch 0 taken 15624 times.
✓ Branch 1 taken 2635 times.
18259 for (size_t x = 0; x < line_size; x += block_size) {
113 15624 const int elems = FFMIN(block_size, filter->dst_size - x);
114
2/2
✓ Branch 0 taken 41980 times.
✓ Branch 1 taken 15624 times.
57604 for (int j = 0; j < filter_size; j++) {
115 41980 const int jb = j & ~(taps_align - 1);
116 41980 const int ji = j - jb;
117 41980 const size_t idx_base = x * aligned_size + jb * block_size + ji;
118
2/2
✓ Branch 0 taken 671680 times.
✓ Branch 1 taken 41980 times.
713660 for (int i = 0; i < elems; i++) {
119 671680 const int w = filter->weights[(x + i) * filter_size + j];
120 671680 size_t idx = idx_base;
121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 671680 times.
671680 if (uop->type == SWS_PIXEL_U8) {
122 /* Interleave the pixels within each lane, i.e.:
123 * [a0 a1 a2 a3 | b0 b1 b2 b3 ] pixels 0-1, taps 0-3 (lane 0)
124 * [e0 e1 e2 e3 | f0 f1 f2 f3 ] pixels 4-5, taps 0-3 (lane 1)
125 * [c0 c1 c2 c3 | d0 d1 d2 d3 ] pixels 2-3, taps 0-3 (lane 0)
126 * [g0 g1 g2 g3 | h0 h1 h2 h3 ] pixels 6-7, taps 0-3 (lane 1)
127 * [i0 i1 i2 i3 | j0 j1 j2 j3 ] pixels 8-9, taps 0-3 (lane 0)
128 * ...
129 * [o0 o1 o2 o3 | p0 p1 p2 p3 ] pixels 14-15, taps 0-3 (lane 1)
130 * (repeat for taps 4-7, etc.)
131 */
132 ✗ const int gather_base = i & ~(gather_size - 1);
133 ✗ const int gather_pos = i - gather_base;
134 ✗ const int lane_idx = gather_pos >> 2;
135 ✗ const int pos_in_lane = gather_pos & 3;
136 ✗ idx += gather_base * 4 /* which gather (m0 or m1) */
137 ✗ + (pos_in_lane >> 1) * (mmsize / 2) /* lo/hi unpack */
138 ✗ + lane_idx * 8 /* 8 ints per lane */
139 ✗ + (pos_in_lane & 1) * 4; /* 4 taps per pair */
140 } else {
141 671680 idx += i * taps_align;
142 }
143
144
1/4
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 671680 times.
✗ Branch 3 not taken.
671680 switch (uop->type) {
145 ✗ case SWS_PIXEL_U8: weights.i16[idx] = w; break;
146 ✗ case SWS_PIXEL_U16: weights.i16[idx] = w; break;
147 671680 case SWS_PIXEL_F32: weights.f32[idx] = w; break;
148 }
149 }
150 }
151 }
152
153 2635 out->priv.ptr = weights.ptr;
154 2635 out->priv.uptr[1] = aligned_size;
155 2635 out->free = ff_op_priv_free;
156
157
2/2
✓ Branch 0 taken 10540 times.
✓ Branch 1 taken 2635 times.
13175 for (int i = 0; i < 4; i++) {
158
2/2
✓ Branch 0 taken 7153 times.
✓ Branch 1 taken 3387 times.
10540 if (uop->mask & SWS_COMP(i))
159 7153 out->over_read[i] = (aligned_size - filter_size) * pixel_size;
160 }
161 2635 return 0;
162 }
163
164 19254 static bool check_filter_h_4x4(const SwsImplParams *params)
165 {
166 19254 SwsContext *ctx = params->ctx;
167 19254 const SwsUOp *uop = params->uop;
168
4/4
✓ Branch 0 taken 5430 times.
✓ Branch 1 taken 13824 times.
✓ Branch 2 taken 2635 times.
✓ Branch 3 taken 2795 times.
19254 if ((ctx->flags & SWS_BITEXACT) && uop->type == SWS_PIXEL_F32)
169 2635 return false; /* different accumulation order due to 4x4 transpose */
170
171 16619 const int cpu_flags = av_get_cpu_flags();
172
1/2
✓ Branch 0 taken 16619 times.
✗ Branch 1 not taken.
16619 if (cpu_flags & AV_CPU_FLAG_SLOW_GATHER)
173 16619 return true; /* always prefer over gathers if gathers are slow */
174
175 /**
176 * Otherwise, prefer it above a certain filter size. Empirically, this
177 * kernel seems to be faster whenever the reference/gather kernel crosses
178 * a breakpoint for the number of gathers needed, but this filter doesn't.
179 *
180 * Tested on a Lunar Lake (Intel Core Ultra 7 258V) system.
181 */
182 ✗ const SwsFilterWeights *filter = uop->data.kernel;
183 ✗ return uop->type == SWS_PIXEL_U8 && filter->filter_size > 12 ||
184 ✗ uop->type == SWS_PIXEL_U16 && filter->filter_size > 4 ||
185 ✗ uop->type == SWS_PIXEL_F32 && filter->filter_size > 1;
186 }
187
188 16619 static int setup_filter_h_4x4(const SwsImplParams *params, SwsImplResult *out)
189 {
190 16619 const SwsUOp *uop = params->uop;
191 16619 const SwsFilterWeights *filter = uop->data.kernel;
192 16619 const int pixel_size = ff_sws_pixel_type_size(uop->type);
193 16619 const int sizeof_weights = hscale_sizeof_weight(uop);
194 16619 const int block_size = params->table->block_size;
195 16619 const int taps_align = 16 / sizeof_weights; /* taps per iteration (XMM) */
196 16619 const int pixels_align = 4; /* pixels per iteration */
197 16619 const int filter_size = filter->filter_size;
198 16619 const size_t aligned_size = FFALIGN(filter_size, taps_align);
199 16619 const int line_size = FFALIGN(filter->dst_size, block_size);
200 av_assert1(FFALIGN(line_size, pixels_align) == line_size);
201
202 union {
203 void *ptr;
204 int16_t *i16;
205 float *f32;
206 } weights;
207
208 16619 weights.ptr = av_calloc(line_size, aligned_size * sizeof_weights);
209
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 16619 times.
16619 if (!weights.ptr)
210 ✗ return AVERROR(ENOMEM);
211
212 /**
213 * Desired memory layout: [w][taps][pixels_align][taps_align]
214 *
215 * Example with taps_align=8, pixels_align=4:
216 * [a0, a1, ... a7] weights for pixel 0, taps 0..7
217 * [b0, b1, ... b7] weights for pixel 1, taps 0..7
218 * [c0, c1, ... c7] weights for pixel 2, taps 0..7
219 * [d0, d1, ... d7] weights for pixel 3, taps 0..7
220 * [a8, a9, ... a15] weights for pixel 0, taps 8..15
221 * ...
222 * repeat for all taps, then move on to pixels 4..7, etc.
223 */
224
2/2
✓ Branch 0 taken 1593600 times.
✓ Branch 1 taken 16619 times.
1610219 for (int x = 0; x < filter->dst_size; x++) {
225
2/2
✓ Branch 0 taken 5594144 times.
✓ Branch 1 taken 1593600 times.
7187744 for (int j = 0; j < filter_size; j++) {
226 5594144 const int xb = x & ~(pixels_align - 1);
227 5594144 const int jb = j & ~(taps_align - 1);
228 5594144 const int xi = x - xb, ji = j - jb;
229 5594144 const int w = filter->weights[x * filter_size + j];
230 5594144 const int idx = xb * aligned_size + jb * pixels_align + xi * taps_align + ji;
231
232
3/4
✓ Branch 0 taken 1815808 times.
✓ Branch 1 taken 3356384 times.
✓ Branch 2 taken 421952 times.
✗ Branch 3 not taken.
5594144 switch (uop->type) {
233 1815808 case SWS_PIXEL_U8: weights.i16[idx] = w; break;
234 3356384 case SWS_PIXEL_U16: weights.i16[idx] = w; break;
235 421952 case SWS_PIXEL_F32: weights.f32[idx] = w; break;
236 }
237 }
238 }
239
240 16619 out->priv.ptr = weights.ptr;
241 16619 out->priv.uptr[1] = aligned_size * sizeof_weights;
242 16619 out->free = ff_op_priv_free;
243
244
2/2
✓ Branch 0 taken 66476 times.
✓ Branch 1 taken 16619 times.
83095 for (int i = 0; i < 4; i++) {
245
2/2
✓ Branch 0 taken 45532 times.
✓ Branch 1 taken 20944 times.
66476 if (uop->mask & SWS_COMP(i))
246 45532 out->over_read[i] = (aligned_size - filter_size) * pixel_size;
247 }
248 16619 return 0;
249 }
250
251 11800 static int setup_scale(const SwsImplParams *params, SwsImplResult *out)
252 {
253 11800 const SwsUOp *uop = params->uop;
254
4/5
✓ Branch 0 taken 68 times.
✓ Branch 1 taken 163 times.
✓ Branch 2 taken 24 times.
✓ Branch 3 taken 11545 times.
✗ Branch 4 not taken.
11800 switch (uop->type) {
255 68 case SWS_PIXEL_U8: out->priv.u16[0] = uop->data.scalar.u8; break; /* for pmullw */
256 163 case SWS_PIXEL_U16: out->priv.u16[0] = uop->data.scalar.u16; break;
257 24 case SWS_PIXEL_U32: out->priv.u32[0] = uop->data.scalar.u32; break;
258 11545 case SWS_PIXEL_F32: out->priv.f32[0] = uop->data.scalar.f32; break;
259 ✗ default: return AVERROR(EINVAL);
260 }
261
262 11800 return 0;
263 }
264
265 8327 static int setup_clear(const SwsImplParams *params, SwsImplResult *out)
266 {
267 8327 const SwsUOp *uop = params->uop;
268
2/2
✓ Branch 0 taken 33308 times.
✓ Branch 1 taken 8327 times.
41635 for (int i = 0; i < 4; i++)
269 33308 out->priv.u32[i] = uop->data.vec4[i].u32;
270 8327 return 0;
271 }
272
273 28110 static int setup_dither(const SwsImplParams *params, SwsImplResult *out)
274 {
275 28110 out->priv.ptr = av_refstruct_ref(params->uop->data.ptr);
276 28110 out->free = ff_op_priv_unref;
277 28110 return 0;
278 }
279
280 10180 static void splat_lane(void *dst, SwsPixelType type, SwsPixel px)
281 {
282
2/4
✗ Branch 0 not taken.
✓ Branch 1 taken 9460 times.
✓ Branch 2 taken 720 times.
✗ Branch 3 not taken.
10180 switch (ff_sws_pixel_type_size(type)) {
283 ✗ case 1:
284 ✗ memset(dst, px.u8, 16);
285 ✗ break;
286 9460 case 2:
287
2/2
✓ Branch 0 taken 75680 times.
✓ Branch 1 taken 9460 times.
85140 for (int i = 0; i < 8; i++)
288 75680 ((uint16_t *) dst)[i] = px.u16;
289 9460 break;
290 720 case 4:
291
2/2
✓ Branch 0 taken 2880 times.
✓ Branch 1 taken 720 times.
3600 for (int i = 0; i < 4; i++)
292 2880 ((uint32_t *) dst)[i] = px.u32;
293 720 break;
294 }
295 10180 }
296
297 31535 static int setup_linear(const SwsImplParams *params, SwsImplResult *out)
298 {
299 31535 const SwsUOp *uop = params->uop;
300
2/2
✓ Branch 0 taken 31026 times.
✓ Branch 1 taken 509 times.
31535 if (uop->type == SWS_PIXEL_F32) {
301 31026 out->priv.ptr = av_memdup(uop->data.mat4x5, sizeof(uop->data.mat4x5));
302 31026 out->free = ff_op_priv_free;
303
1/2
✓ Branch 0 taken 31026 times.
✗ Branch 1 not taken.
31026 return out->priv.ptr ? 0 : AVERROR(ENOMEM);
304 }
305
306 509 uint8_t *mat = av_malloc(4 * 5 * 16); /* one lane per component */
307
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 509 times.
509 if (!mat)
308 ✗ return AVERROR(ENOMEM);
309 509 out->priv.ptr = mat;
310 509 out->free = ff_op_priv_free;
311
312
2/2
✓ Branch 0 taken 2036 times.
✓ Branch 1 taken 509 times.
2545 for (int i = 0; i < 4; i++) {
313
2/2
✓ Branch 0 taken 10180 times.
✓ Branch 1 taken 2036 times.
12216 for (int j = 0; j < 5; j++) {
314 10180 SwsPixel px = uop->data.mat4x5[i][j];
315 10180 SwsPixelType type = uop->type;
316
2/2
✓ Branch 0 taken 2860 times.
✓ Branch 1 taken 7320 times.
10180 if (type == SWS_PIXEL_U8) {
317 2860 type = SWS_PIXEL_U16; /* for pmullw */
318 2860 px.u16 = (px.u8 << 8) | px.u8;
319 }
320
321 10180 splat_lane(mat, type, px);
322 10180 mat += 16;
323 }
324 }
325 509 return 0;
326 }
327
328 437851 static bool uop_is_type_invariant(const SwsUOpType uop)
329 {
330
2/2
✓ Branch 0 taken 103041 times.
✓ Branch 1 taken 334810 times.
437851 switch (uop) {
331 103041 case SWS_UOP_READ_PLANAR:
332 case SWS_UOP_WRITE_PLANAR:
333 case SWS_UOP_CLEAR:
334 103041 return true;
335 334810 default:
336 334810 return false;
337 }
338 }
339
340 #define REF_ENTRY(EXT, NAME, ...) &uop_##NAME##EXT,
341 #define DECL_ENTRY(EXT, CHECK, SETUP, NAME, ...) \
342 void ff_##NAME##EXT(void); \
343 static const SwsUOpEntry uop_##NAME##EXT = { \
344 .func = (SwsFuncPtr) ff_##NAME##EXT, \
345 .check = CHECK, \
346 .setup = SETUP, \
347 __VA_ARGS__, \
348 };
349
350 /* Define all UOPs except conversion ops and type-invariant ops */
351 #define DECL_OPS_COMMON(EXT, TYPE) \
352 SWS_FOR_STRUCT(TYPE, READ_PACKED, DECL_ENTRY, EXT, NULL, setup_rw_packed) \
353 SWS_FOR_STRUCT(TYPE, READ_NIBBLE, DECL_ENTRY, EXT, NULL, NULL) \
354 SWS_FOR_STRUCT(TYPE, READ_BIT, DECL_ENTRY, EXT, NULL, NULL) \
355 SWS_FOR_STRUCT(TYPE, READ_PALETTE, DECL_ENTRY, EXT, NULL, NULL) \
356 SWS_FOR_STRUCT(TYPE, WRITE_PACKED, DECL_ENTRY, EXT, NULL, setup_rw_packed) \
357 SWS_FOR_STRUCT(TYPE, WRITE_NIBBLE, DECL_ENTRY, EXT, NULL, NULL) \
358 SWS_FOR_STRUCT(TYPE, WRITE_BIT, DECL_ENTRY, EXT, NULL, NULL) \
359 SWS_FOR_STRUCT(TYPE, SWAP_BYTES, DECL_ENTRY, EXT, NULL, NULL) \
360 SWS_FOR_STRUCT(TYPE, EXPAND_BIT, DECL_ENTRY, EXT, NULL, NULL) \
361 SWS_FOR_STRUCT(TYPE, PERMUTE, DECL_ENTRY, EXT, NULL, NULL) \
362 SWS_FOR_STRUCT(TYPE, COPY, DECL_ENTRY, EXT, NULL, NULL) \
363 SWS_FOR_STRUCT(TYPE, SCALE, DECL_ENTRY, EXT, NULL, setup_scale) \
364 SWS_FOR_STRUCT(TYPE, ADD, DECL_ENTRY, EXT, NULL, ff_sws_setup_vec4) \
365 SWS_FOR_STRUCT(TYPE, MIN, DECL_ENTRY, EXT, NULL, ff_sws_setup_vec4) \
366 SWS_FOR_STRUCT(TYPE, MAX, DECL_ENTRY, EXT, NULL, ff_sws_setup_vec4) \
367 SWS_FOR_STRUCT(TYPE, UNPACK, DECL_ENTRY, EXT, NULL, NULL) \
368 SWS_FOR_STRUCT(TYPE, PACK, DECL_ENTRY, EXT, NULL, NULL) \
369 SWS_FOR_STRUCT(TYPE, LSHIFT, DECL_ENTRY, EXT, NULL, NULL) \
370 SWS_FOR_STRUCT(TYPE, RSHIFT, DECL_ENTRY, EXT, NULL, NULL) \
371 SWS_FOR_STRUCT(TYPE, LINEAR, DECL_ENTRY, EXT, NULL, setup_linear) \
372 SWS_FOR_STRUCT(TYPE, LINEAR_FMA, DECL_ENTRY, EXT, NULL, setup_linear) \
373 SWS_FOR_STRUCT(TYPE, DITHER, DECL_ENTRY, EXT, NULL, setup_dither) \
374 /* end of macro */
375
376 #define REF_OPS_COMMON(EXT, TYPE) \
377 SWS_FOR(TYPE, READ_PACKED, REF_ENTRY, EXT) \
378 SWS_FOR(TYPE, READ_NIBBLE, REF_ENTRY, EXT) \
379 SWS_FOR(TYPE, READ_BIT, REF_ENTRY, EXT) \
380 SWS_FOR(TYPE, READ_PALETTE, REF_ENTRY, EXT) \
381 SWS_FOR(TYPE, WRITE_PACKED, REF_ENTRY, EXT) \
382 SWS_FOR(TYPE, WRITE_NIBBLE, REF_ENTRY, EXT) \
383 SWS_FOR(TYPE, WRITE_BIT, REF_ENTRY, EXT) \
384 SWS_FOR(TYPE, SWAP_BYTES, REF_ENTRY, EXT) \
385 SWS_FOR(TYPE, EXPAND_BIT, REF_ENTRY, EXT) \
386 SWS_FOR(TYPE, PERMUTE, REF_ENTRY, EXT) \
387 SWS_FOR(TYPE, COPY, REF_ENTRY, EXT) \
388 SWS_FOR(TYPE, SCALE, REF_ENTRY, EXT) \
389 SWS_FOR(TYPE, ADD, REF_ENTRY, EXT) \
390 SWS_FOR(TYPE, MIN, REF_ENTRY, EXT) \
391 SWS_FOR(TYPE, MAX, REF_ENTRY, EXT) \
392 SWS_FOR(TYPE, UNPACK, REF_ENTRY, EXT) \
393 SWS_FOR(TYPE, PACK, REF_ENTRY, EXT) \
394 SWS_FOR(TYPE, LSHIFT, REF_ENTRY, EXT) \
395 SWS_FOR(TYPE, RSHIFT, REF_ENTRY, EXT) \
396 SWS_FOR(TYPE, LINEAR, REF_ENTRY, EXT) \
397 SWS_FOR(TYPE, LINEAR_FMA, REF_ENTRY, EXT) \
398 SWS_FOR(TYPE, DITHER, REF_ENTRY, EXT) \
399 /* end of macro */
400
401 #define DECL_TABLE_U8(EXT, SIZE, FLAG) \
402 DECL_OPS_COMMON(EXT, U8) \
403 SWS_FOR_STRUCT(U8, READ_PLANAR, DECL_ENTRY, EXT, NULL, NULL) \
404 SWS_FOR_STRUCT(U8, WRITE_PLANAR, DECL_ENTRY, EXT, NULL, NULL) \
405 SWS_FOR_STRUCT(U8, CLEAR, DECL_ENTRY, EXT, NULL, setup_clear) \
406 \
407 static const SwsUOpTable uops_u8##EXT = { \
408 .cpu_flags = AV_CPU_FLAG_##FLAG, \
409 .block_size = SIZE, \
410 .entries = { \
411 REF_OPS_COMMON(EXT, U8) \
412 SWS_FOR(U8, READ_PLANAR, REF_ENTRY, EXT) \
413 SWS_FOR(U8, WRITE_PLANAR, REF_ENTRY, EXT) \
414 SWS_FOR(U8, CLEAR, REF_ENTRY, EXT) \
415 NULL \
416 }, \
417 };
418
419 #define DECL_TABLE_U16(EXT, SIZE, FLAG) \
420 DECL_OPS_COMMON(EXT, U16) \
421 SWS_FOR_STRUCT(U8, TO_U16, DECL_ENTRY, EXT, NULL, NULL) \
422 SWS_FOR_STRUCT(U16, TO_U8, DECL_ENTRY, EXT, NULL, NULL) \
423 SWS_FOR_STRUCT(U8, EXPAND_PAIR, DECL_ENTRY, EXT, NULL, NULL) \
424 \
425 static const SwsUOpTable uops_u16##EXT = { \
426 .cpu_flags = AV_CPU_FLAG_##FLAG, \
427 .block_size = SIZE, \
428 .entries = { \
429 REF_OPS_COMMON(EXT, U16) \
430 SWS_FOR(U8, TO_U16, REF_ENTRY, EXT) \
431 SWS_FOR(U16, TO_U8, REF_ENTRY, EXT) \
432 SWS_FOR(U8, EXPAND_PAIR, REF_ENTRY, EXT) \
433 NULL \
434 }, \
435 };
436
437 #define DECL_TABLE_U32(EXT, SIZE, FLAG) \
438 DECL_OPS_COMMON(EXT, U32) \
439 SWS_FOR_STRUCT(U8, TO_U32, DECL_ENTRY, EXT, NULL, NULL) \
440 SWS_FOR_STRUCT(U32, TO_U8, DECL_ENTRY, EXT, NULL, NULL) \
441 SWS_FOR_STRUCT(U16, TO_U32, DECL_ENTRY, EXT, NULL, NULL) \
442 SWS_FOR_STRUCT(U32, TO_U16, DECL_ENTRY, EXT, NULL, NULL) \
443 SWS_FOR_STRUCT(U8, EXPAND_QUAD, DECL_ENTRY, EXT, NULL, NULL) \
444 \
445 static const SwsUOpTable uops_u32##EXT = { \
446 .cpu_flags = AV_CPU_FLAG_##FLAG, \
447 .block_size = SIZE, \
448 .entries = { \
449 REF_OPS_COMMON(EXT, U32) \
450 SWS_FOR(U8, TO_U32, REF_ENTRY, EXT) \
451 SWS_FOR(U32, TO_U8, REF_ENTRY, EXT) \
452 SWS_FOR(U16, TO_U32, REF_ENTRY, EXT) \
453 SWS_FOR(U32, TO_U16, REF_ENTRY, EXT) \
454 SWS_FOR(U8, EXPAND_QUAD, REF_ENTRY, EXT) \
455 NULL \
456 }, \
457 };
458
459 #define DECL_TABLE_F32(EXT, SIZE, FLAG) \
460 DECL_OPS_COMMON(EXT, F32) \
461 SWS_FOR_STRUCT(U8, TO_F32, DECL_ENTRY, EXT, NULL, NULL) \
462 SWS_FOR_STRUCT(F32, TO_U8, DECL_ENTRY, EXT, NULL, NULL) \
463 SWS_FOR_STRUCT(U16, TO_F32, DECL_ENTRY, EXT, NULL, NULL) \
464 SWS_FOR_STRUCT(F32, TO_U16, DECL_ENTRY, EXT, NULL, NULL) \
465 SWS_FOR_STRUCT(U8, READ_PLANAR_FH, DECL_ENTRY, EXT, NULL, setup_filter_h) \
466 SWS_FOR_STRUCT(U16, READ_PLANAR_FH, DECL_ENTRY, EXT, NULL, setup_filter_h) \
467 SWS_FOR_STRUCT(F32, READ_PLANAR_FH, DECL_ENTRY, EXT, NULL, setup_filter_h) \
468 SWS_FOR_STRUCT(U8, READ_PLANAR_FH, DECL_ENTRY, _4x4##EXT, \
469 check_filter_h_4x4, setup_filter_h_4x4) \
470 SWS_FOR_STRUCT(U16, READ_PLANAR_FH, DECL_ENTRY, _4x4##EXT, \
471 check_filter_h_4x4, setup_filter_h_4x4) \
472 SWS_FOR_STRUCT(F32, READ_PLANAR_FH, DECL_ENTRY, _4x4##EXT, \
473 check_filter_h_4x4, setup_filter_h_4x4) \
474 SWS_FOR_STRUCT(U8, READ_PLANAR_FV, DECL_ENTRY, EXT, NULL, setup_filter_v) \
475 SWS_FOR_STRUCT(U16, READ_PLANAR_FV, DECL_ENTRY, EXT, NULL, setup_filter_v) \
476 SWS_FOR_STRUCT(F32, READ_PLANAR_FV, DECL_ENTRY, EXT, NULL, setup_filter_v) \
477 SWS_FOR_STRUCT(U8, READ_PLANAR_FV_FMA, DECL_ENTRY, EXT, NULL, setup_filter_v) \
478 SWS_FOR_STRUCT(U16, READ_PLANAR_FV_FMA, DECL_ENTRY, EXT, NULL, setup_filter_v) \
479 SWS_FOR_STRUCT(F32, READ_PLANAR_FV_FMA, DECL_ENTRY, EXT, NULL, setup_filter_v) \
480 \
481 static const SwsUOpTable uops_f32##EXT = { \
482 .cpu_flags = AV_CPU_FLAG_##FLAG, \
483 .block_size = SIZE, \
484 .entries = { \
485 REF_OPS_COMMON(EXT, F32) \
486 SWS_FOR(U8, TO_F32, REF_ENTRY, EXT) \
487 SWS_FOR(F32, TO_U8, REF_ENTRY, EXT) \
488 SWS_FOR(U16, TO_F32, REF_ENTRY, EXT) \
489 SWS_FOR(F32, TO_U16, REF_ENTRY, EXT) \
490 SWS_FOR(U8, READ_PLANAR_FH, REF_ENTRY, _4x4##EXT) \
491 SWS_FOR(U16, READ_PLANAR_FH, REF_ENTRY, _4x4##EXT) \
492 SWS_FOR(F32, READ_PLANAR_FH, REF_ENTRY, _4x4##EXT) \
493 SWS_FOR(U8, READ_PLANAR_FH, REF_ENTRY, EXT) \
494 SWS_FOR(U16, READ_PLANAR_FH, REF_ENTRY, EXT) \
495 SWS_FOR(F32, READ_PLANAR_FH, REF_ENTRY, EXT) \
496 SWS_FOR(U8, READ_PLANAR_FV, REF_ENTRY, EXT) \
497 SWS_FOR(U16, READ_PLANAR_FV, REF_ENTRY, EXT) \
498 SWS_FOR(F32, READ_PLANAR_FV, REF_ENTRY, EXT) \
499 SWS_FOR(U8, READ_PLANAR_FV_FMA, REF_ENTRY, EXT) \
500 SWS_FOR(U16, READ_PLANAR_FV_FMA, REF_ENTRY, EXT) \
501 SWS_FOR(F32, READ_PLANAR_FV_FMA, REF_ENTRY, EXT) \
502 NULL \
503 }, \
504 };
505
506 DECL_TABLE_U8( _m1_sse4, 16, SSE4)
507 DECL_TABLE_U8( _m1_avx2, 32, AVX2)
508 DECL_TABLE_U8( _m2_sse4, 32, SSE4)
509 DECL_TABLE_U8( _m2_avx2, 64, AVX2)
510 DECL_TABLE_U16(_m1_avx2, 16, AVX2)
511 DECL_TABLE_U16(_m2_avx2, 32, AVX2)
512 DECL_TABLE_U32(_m2_avx2, 16, AVX2)
513 DECL_TABLE_F32(_m2_avx2, 16, AVX2)
514
515 static const SwsUOpTable *const tables[] = {
516 &uops_u8_m1_sse4,
517 &uops_u8_m1_avx2, /* order before _m2_sse4 */
518 &uops_u8_m2_sse4,
519 &uops_u8_m2_avx2,
520 &uops_u16_m1_avx2,
521 &uops_u16_m2_avx2,
522 &uops_u32_m2_avx2,
523 &uops_f32_m2_avx2,
524 };
525
526 SWS_DECL_FUNC(ff_sws_process1_x86);
527 SWS_DECL_FUNC(ff_sws_process2_x86);
528 SWS_DECL_FUNC(ff_sws_process3_x86);
529 SWS_DECL_FUNC(ff_sws_process4_x86);
530
531 /* Declare packed shuffle functions */
532 SWS_FOR_STRUCT(U8, RW_SHUFFLE, DECL_ENTRY, _sse4, NULL, NULL)
533 SWS_FOR_STRUCT(U8, RW_SHUFFLE, DECL_ENTRY, _avx2, NULL, NULL)
534 SWS_FOR_STRUCT(U8, RW_SHUFFLE, DECL_ENTRY, _avx512, NULL, NULL)
535 SWS_FOR_STRUCT(U8, RW_SHUFFLE, DECL_ENTRY, _avx512icl, NULL, NULL)
536
537 212612 static int get_mmsize(void)
538 {
539 212612 const int cpu_flags = av_get_cpu_flags();
540
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 212612 times.
212612 if (EXTERNAL_AVX512(cpu_flags))
541 ✗ return 64;
542
2/2
✓ Branch 0 taken 204136 times.
✓ Branch 1 taken 8476 times.
212612 else if (EXTERNAL_AVX2(cpu_flags))
543 204136 return 32;
544
2/2
✓ Branch 0 taken 3912 times.
✓ Branch 1 taken 4564 times.
8476 else if (EXTERNAL_SSE4(cpu_flags))
545 3912 return 16;
546 else
547 4564 return AVERROR(ENOTSUP);
548 }
549
550 5294 static int movsize(const int bytes, const int mmsize)
551 {
552
2/2
✓ Branch 0 taken 5057 times.
✓ Branch 1 taken 237 times.
10351 return bytes <= 4 ? 4 : /* movd */
553
2/2
✓ Branch 0 taken 4322 times.
✓ Branch 1 taken 735 times.
9379 bytes <= 8 ? 8 : /* movq */
554
2/2
✓ Branch 0 taken 3140 times.
✓ Branch 1 taken 1182 times.
7462 bytes <= 16 ? 16 : /* xmm movu */
555
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3140 times.
3140 bytes <= 32 ? 32 : /* ymm movu */
556 mmsize; /* zmm movu */
557 }
558
559 2647 static int translate_shuffle(const SwsUOp *uop, int mmsize, SwsCompiledOp *out)
560 {
561 /* We can't shuffle across lanes, so restrict the vector size to XMM
562 * whenever the read/write size would be a subset of the full vector,
563 * unless we have access to AVX-512 ICL vpermb */
564 2647 const SwsShuffleUOp *par = &uop->par.shuffle;
565
2/2
✓ Branch 0 taken 1587 times.
✓ Branch 1 taken 1060 times.
4234 const int lane_aligned = par->read_size == par->write_size &&
566
2/2
✓ Branch 0 taken 1570 times.
✓ Branch 1 taken 17 times.
1587 16 % par->read_size == 0;
567
3/4
✓ Branch 0 taken 1077 times.
✓ Branch 1 taken 1570 times.
✓ Branch 3 taken 1077 times.
✗ Branch 4 not taken.
2647 if (!lane_aligned && !EXTERNAL_AVX512ICL(av_get_cpu_flags()))
568 1077 mmsize = 16;
569
570 /* Generate the shuffle mask */
571
2/2
✓ Branch 0 taken 1077 times.
✓ Branch 1 taken 1570 times.
2647 const int mask_size = lane_aligned ? 16 : mmsize;
572 2647 int8_t *mask = av_malloc(mask_size);
573
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2647 times.
2647 if (!mask)
574 ✗ return AVERROR(ENOMEM);
575
576 2647 const int groups = ff_sws_shuffle_mask(uop, mask, mask_size);
577
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2647 times.
2647 if (groups < 0) {
578 ✗ av_free(mask);
579 ✗ return groups;
580 }
581
582 2647 const int read_chunk = groups * par->read_size;
583 2647 const int write_chunk = groups * par->write_size;
584
2/2
✓ Branch 0 taken 1570 times.
✓ Branch 1 taken 1077 times.
2647 const int num_lanes = lane_aligned ? mmsize / 16 : 1;
585 2647 const int in_total = num_lanes * read_chunk;
586 2647 const int out_total = num_lanes * write_chunk;
587 2647 *out = (SwsCompiledOp) {
588 .priv = mask,
589 .free = av_free,
590 .slice_align = 1,
591 2647 .block_size = groups * uop->data.shuffle.pixels * num_lanes,
592 2647 .over_read = { movsize(in_total, mmsize) - in_total },
593 2647 .over_write = { movsize(out_total, mmsize) - out_total },
594 };
595
596 #define ASSIGN_SHUFFLE_FUNC(CPU, EXT, NAME, ...) \
597 do { \
598 const SwsUOpEntry *entry = &uop_##NAME##EXT; \
599 if (!memcmp(&uop->par, &entry->par, sizeof(uop->par))) { \
600 out->func = (SwsOpFunc) entry->func; \
601 out->cpu_flags = AV_CPU_FLAG_##CPU; \
602 } \
603 } while (0);
604
605
2/4
✓ Branch 0 taken 1077 times.
✓ Branch 1 taken 1570 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
2647 switch (mmsize) {
606
42/44
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1073 times.
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 1057 times.
✓ Branch 4 taken 11 times.
✓ Branch 5 taken 1066 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 1075 times.
✓ Branch 8 taken 8 times.
✓ Branch 9 taken 1069 times.
✓ Branch 10 taken 54 times.
✓ Branch 11 taken 1023 times.
✓ Branch 12 taken 48 times.
✓ Branch 13 taken 1029 times.
✓ Branch 14 taken 2 times.
✓ Branch 15 taken 1075 times.
✓ Branch 16 taken 15 times.
✓ Branch 17 taken 1062 times.
✓ Branch 18 taken 11 times.
✓ Branch 19 taken 1066 times.
✓ Branch 20 taken 32 times.
✓ Branch 21 taken 1045 times.
✓ Branch 22 taken 2 times.
✓ Branch 23 taken 1075 times.
✓ Branch 24 taken 179 times.
✓ Branch 25 taken 898 times.
✓ Branch 26 taken 554 times.
✓ Branch 27 taken 523 times.
✓ Branch 28 taken 45 times.
✓ Branch 29 taken 1032 times.
✗ Branch 30 not taken.
✓ Branch 31 taken 1077 times.
✓ Branch 32 taken 4 times.
✓ Branch 33 taken 1073 times.
✓ Branch 34 taken 19 times.
✓ Branch 35 taken 1058 times.
✓ Branch 36 taken 10 times.
✓ Branch 37 taken 1067 times.
✓ Branch 38 taken 27 times.
✓ Branch 39 taken 1050 times.
✓ Branch 40 taken 30 times.
✓ Branch 41 taken 1047 times.
✗ Branch 42 not taken.
✓ Branch 43 taken 1077 times.
1077 case 16: SWS_FOR(U8, RW_SHUFFLE, ASSIGN_SHUFFLE_FUNC, SSE4, _sse4); break;
607
24/44
✗ Branch 0 not taken.
✓ Branch 1 taken 1570 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1570 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1570 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 1570 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 1570 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 1570 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 1570 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 1570 times.
✗ Branch 16 not taken.
✓ Branch 17 taken 1570 times.
✗ Branch 18 not taken.
✓ Branch 19 taken 1570 times.
✗ Branch 20 not taken.
✓ Branch 21 taken 1570 times.
✗ Branch 22 not taken.
✓ Branch 23 taken 1570 times.
✗ Branch 24 not taken.
✓ Branch 25 taken 1570 times.
✗ Branch 26 not taken.
✓ Branch 27 taken 1570 times.
✗ Branch 28 not taken.
✓ Branch 29 taken 1570 times.
✓ Branch 30 taken 1543 times.
✓ Branch 31 taken 27 times.
✗ Branch 32 not taken.
✓ Branch 33 taken 1570 times.
✗ Branch 34 not taken.
✓ Branch 35 taken 1570 times.
✗ Branch 36 not taken.
✓ Branch 37 taken 1570 times.
✗ Branch 38 not taken.
✓ Branch 39 taken 1570 times.
✗ Branch 40 not taken.
✓ Branch 41 taken 1570 times.
✓ Branch 42 taken 27 times.
✓ Branch 43 taken 1543 times.
1570 case 32: SWS_FOR(U8, RW_SHUFFLE, ASSIGN_SHUFFLE_FUNC, AVX2, _avx2); break;
608 ✗ case 64:
609 ✗ if (lane_aligned) {
610 ✗ SWS_FOR(U8, RW_SHUFFLE, ASSIGN_SHUFFLE_FUNC, AVX512, _avx512);
611 } else { /* vpermb variant */
612 ✗ SWS_FOR(U8, RW_SHUFFLE, ASSIGN_SHUFFLE_FUNC, AVX512ICL, _avx512icl);
613 }
614 ✗ break;
615 }
616
617
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2647 times.
2647 if (!out->func) {
618 ✗ av_free(mask);
619 ✗ return AVERROR(ENOTSUP);
620 }
621
622 2647 return 0;
623 }
624
625 /* Expand pixel value to 32-bits by repeating as necessary */
626 50996 static uint32_t expand32(const SwsPixelType type, const SwsPixel value)
627 {
628
3/5
✓ Branch 0 taken 15524 times.
✓ Branch 1 taken 29388 times.
✓ Branch 2 taken 6084 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
50996 switch (type) {
629 15524 case SWS_PIXEL_U8: return value.u8 * 0x01010101u;
630 29388 case SWS_PIXEL_U16: return value.u16 * 0x00010001u;
631 6084 case SWS_PIXEL_U32: return value.u32;
632 ✗ case SWS_PIXEL_F32: return value.u32; /* reinterpret */
633 ✗ default: return 0;
634 }
635 }
636
637 12749 static void normalize_clear(SwsUOp *uop)
638 {
639
2/2
✓ Branch 0 taken 50996 times.
✓ Branch 1 taken 12749 times.
63745 for (int i = 0; i < 4; i++)
640 50996 uop->data.vec4[i].u32 = expand32(uop->type, uop->data.vec4[i]);
641 12749 }
642
643 94735 static int compile_uops_x86(SwsContext *ctx, const SwsUOpList *uops, SwsCompiledOp *out)
644 {
645 94735 int ret, mmsize = get_mmsize();
646
2/2
✓ Branch 0 taken 4564 times.
✓ Branch 1 taken 90171 times.
94735 if (mmsize < 0)
647 4564 return mmsize;
648
649
3/4
✓ Branch 0 taken 2647 times.
✓ Branch 1 taken 87524 times.
✓ Branch 2 taken 2647 times.
✗ Branch 3 not taken.
90171 if (uops->num_ops == 1 && uops->ops[0].uop == SWS_UOP_RW_SHUFFLE) {
650 2647 const SwsUOp *uop = &uops->ops[0];
651 2647 ret = translate_shuffle(uop, mmsize, out);
652
1/2
✓ Branch 0 taken 2647 times.
✗ Branch 1 not taken.
2647 if (ret >= 0) {
653 char name[SWS_UOP_NAME_MAX];
654 2647 ff_sws_uop_name(uop, name);
655 2647 av_log(ctx, AV_LOG_VERBOSE, "Using x86 packed shuffle fast path: %s\n", name);
656 }
657 2647 return ret;
658 }
659
660 87524 SwsOpChain *chain = ff_sws_op_chain_alloc();
661
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 87524 times.
87524 if (!chain)
662 ✗ return AVERROR(ENOMEM);
663
664 87524 *out = (SwsCompiledOp) {
665 /* Use at most two full YMM regs during the widest precision section */
666 87524 .block_size = 2 * FFMIN(mmsize, 32) / uops->pixel_size_max,
667 .slice_align = 1,
668 .free = ff_sws_op_chain_free_cb,
669 .priv = chain,
670 };
671
672
2/2
✓ Branch 0 taken 437851 times.
✓ Branch 1 taken 75555 times.
513406 for (int i = 0; i < uops->num_ops; i++) {
673 437851 SwsUOp *uop = &uops->ops[i];
674 437851 int op_block_size = out->block_size;
675
676
2/2
✓ Branch 1 taken 103041 times.
✓ Branch 2 taken 334810 times.
437851 if (uop_is_type_invariant(uop->uop)) {
677
2/2
✓ Branch 0 taken 12749 times.
✓ Branch 1 taken 90292 times.
103041 if (uop->uop == SWS_UOP_CLEAR)
678 12749 normalize_clear(uop);
679 103041 op_block_size *= ff_sws_pixel_type_size(uop->type);
680 103041 uop->type = SWS_PIXEL_U8;
681 }
682
683 437851 ret = ff_sws_uop_lookup(ctx, tables, FF_ARRAY_ELEMS(tables), uop,
684 op_block_size, chain);
685
2/2
✓ Branch 0 taken 11969 times.
✓ Branch 1 taken 425882 times.
437851 if (ret < 0)
686 11969 goto fail;
687 }
688
689
4/5
✓ Branch 0 taken 13846 times.
✓ Branch 1 taken 3273 times.
✓ Branch 2 taken 48940 times.
✓ Branch 3 taken 9496 times.
✗ Branch 4 not taken.
75555 switch (av_popcount(uops->planes_in | uops->planes_out)) {
690 13846 case 1: out->func = ff_sws_process1_x86; break;
691 3273 case 2: out->func = ff_sws_process2_x86; break;
692 48940 case 3: out->func = ff_sws_process3_x86; break;
693 9496 case 4: out->func = ff_sws_process4_x86; break;
694 }
695
696
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 75555 times.
75555 if (ret < 0) {
697 ✗ ff_sws_op_chain_free(chain);
698 ✗ return ret;
699 }
700
701 75555 out->cpu_flags = chain->cpu_flags;
702 75555 memcpy(out->over_read, chain->over_read, sizeof(out->over_read));
703 75555 memcpy(out->over_write, chain->over_write, sizeof(out->over_write));
704
705 75555 av_log(ctx, AV_LOG_DEBUG, "Compiled micro-ops:\n");
706
2/2
✓ Branch 0 taken 402368 times.
✓ Branch 1 taken 75555 times.
477923 for (int i = 0; i < uops->num_ops; i++) {
707 char name[SWS_UOP_NAME_MAX];
708 402368 ff_sws_uop_name(&uops->ops[i], name);
709 402368 av_log(ctx, AV_LOG_DEBUG, " %s\n", name);
710 }
711
712 75555 return 0;
713
714 11969 fail:
715 11969 ff_sws_op_chain_free(chain);
716 11969 return ret;
717 }
718
719 117877 static int compile_x86(SwsContext *ctx, const SwsOpList *ops, SwsCompiledOp *out)
720 {
721 117877 const int cpu_flags = av_get_cpu_flags();
722 117877 const int mmsize = get_mmsize();
723
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 117877 times.
117877 if (mmsize < 0)
724 ✗ return mmsize;
725
726 117877 SwsUOpFlags flags = SWS_UOP_FLAG_PSHUFB
727 | SWS_UOP_FLAG_EXPAND_BIT
728 | SWS_UOP_FLAG_READ_PALETTE
729 | SWS_UOP_FLAG_ADD;
730
1/2
✓ Branch 0 taken 117877 times.
✗ Branch 1 not taken.
117877 if (EXTERNAL_FMA3(cpu_flags))
731 117877 flags |= SWS_UOP_FLAG_FMA;
732
733 117877 SwsUOpList *uops = ff_sws_uop_list_alloc();
734
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 117877 times.
117877 if (!uops)
735 ✗ return AVERROR(ENOMEM);
736
737 117877 int ret = ff_sws_ops_translate(ctx, ops, flags, uops);
738
2/2
✓ Branch 0 taken 32270 times.
✓ Branch 1 taken 85607 times.
117877 if (ret < 0)
739 32270 goto fail;
740
741 85607 ret = compile_uops_x86(ctx, uops, out);
742
743 117877 fail:
744 117877 ff_sws_uop_list_free(&uops);
745 117877 return ret;
746 }
747
748 const SwsOpBackend backend_x86 = {
749 .name = "x86",
750 .flags = SWS_BACKEND_X86,
751 .compile = compile_x86,
752 .compile_uops = compile_uops_x86,
753 .hw_format = AV_PIX_FMT_NONE,
754 };
755