1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2026-06-19 19:03:00 +02:00

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 <git@haasn.dev>
This commit is contained in:
Niklas Haas
2026-06-02 14:39:34 +02:00
committed by Niklas Haas
parent 01c007ec4d
commit b488ee5553
9 changed files with 103 additions and 37 deletions
+8 -4
View File
@@ -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;
+13 -9
View File
@@ -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;
}
+20 -4
View File
@@ -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;
+15 -1
View File
@@ -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
+6 -1
View File
@@ -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);
+4 -4
View File
@@ -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;
+2 -2
View File
@@ -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;
+8 -8
View File
@@ -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 {
+27 -4
View File
@@ -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}
});