|
|
|
|
Quelle textureproviderobserver.hpp
Sprache: C
|
|
/*****************************************************************************
* Copyright (C) 2025 VLC authors and VideoLAN
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* ( at your option ) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef TEXTUREPROVIDEROBSERVER_HPP
#define TEXTUREPROVIDEROBSERVER_HPP
#include <QObject>
#include <QReadWriteLock>
#include <QPointer>
#include <QSize>
#include <QQuickItem>
class QSGTextureProvider;
// This utility class observes a texture provider and exposes its texture's properties
// so that they can be accessed in QML where texture introspection is not possible.
class TextureProviderObserver : public QObject
{
Q_OBJECT
// NOTE: source must be a texture provider item.
// NOTE: If source's texture provider changes, it is required to re-set the property.
Q_PROPERTY(const QQuickItem* source MEMBER m_source WRITE setSource NOTIFY sourceChanged FINAL)
// WARNING: Texture properties are updated in the rendering thread.
// WARNING: Individual properties do not necessarily reflect the same texture at any arbitrary time.
// In other words, properties are not updated atomically as a whole but independently
// once the source texture changes. It depends on when you are reading the properties,
// such that if you read properties before `updateTextureSize()` returns, the properties
// may be inconsistent with each other (some reflecting the old texture, some the new
// texture). This is done to reduce blocking the GUI thread from the rendering thread,
// which is a problem unlike vice versa, and is considered acceptable because sampling
// is supposed to be done periodically anyway (the sampling point must be chosen carefully
// to not conflict with the updates, if the properties must reflect the immediately up-to-date
// texture and the properties change each frame, as otherwise it might end up in a
// "forever chase"), so by the time the sampling is done the properties should be consistent.
// NOTE: If the source texture is a dynamic texture (such as an item layer), these properties are
// notified at most per frame, provided that `notifyAllChanges` is set. This is done to prevent
// event backlogging. If the source texture is not a dynamic texture, these are notified as soon
// as applicable, where in that case `notifyAllChanges` is not required to be set. The method
// `::sampleAndNotifyProperties()` may also be called to notify changes at an arbitrary time.
// It is guaranteed that notifications are emitted from the GUI thread, unless an arbitrary
// `sampleAndNotifyProperties()` call was made in a different thread. Note that if the texture
// is a dynamic texture and `notifyAllChanges` is set, the notifications will be signalled from
// the thread of the window of the source texture provider item, particularly during
// `QQuickWindow::afterAnimating()`, which is guaranteed to be GUI thread but not necessarily
// the thread of the items that use this observer. For that reason it is not recommended to
// use the observer for an item that belongs to the same window of the source texture provider
// item in such a case (texture is dynamic and `notifyAllChanges` is set). Also note that if
// the source texture is of `QSGDynamicTexture` type but is not meant to be dynamic (such as
// an item layer with `live: false`), it should ideally be provided as a hint to the observer
// observer by unsetting `notifyAllChanges`, so that it does not do sampling per frame. That
// being said, since event backlogging is still not possible, such benefit is minimal and
// should only be done if it does not hinder maintenance.
Q_PROPERTY(bool notifyAllChanges MEMBER m_notifyAllChanges NOTIFY notifyAllChangesChanged FINAL)
Q_PROPERTY(QSize textureSize READ textureSize NOTIFY textureSizeChanged FINAL) // Scene graph texture size
Q_PROPERTY(QSize nativeTextureSize READ nativeTextureSize NOTIFY nativeTextureSizeChanged FINAL) // Native texture size (e.g. for atlas textures, the atlas size)
Q_PROPERTY(QRectF normalizedTextureSubRect READ normalizedTextureSubRect NOTIFY normalizedTextureSubRectChanged FINAL)
Q_PROPERTY(qint64 comparisonKey READ comparisonKey NOTIFY comparisonKeyChanged FINAL)
// NOTE: Since it is not expected that these properties change rapidly, they have notify signals.
// These signals may be emitted in the rendering thread, thus if the connection is auto
// connection (default), it may be queued in the receiver thread:
Q_PROPERTY(bool hasAlphaChannel READ hasAlphaChannel NOTIFY hasAlphaChannelChanged FINAL)
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(bool isDynamic READ isDynamic NOTIFY isDynamicChanged FINAL) // whether the texture is `QSGDynamicTexture`
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;
QRectF normalizedTextureSubRect() const;
bool hasAlphaChannel() const;
bool hasMipmaps() const;
bool isAtlasTexture() const;
bool isValid() const;
bool isDynamic() const;
public slots:
// This is mostly relevant for dynamic textures, since for static textures
// notification is done regardless of sampling. This method is thread-safe.
void sampleAndNotifyProperties();
signals:
// Similar to property notify signals, if the texture is dynamic, this
// signal is compressed and emitted at once once per frame from the GUI
// thread. If the texture is not dynamic, it is not compressed and emitted
// from the sg/rendering thread. You may use auto connection to connect
// to this signal, because there is no risk for backlogging (static
// textures are not expected to change rapidly).
void textureChanged();
void notifyAllChangesChanged();
void sourceChanged();
void textureSizeChanged(const QSize&);
void nativeTextureSizeChanged(const QSize&);
void normalizedTextureSubRectChanged(const QRectF&);
void hasAlphaChannelChanged(bool);
void hasMipmapsChanged(bool);
void isAtlasTextureChanged(bool);
void isValidChanged(bool);
void isDynamicChanged(bool);
void comparisonKeyChanged(qint64);
private slots:
void updateProperties();
void resetProperties(std::memory_order memoryOrder = std::memory_order_seq_cst);
// This must be called from the same thread the source item lives:
void adjustSampleAndNotifyConnection();
private:
QPointer<const QQuickItem> m_source;
QPointer<QSGTextureProvider> m_provider;
QPointer<QQuickWindow> m_window;
QMetaObject::Connection m_windowSampleAndNotifyPropertiesConnection;
// It is not clear when `QSGTextureProvider::textureChanged()` can be signalled.
// If it is only signalled during SG synchronization where Qt blocks the GUI thread,
// we do not need explicit synchronization (with atomic, or mutex) here. If it can be
// signalled at any time, we can still rely on SG synchronization (instead of explicit
// synchronization) by waiting until the next synchronization, but the delay might be
// more in that case. At the same time, the source might be living in a different window
// where the SG synchronization would not be blocking the (GUI) thread where this
// observer lives.
std::atomic<bool> m_textureIsDynamic = false;
std::atomic<bool> m_notifyAllChanges = true;
std::atomic<QSize> m_textureSize {{}}; // invalid by default
std::atomic<QSize> m_oldTextureSize {{}}; // invalid by default
std::atomic<QSize> m_nativeTextureSize {{}}; // invalid by default
std::atomic<QSize> m_oldNativeTextureSize {{}}; // invalid by default
std::atomic<qint64> m_comparisonKey {-1};
std::atomic<qint64> m_oldComparisonKey {-1};
std::atomic<QRectF> m_normalizedTextureSubRect {{}}; // invalid by default
std::atomic<QRectF> m_oldNormalizedTextureSubRect {{}}; // invalid by default
std::atomic<bool> m_hasAlphaChannel = false;
std::atomic<bool> m_hasMipmaps = false;
std::atomic<bool> m_isAtlasTexture = false;
std::atomic<bool> m_isValid = false;
std::atomic<bool> m_pendingNotifyTextureChange = false;
};
#endif // TEXTUREPROVIDEROBSERVER_HPP
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet am 2026-11-04)
¤
*© Formatika GbR, Deutschland
|
|
2026-11-01
|
|
|
|
|
Neuigkeiten |
| Aktuelles |
| Motto des Tages |
|
Open Source Software |
|
|
|
Jenseits des Üblichen ....
|
|
Besucherstatistik |
|
|
| Statistik |
| #Sources=1104857 |
| #Domains=418205 |
|
|