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]

Reply via email to