[vlc-commits] [Git][videolan/vlc][master] 4 commits: qml: implement denoting viewport in `DualKawaseBlur`

Steve Lhomme (@robUx4) gitlab at videolan.org
Mon Sep 29 16:13:59 UTC 2025



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
debdddbd by Fatih Uzunoglu at 2025-09-29T16:01:07+00:00
qml: implement denoting viewport in `DualKawaseBlur`

This makes it possible to clip the result without
post-processing (be it stencil mask, or even the
fragment shader if you consider it post process),
but rather in layer stage!

As a corollary, depending on the excess area, this
can save a lot of video memory. The saving is
_relatively_ more in two-pass mode, because we are
not back-propagating the viewport to the intermediate
layers in four-pass mode (at least for now). As
a reminder, in two-pass mode, there is only one layer
which is the final layer, and in four-pass mode, there
are two intermediate layers, and one final layer.
However with the recent static optimization hint
work, which can be used to the intermediate layers
in four-pass mode, it should not be necessary (can
be used to reduce peak video memory consumption, but
that is less important anyway).

One might ask, would not this be problematic for
static sources, where we would apply the blur once
and show the relevant part by clipping and save
some computing. First, that is only relevant for
the cases where the effect scale does not change
(which is not a use case for us atm), second, it is
arguably better to save video memory than GPU
cycles (computing) due to incurred re-blurring,
and third, if wanted it can still be used (instead
of the new `viewport` property, `clip: true` can
be used). It should be noted that since we are
supporting live blurring (such as item layers),
continuous re-blurring must be tolerable anyway
and modern GPUs are good at that but they still
lack adequate video memory.

There are two use cases for this at the moment:

1) Artists page, where we use Qt's `clip: true`
to clip the excess part. However, it means the
final layer contains the excess part consuming
video memory unnecessarily. If Qt is using stencil
mask for clipping, at least due to early stencil the
fragment shader would not run for the obscured
fragments. This approach already has the same
behavior, by completely getting rid of the excess
fragments, but with less video memory consumption.
At the same time, not having a clip node should
save some of the work the batch renderer is doing.

2) Player page. There, we are not even bothering
with `clip: true`, because the window itself
naturally clips the content. This approach is
going to save less relative memory there because
we are using the four-pass configuration. With
the static optimization hint, the relative saving
should be equal for two/four-pass modes.

- - - - -
7c04e996 by Fatih Uzunoglu at 2025-09-29T16:01:07+00:00
qml: use viewport for the blur effect in `ArtistTopBanner`

Clipping is disabled in the subsequent commit.

- - - - -
019735c3 by Fatih Uzunoglu at 2025-09-29T16:01:07+00:00
qml: disable clipping for the blur effect in `ArtistTopBanner`

Denoting the viewport makes clipping/clip node redundant.

- - - - -
f3ddea08 by Fatih Uzunoglu at 2025-09-29T16:01:07+00:00
qml: use viewport for the blur effect in `Player`

- - - - -


3 changed files:

- modules/gui/qt/medialibrary/qml/ArtistTopBanner.qml
- modules/gui/qt/player/qml/Player.qml
- modules/gui/qt/widgets/qml/DualKawaseBlur.qml


Changes:

=====================================
modules/gui/qt/medialibrary/qml/ArtistTopBanner.qml
=====================================
@@ -77,11 +77,6 @@ FocusScope {
     Item {
         anchors.fill: background
 
-        // TODO: Clipping should not be necessary, we can crop like
-        //       `ImageExt` is doing for `PreserveAspectCrop`, but
-        //       `DualKawaseBlur` does not support that for now.
-        clip: !blurEffect.sourceNeedsLayering
-
         visible: (GraphicsInfo.shaderType === GraphicsInfo.RhiShader)
 
         // This blur effect does not create an implicit layer that is updated
@@ -118,6 +113,11 @@ FocusScope {
                 source: blurEffect.sourceNeedsLayering ? backgroundLayer : background
             }
 
+            // Instead of clipping in the parent, denote the viewport here so we both
+            // do not need to clip the excess, and also save significant video memory:
+            viewportRect: !blurEffect.sourceNeedsLayering ? Qt.rect((width - parent.width) / 2, (height - parent.height) / 2, parent.width, parent.height)
+                                                          : Qt.rect(0, 0, 0, 0)
+
             ShaderEffectSource {
                 id: backgroundLayer
 


=====================================
modules/gui/qt/player/qml/Player.qml
=====================================
@@ -305,6 +305,10 @@ FocusScope {
                         tint: bgtheme.palette.isDark ? "black" : "white"
                         tintStrength: 0.5
 
+                        // The window naturally clips the content, but having this saves some
+                        // video memory, depending on the excess content in the last layer:
+                        viewportRect: Qt.rect((width - parent.width) / 2, (height - parent.height) / 2, parent.width, parent.height)
+
                         Widgets.TextureProviderItem {
                             id: textureProviderItem
 


=====================================
modules/gui/qt/widgets/qml/DualKawaseBlur.qml
=====================================
@@ -85,6 +85,10 @@ Item {
     // `QSGTextureView` can also be used instead of sub-texturing here.
     property rect sourceRect
 
+    // Viewport rect allows to discard an unwanted area in effect's local coordinates.
+    // Since the discard occurs at layer level, using this saves video memory.
+    property rect viewportRect
+
     property alias sourceTextureProviderObserver: ds1SourceObserver // for accessory
 
     readonly property bool sourceTextureIsValid: sourceTextureProviderObserver.isValid
@@ -140,7 +144,7 @@ Item {
             root.scheduleUpdate(false) // this triggers releasing intermediate layers (when applicable)
         }
     }
-    
+
     // TODO: Get rid of this in favor of GLSL 1.30's `textureSize()`
     Connections {
         target: root.Window.window
@@ -238,6 +242,15 @@ Item {
 
         live: root.live
 
+        // Divided by two, because viewport rect is local to the final upsampler (us2), which is the painter delegate here (regardless of the mode):
+        // Viewport rect is only relevant for the last layer, so in here (ds1layer) it is only for the two-pass mode:
+        sourceRect: ((root.configuration === DualKawaseBlur.Configuration.TwoPass) &&
+                    (root.viewportRect.width > 0 && root.viewportRect.height > 0)) ? Qt.rect(root.viewportRect.x / 2,
+                                                                                             root.viewportRect.y / 2,
+                                                                                             root.viewportRect.width / 2,
+                                                                                             root.viewportRect.height / 2)
+                                                                                   : Qt.rect(0, 0, 0, 0)
+        
         function scheduleChainedUpdate() {
             if (!ds1layer) // context is lost, Qt bug (reproduced with 6.2)
                 return
@@ -374,6 +387,15 @@ Item {
 
         live: root.live
 
+        // Divided by two, because viewport rect is local to the final upsampler (us2), which is the painter delegate here (regardless of the mode):
+        // No need to check for the mode, because this is not used in two pass mode (calculations are negligible).
+        // TODO: Investigate if we can propagate the viewport rect back to the intermediate layers in four-pass mode to save more video memory.
+        sourceRect: (root.viewportRect.width > 0 && root.viewportRect.height > 0) ? Qt.rect(root.viewportRect.x / 2,
+                                                                                            root.viewportRect.y / 2,
+                                                                                            root.viewportRect.width / 2,
+                                                                                            root.viewportRect.height / 2)
+                                                                                  : Qt.rect(0, 0, 0, 0)
+
         function scheduleChainedUpdate() {
             if (!us1layer) // context is lost, Qt bug (reproduced with 6.2)
                 return
@@ -413,7 +435,10 @@ Item {
     ShaderEffect {
         id: us2
 
-        anchors.fill: parent // {us1/ds1}.size * 2
+        // {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
 
         readonly property Item source: (root.configuration === DualKawaseBlur.Configuration.TwoPass) ? ds1layer : us1layer
         property rect normalRect // not necessary here, added because of the warning



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/898a147a5cb4068a7c8d46bcd02c9c1f450a5eea...f3ddea0864678d3f5a5c90344b16d0abee7eb459

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


VideoLAN code repository instance


More information about the vlc-commits mailing list