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