PR #23700 opened by charlymp
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23700
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23700.patch

deblocking_boundary_strengths() runs on the full 8x8 grid without
knowing the PU partitioning, so on inter content the two MvField
entries it compares are identical most of the time. When they are
identical and both sides use the same RefPicList the result is always
0: same references, zero mv deltas. Check for this first, and compare
whole edge lines at once in the TU internal loops; anything that does
not match goes through the unchanged code path.

Up to 3% faster single threaded decoding on 4K inter streams.

Signed-off-by: Charly Morgand-Poyac <[email protected]>

# Summary of changes

Briefly describe what this PR does and why.

<!--
If this PR requires new FATE test samples, attach them to the PR and
list their target paths below (relative to the fate-suite root).

Attached filenames must match the sample's filename:

```fate-samples
# e.g. vorbis/new-sample.ogg
```
-->



>From 4bb5af57931e7b564c6ec5bd1c7dca8ce53c8ea6 Mon Sep 17 00:00:00 2001
From: Charly Morgand-Poyac <[email protected]>
Date: Sat, 4 Jul 2026 18:36:31 +0200
Subject: [PATCH] lavc/hevcdec: skip boundary strength computation for
 identical MvFields

deblocking_boundary_strengths() runs on the full 8x8 grid without
knowing the PU partitioning, so on inter content the two MvField
entries it compares are identical most of the time. When they are
identical and both sides use the same RefPicList the result is always
0: same references, zero mv deltas. Check for this first, and compare
whole edge lines at once in the TU internal loops; anything that does
not match goes through the unchanged code path.

Up to 3% faster single threaded decoding on 4K inter streams.

Signed-off-by: Charly Morgand-Poyac <[email protected]>
---
 libavcodec/hevc/filter.c | 67 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 65 insertions(+), 2 deletions(-)

diff --git a/libavcodec/hevc/filter.c b/libavcodec/hevc/filter.c
index e897ad5d58..3390202b38 100644
--- a/libavcodec/hevc/filter.c
+++ b/libavcodec/hevc/filter.c
@@ -675,6 +675,25 @@ static void deblocking_filter_CTB(const HEVCContext *s, 
const HEVCLayerContext *
     }
 }
 
+static av_always_inline int mvf_equal(const MvField *a, const MvField *b)
+{
+    return !memcmp(a, b, offsetof(MvField, pred_flag) + sizeof(a->pred_flag));
+}
+
+static av_always_inline int mvf_run_equal(const MvField *a, const MvField *b, 
int m)
+{
+    const size_t tail = offsetof(MvField, pred_flag) + sizeof(a->pred_flag);
+
+    switch (m) {
+    case 15: return !memcmp(a, b, 15 * sizeof(*a) + tail);
+    case 12: return !memcmp(a, b, 12 * sizeof(*a) + tail);
+    case  7: return !memcmp(a, b,  7 * sizeof(*a) + tail);
+    case  4: return !memcmp(a, b,  4 * sizeof(*a) + tail);
+    case  3: return !memcmp(a, b,  3 * sizeof(*a) + tail);
+    default: return !memcmp(a, b, tail);
+    }
+}
+
 static int boundary_strength(const HEVCContext *s, const MvField *curr, const 
MvField *neigh,
                              const RefPicList *neigh_refPicList)
 {
@@ -769,6 +788,7 @@ void ff_hevc_deblocking_boundary_strengths(HEVCLocalContext 
*lc, const HEVCLayer
         const RefPicList *rpl_top = (lc->boundary_flags & 
BOUNDARY_UPPER_SLICE) ?
                                     ff_hevc_get_ref_list(s->cur_frame, x0, y0 
- 1) :
                                     s->cur_frame->refPicList;
+        int same_rpl = rpl_top == s->cur_frame->refPicList;
         int yp_pu = (y0 - 1) >> log2_min_pu_size;
         int yq_pu =  y0      >> log2_min_pu_size;
         int yp_tu = (y0 - 1) >> log2_min_tu_size;
@@ -786,6 +806,8 @@ void ff_hevc_deblocking_boundary_strengths(HEVCLocalContext 
*lc, const HEVCLayer
                     bs = 2;
                 else if (curr_cbf_luma || top_cbf_luma)
                     bs = 1;
+                else if (same_rpl && mvf_equal(curr, top))
+                    bs = 0;
                 else
                     bs = boundary_strength(s, curr, top, rpl_top);
                 l->horizontal_bs[((x0 + i) + y0 * l->bs_width) >> 2] = bs;
@@ -807,6 +829,7 @@ void ff_hevc_deblocking_boundary_strengths(HEVCLocalContext 
*lc, const HEVCLayer
         const RefPicList *rpl_left = (lc->boundary_flags & 
BOUNDARY_LEFT_SLICE) ?
                                      ff_hevc_get_ref_list(s->cur_frame, x0 - 
1, y0) :
                                      s->cur_frame->refPicList;
+        int same_rpl = rpl_left == s->cur_frame->refPicList;
         int xp_pu = (x0 - 1) >> log2_min_pu_size;
         int xq_pu =  x0      >> log2_min_pu_size;
         int xp_tu = (x0 - 1) >> log2_min_tu_size;
@@ -824,6 +847,8 @@ void ff_hevc_deblocking_boundary_strengths(HEVCLocalContext 
*lc, const HEVCLayer
                     bs = 2;
                 else if (curr_cbf_luma || left_cbf_luma)
                     bs = 1;
+                else if (same_rpl && mvf_equal(curr, left))
+                    bs = 0;
                 else
                     bs = boundary_strength(s, curr, left, rpl_left);
                 l->vertical_bs[(x0 + (y0 + i) * l->bs_width) >> 2] = bs;
@@ -833,6 +858,44 @@ void 
ff_hevc_deblocking_boundary_strengths(HEVCLocalContext *lc, const HEVCLayer
     if (log2_trafo_size > log2_min_pu_size && !is_intra) {
         const RefPicList *rpl = s->cur_frame->refPicList;
 
+        // whole edge lines can be tested at once when there is one MvField 
entry per 4px segment
+        if (log2_min_pu_size == 2 && log2_trafo_size >= 4 && log2_trafo_size 
<= 6) {
+            int n     = 1 << (log2_trafo_size - 2);
+            int x_pu0 = x0 >> 2;
+
+            // bs for TU internal horizontal PU boundaries
+            for (j = 8; j < (1 << log2_trafo_size); j += 8) {
+                int yq_pu = (y0 + j) >> 2;
+                const MvField *top  = &tab_mvf[(yq_pu - 1) * min_pu_width + 
x_pu0];
+                const MvField *curr = &tab_mvf[ yq_pu      * min_pu_width + 
x_pu0];
+                uint8_t *bs_h = &l->horizontal_bs[(x0 + (y0 + j) * 
l->bs_width) >> 2];
+
+                if (mvf_run_equal(top, curr, n - 1)) {
+                    memset(bs_h, 0, n);
+                } else {
+                    for (i = 0; i < n; i++)
+                        bs_h[i] = mvf_equal(&curr[i], &top[i]) ? 0 : 
boundary_strength(s, &curr[i], &top[i], rpl);
+                }
+            }
+
+            // bs for TU internal vertical PU boundaries
+            for (j = 0; j < (1 << log2_trafo_size); j += 4) {
+                int y_pu = (y0 + j) >> 2;
+                const MvField *row = &tab_mvf[y_pu * min_pu_width + x_pu0];
+                uint8_t *bs_v = &l->vertical_bs[(x0 + (y0 + j) * l->bs_width) 
>> 2];
+
+                // row[1..] vs row[2..] checks all adjacent pairs, a superset 
of the pairs needed
+                if (mvf_run_equal(row + 1, row + 2, n - 4)) {
+                    for (i = 2; i < n; i += 2)
+                        bs_v[i] = 0;
+                } else {
+                    for (i = 2; i < n; i += 2)
+                        bs_v[i] = mvf_equal(&row[i], &row[i - 1]) ? 0 : 
boundary_strength(s, &row[i], &row[i - 1], rpl);
+                }
+            }
+            return;
+        }
+
         // bs for TU internal horizontal PU boundaries
         for (j = 8; j < (1 << log2_trafo_size); j += 8) {
             int yp_pu = (y0 + j - 1) >> log2_min_pu_size;
@@ -843,7 +906,7 @@ void ff_hevc_deblocking_boundary_strengths(HEVCLocalContext 
*lc, const HEVCLayer
                 const MvField *top  = &tab_mvf[yp_pu * min_pu_width + x_pu];
                 const MvField *curr = &tab_mvf[yq_pu * min_pu_width + x_pu];
 
-                bs = boundary_strength(s, curr, top, rpl);
+                bs = mvf_equal(curr, top) ? 0 : boundary_strength(s, curr, 
top, rpl);
                 l->horizontal_bs[((x0 + i) + (y0 + j) * l->bs_width) >> 2] = 
bs;
             }
         }
@@ -858,7 +921,7 @@ void ff_hevc_deblocking_boundary_strengths(HEVCLocalContext 
*lc, const HEVCLayer
                 const MvField *left = &tab_mvf[y_pu * min_pu_width + xp_pu];
                 const MvField *curr = &tab_mvf[y_pu * min_pu_width + xq_pu];
 
-                bs = boundary_strength(s, curr, left, rpl);
+                bs = mvf_equal(curr, left) ? 0 : boundary_strength(s, curr, 
left, rpl);
                 l->vertical_bs[((x0 + i) + (y0 + j) * l->bs_width) >> 2] = bs;
             }
         }
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to