From e1a58f84ee48afefba72ca0a9a5b7ff7eeffafa5 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 1 Jun 2022 16:57:57 +0200 Subject: [PATCH] QskPaintedNode improved --- src/nodes/QskArcNode.cpp | 34 +++++++--- src/nodes/QskArcNode.h | 16 ++--- src/nodes/QskPaintedNode.cpp | 123 ++++++++++++++++++++++------------- src/nodes/QskPaintedNode.h | 43 +++++++++--- 4 files changed, 145 insertions(+), 71 deletions(-) diff --git a/src/nodes/QskArcNode.cpp b/src/nodes/QskArcNode.cpp index 698e8364..3b9f3726 100644 --- a/src/nodes/QskArcNode.cpp +++ b/src/nodes/QskArcNode.cpp @@ -4,7 +4,19 @@ *****************************************************************************/ #include "QskArcNode.h" +#include "QskArcMetrics.h" #include "QskArcRenderer.h" +#include "QskGradient.h" + +namespace +{ + class ArcData + { + public: + const QskArcMetrics& metrics; + const QskGradient& gradient; + }; +} QskArcNode::QskArcNode() { @@ -17,26 +29,28 @@ QskArcNode::~QskArcNode() void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& metrics, const QskGradient& gradient, QQuickWindow* window ) { - m_metrics = metrics; - m_gradient = gradient; - - update( window, QskTextureRenderer::AutoDetect, rect.toRect() ); + const ArcData arcData { metrics, gradient }; + update( window, rect.toRect(), &arcData ); } -void QskArcNode::paint( QPainter* painter, const QSizeF& size ) +void QskArcNode::paint( QPainter* painter, const QSizeF& size, const void* nodeData ) { - const qreal w = m_metrics.width(); + const auto arcData = reinterpret_cast< const ArcData* >( nodeData ); + + const qreal w = arcData->metrics.width(); const QRectF rect( 0.5 * w, 0.5 * w, size.width() - w, size.height() - w ); QskArcRenderer renderer; - renderer.renderArc( rect, m_metrics, m_gradient, painter ); + renderer.renderArc( rect, arcData->metrics, arcData->gradient, painter ); } -QskHashValue QskArcNode::hash() const +QskHashValue QskArcNode::hash( const void* nodeData ) const { - auto h = m_metrics.hash(); + const auto arcData = reinterpret_cast< const ArcData* >( nodeData ); - for( const auto& stop : qAsConst( m_gradient.stops() ) ) + auto h = arcData->metrics.hash(); + + for( const auto& stop : qAsConst( arcData->gradient.stops() ) ) h = stop.hash( h ); return h; diff --git a/src/nodes/QskArcNode.h b/src/nodes/QskArcNode.h index 0bf3724c..411c7582 100644 --- a/src/nodes/QskArcNode.h +++ b/src/nodes/QskArcNode.h @@ -6,10 +6,13 @@ #ifndef QSK_ARC_NODE_H #define QSK_ARC_NODE_H -#include "QskArcMetrics.h" -#include "QskGradient.h" #include "QskPaintedNode.h" +class QskArcMetrics; +class QskGradient; + +// should be a QSGGeometryNode, TODO .. + class QSK_EXPORT QskArcNode : public QskPaintedNode { public: @@ -19,12 +22,9 @@ class QSK_EXPORT QskArcNode : public QskPaintedNode void setArcData( const QRectF&, const QskArcMetrics&, const QskGradient&, QQuickWindow* ); - void paint( QPainter* painter, const QSizeF& size ) override; - QskHashValue hash() const override; - - private: - QskArcMetrics m_metrics; - QskGradient m_gradient; + protected: + void paint( QPainter*, const QSizeF&, const void* nodeData ) override; + QskHashValue hash( const void* nodeData ) const override; }; #endif diff --git a/src/nodes/QskPaintedNode.cpp b/src/nodes/QskPaintedNode.cpp index 518fb717..c0490fbd 100644 --- a/src/nodes/QskPaintedNode.cpp +++ b/src/nodes/QskPaintedNode.cpp @@ -22,7 +22,11 @@ QSK_QT_PRIVATE_END #if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) +#include + +QSK_QT_PRIVATE_BEGIN #include +QSK_QT_PRIVATE_END static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo ) { @@ -76,10 +80,12 @@ namespace { const quint8 imageRole = 250; // reserved for internal use - inline QSGImageNode* findImageNode( QSGNode* parentNode ) + inline QSGImageNode* findImageNode( const QSGNode* parentNode ) { - return static_cast< QSGImageNode* >( - QskSGNode::findChildNode( parentNode, imageRole ) ); + auto node = QskSGNode::findChildNode( + const_cast< QSGNode* >( parentNode ), imageRole ); + + return static_cast< QSGImageNode* >( node ); } static inline bool qskHasOpenGLRenderer( QQuickWindow* window ) @@ -108,8 +114,24 @@ QskPaintedNode::~QskPaintedNode() { } +void QskPaintedNode::setRenderHint( RenderHint renderHint ) +{ + m_renderHint = renderHint; +} + +QskPaintedNode::RenderHint QskPaintedNode::renderHint() const +{ + return m_renderHint; +} + +QRectF QskPaintedNode::rect() const +{ + const auto imageNode = findImageNode( this ); + return imageNode ? imageNode->rect() : QRectF(); +} + void QskPaintedNode::update( QQuickWindow* window, - QskTextureRenderer::RenderMode renderMode, const QRectF& rect ) + const QRectF& rect, const void* nodeData ) { auto imageNode = findImageNode( this ); @@ -120,38 +142,33 @@ void QskPaintedNode::update( QQuickWindow* window, removeChildNode( imageNode ); delete imageNode; } + + return; } - else + + bool isDirty = false; + + const auto newHash = hash( nodeData ); + if ( ( newHash == 0 ) || ( newHash != m_hash ) ) { - bool isDirty = false; + m_hash = newHash; + isDirty = true; + } - const auto newHash = hash(); - if ( ( newHash == 0 ) || ( newHash != m_hash ) ) - { - m_hash = newHash; - isDirty = true; - } + if ( !isDirty ) + isDirty = ( imageNode == nullptr ) || ( imageNode->rect() != rect ); - if ( !isDirty ) - isDirty = ( imageNode == nullptr ) || ( imageNode->rect() != rect ); - - if ( isDirty ) - { - if ( renderMode != QskTextureRenderer::Raster ) - { - if ( !qskHasOpenGLRenderer( window ) ) - renderMode = QskTextureRenderer::Raster; - } - - if ( renderMode == QskTextureRenderer::Raster ) - updateImageNode( window, rect ); - else - updateImageNodeGL( window, rect ); - } + if ( isDirty ) + { + if ( ( m_renderHint == OpenGL ) && qskHasOpenGLRenderer( window ) ) + updateImageNodeGL( window, rect, nodeData ); + else + updateImageNode( window, rect, nodeData ); } } -void QskPaintedNode::updateImageNode( QQuickWindow* window, const QRectF& rect ) +void QskPaintedNode::updateImageNode( + QQuickWindow* window, const QRectF& rect, const void* nodeData ) { const auto ratio = window->effectiveDevicePixelRatio(); const auto size = rect.size() * ratio; @@ -167,7 +184,7 @@ void QskPaintedNode::updateImageNode( QQuickWindow* window, const QRectF& rect ) value >= 1.0. So we have to scale manually. */ painter.scale( ratio, ratio ); - paint( &painter, rect.size() ); + paint( &painter, rect.size(), nodeData ); } auto imageNode = findImageNode( this ); @@ -190,7 +207,8 @@ void QskPaintedNode::updateImageNode( QQuickWindow* window, const QRectF& rect ) imageNode->setRect( rect ); } -void QskPaintedNode::updateImageNodeGL( QQuickWindow* window, const QRectF& rect ) +void QskPaintedNode::updateImageNodeGL( + QQuickWindow* window, const QRectF& rect, const void* nodeData ) { const auto ratio = window->effectiveDevicePixelRatio(); const QSize size( ratio * rect.width(), ratio * rect.height() ); @@ -217,7 +235,12 @@ void QskPaintedNode::updateImageNodeGL( QQuickWindow* window, const QRectF& rect imageNode->setTexture( texture ); } - const auto textureId = createTexture( window, size ); + /* + QQuickFramebufferObject does the FBO rendering early + ( QQuickWindow::beforeRendering ). However doing it below updatePaintNode + seems to work as well. Let's see if we run into issues ... + */ + const auto textureId = createTexture( window, size, nodeData ); auto rhi = QQuickWindowPrivate::get( window )->rhi; @@ -241,9 +264,28 @@ void QskPaintedNode::updateImageNodeGL( QQuickWindow* window, const QRectF& rect imageNode->setRect( rect ); } -// this method will be moved to QskTextureRenderer. TODO ... -uint32_t QskPaintedNode::createTexture( QQuickWindow* window, const QSize& size ) +uint32_t QskPaintedNode::createTexture( + QQuickWindow* window, const QSize& size, const void* nodeData ) { + /* + Binding GL_ARRAY_BUFFER/GL_ELEMENT_ARRAY_BUFFER to 0 seems to be enough. + + However - as we do not know what is finally painted and what the + OpenGL paint engine is doing with better reinitialize everything. + + Hope this has no side effects as the context will leave the function + in a modified state. Otherwise we could try to change the buffers + only and reset them, before leaving. + */ + + window->beginExternalCommands(); + +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) + QQuickOpenGLUtils::resetOpenGLState(); +#else + window->resetOpenGLState(); +#endif + auto context = QOpenGLContext::currentContext(); QOpenGLFramebufferObjectFormat format1; @@ -257,13 +299,6 @@ uint32_t QskPaintedNode::createTexture( QQuickWindow* window, const QSize& size pd.setPaintFlipped( true ); { - int bufferId; - - auto gl = context->functions(); - - gl->glGetIntegerv( GL_ARRAY_BUFFER_BINDING, &bufferId); - gl->glBindBuffer( GL_ARRAY_BUFFER, 0 ); - const auto ratio = window->effectiveDevicePixelRatio(); QPainter painter( &pd ); @@ -273,9 +308,7 @@ uint32_t QskPaintedNode::createTexture( QQuickWindow* window, const QSize& size painter.fillRect( 0, 0, size.width(), size.height(), Qt::transparent ); painter.setCompositionMode( QPainter::CompositionMode_SourceOver ); - paint( &painter, size ); - - gl->glBindBuffer( GL_ARRAY_BUFFER, bufferId ); + paint( &painter, size, nodeData ); } QOpenGLFramebufferObjectFormat format2; @@ -288,5 +321,7 @@ uint32_t QskPaintedNode::createTexture( QQuickWindow* window, const QSize& size QOpenGLFramebufferObject::blitFramebuffer( &fbo, fboRect, &multisampledFbo, fboRect ); + window->endExternalCommands(); + return qskTakeTexture( fbo ); } diff --git a/src/nodes/QskPaintedNode.h b/src/nodes/QskPaintedNode.h index 06065406..5ae43ec3 100644 --- a/src/nodes/QskPaintedNode.h +++ b/src/nodes/QskPaintedNode.h @@ -6,31 +6,56 @@ #ifndef QSK_PAINTED_NODE_H #define QSK_PAINTED_NODE_H -#include "QskTextureRenderer.h" +#include "QskGlobal.h" #include +class QQuickWindow; +class QPainter; + class QSK_EXPORT QskPaintedNode : public QSGNode { public: + /* + Raster usually provides a better antialiasing and is less buggy, + while OpenGL might be faster - depending on the content that has + to be painted. + + Since Qt 5.10 X11 is back and could be an interesting option + with good quality and hardware accelerated performance. TODO ... + + OpenGL might be ignored depending on the backend used by the + application. + */ + enum RenderHint + { + Raster, + OpenGL + }; + QskPaintedNode(); ~QskPaintedNode() override; - void update( QQuickWindow*, - QskTextureRenderer::RenderMode, const QRectF& ); + void setRenderHint( RenderHint ); + RenderHint renderHint() const; + + QRectF rect() const; protected: - virtual void paint( QPainter*, const QSizeF& ) = 0; + void update( QQuickWindow*, const QRectF&, const void* nodeData ); + + virtual void paint( QPainter*, const QSizeF&, const void* nodeData ) = 0; // a hash value of '0' always results in repainting - virtual QskHashValue hash() const = 0; + virtual QskHashValue hash( const void* nodeData ) const = 0; private: - void updateImageNode( QQuickWindow*, const QRectF& ); - void updateImageNodeGL( QQuickWindow*, const QRectF& ); + void updateImageNode( QQuickWindow*, const QRectF&, const void* nodeData ); + void updateImageNodeGL( QQuickWindow*, const QRectF&, const void* nodeData ); - uint32_t createTexture( QQuickWindow*, const QSize& ); + uint32_t createTexture( QQuickWindow*, const QSize&, const void* nodeData ); - QskHashValue m_hash; + RenderHint m_renderHint = OpenGL; + QskHashValue m_hash = 0; }; #endif