From 63b7d5b0892e0903c775908d6b032ac18e923450 Mon Sep 17 00:00:00 2001
From: Niklas Haas <git@haasn.dev>
Date: Mon, 25 Sep 2023 19:56:54 +0200
Subject: [PATCH 1/4] filters: move helper function to filters.h

---
 src/filters.c | 12 ++----------
 src/filters.h |  6 ++++++
 2 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/src/filters.c b/src/filters.c
index 86332900..9103a47c 100644
--- a/src/filters.c
+++ b/src/filters.c
@@ -75,17 +75,9 @@ bool pl_filter_config_eq(const struct pl_filter_config *a,
     return eq;
 }
 
-static inline float filter_radius(const struct pl_filter_config *c)
-{
-    float radius = c->radius && c->kernel->resizable ? c->radius : c->kernel->radius;
-    if (c->blur > 0.0)
-        radius *= c->blur;
-    return radius;
-}
-
 double pl_filter_sample(const struct pl_filter_config *c, double x)
 {
-    const float radius = filter_radius(c);
+    const float radius = pl_filter_radius_bound(c);
 
     // All filters are symmetric, and in particular only need to be defined
     // for [0, radius].
@@ -130,7 +122,7 @@ double pl_filter_sample(const struct pl_filter_config *c, double x)
 static void filter_cutoffs(const struct pl_filter_config *c, float cutoff,
                            float *out_radius, float *out_radius_zero)
 {
-    const float bound = filter_radius(c);
+    const float bound = pl_filter_radius_bound(c);
     float prev = 0.0, fprev = pl_filter_sample(c, prev);
     bool found_root = false;
 
diff --git a/src/filters.h b/src/filters.h
index 10da0fad..c3227db4 100644
--- a/src/filters.h
+++ b/src/filters.h
@@ -19,6 +19,12 @@
 
 #include <libplacebo/filters.h>
 
+static inline float pl_filter_radius_bound(const struct pl_filter_config *c)
+{
+    const float r = c->radius && c->kernel->resizable ? c->radius : c->kernel->radius;
+    return c->blur > 0.0 ? r * c->blur : r;
+}
+
 #define COMMON_FILTER_PRESETS                                                   \
     /* Highest priority / recommended filters */                                \
     {"bilinear",            &pl_filter_bilinear,    "Bilinear"},                \
-- 
GitLab


From faee50cfcd3530e7295e71107f8b6de6768ea1cf Mon Sep 17 00:00:00 2001
From: Niklas Haas <git@haasn.dev>
Date: Mon, 25 Sep 2023 19:57:08 +0200
Subject: [PATCH 2/4] renderer: add missing validation check

---
 src/renderer.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/renderer.c b/src/renderer.c
index 9fc1aa82..fdab0a93 100644
--- a/src/renderer.c
+++ b/src/renderer.c
@@ -3265,6 +3265,7 @@ bool pl_render_image_mix(pl_renderer rr, const struct pl_frame_mix *images,
     pl_dispatch_mark_dynamic(rr->dp, params->dynamic_constants);
 
     require(images->num_frames >= 1);
+    require(images->vsync_duration > 0.0);
     for (int i = 0; i < images->num_frames - 1; i++)
         require(images->timestamps[i] <= images->timestamps[i+1]);
 
-- 
GitLab


From 7872af19ccc88da39cf64ddc9beb3df68e888d89 Mon Sep 17 00:00:00 2001
From: Niklas Haas <git@haasn.dev>
Date: Mon, 25 Sep 2023 19:58:21 +0200
Subject: [PATCH 3/4] renderer: properly blur frame mixer

Fixes: https://code.videolan.org/videolan/libplacebo/-/issues/309
---
 src/include/libplacebo/renderer.h |  5 ++++-
 src/renderer.c                    | 28 +++++++++++++++++++++-------
 2 files changed, 25 insertions(+), 8 deletions(-)

diff --git a/src/include/libplacebo/renderer.h b/src/include/libplacebo/renderer.h
index 9c21840f..49255e5b 100644
--- a/src/include/libplacebo/renderer.h
+++ b/src/include/libplacebo/renderer.h
@@ -785,7 +785,10 @@ struct pl_frame_mix {
     // "next" frames.
 };
 
-// Helper function to calculate the frame mixing radius.
+// Helper function to calculate the base frame mixing radius.
+//
+// Note: When the source FPS exceeds the display FPS, this radius must be
+// increased by the corresponding ratio.
 static inline float pl_frame_mix_radius(const struct pl_render_params *params)
 {
     // For backwards compatibility, allow !frame_mixer->kernel
diff --git a/src/renderer.c b/src/renderer.c
index fdab0a93..cc39eed8 100644
--- a/src/renderer.c
+++ b/src/renderer.c
@@ -3301,6 +3301,21 @@ bool pl_render_image_mix(pl_renderer rr, const struct pl_frame_mix *images,
     for (int i = 0; i < rr->frames.num; i++)
         rr->frames.elem[i].evict = true;
 
+    // Blur frame mixer according to vsync ratio (source / display)
+    struct pl_filter_config mixer;
+    if (params->frame_mixer) {
+        mixer = *params->frame_mixer;
+        mixer.blur = PL_DEF(mixer.blur, 1.0);
+        for (int i = 1; i < images->num_frames; i++) {
+            if (images->timestamps[i] >= 0.0 && images->timestamps[i - 1] < 0) {
+                float frame_dur = images->timestamps[i] - images->timestamps[i - 1];
+                if (!params->skip_anti_aliasing)
+                    mixer.blur *= images->vsync_duration / frame_dur;
+                break;
+            }
+        }
+    }
+
     // Traverse the input frames and determine/prepare the ones we need
     bool single_frame = !params->frame_mixer || images->num_frames == 1;
 retry:
@@ -3318,7 +3333,6 @@ retry:
         }
 
         float weight;
-        const struct pl_filter_config *mixer = params->frame_mixer;
         if (single_frame) {
 
             // Only render the refimg, ignore others
@@ -3330,7 +3344,7 @@ retry:
             }
 
         // For backwards compatibility, treat !kernel as oversample
-        } else if (!mixer->kernel || mixer->kernel == &pl_filter_function_oversample) {
+        } else if (!mixer.kernel || mixer.kernel == &pl_filter_function_oversample) {
 
             // Compute the visible interval [rts, end] of this frame
             float end = i+1 < images->num_frames ? images->timestamps[i+1] : INFINITY;
@@ -3348,21 +3362,21 @@ retry:
             PL_TRACE(rr, "  -> Frame [%f, %f] intersects [%f, %f] = weight %f",
                      rts, end, 0.0, images->vsync_duration, weight);
 
-            if (weight < mixer->kernel->params[0]) {
+            if (weight < mixer.kernel->params[0]) {
                 PL_TRACE(rr, "     (culling due to threshold)");
                 weight = 0.0;
             }
 
         } else {
 
-            if (fabsf(rts) >= mixer->kernel->radius) {
-                PL_TRACE(rr, "  -> Skipping: outside filter radius (%f)",
-                         mixer->kernel->radius);
+            const float radius = pl_filter_radius_bound(&mixer);
+            if (fabsf(rts) >= radius) {
+                PL_TRACE(rr, "  -> Skipping: outside filter radius (%f)", radius);
                 continue;
             }
 
             // Weight is directly sampled from the filter
-            weight = pl_filter_sample(mixer, rts);
+            weight = pl_filter_sample(&mixer, rts);
             PL_TRACE(rr, "  -> Filter offset %f = weight %f", rts, weight);
 
         }
-- 
GitLab


From ebd131621e7dc391b65d249279bcc455036aa88a Mon Sep 17 00:00:00 2001
From: Niklas Haas <git@haasn.dev>
Date: Mon, 25 Sep 2023 20:06:14 +0200
Subject: [PATCH 4/4] utils/frame_queue: increase queue size based on fps ratio

Add extra frames when FPS is larger than VPS.
---
 src/utils/frame_queue.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/utils/frame_queue.c b/src/utils/frame_queue.c
index 7078391a..01559839 100644
--- a/src/utils/frame_queue.c
+++ b/src/utils/frame_queue.c
@@ -809,8 +809,10 @@ static enum pl_queue_status interpolate(pl_queue p, struct pl_frame_mix *mix,
     if (!params->radius)
         return oversample(p, mix, params);
 
-    double min_pts = params->pts - params->radius * p->fps.estimate,
-           max_pts = params->pts + params->radius * p->fps.estimate;
+    pl_assert(p->fps.estimate && p->vps.estimate);
+    float radius = params->radius * fmaxf(1.0f, p->vps.estimate / p->fps.estimate);
+    double min_pts = params->pts - radius * p->fps.estimate,
+           max_pts = params->pts + radius * p->fps.estimate;
 
     enum pl_queue_status ret;
     switch ((ret = advance(p, min_pts, params))) {
-- 
GitLab