[vlc-commits] [Git][videolan/vlc][master] 7 commits: qml: use common component for `ShaderEffect` in `DualKawaseBlur`

Steve Lhomme (@robUx4) gitlab at videolan.org
Fri Nov 14 06:43:48 UTC 2025



Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
6af130a0 by Fatih Uzunoglu at 2025-11-14T06:19:59+00:00
qml: use common component for `ShaderEffect` in `DualKawaseBlur`

This is only for maintenance, no behavioral change is intended.

- - - - -
bf7f7074 by Fatih Uzunoglu at 2025-11-14T06:19:59+00:00
qml: do not bother updating texture size if not necessary in `DualKawaseBlur`

The intermediate layers are not used in two pass configuration, so it is not
necessary to update the source texture size for them.

- - - - -
a8c6dd62 by Fatih Uzunoglu at 2025-11-14T06:19:59+00:00
qml: do not constantly update texture sizes if non-live in `DualKawaseBlur`

If the effect is not live, we can update the source texture sizes of the
layers once. There is no need to constantly update the texture sizes in
non-live case, because the textures do not change.

- - - - -
3a13b49a by Fatih Uzunoglu at 2025-11-14T06:19:59+00:00
qml: rename "configuration" to "mode" in `DualKawaseBlur`

The reason for that change is "configuration" is too long.

- - - - -
dcb834f7 by Fatih Uzunoglu at 2025-11-14T06:19:59+00:00
qml: define the postprocess properties in root in `DualKawaseBlur`

This is mainly to reveal the defaults in root, so that the reader
does not need to check `us2` to see the default values.

- - - - -
d6ebe030 by Fatih Uzunoglu at 2025-11-14T06:19:59+00:00
qml: calculate local viewport rect once in `DualKawaseBlur`

- - - - -
28a5cc2b by Fatih Uzunoglu at 2025-11-14T06:19:59+00:00
qml: use common component for `ShaderEffectSource` in `DualKawaseBlur`

This is only for maintenance, no behavioral change is intended.

- - - - -


2 changed files:

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


Changes:

=====================================
modules/gui/qt/medialibrary/qml/ArtistTopBanner.qml
=====================================
@@ -128,7 +128,7 @@ FocusScope {
             }
 
             // Strong blurring is not wanted here:
-            configuration: Widgets.DualKawaseBlur.Configuration.TwoPass
+            mode: Widgets.DualKawaseBlur.Mode.TwoPass
             radius: 1
         }
     }


=====================================
modules/gui/qt/widgets/qml/DualKawaseBlur.qml
=====================================
@@ -34,23 +34,23 @@ Item {
     implicitWidth: source ? Math.min(source.paintedWidth ?? Number.MAX_VALUE, source.width) : 0
     implicitHeight: source ? Math.min(source.paintedHeight ?? Number.MAX_VALUE, source.height) : 0
 
-    enum Configuration {
+    enum Mode {
         FourPass, // 2 downsample + 2 upsamples (3 layers/buffers)
         TwoPass // 1 downsample + 1 upsample (1 layer/buffer)
     }
 
+    property int mode: DualKawaseBlur.Mode.FourPass
+
     /// <postprocess>
     // The following property must be set in order to make other properties respected:
     property bool postprocess: false
 
-    property alias tint: us2.tint
-    property alias tintStrength: us2.tintStrength
-    property alias noiseStrength: us2.noiseStrength
-    property alias exclusionStrength: us2.exclusionStrength
+    property color tint: "transparent"
+    property real tintStrength: 0.0
+    property real noiseStrength: 0.0
+    property real exclusionStrength: 0.0
     /// </postprocess>
 
-    property int configuration: DualKawaseBlur.Configuration.FourPass
-
     // NOTE: This property is also an optimization hint. When it is false, the
     //       intermediate buffers for the blur passes may be released (only
     //       the two intermediate layers in four pass mode, we must have one
@@ -89,7 +89,15 @@ Item {
     // Since the discard occurs at layer level, using this saves video memory.
     property rect viewportRect
 
-    property alias sourceTextureProviderObserver: ds1SourceObserver // for accessory
+    // 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,
+                                                                                                               viewportRect.y / 2,
+                                                                                                               viewportRect.width / 2,
+                                                                                                               viewportRect.height / 2)
+                                                                                                     : Qt.rect(0, 0, 0, 0)
+
+    property alias sourceTextureProviderObserver: ds1.tpObserver // for accessory
 
     readonly property bool sourceTextureIsValid: sourceTextureProviderObserver.isValid
 
@@ -148,10 +156,12 @@ Item {
         }
     }
 
-    // TODO: Get rid of this in favor of GLSL 1.30's `textureSize()`
+    // TODO: We could use `textureSize()` and get rid of this, but we
+    //       can not because we are targeting GLSL 1.20/ESSL 1.0, even
+    //       though the shader is written in GLSL 4.40.
     Connections {
         target: root.Window.window
-        enabled: root.visible
+        enabled: root.visible && root.live
 
         function onAfterAnimating() {
             // Sampling point for getting the native texture sizes:
@@ -163,10 +173,12 @@ Item {
             // implicitly (due to source texture provider's signal
             // `textureChanged()`).
 
-            ds1.sourceTextureSize = ds1SourceObserver.nativeTextureSize
-            ds2.sourceTextureSize = ds2SourceObserver.nativeTextureSize
-            us1.sourceTextureSize = us1SourceObserver.nativeTextureSize
-            us2.sourceTextureSize = us2SourceObserver.nativeTextureSize
+            ds1.sourceTextureSize = ds1.tpObserver.nativeTextureSize
+            if (root.mode === DualKawaseBlur.Mode.FourPass) {
+                ds2.sourceTextureSize = ds2.tpObserver.nativeTextureSize
+                us1.sourceTextureSize = us1.tpObserver.nativeTextureSize
+            }
+            us2.sourceTextureSize = us2.tpObserver.nativeTextureSize
 
             // It is not clear if `ShaderEffect` updates the uniform
             // buffer after `afterAnimating()` signal but before the
@@ -184,80 +196,99 @@ Item {
                 // No need to check for if such slot exists for each,
                 // this is basically Qt version check in disguise.
                 ds1.ensurePolished()
-                ds2.ensurePolished()
-                us1.ensurePolished()
+                if (root.mode === DualKawaseBlur.Mode.FourPass) {
+                    ds2.ensurePolished()
+                    us1.ensurePolished()
+                }
                 us2.ensurePolished()
             }
         }
     }
 
-    ShaderEffect {
-        id: ds1
-
-        // When downsampled, we can decrease the size here so that the layer occupies less VRAM:
-        width: parent.width / 2
-        height: parent.height / 2
-
-        readonly property Item source: root.source
-
-        // TODO: Instead of normalizing here, we could use GLSL 1.30's `textureSize()`
-        //       and normalize in the vertex shader, but we can not because we are
-        //       targeting GLSL 1.20/ESSL 1.0, even though the shader is written in
-        //       GLSL 4.40.
-        readonly property rect normalRect: (root.sourceRect.width > 0.0 && root.sourceRect.height > 0.0) ? Qt.rect(root.sourceRect.x / sourceTextureSize.width,
-                                                                                                                   root.sourceRect.y / sourceTextureSize.height,
-                                                                                                                   root.sourceRect.width / sourceTextureSize.width,
-                                                                                                                   root.sourceRect.height / sourceTextureSize.height)
-                                                                                                         : Qt.rect(0.0, 0.0, 0.0, 0.0)
+    component DefaultShaderEffect : ShaderEffect {
+        id: shaderEffect
 
+        required property Item source
         readonly property int radius: root.radius
 
-        // TODO: We could use `textureSize()` and get rid of this, but we
-        //       can not because we are targeting GLSL 1.20/ESSL 1.0, even
-        //       though the shader is written in GLSL 4.40.
-        TextureProviderObserver {
-            id: ds1SourceObserver
-            source: ds1.source
-        }
-
         property size sourceTextureSize
+        property rect normalRect // may not be necessary, but still needed to prevent warning
+
+        property alias tpObserver: textureProviderObserver
 
         // cullMode: ShaderEffect.BackFaceCulling // QTBUG-136611 (Layering breaks culling with OpenGL)
 
-        fragmentShader: "qrc:///shaders/DualKawaseBlur_downsample.frag.qsb"
         // Maybe we should have vertex shader unconditionally, and calculate the half pixel there instead of fragment shader?
         vertexShader: (normalRect.width > 0.0 && normalRect.height > 0.0) ? "qrc:///shaders/SubTexture.vert.qsb"
                                                                           : ""
 
-        visible: false
-
         supportsAtlasTextures: true
 
         blending: root.blending
+
+        visible: false
+
+        TextureProviderObserver {
+            id: textureProviderObserver
+            source: shaderEffect.source
+        }
     }
 
-    ShaderEffectSource {
-        id: ds1layer
+    component DownsamplerShaderEffect : DefaultShaderEffect {
+        fragmentShader: source ? "qrc:///shaders/DualKawaseBlur_downsample.frag.qsb"
+                               : "" // to prevent warning if source becomes null
+    }
+
+    component UpsamplerShaderEffect : DefaultShaderEffect {
+        fragmentShader: source ? "qrc:///shaders/DualKawaseBlur_upsample.frag.qsb"
+                               : "" // to prevent warning if source becomes null
+    }
+
+    component DefaultShaderEffectSource : ShaderEffectSource {
+        // This is necessary to release resources even if `sourceItem` becomes null (non-live case):
+        parent: sourceItem ? root : null
 
-        sourceItem: ds1
         visible: false
+        live: root.live
         smooth: true
+    }
 
-        live: root.live
+    DownsamplerShaderEffect {
+        id: ds1
+
+        // When downsampled, we can decrease the size here so that the layer occupies less VRAM:
+        width: parent.width / 2
+        height: parent.height / 2
 
-        // 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)
+        source: root.source
+
+        // TODO: Instead of normalizing here, we could use GLSL 1.30's `textureSize()`
+        //       and normalize in the vertex shader, but we can not because we are
+        //       targeting GLSL 1.20/ESSL 1.0, even though the shader is written in
+        //       GLSL 4.40.
+        normalRect: (root.sourceRect.width > 0.0 && root.sourceRect.height > 0.0) ? Qt.rect(root.sourceRect.x / sourceTextureSize.width,
+                                                                                            root.sourceRect.y / sourceTextureSize.height,
+                                                                                            root.sourceRect.width / sourceTextureSize.width,
+                                                                                            root.sourceRect.height / sourceTextureSize.height)
+                                                                                  : Qt.rect(0.0, 0.0, 0.0, 0.0)
+    }
+
+    DefaultShaderEffectSource {
+        id: ds1layer
+
+        sourceItem: ds1
+
+        // Last layer for two pass mode, so use the viewport rect:
+        sourceRect: (root.mode === DualKawaseBlur.Mode.TwoPass) ? root._localViewportRect : Qt.rect(0, 0, 0, 0)
         
         function scheduleChainedUpdate() {
             if (!ds1layer) // context is lost, Qt bug (reproduced with 6.2)
                 return
 
+            ds1.sourceTextureSize = ds1.tpObserver.nativeTextureSize
+            if (ds1.ensurePolished)
+                ds1.ensurePolished()
+
             // Common for both four and two pass mode:
             ds1layer.parent = root
             ds1layer.scheduleUpdate()
@@ -266,7 +297,7 @@ Item {
                 root._window.afterAnimating.disconnect(ds1layer, ds1layer.scheduleChainedUpdate)
 
                 // In four pass mode, we can release the two intermediate layers:
-                if (root.configuration === DualKawaseBlur.Configuration.FourPass) {
+                if (root.mode === DualKawaseBlur.Mode.FourPass) {
                     // Scheduling update must be done sequentially for each layer in
                     // a chain. It seems that each layer needs one frame for it to be
                     // used as a source in another layer, so we can not schedule
@@ -279,7 +310,7 @@ Item {
         }
     }
 
-    ShaderEffect {
+    DownsamplerShaderEffect {
         id: ds2
 
         // When downsampled, we can decrease the size here so that the layer occupies less VRAM:
@@ -287,45 +318,18 @@ Item {
         height: ds1.height / 2
 
         // Qt uses reference counting, otherwise ds1layer may not be released, even if it has no parent (see `QQuickItemPrivate::derefWindow()`):
-        readonly property Item source: ((root.configuration === DualKawaseBlur.Configuration.TwoPass) || !ds1layer.parent) ? null : ds1layer
-        property rect normalRect // not necessary here, added because of the warning
-        readonly property int radius: root.radius
-
-        // TODO: We could use `textureSize()` and get rid of this, but we
-        //       can not because we are targeting GLSL 1.20/ESSL 1.0, even
-        //       though the shader is written in GLSL 4.40.
-        TextureProviderObserver {
-            id: ds2SourceObserver
-            source: ds2.source
-        }
-
-        property size sourceTextureSize
-
-        // cullMode: ShaderEffect.BackFaceCulling // QTBUG-136611 (Layering breaks culling with OpenGL)
-
-        visible: false
-
-        fragmentShader: source ? "qrc:///shaders/DualKawaseBlur_downsample.frag.qsb" : "" // to prevent warning if source becomes null
-
-        supportsAtlasTextures: true
-
-        blending: root.blending
+        source: ((root.mode === DualKawaseBlur.Mode.TwoPass) || !ds1layer.parent) ? null : ds1layer
     }
 
-    ShaderEffectSource {
+    DefaultShaderEffectSource {
         id: ds2layer
 
-        // So that if configuration is two pass (this is not used), the buffer is released:
-        // This is mainly relevant for switching configuration case, as initially if this was
+        // So that if the mode is two pass (this is not used), the buffer is released:
+        // This is mainly relevant for switching the mode case, as initially if this was
         // never visible and was never used as texture provider, it should have never allocated
         // resources to begin with.
-        sourceItem: (root.configuration === DualKawaseBlur.Configuration.FourPass) ? ds2 : null
-        parent: (!inhibitParent && sourceItem) ? root : null // this seems necessary to release resources even if sourceItem becomes null (non-live case)
-
-        visible: false
-        smooth: true
-
-        live: root.live
+        sourceItem: (root.mode === DualKawaseBlur.Mode.FourPass) ? ds2 : null
+        parent: (!inhibitParent && sourceItem) ? root : null // necessary to release resources even if `sourceItem` becomes null (non-live case)
 
         property bool inhibitParent: false
 
@@ -333,6 +337,10 @@ Item {
             if (!ds2layer) // context is lost, Qt bug (reproduced with 6.2)
                 return
 
+            ds2.sourceTextureSize = ds2.tpObserver.nativeTextureSize
+            if (ds2.ensurePolished)
+                ds2.ensurePolished()
+
             ds2layer.inhibitParent = false
             ds2layer.scheduleUpdate()
 
@@ -343,66 +351,37 @@ Item {
         }
     }
 
-    ShaderEffect {
+    UpsamplerShaderEffect {
         id: us1
 
         width: ds2.width * 2
         height: ds2.height * 2
 
         // Qt uses reference counting, otherwise ds2layer may not be released, even if it has no parent (see `QQuickItemPrivate::derefWindow()`):
-        readonly property Item source: ((root.configuration === DualKawaseBlur.Configuration.TwoPass) || !ds2layer.parent) ? null : ds2layer
-        property rect normalRect // not necessary here, added because of the warning
-        readonly property int radius: root.radius
-
-        // TODO: We could use `textureSize()` and get rid of this, but we
-        //       can not because we are targeting GLSL 1.20/ESSL 1.0, even
-        //       though the shader is written in GLSL 4.40.
-        TextureProviderObserver {
-            id: us1SourceObserver
-            source: us1.source
-        }
-
-        property size sourceTextureSize
-
-        // cullMode: ShaderEffect.BackFaceCulling // QTBUG-136611 (Layering breaks culling with OpenGL)
-
-        visible: false
-
-        fragmentShader: source ? "qrc:///shaders/DualKawaseBlur_upsample.frag.qsb" : "" // to prevent warning if source becomes null
-
-        supportsAtlasTextures: true
-
-        blending: root.blending
+        source: ((root.mode === DualKawaseBlur.Mode.TwoPass) || !ds2layer.parent) ? null : ds2layer
     }
 
-    ShaderEffectSource {
+    DefaultShaderEffectSource {
         id: us1layer
 
-        // So that if configuration is two pass (this is not used), the buffer is released:
-        // This is mainly relevant for switching configuration case, as initially if this was
+        // So that if the mode is two pass (this is not used), the buffer is released:
+        // This is mainly relevant for switching the mode case, as initially if this was
         // never visible and was never used as texture provider, it should have never allocated
         // resources to begin with.
-        sourceItem: (root.configuration === DualKawaseBlur.Configuration.FourPass) ? us1 : null
-        parent: sourceItem ? root : null // this seems necessary to release resources even if sourceItem becomes null (non-live case)
+        sourceItem: (root.mode === DualKawaseBlur.Mode.FourPass) ? us1 : null
 
-        visible: false
-        smooth: true
-
-        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)
+        // Last layer for four pass mode, so use the viewport rect:
+        // No need to check for the mode because this layer is not used in two pass mode anyway.
+        sourceRect: root._localViewportRect
 
         function scheduleChainedUpdate() {
             if (!us1layer) // context is lost, Qt bug (reproduced with 6.2)
                 return
 
+            us1.sourceTextureSize = us1.tpObserver.nativeTextureSize
+            if (us1.ensurePolished)
+                us1.ensurePolished()
+
             us1layer.scheduleUpdate()
 
             if (root._window) {
@@ -415,6 +394,8 @@ Item {
             if (!ds1layer || !ds2layer) // context is lost, Qt bug (reproduced with 6.2)
                 return
 
+            us2.sourceTextureSize = us2.tpObserver.nativeTextureSize
+
             // Last layer is updated, now it is time to release the intermediate buffers:
             console.debug(root, ": releasing intermediate layers, expect the video memory consumption to drop.")
 
@@ -435,7 +416,7 @@ Item {
         }
     }
 
-    ShaderEffect {
+    UpsamplerShaderEffect {
         id: us2
 
         // {us1/ds1}.size * 2
@@ -443,34 +424,16 @@ Item {
         width: (root.viewportRect.width > 0) ? root.viewportRect.width : parent.width
         height: (root.viewportRect.height > 0) ? root.viewportRect.height : parent.height
 
-        visible: us2SourceObserver.isValid
-
-        readonly property Item source: (root.configuration === DualKawaseBlur.Configuration.TwoPass) ? ds1layer : us1layer
-        property rect normalRect // not necessary here, added because of the warning
-        readonly property int radius: root.radius
-
-        // TODO: We could use `textureSize()` and get rid of this, but we
-        //       can not because we are targeting GLSL 1.20/ESSL 1.0, even
-        //       though the shader is written in GLSL 4.40.
-        TextureProviderObserver {
-            id: us2SourceObserver
-            source: us2.source
-        }
-
-        property size sourceTextureSize
+        visible: tpObserver.isValid
 
-        // cullMode: ShaderEffect.BackFaceCulling // QTBUG-136611 (Layering breaks culling with OpenGL)
+        source: (root.mode === DualKawaseBlur.Mode.TwoPass) ? ds1layer : us1layer
 
-        property color tint: "transparent"
-        property real tintStrength: 0.0
-        property real noiseStrength: 0.0
-        property real exclusionStrength: 0.0
+        property alias tint: root.tint
+        property alias tintStrength: root.tintStrength
+        property alias noiseStrength: root.noiseStrength
+        property alias exclusionStrength: root.exclusionStrength
 
         fragmentShader: root.postprocess ? "qrc:///shaders/DualKawaseBlur_upsample_postprocess.frag.qsb"
                                          : "qrc:///shaders/DualKawaseBlur_upsample.frag.qsb"
-
-        supportsAtlasTextures: true
-
-        blending: root.blending
     }
 }



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/1dd7f9d121a7a4647168907599a5f4383539b122...28a5cc2b31de26dfbb911c762f1d48c25aa0a696

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/1dd7f9d121a7a4647168907599a5f4383539b122...28a5cc2b31de26dfbb911c762f1d48c25aa0a696
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