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