| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * Copyright (C) 2025 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 | #ifndef SWSCALE_UOPS_H | ||
| 22 | #define SWSCALE_UOPS_H | ||
| 23 | |||
| 24 | #include <assert.h> | ||
| 25 | #include <stdbool.h> | ||
| 26 | #include <stdint.h> | ||
| 27 | |||
| 28 | /*************************************************************************** | ||
| 29 | * Note: This header must be usable at build time, to generate asm sources * | ||
| 30 | ***************************************************************************/ | ||
| 31 | |||
| 32 | #include "libavutil/attributes.h" | ||
| 33 | #include "rational64.h" | ||
| 34 | |||
| 35 | typedef struct SwsContext SwsContext; | ||
| 36 | typedef struct SwsFilterWeights SwsFilterWeights; | ||
| 37 | typedef struct SwsLut3D SwsLut3D; | ||
| 38 | typedef struct SwsOpList SwsOpList; | ||
| 39 | |||
| 40 | typedef enum SwsPixelType { | ||
| 41 | SWS_PIXEL_NONE = 0, | ||
| 42 | SWS_PIXEL_U8, | ||
| 43 | SWS_PIXEL_U16, | ||
| 44 | SWS_PIXEL_U32, | ||
| 45 | SWS_PIXEL_F32, | ||
| 46 | SWS_PIXEL_TYPE_NB | ||
| 47 | } SwsPixelType; | ||
| 48 | |||
| 49 | const char *ff_sws_pixel_type_name(SwsPixelType type); | ||
| 50 | |||
| 51 | 23551762 | static inline av_const int ff_sws_pixel_type_size(SwsPixelType type) | |
| 52 | { | ||
| 53 |
4/7✓ Branch 0 taken 3849715 times.
✓ Branch 1 taken 9933387 times.
✓ Branch 2 taken 1590733 times.
✓ Branch 3 taken 8177927 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
23551762 | switch (type) { |
| 54 | 3849715 | case SWS_PIXEL_U8: return sizeof(uint8_t); | |
| 55 | 9933387 | case SWS_PIXEL_U16: return sizeof(uint16_t); | |
| 56 | 1590733 | case SWS_PIXEL_U32: return sizeof(uint32_t); | |
| 57 | 8177927 | case SWS_PIXEL_F32: return sizeof(float); | |
| 58 | ✗ | case SWS_PIXEL_NONE: break; | |
| 59 | ✗ | case SWS_PIXEL_TYPE_NB: break; | |
| 60 | } | ||
| 61 | ✗ | return 0; | |
| 62 | } | ||
| 63 | |||
| 64 | 141691531 | static inline av_const bool ff_sws_pixel_type_is_int(SwsPixelType type) | |
| 65 | { | ||
| 66 |
2/4✓ Branch 0 taken 100073943 times.
✓ Branch 1 taken 41617588 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
141691531 | switch (type) { |
| 67 | 100073943 | case SWS_PIXEL_U8: | |
| 68 | case SWS_PIXEL_U16: | ||
| 69 | case SWS_PIXEL_U32: | ||
| 70 | 100073943 | return true; | |
| 71 | 41617588 | case SWS_PIXEL_F32: | |
| 72 | 41617588 | return false; | |
| 73 | ✗ | case SWS_PIXEL_NONE: | |
| 74 | ✗ | case SWS_PIXEL_TYPE_NB: break; | |
| 75 | } | ||
| 76 | ✗ | return false; | |
| 77 | } | ||
| 78 | |||
| 79 | typedef union SwsPixel { | ||
| 80 | char data[4]; | ||
| 81 | |||
| 82 | uint8_t u8; | ||
| 83 | uint16_t u16; | ||
| 84 | uint32_t u32; | ||
| 85 | float f32; | ||
| 86 | } SwsPixel; | ||
| 87 | |||
| 88 | /* Ensures (SwsPixel) {0} is properly initialized to all zeros */ | ||
| 89 | static_assert(sizeof(SwsPixel) == sizeof(char[4]), "SwsPixel size mismatch"); | ||
| 90 | |||
| 91 | 156521686 | static inline SwsPixel ff_sws_pixel_from_q64(SwsPixelType type, AVRational64 val) | |
| 92 | { | ||
| 93 |
4/6✓ Branch 0 taken 105360 times.
✓ Branch 1 taken 220351 times.
✓ Branch 2 taken 42126 times.
✓ Branch 3 taken 156153849 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
156521686 | switch (type) { |
| 94 | 156521686 | case SWS_PIXEL_U8: return (SwsPixel) { .u8 = val.num / val.den }; | |
| 95 | 220351 | case SWS_PIXEL_U16: return (SwsPixel) { .u16 = val.num / val.den }; | |
| 96 | 42126 | case SWS_PIXEL_U32: return (SwsPixel) { .u32 = val.num / val.den }; | |
| 97 | 156153849 | case SWS_PIXEL_F32: return (SwsPixel) { .f32 = (float) val.num / val.den }; | |
| 98 | ✗ | case SWS_PIXEL_NONE: | |
| 99 | ✗ | case SWS_PIXEL_TYPE_NB: break; | |
| 100 | } | ||
| 101 | ✗ | return (SwsPixel) {0}; | |
| 102 | } | ||
| 103 | |||
| 104 | 322552 | static inline bool ff_sws_pixel_is_1s(SwsPixelType type, SwsPixel val) | |
| 105 | { | ||
| 106 |
3/4✓ Branch 0 taken 51886 times.
✓ Branch 1 taken 139250 times.
✓ Branch 2 taken 131416 times.
✗ Branch 3 not taken.
|
322552 | switch (ff_sws_pixel_type_size(type)) { |
| 107 | 51886 | case 1: return val.u8 == UINT8_MAX; | |
| 108 | 139250 | case 2: return val.u16 == UINT16_MAX; | |
| 109 | 131416 | case 4: return val.u32 == UINT32_MAX; | |
| 110 | ✗ | default: break; | |
| 111 | } | ||
| 112 | ✗ | return false; | |
| 113 | } | ||
| 114 | |||
| 115 | /** | ||
| 116 | * Bit-mask of components. Exact meaning depends on the usage context. | ||
| 117 | */ | ||
| 118 | typedef uint8_t SwsCompMask; | ||
| 119 | enum { | ||
| 120 | SWS_COMP_NONE = 0, | ||
| 121 | SWS_COMP_ALL = 0xF, | ||
| 122 | #define SWS_COMP(X) (1 << (X)) | ||
| 123 | #define SWS_COMP_TEST(mask, X) (!!((mask) & SWS_COMP(X))) | ||
| 124 | #define SWS_COMP_INV(mask) ((mask) ^ SWS_COMP_ALL) | ||
| 125 | #define SWS_COMP_ELEMS(N) ((1 << (N)) - 1) | ||
| 126 | #define SWS_COMP_COUNT(mask) (av_popcount((mask) & SWS_COMP_ALL)) | ||
| 127 | #define SWS_COMP_MASK(X, Y, Z, W) \ | ||
| 128 | (((X) ? SWS_COMP(0) : 0) | \ | ||
| 129 | ((Y) ? SWS_COMP(1) : 0) | \ | ||
| 130 | ((Z) ? SWS_COMP(2) : 0) | \ | ||
| 131 | ((W) ? SWS_COMP(3) : 0)) | ||
| 132 | }; | ||
| 133 | |||
| 134 | |||
| 135 | #define ff_sws_comp_mask_str(mask) ff_sws_comp_mask_print(mask, (char[5]){0}) | ||
| 136 | 37141679 | static inline char *ff_sws_comp_mask_print(SwsCompMask mask, char buf[5]) | |
| 137 | { | ||
| 138 | 37141679 | char *ptr = buf; | |
| 139 |
2/2✓ Branch 0 taken 148566716 times.
✓ Branch 1 taken 37141679 times.
|
185708395 | for (int c = 0; c < 4; c++) { |
| 140 |
2/2✓ Branch 0 taken 53491665 times.
✓ Branch 1 taken 95075051 times.
|
148566716 | if (SWS_COMP_TEST(mask, c)) |
| 141 | 53491665 | *ptr++ = "xyzw"[c]; | |
| 142 | } | ||
| 143 | 37141679 | *ptr = '\0'; | |
| 144 | 37141679 | return buf; | |
| 145 | } | ||
| 146 | |||
| 147 | typedef uint32_t SwsUOpFlags; | ||
| 148 | typedef enum SwsUOpFlagBits { | ||
| 149 | SWS_UOP_FLAG_NONE = 0, | ||
| 150 | SWS_UOP_FLAG_FMA = (1 << 0), /* platform supports FMA ops */ | ||
| 151 | SWS_UOP_FLAG_PSHUFB = (1 << 1), /* platform supports pshufb equivalent */ | ||
| 152 | SWS_UOP_FLAG_EXPAND_BIT = (1 << 2), /* backend implements SWS_UOP_EXPAND_BIT */ | ||
| 153 | SWS_UOP_FLAG_READ_PALETTE = (1 << 3), /* backend implements SWS_UOP_READ_PALETTE */ | ||
| 154 | SWS_UOP_FLAG_ADD = (1 << 4), /* backend implements SWS_UOP_ADD */ | ||
| 155 | } SwsUOpFlagBits; | ||
| 156 | |||
| 157 | typedef enum SwsUOpType { | ||
| 158 | SWS_UOP_INVALID = 0, | ||
| 159 | |||
| 160 | /* Read/write uops; mask = components to read/write */ | ||
| 161 | SWS_UOP_READ_PLANAR, /* simple planar byte-aligned read */ | ||
| 162 | SWS_UOP_READ_PLANAR_FH, /* planar read with horizontal filter */ | ||
| 163 | SWS_UOP_READ_PLANAR_FV, /* planar read with vertical filter */ | ||
| 164 | SWS_UOP_READ_PLANAR_FV_FMA, | ||
| 165 | SWS_UOP_READ_PACKED, /* simple packed byte-aligned read */ | ||
| 166 | SWS_UOP_READ_NIBBLE, /* fractional read (4 bits) from single plane */ | ||
| 167 | SWS_UOP_READ_BIT, /* fractional read (1 bit) from single plane */ | ||
| 168 | SWS_UOP_READ_PALETTE, /* indexed read from palette in plane 1 */ | ||
| 169 | |||
| 170 | SWS_UOP_WRITE_PLANAR, /* simple planar byte-aligned write */ | ||
| 171 | SWS_UOP_WRITE_PACKED, /* simple packed byte-aligned write */ | ||
| 172 | SWS_UOP_WRITE_NIBBLE, /* fractional write (4 bits) to single plane */ | ||
| 173 | SWS_UOP_WRITE_BIT, /* fractional write (1 bit) to single plane */ | ||
| 174 | |||
| 175 | /* Packed shuffle / gather uops */ | ||
| 176 | SWS_UOP_RW_SHUFFLE, /* in-place (packed) indexed shuffle/gather */ | ||
| 177 | |||
| 178 | /* Data rearrangement uops; mask = needed or trivial components */ | ||
| 179 | SWS_UOP_PERMUTE, /* permute pointers (no duplicates) */ | ||
| 180 | SWS_UOP_COPY, /* permute data (may contain duplicates) */ | ||
| 181 | |||
| 182 | /* Data conversion / manipulation uops; mask = affected components */ | ||
| 183 | SWS_UOP_SWAP_BYTES, /* swap byte order in components */ | ||
| 184 | SWS_UOP_EXPAND_BIT, /* expand low-order bit to all bits in type */ | ||
| 185 | SWS_UOP_EXPAND_PAIR, /* expand bytes in pairs (16 bit) */ | ||
| 186 | SWS_UOP_EXPAND_QUAD, /* expand bytes in quads (32 bit) */ | ||
| 187 | SWS_UOP_TO_U8, /* cast pixel values to SWS_PIXEL_U8 */ | ||
| 188 | SWS_UOP_TO_U16, /* cast pixel values to SWS_PIXEL_U16 */ | ||
| 189 | SWS_UOP_TO_U32, /* cast pixel values to SWS_PIXEL_U32 */ | ||
| 190 | SWS_UOP_TO_F32, /* cast pixel values to SWS_PIXEL_F32 */ | ||
| 191 | |||
| 192 | /* Arithmetic uops */ | ||
| 193 | SWS_UOP_SCALE, /* multiply masked components by scalar */ | ||
| 194 | SWS_UOP_ADD, /* add vec4 to masked components */ | ||
| 195 | SWS_UOP_MIN, /* min(x, vec4) on masked components */ | ||
| 196 | SWS_UOP_MAX, /* max(x, vec4) on masked components */ | ||
| 197 | |||
| 198 | /* Identical to corresponding SwsOpType */ | ||
| 199 | SWS_UOP_UNPACK, /* mask = nonzero components in pack pattern */ | ||
| 200 | SWS_UOP_PACK, /* mask = nonzero components in pack pattern */ | ||
| 201 | SWS_UOP_LSHIFT, /* mask = components to shift */ | ||
| 202 | SWS_UOP_RSHIFT, /* mask = components to shift */ | ||
| 203 | SWS_UOP_CLEAR, /* mask = components to clear */ | ||
| 204 | SWS_UOP_LINEAR, /* mask = non-trivial output rows */ | ||
| 205 | SWS_UOP_LINEAR_FMA, /* with SWS_UOP_FLAG_FMA */ | ||
| 206 | SWS_UOP_DITHER, /* mask = components to dither */ | ||
| 207 | SWS_UOP_LUT_3D, /* mask = needed output components */ | ||
| 208 | |||
| 209 | /* Platform-specific uops would go here */ | ||
| 210 | SWS_UOP_TYPE_NB, | ||
| 211 | } SwsUOpType; | ||
| 212 | |||
| 213 | typedef struct SwsShuffleUOp { | ||
| 214 | uint8_t clear_value; /* value to clear elements with negative indices to */ | ||
| 215 | uint8_t read_size; /* input bytes per iteration */ | ||
| 216 | uint8_t write_size; /* output bytes per iteration */ | ||
| 217 | } SwsShuffleUOp; | ||
| 218 | |||
| 219 | typedef struct SwsShuffleMask { | ||
| 220 | int8_t mask[16]; /* shuffle index mask, or -1 to clear bytes (to `clear_value`) */ | ||
| 221 | uint8_t pixels; /* number of pixels per iteration */ | ||
| 222 | } SwsShuffleMask; | ||
| 223 | |||
| 224 | typedef struct SwsFilterUOp { | ||
| 225 | SwsPixelType type; /* pixel type to store result as */ | ||
| 226 | } SwsFilterUOp; | ||
| 227 | |||
| 228 | typedef struct SwsShiftUOp { | ||
| 229 | uint8_t amount; | ||
| 230 | } SwsShiftUOp; | ||
| 231 | |||
| 232 | typedef struct SwsMoveUOp { | ||
| 233 | /* The worst case number of moves (for two independent cycles) */ | ||
| 234 | #define SWS_UOP_MOVE_MAX 6 | ||
| 235 | int num_moves; | ||
| 236 | |||
| 237 | /* This may involve a temporary register (index -1) */ | ||
| 238 | int8_t dst[SWS_UOP_MOVE_MAX]; /* destination register index */ | ||
| 239 | int8_t src[SWS_UOP_MOVE_MAX]; /* source register index */ | ||
| 240 | } SwsMoveUOp; | ||
| 241 | |||
| 242 | typedef struct SwsPackUOp { | ||
| 243 | uint8_t pattern[4]; /* bit depth pattern, from MSB to LSB */ | ||
| 244 | } SwsPackUOp; | ||
| 245 | |||
| 246 | typedef struct SwsClearUOp { | ||
| 247 | SwsCompMask one; /* mask of coefficients equal to all 1s */ | ||
| 248 | SwsCompMask zero; /* mask of coefficients equal to all 0s */ | ||
| 249 | } SwsClearUOp; | ||
| 250 | |||
| 251 | typedef struct SwsLinearUOp { | ||
| 252 | uint32_t one; /* mask of coefficients equal to one */ | ||
| 253 | uint32_t zero; /* mask of coefficients equal to zero */ | ||
| 254 | |||
| 255 | /* for SWS_UOP_LINEAR_FMA only */ | ||
| 256 | uint32_t exact; /* mask of coefficients whose product is exact */ | ||
| 257 | } SwsLinearUOp; | ||
| 258 | |||
| 259 | #define SWS_MASK(I, J) (1 << (5 * (I) + (J))) | ||
| 260 | #define SWS_MASK_OFF(I) SWS_MASK(I, 4) | ||
| 261 | #define SWS_MASK_ROW(I) (0x1F << (5 * (I))) | ||
| 262 | #define SWS_MASK_COL(J) (0x8421 << J) | ||
| 263 | #define SWS_MASK_DIAG4 (0x41041) | ||
| 264 | |||
| 265 | typedef struct SwsDitherUOp { | ||
| 266 | uint8_t y_offset[4]; | ||
| 267 | uint8_t size_log2; | ||
| 268 | } SwsDitherUOp; | ||
| 269 | |||
| 270 | typedef struct SwsLut3DUOp { | ||
| 271 | int dynamic; | ||
| 272 | } SwsLut3DUOp; | ||
| 273 | |||
| 274 | /** | ||
| 275 | * Computes (1 << size_log2) + MAX(y_offset). The dither matrix attached to | ||
| 276 | * the SwsUOp is always pre-padded to this number of lines. | ||
| 277 | */ | ||
| 278 | int ff_sws_dither_height(const SwsDitherUOp *dither); | ||
| 279 | |||
| 280 | typedef union SwsUOpParams { | ||
| 281 | SwsShuffleUOp shuffle; /* for SWS_UOP_RW_SHUFFLE */ | ||
| 282 | SwsFilterUOp filter; /* for SWS_UOP_READ_*_FV/FH */ | ||
| 283 | SwsShiftUOp shift; | ||
| 284 | SwsMoveUOp move; /* for SWS_UOP_PERMUTE and SWS_UOP_COPY */ | ||
| 285 | SwsPackUOp pack; | ||
| 286 | SwsClearUOp clear; | ||
| 287 | SwsLinearUOp lin; | ||
| 288 | SwsDitherUOp dither; | ||
| 289 | SwsLut3DUOp lut3d; | ||
| 290 | } SwsUOpParams; | ||
| 291 | |||
| 292 | typedef struct SwsUOp { | ||
| 293 | /* These fields uniquely identify the uop implementation */ | ||
| 294 | SwsPixelType type; | ||
| 295 | SwsUOpType uop; | ||
| 296 | SwsCompMask mask; | ||
| 297 | SwsUOpParams par; | ||
| 298 | |||
| 299 | /* Constant data for this uop; not part of the unique identifier */ | ||
| 300 | union { | ||
| 301 | SwsFilterWeights *kernel; /* refstruct */ | ||
| 302 | SwsPixel *ptr; /* refstruct */ | ||
| 303 | SwsPixel scalar; | ||
| 304 | SwsPixel vec4[4]; | ||
| 305 | SwsPixel mat4x5[4][5]; /* row major */ | ||
| 306 | SwsShuffleMask shuffle; /* for SWS_UOP_RW_SHUFFLE */ | ||
| 307 | const SwsLut3D *lut3d; /* for SWS_UOP_LUT_3D; refstruct */ | ||
| 308 | void *opaque; /* reserved for internal use */ | ||
| 309 | } data; | ||
| 310 | } SwsUOp; | ||
| 311 | |||
| 312 | /** | ||
| 313 | * Compare two SwsUOps for equality (excluding constant data). | ||
| 314 | */ | ||
| 315 | int ff_sws_uop_cmp(const SwsUOp *a, const SwsUOp *b); | ||
| 316 | |||
| 317 | 58128857 | static inline int ff_sws_uop_cmp_v(const void *a, const void *b) | |
| 318 | { | ||
| 319 | 58128857 | return ff_sws_uop_cmp(a, b); | |
| 320 | } | ||
| 321 | |||
| 322 | /** | ||
| 323 | * Generate a unique name for a SwsUOp. | ||
| 324 | */ | ||
| 325 | #define SWS_UOP_NAME_MAX 64 | ||
| 326 | void ff_sws_uop_name(const SwsUOp *op, char buf[SWS_UOP_NAME_MAX]); | ||
| 327 | |||
| 328 | typedef struct SwsUOpList { | ||
| 329 | SwsUOp *ops; | ||
| 330 | int num_ops; | ||
| 331 | |||
| 332 | /* Additional metadata for implementations */ | ||
| 333 | SwsCompMask planes_in; /* mask of planes read from */ | ||
| 334 | SwsCompMask planes_out; /* mask of planes written to */ | ||
| 335 | int pixel_size_max; /* size of largest pixel type seen in any uop */ | ||
| 336 | } SwsUOpList; | ||
| 337 | |||
| 338 | SwsUOpList *ff_sws_uop_list_alloc(void); | ||
| 339 | void ff_sws_uop_list_free(SwsUOpList **ops); | ||
| 340 | void ff_sws_uop_list_remove_at(SwsUOpList *uops, int index, int count); | ||
| 341 | |||
| 342 | /* Takes over ownership of `uop` and sets it to {0}, even on failure. */ | ||
| 343 | int ff_sws_uop_list_append(SwsUOpList *uops, SwsUOp *uop); | ||
| 344 | |||
| 345 | /** | ||
| 346 | * Called internally by ff_sws_ops_translate(). | ||
| 347 | */ | ||
| 348 | int ff_sws_uop_list_optimize(SwsContext *ctx, SwsUOpFlags flags, SwsUOpList *uops); | ||
| 349 | |||
| 350 | /** | ||
| 351 | * Translate a list of operations down to micro-ops, which can be further | ||
| 352 | * optimized and then directly executed by backends. | ||
| 353 | * | ||
| 354 | * Return 0 or a negative error code. | ||
| 355 | */ | ||
| 356 | int ff_sws_ops_translate(SwsContext *ctx, const SwsOpList *ops, | ||
| 357 | SwsUOpFlags flags, SwsUOpList *uops); | ||
| 358 | |||
| 359 | /** | ||
| 360 | * Compute a shuffle mask for `pshufb`-style ASM functions, by repeating | ||
| 361 | * the shuffle pattern for as many groups as will fit. | ||
| 362 | * | ||
| 363 | * @param uop An operation of type SWS_UOP_RW_SHUFFLE. | ||
| 364 | * @param shuffle The output shuffle index mask (or -1 to clear bytes). | ||
| 365 | * @param size The maximum size (in bytes) of the output shuffle mask. | ||
| 366 | * | ||
| 367 | * @return the number of groups on success, or a negative error code. | ||
| 368 | * | ||
| 369 | * @note The shuffle mask is already pre-expanded to fill up to 16 bytes, | ||
| 370 | * so this is only needed for larger shuffle instructions (e.g. vpermb). | ||
| 371 | */ | ||
| 372 | int ff_sws_shuffle_mask(const SwsUOp *uop, int8_t shuffle[], int size); | ||
| 373 | |||
| 374 | #endif | ||
| 375 |