This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 6c770f33c8e94ea18000cd81da4f1e0ad0bac7a7 Author: Niklas Haas <[email protected]> AuthorDate: Thu Jul 23 19:48:24 2026 +0200 Commit: Niklas Haas <[email protected]> CommitDate: Mon Aug 3 09:32:30 2026 +0000 swscale/ops: add SWS_OP_LUT_3D and supporting code After extensive testing, prototyping and benchmarking across a range of systems, I determined that the optimal data layout for the SIMD 3DLUT is essentially exactly the one we have. 16-bit integers are near optimal for quality vs compactness, and crucially, x86 lets us load the entire packed 3DLUT entry with a single `vpgatherdq` instruction. This dwarfs the loss from needing to cast the resulting 16-bit integers back to f32 and renormalize. Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops.c | 37 ++++++++++++++++++++++++++++++++++++- libswscale/ops.h | 31 +++++++++++++++++++++++++++++++ libswscale/ops_optimizer.c | 11 +++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) diff --git a/libswscale/ops.c b/libswscale/ops.c index 21462df386..8482f7c5ec 100644 --- a/libswscale/ops.c +++ b/libswscale/ops.c @@ -88,6 +88,7 @@ const char *ff_sws_op_type_name(SwsOpType op) case SWS_OP_DITHER: return "SWS_OP_DITHER"; case SWS_OP_FILTER_H: return "SWS_OP_FILTER_H"; case SWS_OP_FILTER_V: return "SWS_OP_FILTER_V"; + case SWS_OP_LUT_3D: return "SWS_OP_LUT_3D"; case SWS_OP_INVALID: return "SWS_OP_INVALID"; case SWS_OP_TYPE_NB: break; } @@ -266,6 +267,11 @@ void ff_sws_apply_op_q(const SwsOp *op, AVRational64 x[4]) /* Filters have normalized energy by definition, so they don't * conceptually modify individual components */ return; + case SWS_OP_LUT_3D: + /* 3D LUTs are treated as a black box, so set those values to NaN */ + for (int i = 0; i < 3; i++) + x[i] = (AVRational64) {0}; + return; } av_unreachable("Invalid operation type!"); @@ -323,6 +329,7 @@ void ff_sws_op_list_update_comps(SwsOpList *ops) case SWS_OP_UNPACK: case SWS_OP_FILTER_H: case SWS_OP_FILTER_V: + case SWS_OP_LUT_3D: break; /* special cases, handled below */ default: memcpy(op->comps.min, prev.min, sizeof(prev.min)); @@ -518,7 +525,21 @@ void ff_sws_op_list_update_comps(SwsOpList *ops) apply_filter_weights(&op->comps, &prev, op->filter.kernel); break; } - + case SWS_OP_LUT_3D: + for (int i = 0; i < 3; i++) { + /* 3x3 dependency matrix; strip all information except + * SWS_COMP_GARBAGE (for correctness validation) */ + for (int j = 0; j < 3; j++) + FORWARD(i, j, flags & SWS_COMP_GARBAGE); + /* LUT output domain is always scaled to full 16-bit range */ + op->comps.min[i] = Q(0); + op->comps.max[i] = Q(UINT16_MAX); + } + /* Pass through alpha channel untouched */ + FORWARD(3, 3, flags); + op->comps.min[3] = prev.min[3]; + op->comps.max[3] = prev.max[3]; + break; case SWS_OP_INVALID: case SWS_OP_TYPE_NB: av_unreachable("Invalid operation type!"); @@ -587,6 +608,11 @@ void ff_sws_op_list_update_comps(SwsOpList *ops) } } break; + case SWS_OP_LUT_3D: + for (int i = 0; i < 3; i++) + need_in[i] = need_out[0] | need_out[1] | need_out[2]; + need_in[3] = need_out[3]; + break; } memcpy(need_out, need_in, sizeof(need_in)); @@ -609,6 +635,9 @@ static void op_uninit(SwsOp *op) case SWS_OP_FILTER_V: av_refstruct_unref(&op->filter.kernel); break; + case SWS_OP_LUT_3D: + av_refstruct_unref(&op->lut3d.lut); + break; } *op = (SwsOp) {0}; @@ -672,6 +701,9 @@ SwsOpList *ff_sws_op_list_duplicate(const SwsOpList *ops) case SWS_OP_FILTER_V: av_refstruct_ref(op->filter.kernel); break; + case SWS_OP_LUT_3D: + av_refstruct_ref_c(op->lut3d.lut); + break; } } @@ -930,6 +962,9 @@ void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op) kernel->name, kernel->filter_size); break; } + case SWS_OP_LUT_3D: + av_bprintf(bp, "%-20s: %s", name, op->lut3d.dynamic ? "dynamic" : "static"); + break; case SWS_OP_TYPE_NB: break; } diff --git a/libswscale/ops.h b/libswscale/ops.h index 83ef2b49df..008e8c5b31 100644 --- a/libswscale/ops.h +++ b/libswscale/ops.h @@ -29,6 +29,7 @@ #include "graph.h" #include "filters.h" +#include "lut3d.h" #include "rational64.h" #include "uops.h" @@ -62,6 +63,9 @@ typedef enum SwsOpType { SWS_OP_FILTER_H, /* horizontal filtering */ SWS_OP_FILTER_V, /* vertical filtering */ + /* Table-based operations. Defined for floating point types only. */ + SWS_OP_LUT_3D, /* apply a SwsLut3D */ + SWS_OP_TYPE_NB, } SwsOpType; @@ -204,6 +208,32 @@ typedef struct SwsFilterOp { SwsPixelType type; /* pixel type to store result as */ } SwsFilterOp; +typedef struct SwsLut3dOp { + /** + * Reference to the external LUT3D to apply. This is managed by the caller, + * and must remain valid for the lifetime of the SwsOp and any compiled + * functions derived from it. + * + * *lut is never dereferenced by the SwsOp code itself, only at runtime by + * the actual dispatched implementation, and may be freely modified even + * after op compilation to place new values for dynamic tone-mapping. + * + * The reference algorithm for this operation lives in lut3d.c, and + * includes a tetrahedral interpolation component for the input LUT, and + * then an optional linear tone mapping LUT plus trilinear output LUT + * (when lut->dynamic is true). + * + * All linear interpolations are performed in the pixel value's native + * representation, even though the LUTs themselves are stored as unsigned + * packed 16-bit integers. The input value range is assumed to be scaled + * and clamped to the LUT's domain (i.e. [0, INPUT_LUT_SIZE - 1]), and the + * output value range will be [0, 2^16-1], except for the alpha channel, + * which is passed through untouched. + */ + const SwsLut3D *lut; /* refstruct */ + bool dynamic; +} SwsLut3dOp; + typedef struct SwsOp { SwsOpType op; /* operation to perform */ SwsPixelType type; /* pixel type to operate on */ @@ -219,6 +249,7 @@ typedef struct SwsOp { SwsScaleOp scale; SwsDitherOp dither; SwsFilterOp filter; + SwsLut3dOp lut3d; }; /** diff --git a/libswscale/ops_optimizer.c b/libswscale/ops_optimizer.c index 3546d8bc9e..fd3b70deed 100644 --- a/libswscale/ops_optimizer.c +++ b/libswscale/ops_optimizer.c @@ -77,6 +77,7 @@ static bool op_commute_clear(SwsOp *op, SwsOp *next) case SWS_OP_PACK: case SWS_OP_UNPACK: case SWS_OP_CLEAR: + case SWS_OP_LUT_3D: return false; case SWS_OP_TYPE_NB: break; @@ -157,6 +158,7 @@ static bool op_commute_swizzle(SwsOp *op, SwsOp *next) case SWS_OP_LINEAR: case SWS_OP_PACK: case SWS_OP_UNPACK: + case SWS_OP_LUT_3D: return false; case SWS_OP_TYPE_NB: break; @@ -198,6 +200,7 @@ static bool op_commute_filter(SwsOp *op, SwsOp *prev) case SWS_OP_MAX: case SWS_OP_FILTER_H: case SWS_OP_FILTER_V: + case SWS_OP_LUT_3D: return false; case SWS_OP_TYPE_NB: break; @@ -737,6 +740,14 @@ retry: goto retry; } break; + + case SWS_OP_LUT_3D: + /* Eliminate unnecessary 3DLUT */ + if (!(needed & SWS_COMP_ELEMS(3))) { + ff_sws_op_list_remove_at(ops, n, 1); + goto retry; + } + break; } } _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
