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]

Reply via email to