[vlc-commits] [Git][videolan/vlc][master] 3 commits: qml: introduce `visualRect` property in `DualKawaseBlur`

Steve Lhomme (@robUx4) gitlab at videolan.org
Mon Apr 27 17:02:30 UTC 2026



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
393d5472 by Fatih Uzunoglu at 2026-04-27T16:40:15+00:00
qml: introduce `visualRect` property in `DualKawaseBlur`

This property can be used in these two cases (but not
limited to):
- Using viewport rect to only blur area of interest,
  discarding empty* margins, and extend the visual to
  make it seem as if viewport rect is never used. If
  the margins are truly empty, the result would be
  the same since blurring a uniform solid color would
  not result in anything different than the color.
- Freely adjusting the visual position, since with
  viewport rect, it is always centered in the parent
  (root, effect).

* Empty does not necessarily mean transparent, it can
  be any uniform color plane as well.

- - - - -
f258ddd4 by Fatih Uzunoglu at 2026-04-27T16:40:15+00:00
qml: limit the blur effect intermediate layer sizes to area of interest in `MainDisplay`

This patch implements the todo mentioned previously in 2630a5cd:

> It is currently a todo to further reduce the VRAM consumption
> by trimming the intermediate layer size within the effect,
> since we do not need to apply blurring in the plain area which
> has only one unique color (the background color).

Now, another todo is to further limit the layer sizes based on the view's
content margins (`Flickable` margins and control paddings and insets).

- - - - -
df2fc34f by Fatih Uzunoglu at 2026-04-27T16:40:15+00:00
qml: no longer anchor the partial effect in `MainDisplay`

We can simply use `sourceVisualRect` at all times. I do
not have a strong opinion on this, but anchoring the
effect is an ugly workaround.

- - - - -


2 changed files:

- modules/gui/qt/maininterface/qml/MainDisplay.qml
- modules/gui/qt/widgets/qml/DualKawaseBlur.qml


Changes:

=====================================
modules/gui/qt/maininterface/qml/MainDisplay.qml
=====================================
@@ -260,7 +260,7 @@ FocusScope {
                 // since 03b0de26, already provides the effect the whole source texture with a proper sub-rect,
                 // so the effect here can sample the top edge neighbour pixels, but for the bottom edge we
                 // need to configure the layer:
-                readonly property int bottomExtension: 16
+                readonly property int edgeExtension: 16
                 // The layer width is smaller than the item width, this is intentional because we do not want
                 // to include the area that playqueue occupies in the layer since it is empty. Note that we
                 // still want to do layering here, because even though the layer is smaller than the item
@@ -269,14 +269,14 @@ FocusScope {
                 // need to use background coloring. It is currently a todo to further reduce video memory
                 // consumption by covering the effect for only the area of interest, currently the blur
                 // effect does not support having an extension area for postprocessing.
-                layer.sourceRect: Qt.rect(0, 0, stackView.width + 1, height + bottomExtension)
+                layer.sourceRect: Qt.rect(0, 0, Math.min(stackView.width + edgeExtension, stackViewParent.width), height + edgeExtension)
 
                 Rectangle {
                     // Extension of parent rectangle for the bottom extension.
                     anchors.top: parent.bottom
                     anchors.left: parent.left
                     anchors.right: parent.right
-                    height: stackViewParent.bottomExtension
+                    height: stackViewParent.edgeExtension
                     visible: stackViewParent.layer.enabled && (height > 0)
                     color: parent.color
                 }
@@ -284,28 +284,22 @@ FocusScope {
                 layer.effect: Widgets.PartialEffect {
                     id: stackViewParentLayerEffect
 
-                    // Setting `height` does not seem to work here. Anchoring the effect is not very nice, but it works:
-                    anchors.fill: stackViewParent // WARNING: layered item is not necessarily the visual parent of its layer effect.
-                    anchors.bottomMargin: -stackViewParent.bottomExtension
-
                     blending: stackViewParent.color.a < (1.0 - Number.EPSILON)
 
                     // Each pass of the blur effect also suffers from the border neighbour pixel issue mentioned
                     // above, making all the borders problematic, to a less considerable extent. For that reason,
                     // we extend both the top and the bottom edges and use viewport to prevent overdraw:
                     effectRect: Qt.rect(0,
-                                        stackView.height - stackViewParent.bottomExtension,
+                                        stackView.height - stackViewParent.edgeExtension,
                                         width,
-                                        loaderProgress.height + miniPlayer.height + 2 * stackViewParent.bottomExtension)
+                                        loaderProgress.height + miniPlayer.height + 2 * stackViewParent.edgeExtension)
 
-                    // Bottom extension is not necessary here, but it is provided to prevent stretching glitch at
+                    // Edge extension is not necessary here, but it is provided to prevent stretching glitch at
                     // initialization. Currently this is not a problem because the effect is opaque since the
                     // background is opaque, and effect visual has higher z than the source visual.
-                    sourceVisualRect: ((stackView.width < stackViewParent.width) || blending) ?
-                                      Qt.rect(0, 0,
-                                              stackView.width,
-                                              stackView.height + (frostedGlassEffect.blending ? 0 : stackViewParent.bottomExtension)) :
-                                      Qt.rect(0, 0, 0, 0)
+                    sourceVisualRect: Qt.rect(0, 0,
+                                              stackViewParent.layer.sourceRect.width,
+                                              stackView.height + (frostedGlassEffect.blending ? 0 : stackViewParent.edgeExtension))
 
                     effect: frostedGlassEffect
 
@@ -321,11 +315,18 @@ FocusScope {
                         backgroundColor: (ready ? "transparent" : stackViewParent.color)
                         tint: frostedTheme.bg.secondary
 
-                        // Prevent overdraw (the extension margin should not be painted):
+                        // Prevent overdraw (the extension margin should not be painted).
+                        // This also saves video memory compared to solely using visual rect.
                         viewportRect: Qt.rect(0,
-                                              stackViewParent.bottomExtension,
-                                              width,
-                                              height - (2 * stackViewParent.bottomExtension))
+                                              stackViewParent.edgeExtension,
+                                              Math.min(stackView.width + stackViewParent.edgeExtension, stackViewParent.width),
+                                              height - (2 * stackViewParent.edgeExtension))
+
+                        visualRect: (stackView.width < stackViewParent.width) ? Qt.rect(viewportRect.x,
+                                                                                        viewportRect.y,
+                                                                                        width,
+                                                                                        viewportRect.height)
+                                                                              : Qt.rect(0, 0, 0, 0)
                     }
                 }
 


=====================================
modules/gui/qt/widgets/qml/DualKawaseBlur.qml
=====================================
@@ -18,6 +18,7 @@
 import QtQuick
 import QtQuick.Window
 
+import VLC.MainInterface
 import VLC.Util
 
 // This item provides the novel "Dual Kawase" effect [1], which offers a very ideal
@@ -101,6 +102,23 @@ Item {
     // Since the discard occurs at layer level, using this saves video memory.
     property rect viewportRect
 
+    // Visual rect allows to extend the visual (us2) in effect's local coordinates.
+    // Unlike viewport rect, visual rect is not relevant to the layers hence will
+    // not allow saving video memory. The reason for this to exist is that when
+    // viewport rect is used to save video memory, the effect may still need to
+    // cover a certain area without stretching the blurred result and to keep
+    // the postprocess coverage available beyond the blurred result but within
+    // the effect area. A particular use case is when blurred content has large
+    // margins that are empty, where in this case visual rect with clamp to edge
+    // wrap mode would make the effect look as if the whole source was blurred,
+    // but with the benefit of saving video memory, provided that the viewport
+    // rect is set to discard the empty margins. Another use case is freely
+    // adjusting the position of the visual when viewport rect is smaller than
+    // the effect size, since with only viewport rect the visual is always
+    // centered in the parent (effect).
+    property rect visualRect
+    property int visualWrapMode: TextureProviderIndirection.ClampToEdge
+
     // Local viewport rect is viewport rect divided by two, because it is used as the source
     // rect of last layer, which is 2x upsampled by the painter delegate (us2):
     readonly property rect _localViewportRect: ((viewportRect.width > 0 && viewportRect.height > 0)) ? Qt.rect(viewportRect.x / 2,
@@ -109,6 +127,13 @@ Item {
                                                                                                                viewportRect.height / 2)
                                                                                                      : Qt.rect(0, 0, 0, 0)
 
+    // This is only supposed to be used in visual delegate (us2), maybe we move it there?
+    readonly property rect _localVisualRect: ((visualRect.width > 0 && visualRect.height > 0)) ? Qt.rect(visualRect.x / 2,
+                                                                                                         visualRect.y / 2,
+                                                                                                         visualRect.width / 2,
+                                                                                                         visualRect.height / 2)
+                                                                                               : Qt.rect(0, 0, 0, 0)
+
     property alias sourceTextureProviderObserver: ds1.tpObserver // for accessory
     sourceTextureProviderObserver.notifyAllChanges: !live
 
@@ -457,14 +482,39 @@ Item {
     UpsamplerShaderEffect {
         id: us2
 
-        // {us1/ds1}.size * 2
-        anchors.centerIn: parent
-        width: (root.viewportRect.width > 0) ? root.viewportRect.width : parent.width
-        height: (root.viewportRect.height > 0) ? root.viewportRect.height : parent.height
+        // {us1/ds1}.size * 2, unless visual rect is used
+        anchors.centerIn: useIndirection ? undefined : parent
+
+        x: useIndirection ? root.visualRect.x : 0
+        y: useIndirection ? root.visualRect.y : 0
+
+        width: {
+            if (useIndirection)
+                return root.visualRect.width
+
+            if (root.viewportRect.width > 0)
+                return root.viewportRect.width
+
+            return parent.width
+        }
+
+        height: {
+            if (useIndirection)
+                return root.visualRect.height
+
+            if (root.viewportRect.height > 0)
+                return root.viewportRect.height
+
+            return parent.height
+        }
+
+        readonly property bool useIndirection: (root.visualRect.width > 0 && root.visualRect.height > 0)
 
         visible: tpObserver.isValid && root.available
 
-        source: (root.mode === DualKawaseBlur.Mode.TwoPass) ? ds1layer : us1layer
+        source: useIndirection ? textureProviderIndirection : targetSource
+
+        readonly property Item targetSource: (root.mode === DualKawaseBlur.Mode.TwoPass) ? ds1layer : us1layer
 
         property alias tint: root.tint
         property alias tintStrength: root.tintStrength
@@ -474,5 +524,29 @@ Item {
 
         fragmentShader: root.postprocess ? "qrc:///shaders/DualKawaseBlur_upsample_postprocess.frag.qsb"
                                          : "qrc:///shaders/DualKawaseBlur_upsample.frag.qsb"
+
+        property real _eDPR: MainCtx.effectiveDevicePixelRatio(Window.window) || 1.0
+
+        Connections {
+            target: MainCtx
+
+            function onIntfDevicePixelRatioChanged() {
+                us2._eDPR = MainCtx.effectiveDevicePixelRatio(us2.Window.window) || 1.0
+            }
+        }
+
+        TextureProviderIndirection {
+            id: textureProviderIndirection
+
+            source: us2.targetSource
+
+            textureSubRect: Qt.rect(0,
+                                    0,
+                                    root._localVisualRect.width * us2._eDPR,
+                                    root._localVisualRect.height * us2._eDPR)
+
+            horizontalWrapMode: root.visualWrapMode
+            verticalWrapMode: root.visualWrapMode
+        }
     }
 }



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/455c91cf004a2a6367e1a1b931654d4b264c07dd...df2fc34fdc1a047cbd4509b70dff1d9e1ceb2689

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/455c91cf004a2a6367e1a1b931654d4b264c07dd...df2fc34fdc1a047cbd4509b70dff1d9e1ceb2689
You're receiving this email because of your account on code.videolan.org.




More information about the vlc-commits mailing list