From 634ca3bed323fc6795f41c698f4e6fd39470c1c3 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 29 Sep 2022 12:40:22 +0200 Subject: [PATCH] smarter matrial updates --- src/nodes/QskGradientMaterial.cpp | 211 ++++++++++++++++++++++-------- src/nodes/QskGradientMaterial.h | 32 +++-- src/nodes/QskShapeNode.cpp | 92 +++++++++++-- 3 files changed, 259 insertions(+), 76 deletions(-) diff --git a/src/nodes/QskGradientMaterial.cpp b/src/nodes/QskGradientMaterial.cpp index cbc510ab..58a1721a 100644 --- a/src/nodes/QskGradientMaterial.cpp +++ b/src/nodes/QskGradientMaterial.cpp @@ -229,9 +229,8 @@ namespace class GradientMaterial : public QskGradientMaterial { public: - GradientMaterial( QGradient::Type type, - const QGradientStops& stops, QGradient::Spread spread ) - : QskGradientMaterial( type, stops, spread ) + GradientMaterial( QGradient::Type type ) + : QskGradientMaterial( type ) { setFlag( Blending | RequiresFullMatrix ); @@ -355,16 +354,39 @@ namespace class LinearMaterial final : public GradientMaterial { public: - LinearMaterial( - const QGradientStops& stops, QGradient::Spread spread, - const QPointF& start, const QPointF& stop ) - : GradientMaterial( QGradient::LinearGradient, stops, spread ) - , m_start( start ) - , m_stop( stop ) + LinearMaterial() + : GradientMaterial( QGradient::LinearGradient ) { } - QSGMaterialShader* createShader() const override; + bool setGradient( const QLinearGradient* gradient ) + { + bool changed = false; + + if ( gradient->stops() != stops() ) + { + setStops( gradient->stops() ); + changed = true; + } + + if ( gradient->spread() != spread() ) + { + setSpread( gradient->spread() ); + changed = true; + } + + const QVector2D start( gradient->start() ); + const QVector2D stop( gradient->finalStop() ); + + if ( m_start != start || m_stop != stop ) + { + m_start = start; + m_stop = stop; + changed = true; + } + + return changed; + } QSGMaterialType* type() const override { @@ -382,8 +404,10 @@ namespace return GradientMaterial::compare( other ); } - const QVector2D m_start; - const QVector2D m_stop; + QSGMaterialShader* createShader() const override; + + QVector2D m_start; + QVector2D m_stop; }; #ifdef SHADER_GL @@ -485,26 +509,58 @@ namespace class RadialMaterial final : public GradientMaterial { public: - RadialMaterial( - const QGradientStops& stops, QGradient::Spread spread, - const QPointF& center, qreal centerRadius, - const QPointF& focalPoint, qreal focalRadius ) - : GradientMaterial( QGradient::RadialGradient, stops, spread ) - , m_center( center ) - , m_focalPoint( focalPoint ) - , m_centerRadius( centerRadius ) - , m_focalRadius( focalRadius ) + RadialMaterial() + : GradientMaterial( QGradient::RadialGradient ) { } - QSGMaterialShader* createShader() const override; - QSGMaterialType* type() const override { static QSGMaterialType type; return &type; } + bool setGradient( const QRadialGradient* gradient ) + { + bool changed = false; + + if ( gradient->stops() != stops() ) + { + setStops( gradient->stops() ); + changed = true; + } + + if ( gradient->spread() != spread() ) + { + setSpread( gradient->spread() ); + changed = true; + } + + const QVector2D center( gradient->center() ); + const float centerRadius = gradient->centerRadius(); + + if ( ( center != m_center ) || ( m_centerRadius != centerRadius ) ) + { + m_center = center; + m_centerRadius = centerRadius; + + changed = true; + } + + const QVector2D focalPoint( gradient->focalPoint() ); + const float focalRadius = gradient->focalRadius(); + + if ( ( focalPoint != m_focalPoint ) || ( focalRadius != m_focalRadius ) ) + { + m_focalPoint = focalPoint; + m_focalRadius = focalRadius; + + changed = true; + } + + return changed; + } + int compare( const QSGMaterial* other ) const override { const auto mat = static_cast< const RadialMaterial* >( other ); @@ -522,10 +578,12 @@ namespace } } - const QVector2D m_center; - const QVector2D m_focalPoint; - const float m_centerRadius; - const float m_focalRadius; + QSGMaterialShader* createShader() const override; + + QVector2D m_center; + QVector2D m_focalPoint; + float m_centerRadius = 0.0; + float m_focalRadius = 0.0; }; #ifdef SHADER_GL @@ -650,22 +708,41 @@ namespace class ConicalMaterial final : public GradientMaterial { public: - ConicalMaterial( const QGradientStops& stops, - const QPointF& center, qreal angle ) - : GradientMaterial( QGradient::ConicalGradient, stops, QGradient::PadSpread ) - , m_center( center ) - , m_radians( -qDegreesToRadians( angle ) ) + ConicalMaterial() + : GradientMaterial( QGradient::ConicalGradient ) { } - QSGMaterialShader* createShader() const override; - QSGMaterialType* type() const override { static QSGMaterialType type; return &type; } + bool setGradient( const QConicalGradient* gradient ) + { + bool changed = false; + + if ( gradient->stops() != stops() ) + { + setStops( gradient->stops() ); + changed = true; + } + + const QVector2D center( gradient->center() ); + const float radians = -qDegreesToRadians( gradient->angle() ); + + if ( center != m_center || radians != m_radians ) + { + m_center = center; + m_radians = radians; + + changed = true; + } + + return changed; + } + int compare( const QSGMaterial* other ) const override { const auto mat = static_cast< const ConicalMaterial* >( other ); @@ -676,8 +753,10 @@ namespace return GradientMaterial::compare( other ); } - const QVector2D m_center; - const float m_radians; + QSGMaterialShader* createShader() const override; + + QVector2D m_center; + float m_radians = 0.0; }; #ifdef SHADER_GL @@ -774,39 +853,65 @@ namespace } } -QskGradientMaterial* QskGradientMaterial::create( const QGradient* gradient ) +QskGradientMaterial::QskGradientMaterial( QGradient::Type type ) + : m_gradientType( type ) { +} + +template< typename Material > +inline Material* qskEnsureMaterial( QskGradientMaterial* material ) +{ + if ( material == nullptr ) + material = new Material(); + + return static_cast< Material* >( material ); +} + +bool QskGradientMaterial::updateGradient( const QGradient* gradient ) +{ + Q_ASSERT( gradient && gradient->type() == m_gradientType ); + + if ( gradient == nullptr || gradient->type() != m_gradientType ) + return false; + switch ( static_cast< int >( gradient->type() ) ) { case QGradient::LinearGradient: { - auto linearGradient = static_cast< const QLinearGradient* >( gradient ); - - return new LinearMaterial( - linearGradient->stops(), linearGradient->spread(), - linearGradient->start(), linearGradient->finalStop() ); + auto material = static_cast< LinearMaterial* >( this ); + return material->setGradient( static_cast< const QLinearGradient* >( gradient ) ); } case QGradient::RadialGradient: { - auto radialGradient = static_cast< const QRadialGradient* >( gradient ); - - return new RadialMaterial( - radialGradient->stops(), radialGradient->spread(), - radialGradient->center(), radialGradient->centerRadius(), - radialGradient->focalPoint(), radialGradient->focalRadius() ); + auto material = static_cast< RadialMaterial* >( this ); + return material->setGradient( static_cast< const QRadialGradient* >( gradient ) ); } case QGradient::ConicalGradient: { - auto conicalGradient = static_cast< const QConicalGradient* >( gradient ); - - return new ConicalMaterial( conicalGradient->stops(), - conicalGradient->center(), conicalGradient->angle() ); + auto material = static_cast< ConicalMaterial* >( this ); + return material->setGradient( static_cast< const QConicalGradient* >( gradient ) ); } } - return nullptr; + return false; } +QskGradientMaterial* QskGradientMaterial::createMaterial( QGradient::Type gradientType ) +{ + switch ( gradientType ) + { + case QGradient::LinearGradient: + return new LinearMaterial(); + case QGradient::RadialGradient: + return new RadialMaterial(); + + case QGradient::ConicalGradient: + return new ConicalMaterial(); + + default: + return nullptr; + } +} diff --git a/src/nodes/QskGradientMaterial.h b/src/nodes/QskGradientMaterial.h index 8f5f89e1..da485999 100644 --- a/src/nodes/QskGradientMaterial.h +++ b/src/nodes/QskGradientMaterial.h @@ -13,36 +13,42 @@ class QSK_EXPORT QskGradientMaterial : public QSGMaterial { public: - static QskGradientMaterial* create( const QGradient* ); + static QskGradientMaterial* createMaterial( QGradient::Type ); + bool updateGradient( const QGradient* ); QGradient::Type gradientType() const; const QGradientStops& stops() const; QGradient::Spread spread() const; protected: - QskGradientMaterial( QGradient::Type, - const QGradientStops&, QGradient::Spread ); + QskGradientMaterial( QGradient::Type ); + + void setStops( const QGradientStops& ); + void setSpread( QGradient::Spread ); private: const QGradient::Type m_gradientType; - const QGradientStops m_stops; - const QGradient::Spread m_spread; -}; -inline QskGradientMaterial::QskGradientMaterial( QGradient::Type type, - const QGradientStops& stops, QGradient::Spread spread ) - : m_gradientType( type ) - , m_stops( stops ) - , m_spread( spread ) -{ -} + QGradientStops m_stops; + QGradient::Spread m_spread = QGradient::PadSpread; +}; inline QGradient::Type QskGradientMaterial::gradientType() const { return m_gradientType; } +inline void QskGradientMaterial::setStops( const QGradientStops& stops ) +{ + m_stops = stops; +} + +inline void QskGradientMaterial::setSpread( QGradient::Spread spread ) +{ + m_spread = spread; +} + inline const QGradientStops& QskGradientMaterial::stops() const { return m_stops; diff --git a/src/nodes/QskShapeNode.cpp b/src/nodes/QskShapeNode.cpp index b87aa8f0..0ccc5691 100644 --- a/src/nodes/QskShapeNode.cpp +++ b/src/nodes/QskShapeNode.cpp @@ -56,6 +56,46 @@ static void qskUpdateGeometry( const QPainterPath& path, QSGGeometry& geometry ) #endif } +static inline void qskResetGeometry( QskShapeNode* node ) +{ + auto g = node->geometry(); + if ( g->vertexCount() > 0 ) + { + g->allocate( 0 ); + node->markDirty( QSGNode::DirtyGeometry ); + } +} + +static inline bool qskIsGradientVisible( const QGradient* gradient ) +{ + if ( gradient && gradient->type() != QGradient::NoGradient ) + { + for ( const auto stop : gradient->stops() ) + { + if ( stop.second.alpha() > 0 ) + return true; + } + } + + return false; +} + +static inline bool qskIsGradientMonochrome( const QGradient* gradient ) +{ + if ( gradient && gradient->type() != QGradient::NoGradient ) + { + const auto stops = gradient->stops(); + + for ( int i = 1; i < stops.count(); i++ ) + { + if ( stops[i].second != stops[i - 1].second ) + return false; + } + } + + return true; +} + class QskShapeNodePrivate final : public QSGGeometryNodePrivate { public: @@ -66,7 +106,7 @@ class QskShapeNodePrivate final : public QSGGeometryNodePrivate } QSGGeometry geometry; - QSGMaterial* material = nullptr; + QGradient::Type gradientType = QGradient::NoGradient; }; QskShapeNode::QskShapeNode() @@ -80,33 +120,65 @@ QskShapeNode::QskShapeNode() void QskShapeNode::updateNode( const QPainterPath& path, const QColor& color ) { + Q_D( QskShapeNode ); + + if ( path.isEmpty() || !color.isValid() || color.alpha() == 0 ) + { + qskResetGeometry( this ); + return; + } + if ( true ) // For the moment we always update the geometry. TODO ... { - qskUpdateGeometry( path, *geometry() ); + qskUpdateGeometry( path, d->geometry ); markDirty( QSGNode::DirtyGeometry ); } - if ( true ) // For the moment we always update the material + if ( material() == nullptr || d->gradientType != QGradient::NoGradient ) { - auto material = new QSGFlatColorMaterial(); - material->setColor( color ); + setMaterial( new QSGFlatColorMaterial() ); + d->gradientType = QGradient::NoGradient; + } - setMaterial( material ); + const auto c = color.toRgb(); + + auto colorMaterial = static_cast< QSGFlatColorMaterial* >( material() ); + if ( colorMaterial->color() != c ) + { + colorMaterial->setColor( c ); markDirty( QSGNode::DirtyMaterial ); } } void QskShapeNode::updateNode( const QPainterPath& path, const QGradient* gradient ) { + if ( path.isEmpty() || !qskIsGradientVisible( gradient ) ) + { + qskResetGeometry( this ); + return; + } + + if ( qskIsGradientMonochrome( gradient ) ) + { + updateNode( path, gradient->stops().first().first ); + return; + } + + Q_D( QskShapeNode ); + if ( true ) // For the moment we always update the geometry. TODO ... { - qskUpdateGeometry( path, *geometry() ); + qskUpdateGeometry( path, d->geometry ); markDirty( QSGNode::DirtyGeometry ); } - if ( true ) // For the moment we always update the material + if ( ( material() == nullptr ) || gradient->type() != d->gradientType ) { - setMaterial( QskGradientMaterial::create( gradient ) ); - markDirty( QSGNode::DirtyMaterial ); + setMaterial( QskGradientMaterial::createMaterial( gradient->type() ) ); + d->gradientType = gradient->type(); } + + auto gradientMaterial = static_cast< QskGradientMaterial* >( material() ); + if ( gradientMaterial->updateGradient( gradient ) ) + markDirty( QSGNode::DirtyMaterial ); }