Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
6385ce8e by Fatih Uzunoglu at 2026-02-05T19:04:05+00:00
qml: apply background coloring even when effect is not ready in `DualKawaseBlur`

This fixes exposing what is underneath, provided that the source texture is not
ready or was invalidated.

This is especially problematic when switching to the player page, because bare
window may be exposed, which means in most cases what is beneath the interface
window getting exposed (such as the desktop). We currently rely on transparency
to expose the video window, so this is not limited to window backdrop blur case.

This should also fix the same in other situations, such as switching the artist
in the artists page, but I can still reproduce the issue. It appears that this 
is
because when `Image` source changes to another valid source, its texture (it is 
a
texture provider) is not invalidated in between (even though we are not using
`retainWhileLoading`). This issue is handled with a workaround in a later 
commit.

Note that if blending is set to false, which may be due to automatic 
adjustments,
any translucent fragment is going to punch a hole in the interface. This may be
a problem during texture changes, since `DualKawaseBlur` makes the decision on
whether to use blending or not based on what the texture says. This decision is
routed through the QML engine due to technical reasons, so it is not possible to
fix this problem completely. If that is a problem, `blending` may be set to 
`true`
manually or the source texture may be invalidated before switching to a new
texture with different properties (such as old texture is opaque while new one
is translucent). Currently this is not a symptomatic matter therefore is not
considered to be a problem.

- - - - -
6f1cc4bc by Fatih Uzunoglu at 2026-02-05T19:04:05+00:00
qml: provide background coloring with no rhi in `DualKawaseBlur`

This eases maintenance, because we don't need to have additional
rectangle where `DualKawaseBlur` is reused depending on the
graphics backend when background coloring is wanted.

- - - - -
e657f180 by Fatih Uzunoglu at 2026-02-05T19:04:05+00:00
qml: get rid of unnecessary rectangle in `Player`

- - - - -
3bada44b by Fatih Uzunoglu at 2026-02-05T19:04:05+00:00
qt: reset properties when applicable in `TextureProviderObserver`

This is mostly relevant when the new source is null (no more source),
since if there is a new source the properties would be updated in
most cases in 1/FPS, if everything goes well, provided that the new
source is a texture provider.

- - - - -
3089d43e by Fatih Uzunoglu at 2026-02-05T19:04:05+00:00
qt: emit `sourceChanged()` in more appropriate time in `TextureProviderObserver`

It is not deterministic relative to the time it takes to update the properties,
currently in some cases properties may be updated before `sourceChanged()` is
emitted and in some cases not, depending on the pace of the rendering and the
rendering thread.

If we signal the change immediately after the source changes, we don't have
this problem.

Currently this is asymptomatic, but better to correct it.

- - - - -
8c8eba64 by Fatih Uzunoglu at 2026-02-05T19:04:05+00:00
qt: handle fast x-y-x source switch in `TextureProviderObserver`

- - - - -
6772512f by Fatih Uzunoglu at 2026-02-05T19:04:05+00:00
qml: manually invalidate the blur on artist change in `ArtistTopBanner`

This fixes window getting exposed when switching
artists in the artist page due to blending being
off when there are translucent fragments.

- - - - -


5 changed files:

- modules/gui/qt/medialibrary/qml/ArtistTopBanner.qml
- modules/gui/qt/player/qml/Player.qml
- modules/gui/qt/util/textureproviderobserver.cpp
- modules/gui/qt/util/textureproviderobserver.hpp
- modules/gui/qt/widgets/qml/DualKawaseBlur.qml


Changes:

=====================================
modules/gui/qt/medialibrary/qml/ArtistTopBanner.qml
=====================================
@@ -61,6 +61,26 @@ FocusScope {
         source: root.artist.id ? (root.artist.cover || VLCStyle.noArtArtist) : 
"" // do not load the fallback image during initialization
         sourceSize: artist.cover ? 
Qt.size(Helpers.alignUp(Screen.desktopAvailableWidth / 2, 32), 0)
                                  : undefined
+
+        onSourceChanged: {
+            // FIXME: Why are we doing this? Because Image texture provider
+            //        does not appear to invalidate its texture when source
+            //        changes in between, even though `retainWhileLoading`
+            //        is not used (not to mention it is debatable whether
+            //        it would be effective with regard to the texture
+            //        provider's texture).
+
+            // FIXME: I am not happy about breaking a potential binding,
+            //        when we could instead use `Binding` as an inhibitor.
+            //        Unfortunately `Binding` is broken in this case with
+            //        certain Qt versions (including 6.10.1), possibly the
+            //        same bug as described in QTBUG-140858.
+
+            const blurEffectSource = blurEffect.source
+            blurEffect.source = null
+            blurEffect.source = blurEffectSource
+        }
+
         mipmap: !!artist.cover
 
         fillMode: artist.cover ? Image.PreserveAspectCrop : Image.Tile


=====================================
modules/gui/qt/player/qml/Player.qml
=====================================
@@ -281,81 +281,73 @@ FocusScope {
                 property real bottomPadding: 
playerSpecializationLoader.bottomPadding
 
                 // background image
-                Rectangle {
-                    focus: false
-                    // NOTE: Rectangle has an optimization that it does not 
use a scene graph node if the color is transparent.
-                    color: blurredBackground.available ? "transparent" // 
background coloring in blur effect is used otherwise
-                                                       : bgtheme.bg.primary
-                    anchors.fill: parent
+                Widgets.DualKawaseBlur {
+                    id: blurredBackground
+
+                    radius: 3
+
+                    live: false
+
+                    //destination aspect ratio
+                    readonly property real dar: parent.width / parent.height
+
+                    anchors.centerIn: parent
+                    width: (cover.sar < dar) ? parent.width :  parent.height * 
cover.sar
+                    height: (cover.sar < dar) ? parent.width / cover.sar :  
parent.height
+
+                    source: textureProviderItem
+
+                    postprocess: true
+                    tint: bgtheme.palette.isDark ? "black" : "white"
+                    tintStrength: 0.5
+                    backgroundColor: bgtheme.bg.primary
 
                     readonly property ColorContext colorContext: ColorContext {
                         id: bgtheme
                         colorSet: ColorContext.View
                     }
 
-                    Widgets.DualKawaseBlur {
-                        id: blurredBackground
+                    // 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
+
+                        // This should not be necessary anymore since 
`DualKawaseBlur`
+                        // does not create layer for the source implicitly as 
`MultiEffect`
+                        // or `FastBlur` does as they deem necessary (in this 
case, it
+                        // is not necessary). But due to a Qt bug when 
`mipmap: true` is
+                        // used where texture sampling becomes broken, we need 
this as
+                        // `QSGTextureView` has a workaround for that bug. 
This is totally
+                        // acceptable as there is virtually no overhead.
+                        source: cover
+                    }
 
-                        radius: 3
+                    Component.onCompleted: {
+                        // Blur layers are effect-size dependent, so once the 
user starts resizing the window (hence the effect),
+                        // we should either momentarily turn on live, or 
repeatedly call `scheduleUpdate()`. Due to the optimization,
+                        // calling `scheduleUpdate()` would continuously 
create and release intermediate layers, which would be a
+                        // really bad idea. So instead, we turn on live and 
after some time passes turn it off again.
+                        widthChanged.connect(liveTimer, 
liveTimer.transientTurnOnLive)
+                        heightChanged.connect(liveTimer, 
liveTimer.transientTurnOnLive)
+                    }
 
-                        live: false
-                        
-                        //destination aspect ratio
-                        readonly property real dar: parent.width / 
parent.height
+                    Timer {
+                        id: liveTimer
 
-                        anchors.centerIn: parent
-                        width: (cover.sar < dar) ? parent.width :  
parent.height * cover.sar
-                        height: (cover.sar < dar) ? parent.width / cover.sar : 
 parent.height
-
-                        source: textureProviderItem
-
-                        postprocess: true
-                        tint: bgtheme.palette.isDark ? "black" : "white"
-                        tintStrength: 0.5
-                        backgroundColor: bgtheme.bg.primary
-
-                        // 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
-
-                            // This should not be necessary anymore since 
`DualKawaseBlur`
-                            // does not create layer for the source implicitly 
as `MultiEffect`
-                            // or `FastBlur` does as they deem necessary (in 
this case, it
-                            // is not necessary). But due to a Qt bug when 
`mipmap: true` is
-                            // used where texture sampling becomes broken, we 
need this as
-                            // `QSGTextureView` has a workaround for that bug. 
This is totally
-                            // acceptable as there is virtually no overhead.
-                            source: cover
-                        }
+                        repeat: false
+                        interval: VLCStyle.duration_humanMoment
 
-                        Component.onCompleted: {
-                            // Blur layers are effect-size dependent, so once 
the user starts resizing the window (hence the effect),
-                            // we should either momentarily turn on live, or 
repeatedly call `scheduleUpdate()`. Due to the optimization,
-                            // calling `scheduleUpdate()` would continuously 
create and release intermediate layers, which would be a
-                            // really bad idea. So instead, we turn on live 
and after some time passes turn it off again.
-                            widthChanged.connect(liveTimer, 
liveTimer.transientTurnOnLive)
-                            heightChanged.connect(liveTimer, 
liveTimer.transientTurnOnLive)
+                        function transientTurnOnLive() {
+                            if (!blurredBackground.sourceTextureIsValid)
+                                return
+                            blurredBackground.live = true
+                            liveTimer.restart()
                         }
 
-                        Timer {
-                            id: liveTimer
-
-                            repeat: false
-                            interval: VLCStyle.duration_humanMoment
-
-                            function transientTurnOnLive() {
-                                if (!blurredBackground.sourceTextureIsValid)
-                                    return
-                                blurredBackground.live = true
-                                liveTimer.restart()
-                            }
-
-                            onTriggered: {
-                                blurredBackground.live = false
-                            }
+                        onTriggered: {
+                            blurredBackground.live = false
                         }
                     }
                 }


=====================================
modules/gui/qt/util/textureproviderobserver.cpp
=====================================
@@ -39,25 +39,28 @@ void TextureProviderObserver::setSource(const QQuickItem 
*source, bool enforce)
     if (!enforce && (m_source == source))
         return;
 
+    if (m_source)
     {
-        m_textureSize = QSize{}; // memory order does not matter, 
`setSource()` is not called frequently.
-
-        if (m_source)
+        if (Q_LIKELY(m_provider))
         {
-            if (Q_LIKELY(m_provider))
-            {
-                disconnect(m_provider, nullptr, this, nullptr);
-                m_provider = nullptr;
-            }
-            else
-            {
-                // source changed before we got its `QSGTextureProvider`
-                disconnect(m_source, nullptr, this, nullptr);
-            }
+            disconnect(m_provider, nullptr, this, nullptr);
+            m_provider = nullptr;
+
+            // We would like to reset the properties in these conditions:
+            // - There is a new valid source. We want to reset because 
updating the properties is
+            //   asynchronous. Even if update occurs, it may take a while, and 
it may never happen.
+            // - There is no more source.
+            resetProperties(); // memory order does not matter, `setSource()` 
is not called frequently.
+        }
+        else
+        {
+            // source changed before we got its `QSGTextureProvider`
+            disconnect(m_source, nullptr, this, nullptr);
         }
     }
 
     m_source = source;
+    emit sourceChanged();
 
     if (m_source)
     {
@@ -91,10 +94,17 @@ void TextureProviderObserver::setSource(const QQuickItem 
*source, bool enforce)
                     return;
                 }
 
-                assert(!m_provider);
+                const auto provider = m_source->textureProvider(); // This can 
only be called in the rendering thread.
+                assert(provider);
 
-                m_provider = m_source->textureProvider(); // This can only be 
called in the rendering thread.
-                assert(m_provider);
+                assert(!m_provider || (provider == m_provider)); // If 
providers are different, source must be different too (which is handled above).
+                if (Q_UNLIKELY(provider == m_provider)) // Unlikely, source 
set to something else (such as null), and then set back to its original value 
really fast.
+                {
+                    updateProperties(); // Not sure if really necessary, but 
we do not lose anything.
+                    return;
+                }
+
+                m_provider = provider;
 
                 connect(m_provider, &QSGTextureProvider::textureChanged, this, 
&TextureProviderObserver::updateProperties, Qt::DirectConnection);
 
@@ -107,8 +117,6 @@ void TextureProviderObserver::setSource(const QQuickItem 
*source, bool enforce)
         else
             connect(m_source, &QQuickItem::windowChanged, this, init, 
Qt::SingleShotConnection);
     }
-
-    emit sourceChanged();
 }
 
 QSize TextureProviderObserver::textureSize() const
@@ -230,6 +238,11 @@ void TextureProviderObserver::updateProperties()
         }
     }
 
+    resetProperties(memoryOrder);
+}
+
+void TextureProviderObserver::resetProperties(std::memory_order memoryOrder)
+{
     m_textureSize.store({}, memoryOrder);
     m_nativeTextureSize.store({}, memoryOrder);
 


=====================================
modules/gui/qt/util/textureproviderobserver.hpp
=====================================
@@ -90,6 +90,7 @@ signals:
 
 private slots:
     void updateProperties();
+    void resetProperties(std::memory_order memoryOrder = 
std::memory_order_seq_cst);
 
 private:
     QPointer<const QQuickItem> m_source;


=====================================
modules/gui/qt/widgets/qml/DualKawaseBlur.qml
=====================================
@@ -437,6 +437,16 @@ Item {
         }
     }
 
+    // This rectangle is going to be visible until the blur effect is ready, 
so that
+    // we don't expose what is beneath meanwhile.
+    Rectangle {
+        anchors.fill: us2
+
+        visible: !us2.visible && root.postprocess && (root.backgroundColor.a > 
0.0)
+
+        color: root.backgroundColor
+    }
+
     UpsamplerShaderEffect {
         id: us2
 
@@ -445,7 +455,7 @@ Item {
         width: (root.viewportRect.width > 0) ? root.viewportRect.width : 
parent.width
         height: (root.viewportRect.height > 0) ? root.viewportRect.height : 
parent.height
 
-        visible: tpObserver.isValid
+        visible: tpObserver.isValid && root.available
 
         source: (root.mode === DualKawaseBlur.Mode.TwoPass) ? ds1layer : 
us1layer
 



View it on GitLab: 
https://code.videolan.org/videolan/vlc/-/compare/0aadefe758b10697b6c0b2515ac94be9186cd1f7...6772512faedd6c2c362f0767981346308c963b7f

-- 
View it on GitLab: 
https://code.videolan.org/videolan/vlc/-/compare/0aadefe758b10697b6c0b2515ac94be9186cd1f7...6772512faedd6c2c362f0767981346308c963b7f
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance
_______________________________________________
vlc-commits mailing list
[email protected]
https://mailman.videolan.org/listinfo/vlc-commits

Reply via email to