You've already forked FFmpeg
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:
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user