This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit b488ee55538e0cbfca3fc188092815406ea42528 Author: Niklas Haas <[email protected]> AuthorDate: Tue Jun 2 14:39:34 2026 +0200 Commit: Niklas Haas <[email protected]> CommitDate: Thu Jun 11 16:27:47 2026 +0000 swscale/ops: generalize SwsReadWriteOp.packed to enum I want to start adding more data layouts, like semiplanar formats (nv12), or palette formats. I made an effort to distinguish existing checks for rw.packed into "mode != PLANAR" and "mode == PACKED", based on the intent of the surrounding code, in anticipation of these new layouts. Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas <[email protected]> --- libswscale/aarch64/ops_impl_conv.c | 12 ++++++++---- libswscale/format.c | 22 +++++++++++++--------- libswscale/ops.c | 24 ++++++++++++++++++++---- libswscale/ops.h | 16 +++++++++++++++- libswscale/ops_dispatch.c | 7 ++++++- libswscale/ops_optimizer.c | 8 ++++---- libswscale/uops.c | 4 ++-- libswscale/vulkan/ops.c | 16 ++++++++-------- tests/checkasm/sw_ops.c | 31 +++++++++++++++++++++++++++---- 9 files changed, 103 insertions(+), 37 deletions(-) diff --git a/libswscale/aarch64/ops_impl_conv.c b/libswscale/aarch64/ops_impl_conv.c index b360e86a8d..98cb89edbc 100644 --- a/libswscale/aarch64/ops_impl_conv.c +++ b/libswscale/aarch64/ops_impl_conv.c @@ -88,10 +88,12 @@ static int convert_to_aarch64_impl(SwsContext *ctx, const SwsOpList *ops, int n, out->op = AARCH64_SWS_OP_READ_NIBBLE; else if (op->rw.frac == 3) out->op = AARCH64_SWS_OP_READ_BIT; - else if (op->rw.packed && op->rw.elems != 1) + else if (op->rw.mode == SWS_RW_PACKED) out->op = AARCH64_SWS_OP_READ_PACKED; - else + else if (op->rw.mode == SWS_RW_PLANAR) out->op = AARCH64_SWS_OP_READ_PLANAR; + else + return AVERROR(ENOTSUP); break; case SWS_OP_WRITE: if (op->rw.filter.op) @@ -104,10 +106,12 @@ static int convert_to_aarch64_impl(SwsContext *ctx, const SwsOpList *ops, int n, out->op = AARCH64_SWS_OP_WRITE_NIBBLE; else if (op->rw.frac == 3) out->op = AARCH64_SWS_OP_WRITE_BIT; - else if (op->rw.packed && op->rw.elems != 1) + else if (op->rw.mode == SWS_RW_PACKED) out->op = AARCH64_SWS_OP_WRITE_PACKED; - else + else if (op->rw.mode == SWS_RW_PLANAR) out->op = AARCH64_SWS_OP_WRITE_PLANAR; + else + return AVERROR(ENOTSUP); break; case SWS_OP_SWAP_BYTES: out->op = AARCH64_SWS_OP_SWAP_BYTES; break; case SWS_OP_SWIZZLE: out->op = AARCH64_SWS_OP_SWIZZLE; break; diff --git a/libswscale/format.c b/libswscale/format.c index 16ab524ce0..d34e9d7be4 100644 --- a/libswscale/format.c +++ b/libswscale/format.c @@ -734,20 +734,20 @@ typedef struct FmtInfo { int shift; } FmtInfo; -#define BITSTREAM_FMT(SWIZ, FRAC, PACKED, ...) (FmtInfo) { \ - .rw = { .elems = 1, .frac = FRAC, .packed = PACKED }, \ +#define BITSTREAM_FMT(SWIZ, FRAC, MODE, ...) (FmtInfo) { \ + .rw = { .elems = 1, .frac = FRAC, .mode = MODE }, \ .swizzle = SWIZ, \ __VA_ARGS__ \ } #define SUBPACKED_FMT(SWIZ, ...) (FmtInfo) { \ - .rw = { .elems = 1, .packed = true }, \ + .rw = { .elems = 1, .mode = SWS_RW_PACKED }, \ .swizzle = SWIZ, \ .pack.pattern = {__VA_ARGS__}, \ } #define PACKED_FMT(SWIZ, N, ...) (FmtInfo) { \ - .rw = { .elems = N, .packed = (N) > 1 }, \ + .rw = { .elems = N, .mode = SWS_RW_PACKED }, \ .swizzle = SWIZ, \ __VA_ARGS__ \ } @@ -767,9 +767,9 @@ static FmtInfo fmt_info_irregular(enum AVPixelFormat fmt) /* Bitstream formats */ case AV_PIX_FMT_MONOWHITE: case AV_PIX_FMT_MONOBLACK: - return BITSTREAM_FMT(RGBA, 3, false); - case AV_PIX_FMT_RGB4: return BITSTREAM_FMT(RGBA, 1, true, .pack = {{ 1, 2, 1 }}); - case AV_PIX_FMT_BGR4: return BITSTREAM_FMT(BGRA, 1, true, .pack = {{ 1, 2, 1 }}); + return BITSTREAM_FMT(RGBA, 3, SWS_RW_PLANAR); + case AV_PIX_FMT_RGB4: return BITSTREAM_FMT(RGBA, 1, SWS_RW_PACKED, .pack = {{ 1, 2, 1 }}); + case AV_PIX_FMT_BGR4: return BITSTREAM_FMT(BGRA, 1, SWS_RW_PACKED, .pack = {{ 1, 2, 1 }}); /* Sub-packed 8-bit aligned formats */ case AV_PIX_FMT_RGB4_BYTE: return SUBPACKED_FMT(RGBA, 1, 2, 1); @@ -865,10 +865,14 @@ static int fmt_analyze_regular(const AVPixFmtDescriptor *desc, SwsReadWriteOp *r *swizzle = swiz; } + SwsReadWriteMode mode = SWS_RW_PLANAR; + if (desc->nb_components > 1 && !(desc->flags & AV_PIX_FMT_FLAG_PLANAR)) + mode = SWS_RW_PACKED; + *shift = (SwsShiftOp) { desc->comp[0].shift }; *rw_op = (SwsReadWriteOp) { - .elems = desc->nb_components, - .packed = desc->nb_components > 1 && !(desc->flags & AV_PIX_FMT_FLAG_PLANAR), + .elems = desc->nb_components, + .mode = mode, }; return 0; } diff --git a/libswscale/ops.c b/libswscale/ops.c index e4d79a2c60..4afeaabf54 100644 --- a/libswscale/ops.c +++ b/libswscale/ops.c @@ -169,7 +169,13 @@ SwsCompMask ff_sws_comp_mask_needed(const SwsOp *op) int ff_sws_rw_op_planes(const SwsOp *op) { av_assert2(op->op == SWS_OP_READ || op->op == SWS_OP_WRITE); - return op->rw.packed ? 1 : op->rw.elems; + switch (op->rw.mode) { + case SWS_RW_PLANAR: return op->rw.elems; + case SWS_RW_PACKED: return 1; + } + + av_unreachable("Invalid read/write mode!"); + return 0; } /* biased towards `a` */ @@ -376,7 +382,12 @@ void ff_sws_op_list_update_comps(SwsOpList *ops) /* Active components are taken from the user-provided values, * other components are explicitly stripped */ for (int i = 0; i < op->rw.elems; i++) { - const int idx = op->rw.packed ? i : ops->plane_src[i]; + int idx = 0; + switch (op->rw.mode) { + case SWS_RW_PACKED: idx = i; break; + case SWS_RW_PLANAR: idx = ops->plane_src[i]; break; + } + av_assert0(!(ops->comps_src.flags[idx] & SWS_COMP_GARBAGE)); op->comps.flags[i] = ops->comps_src.flags[idx]; op->comps.min[i] = ops->comps_src.min[idx]; @@ -731,7 +742,7 @@ bool ff_sws_op_list_is_noop(const SwsOpList *ops) const SwsOp *write = ff_sws_op_list_output(ops); if (!read || !write || ops->num_ops > 2 || read->type != write->type || - read->rw.packed != write->rw.packed || + read->rw.mode != write->rw.mode || read->rw.elems != write->rw.elems || read->rw.frac != write->rw.frac) return false; @@ -857,6 +868,11 @@ static void print_q4(AVBPrint *bp, const AVRational q4[4], SwsCompMask mask) av_bprintf(bp, "}"); } +static const char *const rw_mode_names[] = { + [SWS_RW_PLANAR] = "planar", + [SWS_RW_PACKED] = "packed", +}; + void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op) { const char *name = ff_sws_op_type_name(op->op); @@ -870,7 +886,7 @@ void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op) case SWS_OP_READ: case SWS_OP_WRITE: av_bprintf(bp, "%-20s: %d elem(s) %s >> %d", name, - op->rw.elems, op->rw.packed ? "packed" : "planar", + op->rw.elems, rw_mode_names[op->rw.mode], op->rw.frac); if (!op->rw.filter.op) break; diff --git a/libswscale/ops.h b/libswscale/ops.h index 3660f1ceec..65d9d49e60 100644 --- a/libswscale/ops.h +++ b/libswscale/ops.h @@ -84,6 +84,20 @@ typedef struct SwsComps { AVRational min[4], max[4]; } SwsComps; +typedef enum SwsReadWriteMode { + /** + * Note: 1-component reads are either SWS_RW_PLANAR or SWS_RW_PACKED, + * depending on the underlying interpretation. If multiple components are + * packed into one element (e.g. rgb10a2 -> u16), they are marked as + * SWS_RW_PACKED. Otherwise (e.g. gray16le), they are SWS_RW_PLANAR. + * + * This is a purely semantic/informative difference; the underlying code + * treats 1-components reads/writes the same regardless of mode. + */ + SWS_RW_PLANAR, /* one plane per component */ + SWS_RW_PACKED, /* all components on a single plane */ +} SwsReadWriteMode; + typedef struct SwsReadWriteOp { /** * Examples: @@ -93,9 +107,9 @@ typedef struct SwsReadWriteOp { * monow = 1x u8 (frac 3) * rgb4 = 1x u8 (frac 1) */ + SwsReadWriteMode mode; /* how data is laid out in memory */ uint8_t elems; /* number of elements (of type `op.type`) to read/write */ uint8_t frac; /* fractional pixel step factor (log2) */ - bool packed; /* read multiple elements from a single plane */ /** * Filter kernel to apply to each plane while sampling. Currently, only diff --git a/libswscale/ops_dispatch.c b/libswscale/ops_dispatch.c index fa3f2c75c3..44248195d7 100644 --- a/libswscale/ops_dispatch.c +++ b/libswscale/ops_dispatch.c @@ -464,7 +464,12 @@ static void op_pass_run(const SwsFrame *out, const SwsFrame *in, const int y, static int rw_pixel_bits(const SwsOp *op) { - const int elems = op->rw.packed ? op->rw.elems : 1; + int elems = 0; + switch (op->rw.mode) { + case SWS_RW_PLANAR: elems = 1; break; + case SWS_RW_PACKED: elems = op->rw.elems; break; + } + const int size = ff_sws_pixel_type_size(op->type); const int bits = 8 >> op->rw.frac; av_assert1(bits >= 1); diff --git a/libswscale/ops_optimizer.c b/libswscale/ops_optimizer.c index 4ee9fa3933..08fef00c63 100644 --- a/libswscale/ops_optimizer.c +++ b/libswscale/ops_optimizer.c @@ -405,7 +405,7 @@ retry: switch (op->op) { case SWS_OP_READ: /* "Compress" planar reads where not all components are needed */ - if (!op->rw.packed) { + if (op->rw.mode == SWS_RW_PLANAR) { SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3); int nb_planes = 0; for (int i = 0; i < op->rw.elems; i++) { @@ -529,7 +529,7 @@ retry: } /* Swizzle planes instead of components, if possible */ - if (prev->op == SWS_OP_READ && !prev->rw.packed) { + if (prev->op == SWS_OP_READ && prev->rw.mode == SWS_RW_PLANAR) { for (int dst = 0; dst < prev->rw.elems; dst++) { const int src = op->swizzle.in[dst]; if (src > dst && src < prev->rw.elems) { @@ -545,7 +545,7 @@ retry: } } - if (next->op == SWS_OP_WRITE && !next->rw.packed) { + if (next->op == SWS_OP_WRITE && next->rw.mode == SWS_RW_PLANAR) { for (int dst = 0; dst < next->rw.elems; dst++) { const int src = op->swizzle.in[dst]; if (src > dst && src < next->rw.elems) { @@ -748,7 +748,7 @@ retry: case SWS_OP_FILTER_V: /* Merge with prior simple planar read */ if (prev->op == SWS_OP_READ && !prev->rw.filter.op && - !prev->rw.packed && !prev->rw.frac) { + prev->rw.mode == SWS_RW_PLANAR && !prev->rw.frac) { prev->rw.filter.op = op->op; prev->rw.filter.kernel = av_refstruct_ref(op->filter.kernel); prev->rw.filter.type = op->filter.type; diff --git a/libswscale/uops.c b/libswscale/uops.c index b2d11d996c..7f779a504c 100644 --- a/libswscale/uops.c +++ b/libswscale/uops.c @@ -480,7 +480,7 @@ static int translate_rw_op(SwsContext *ctx, SwsUOpList *ops, SwsUOpFlags flags, const bool is_read = op->op == SWS_OP_READ; if (op->rw.filter.op) { - if (op->op == SWS_OP_WRITE || op->rw.frac || op->rw.packed) + if (op->op == SWS_OP_WRITE || op->rw.frac || op->rw.mode != SWS_RW_PLANAR) return AVERROR(ENOTSUP); uop.par.filter.type = op->rw.filter.type; uop.data.kernel = av_refstruct_ref(op->rw.filter.kernel); @@ -491,7 +491,7 @@ static int translate_rw_op(SwsContext *ctx, SwsUOpList *ops, SwsUOpFlags flags, } else { uop.uop = SWS_UOP_READ_PLANAR_FV; } - } else if (op->rw.packed && op->rw.elems > 1) { + } else if (op->rw.mode == SWS_RW_PACKED && op->rw.elems > 1) { if (op->rw.frac) return AVERROR(ENOTSUP); uop.uop = is_read ? SWS_UOP_READ_PACKED : SWS_UOP_WRITE_PACKED; diff --git a/libswscale/vulkan/ops.c b/libswscale/vulkan/ops.c index cfafa1c27c..4f5b4160af 100644 --- a/libswscale/vulkan/ops.c +++ b/libswscale/vulkan/ops.c @@ -852,7 +852,7 @@ static int read_filtered(SPICtx *spi, SPIRVIDs *id, const SwsOpList *ops, /* Accumulators, initialized to zero */ int acc_s[4] = { id->f32_0, id->f32_0, id->f32_0, id->f32_0 }; int acc_v = id->f32_0; - if (op->rw.packed) + if (op->rw.mode == SWS_RW_PACKED) acc_v = spi_OpCompositeConstruct(spi, id->f32vec4_type, id->f32_0, id->f32_0, id->f32_0, id->f32_0); @@ -877,7 +877,7 @@ static int read_filtered(SPICtx *spi, SPIRVIDs *id, const SwsOpList *ops, spi_OpCompositeConstruct(spi, id->i32vec2_type, c, pos_y) : spi_OpCompositeConstruct(spi, id->i32vec2_type, pos_x, c); - if (op->rw.packed) { + if (op->rw.mode == SWS_RW_PACKED) { int px = spi_OpImageRead(spi, read_vtype, in_img[ops->plane_src[0]], coord, SpvImageOperandsMaskNone); @@ -902,7 +902,7 @@ static int read_filtered(SPICtx *spi, SPIRVIDs *id, const SwsOpList *ops, } } - if (op->rw.packed) + if (op->rw.mode == SWS_RW_PACKED) return acc_v; return spi_OpCompositeConstruct(spi, id->f32vec4_type, acc_s[0], acc_s[1], acc_s[2], acc_s[3]); @@ -1136,7 +1136,7 @@ static int add_ops_spirv(SwsContext *sws, VulkanPriv *p, FFVulkanOpsCtx *s, data = read_filtered(spi, id, ops, op, &id->filt[nb_filter_used++], in_img, gid, gi2); - } else if (op->rw.packed) { + } else if (op->rw.mode == SWS_RW_PACKED) { data = spi_OpImageRead(spi, type_v, in_img[ops->plane_src[0]], src_gid, SpvImageOperandsMaskNone); } else { @@ -1154,7 +1154,7 @@ static int add_ops_spirv(SwsContext *sws, VulkanPriv *p, FFVulkanOpsCtx *s, case SWS_OP_WRITE: if (op->rw.frac || op->rw.filter.op) { return AVERROR(ENOTSUP); - } else if (op->rw.packed) { + } else if (op->rw.mode == SWS_RW_PACKED) { spi_OpImageWrite(spi, out_img[ops->plane_dst[0]], dst_gid, data, SpvImageOperandsMaskNone); } else { @@ -1332,7 +1332,7 @@ static void read_glsl(const SwsOpList *ops, const SwsOp *op, FFVulkanShader *shd wd->filter_size); av_bprintf(&shd->src, " float w = filter_w%i[%s][i];\n", idx, axis); - if (op->rw.packed) { + if (op->rw.mode == SWS_RW_PACKED) { GLSLF(2, tmp += w * %s(imageLoad(src_img[%i], ivec2(%s, %s))); , type_v, ops->plane_src[0], coord_x, coord_y); } else { @@ -1345,7 +1345,7 @@ static void read_glsl(const SwsOpList *ops, const SwsOp *op, FFVulkanShader *shd GLSLC(1, f32 = tmp; ); } else { const char *src_pos = interlaced ? "spos" : "pos"; - if (op->rw.packed) { + if (op->rw.mode == SWS_RW_PACKED) { GLSLF(1, %s = %s(imageLoad(src_img[%i], %s)); , type_name, type_v, ops->plane_src[0], src_pos); } else { @@ -1488,7 +1488,7 @@ static int add_ops_glsl(SwsContext *sws, VulkanPriv *p, FFVulkanOpsCtx *s, const char *dst_pos = ops->dst.interlaced ? "dpos" : "pos"; if (op->rw.frac || op->rw.filter.op) { return AVERROR(ENOTSUP); - } else if (op->rw.packed) { + } else if (op->rw.mode == SWS_RW_PACKED) { GLSLF(1, imageStore(dst_img[%i], %s, %s(%s)); , ops->plane_dst[0], dst_pos, type_v, type_name); } else { diff --git a/tests/checkasm/sw_ops.c b/tests/checkasm/sw_ops.c index fdc17bffa8..99140ced52 100644 --- a/tests/checkasm/sw_ops.c +++ b/tests/checkasm/sw_ops.c @@ -56,7 +56,12 @@ static const char *tprintf(char buf[], size_t size, const char *fmt, ...) static int rw_pixel_bits(const SwsOp *op) { - const int elems = op->rw.packed ? op->rw.elems : 1; + int elems = 0; + switch (op->rw.mode) { + case SWS_RW_PLANAR: elems = 1; break; + case SWS_RW_PACKED: elems = op->rw.elems; break; + } + const int size = ff_sws_pixel_type_size(op->type); const int bits = 8 >> op->rw.frac; av_assert1(bits >= 1); @@ -233,7 +238,7 @@ static void check_compiled(const char *name, break; } - if (write_op->rw.packed) + if (write_op->rw.mode == SWS_RW_PACKED) break; } @@ -394,13 +399,22 @@ static AVRational rndq(SwsPixelType t) static void check_read(const char *name, const SwsUOp *uop) { + SwsReadWriteMode mode; + switch (uop->uop) { + case SWS_UOP_READ_PACKED: + case SWS_UOP_READ_BIT: + case SWS_UOP_READ_NIBBLE: mode = SWS_RW_PACKED; break; + case SWS_UOP_READ_PLANAR: mode = SWS_RW_PLANAR; break; + default: return; + } + const int num = mask_num(uop->mask); check_ops(name, NULL, (SwsOp[]) { { .op = SWS_OP_READ, .type = uop->type, .rw.elems = num, - .rw.packed = uop->uop != SWS_UOP_READ_PLANAR, + .rw.mode = mode, .rw.frac = uop->uop == SWS_UOP_READ_BIT ? 3 : uop->uop == SWS_UOP_READ_NIBBLE ? 1 : 0, }, { @@ -413,6 +427,15 @@ static void check_read(const char *name, const SwsUOp *uop) static void check_write(const char *name, const SwsUOp *uop) { + SwsReadWriteMode mode; + switch (uop->uop) { + case SWS_UOP_WRITE_BIT: + case SWS_UOP_WRITE_NIBBLE: + case SWS_UOP_READ_PACKED: mode = SWS_RW_PACKED; break; + case SWS_UOP_READ_PLANAR: mode = SWS_RW_PLANAR; break; + default: return; + } + const int frac = uop->uop == SWS_UOP_WRITE_BIT ? 3 : uop->uop == SWS_UOP_WRITE_NIBBLE ? 1 : 0; const int num = mask_num(uop->mask); @@ -428,7 +451,7 @@ static void check_write(const char *name, const SwsUOp *uop) .op = SWS_OP_WRITE, .type = uop->type, .rw.elems = num, - .rw.packed = uop->uop != SWS_UOP_WRITE_PLANAR, + .rw.mode = mode, .rw.frac = frac, }, {0} }); _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
