Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC


Commits:
4efe34d7 by Fatih Uzunoglu at 2026-01-23T00:54:13+01:00
qt: add property `comparisonKey` to `TextureProviderObserver`

Even if the target is a layer and has rapidly changing size,
due to resize or other reasons, the comparison key remains
the same. For that reason, it is safe to have a notify
signal for this property.

- - - - -
c3b1c713 by Fatih Uzunoglu at 2026-01-23T00:54:13+01:00
qml: use texture comparison key for scheduling update in `DualKawaseBlur`

This is safer than arbitrarily waiting one frame and expecting that the
texture is updated.

- - - - -


4 changed files:

- 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/player/qml/Player.qml
=====================================
@@ -424,19 +424,12 @@ FocusScope {
                                     cover.source = targetSource
                                 }
 
+                                onSourceChanged: {
+                                    blurredBackground.scheduleUpdate(true) // 
onNextTextureChange
+                                }
+
                                 onStatusChanged: {
-                                    if (status === Image.Ready) {
-                                        // This also covers source (and other 
parameters) change and not only initial loading
-                                        if 
(blurredBackground.sourceTextureIsValid) {
-                                            // Possible image switch and stale 
texture (especially old Qt without patch c871a52), we
-                                            // should wait one frame for the 
texture to be updated to avoid applying blur on stale one.
-                                            
blurredBackground.scheduleUpdate(true)
-                                        } else {
-                                            // If not valid, the blur effect 
is going to wait appropriately until valid itself:
-                                            // Initial case (such as switching 
to player page), or switching images with recent Qt.
-                                            
blurredBackground.scheduleUpdate(false)
-                                        }
-                                    } else if (status === Image.Error) {
+                                    if (status === Image.Error) {
                                         cover.source = VLCStyle.noArtAlbumCover
                                     }
                                 }


=====================================
modules/gui/qt/util/textureproviderobserver.cpp
=====================================
@@ -121,44 +121,34 @@ QSize TextureProviderObserver::textureSize() const
 
 QSize TextureProviderObserver::nativeTextureSize() const
 {
-    // This is likely called in the QML/GUI thread.
-    // QML/GUI thread can freely block the rendering thread to the extent the 
time is reasonable and a
-    // fraction of `1/FPS`, because it is already throttled by v-sync (so it 
would just throttle less).
     return m_nativeTextureSize.load(std::memory_order_acquire);
 }
 
 bool TextureProviderObserver::hasAlphaChannel() const
 {
-    // This is likely called in the QML/GUI thread.
-    // QML/GUI thread can freely block the rendering thread to the extent the 
time is reasonable and a
-    // fraction of `1/FPS`, because it is already throttled by v-sync (so it 
would just throttle less).
     return m_hasAlphaChannel.load(std::memory_order_acquire);
 }
 
 bool TextureProviderObserver::hasMipmaps() const
 {
-    // This is likely called in the QML/GUI thread.
-    // QML/GUI thread can freely block the rendering thread to the extent the 
time is reasonable and a
-    // fraction of `1/FPS`, because it is already throttled by v-sync (so it 
would just throttle less).
     return m_hasMipmaps.load(std::memory_order_acquire);
 }
 
 bool TextureProviderObserver::isAtlasTexture() const
 {
-    // This is likely called in the QML/GUI thread.
-    // QML/GUI thread can freely block the rendering thread to the extent the 
time is reasonable and a
-    // fraction of `1/FPS`, because it is already throttled by v-sync (so it 
would just throttle less).
     return m_isAtlasTexture.load(std::memory_order_acquire);
 }
 
 bool TextureProviderObserver::isValid() const
 {
-    // This is likely called in the QML/GUI thread.
-    // QML/GUI thread can freely block the rendering thread to the extent the 
time is reasonable and a
-    // fraction of `1/FPS`, because it is already throttled by v-sync (so it 
would just throttle less).
     return m_isValid.load(std::memory_order_acquire);
 }
 
+qint64 TextureProviderObserver::comparisonKey() const
+{
+    return m_comparisonKey.load(std::memory_order_acquire);
+}
+
 void TextureProviderObserver::updateProperties()
 {
     // This is likely called in the rendering thread.
@@ -228,6 +218,14 @@ void TextureProviderObserver::updateProperties()
             if (!m_isValid.exchange(true, memoryOrder))
                 emit isValidChanged(true);
 
+            {
+                // Comparison key
+                const qint64 comparisonKey = texture->comparisonKey();
+
+                if (m_comparisonKey.exchange(comparisonKey, memoryOrder) != 
comparisonKey)
+                    emit comparisonKeyChanged(comparisonKey);
+            }
+
             return;
         }
     }
@@ -246,4 +244,7 @@ void TextureProviderObserver::updateProperties()
 
     if (m_isValid.exchange(false, memoryOrder))
         emit isValidChanged(false);
+
+    if (m_comparisonKey.exchange(-1, memoryOrder) != -1)
+        emit comparisonKeyChanged(-1);
 }


=====================================
modules/gui/qt/util/textureproviderobserver.hpp
=====================================
@@ -62,13 +62,19 @@ class TextureProviderObserver : public QObject
     Q_PROPERTY(bool hasMipmaps READ hasMipmaps NOTIFY hasMipmapsChanged FINAL)
     Q_PROPERTY(bool isAtlasTexture READ isAtlasTexture NOTIFY 
isAtlasTextureChanged FINAL)
     Q_PROPERTY(bool isValid READ isValid NOTIFY isValidChanged FINAL) // 
whether a texture is provided or not
+    Q_PROPERTY(qint64 comparisonKey READ comparisonKey NOTIFY 
comparisonKeyChanged FINAL)
 
 public:
     explicit TextureProviderObserver(QObject *parent = nullptr);
 
     void setSource(const QQuickItem* source, bool enforce = false);
+
+    // The following are likely called in the QML/GUI thread.
+    // QML/GUI thread can freely block the rendering thread to the extent the 
time is reasonable and a
+    // fraction of `1/FPS`, because it is already throttled by v-sync (so it 
would just throttle less).
     QSize textureSize() const;
     QSize nativeTextureSize() const;
+    qint64 comparisonKey() const;
     bool hasAlphaChannel() const;
     bool hasMipmaps() const;
     bool isAtlasTexture() const;
@@ -80,6 +86,7 @@ signals:
     void hasMipmapsChanged(bool);
     void isAtlasTextureChanged(bool);
     void isValidChanged(bool);
+    void comparisonKeyChanged(qint64);
 
 private slots:
     void updateProperties();
@@ -99,6 +106,7 @@ private:
 
     std::atomic<QSize> m_textureSize {{}}; // invalid by default
     std::atomic<QSize> m_nativeTextureSize {{}}; // invalid by default
+    std::atomic<qint64> m_comparisonKey {-1};
 
     std::atomic<bool> m_hasAlphaChannel = false;
     std::atomic<bool> m_hasMipmaps = false;


=====================================
modules/gui/qt/widgets/qml/DualKawaseBlur.qml
=====================================
@@ -115,11 +115,7 @@ Item {
         if (root.sourceTextureIsValid) {
             if (root._queuedScheduledUpdate) {
                 root._queuedScheduledUpdate = false
-
-                // Normally it should be fine to call `scheduleUpdate()` 
directly for
-                // the initial layer, even though the subsequent layers must 
be chained
-                // for update scheduling regardless, but old Qt seems to want 
it:
-                root.scheduleUpdate(true)
+                root.scheduleUpdate()
             }
         }
     }
@@ -128,7 +124,23 @@ Item {
 
     property bool _queuedScheduledUpdate: false
 
-    function scheduleUpdate(onNextAfterAnimating /* : bool */ = false) {
+    // WARNING: The QML property type is not `int` because the target property 
type is `qint64`.
+    readonly property var _comparisonKey: 
sourceTextureProviderObserver.comparisonKey
+    property var _oldComparisonKey
+
+    on_ComparisonKeyChanged: {
+        if (_comparisonKey >= 0) {
+            if (_oldComparisonKey !== undefined && _oldComparisonKey !== 
_comparisonKey) {
+                _oldComparisonKey = undefined
+                // If source texture is not valid, update will be requeued in 
`scheduleUpdate()`.
+                // That being said, a non-valid source texture should have 
(-1) as comparison key,
+                // which we already checked here.
+                root.scheduleUpdate()
+            }
+        }
+    }
+
+    function scheduleUpdate(onNextTextureChange /* : bool */ = false) {
         if (live)
             return // no-op
 
@@ -140,6 +152,11 @@ Item {
             return
         }
 
+        if (onNextTextureChange) {
+            root._oldComparisonKey = root._comparisonKey
+            return
+        }
+
         if (root._window) {
             // One possible case for this is that the mipmaps for the source 
texture were generated too fast, and
             // the consumer wants to update the blur to make use of the 
mipmaps before the blur finished chained
@@ -150,11 +167,7 @@ Item {
         }
 
         root._window = root.Window.window
-        if (onNextAfterAnimating) {
-            root._window.afterAnimating.connect(ds1layer, 
ds1layer.scheduleChainedUpdate)
-        } else {
-            ds1layer.scheduleChainedUpdate()
-        }
+        ds1layer.scheduleChainedUpdate()
     }
 
     onLiveChanged: {
@@ -162,7 +175,7 @@ Item {
             ds1layer.parent = root
             ds2layer.inhibitParent = false
         } else {
-            root.scheduleUpdate(false) // this triggers releasing intermediate 
layers (when applicable)
+            root.scheduleUpdate() // this triggers releasing intermediate 
layers (when applicable)
         }
     }
 
@@ -304,8 +317,6 @@ Item {
             ds1layer.scheduleUpdate()
 
             if (root._window) {
-                root._window.afterAnimating.disconnect(ds1layer, 
ds1layer.scheduleChainedUpdate)
-
                 // In four pass mode, we can release the two intermediate 
layers:
                 if (root.mode === DualKawaseBlur.Mode.FourPass) {
                     // Scheduling update must be done sequentially for each 
layer in
@@ -421,7 +432,7 @@ Item {
             if (root._queuedScheduledUpdate) {
                 // Tried calling `scheduleUpdate()` before the ongoing chained 
updates completed.
                 root._queuedScheduledUpdate = false
-                root.scheduleUpdate(false)
+                root.scheduleUpdate()
             }
         }
     }



View it on GitLab: 
https://code.videolan.org/videolan/vlc/-/compare/76933452612934d3a1bb84bc491ea8db2026de63...c3b1c713210add215a4c0b3cba0b2b84fc6c1222

-- 
View it on GitLab: 
https://code.videolan.org/videolan/vlc/-/compare/76933452612934d3a1bb84bc491ea8db2026de63...c3b1c713210add215a4c0b3cba0b2b84fc6c1222
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