From c64d3864775d6280f1d3a44d61bb2fd893c0bc26 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Tue, 31 May 2022 17:56:49 +0200 Subject: [PATCH 01/12] QskPaintedNode reimplemented to work with RHI and software renderer Code will be moved in parts to QskTextureRenderer to do something similar with QskTextureNode. --- src/nodes/QskPaintedNode.cpp | 293 +++++++++++++++++++++++++++++++---- src/nodes/QskPaintedNode.h | 12 +- src/src.pro | 6 +- 3 files changed, 272 insertions(+), 39 deletions(-) diff --git a/src/nodes/QskPaintedNode.cpp b/src/nodes/QskPaintedNode.cpp index 574cae82..518fb717 100644 --- a/src/nodes/QskPaintedNode.cpp +++ b/src/nodes/QskPaintedNode.cpp @@ -4,24 +4,101 @@ *****************************************************************************/ #include "QskPaintedNode.h" -#include "QskTextureRenderer.h" +#include "QskSGNode.h" -class QskPaintedNode::PaintHelper : public QskTextureRenderer::PaintHelper +#include +#include +#include +#include + +#include +#include +#include + +QSK_QT_PRIVATE_BEGIN +#include +#include +QSK_QT_PRIVATE_END + +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) + +#include + +static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo ) { - public: - inline PaintHelper( QskPaintedNode* node ) - : m_node( node ) + /* + See https://bugreports.qt.io/browse/QTBUG-103929 + + As we create a FBO for each update of a node we can't live + without having this ( ugly ) workaround. + */ + class MyFBO { + public: + virtual ~MyFBO() = default; + QScopedPointer< QOpenGLFramebufferObjectPrivate > d_ptr; + }; + + static_assert( sizeof( MyFBO ) == sizeof( QOpenGLFramebufferObject ), + "Bad cast: QOpenGLFramebufferObject does not match" ); + + auto& attachment = reinterpret_cast< MyFBO* >( &fbo )->d_ptr->colorAttachments[0]; + auto guard = attachment.guard; + + const auto textureId = fbo.takeTexture(); + + if ( guard ) + { + class MyGuard : public QOpenGLSharedResourceGuard + { + public: + void invalidateTexture() { invalidateResource(); } + }; + + reinterpret_cast< MyGuard* >( guard )->invalidateTexture(); } - void paint( QPainter* painter, const QSize& size ) override + attachment.guard = guard; + + return textureId; +} + +#else + +static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo ) +{ + return fbo.takeTexture(); +} + +#endif + +namespace +{ + const quint8 imageRole = 250; // reserved for internal use + + inline QSGImageNode* findImageNode( QSGNode* parentNode ) { - m_node->paint( painter, size ); + return static_cast< QSGImageNode* >( + QskSGNode::findChildNode( parentNode, imageRole ) ); } - private: - QskPaintedNode* m_node; -}; + static inline bool qskHasOpenGLRenderer( QQuickWindow* window ) + { + if ( window == nullptr ) + return false; + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + if ( QQuickWindowPrivate::get( window )->rhi ) + { + // does not yet work with the experimental RHI implementation in Qt5 + return false; + } +#endif + + const auto renderer = window->rendererInterface(); + return renderer->graphicsApi() == QSGRendererInterface::OpenGL; + } +} QskPaintedNode::QskPaintedNode() { @@ -32,32 +109,184 @@ QskPaintedNode::~QskPaintedNode() } void QskPaintedNode::update( QQuickWindow* window, - QskTextureRenderer::RenderMode renderMode, const QRect& rect ) + QskTextureRenderer::RenderMode renderMode, const QRectF& rect ) { - bool isTextureDirty = isNull(); + auto imageNode = findImageNode( this ); - if ( !isTextureDirty ) + if ( rect.isEmpty() ) { - const auto oldRect = QskTextureNode::rect(); - isTextureDirty = ( rect.width() != static_cast< int >( oldRect.width() ) ) || - ( rect.height() != static_cast< int >( oldRect.height() ) ); + if ( imageNode ) + { + removeChildNode( imageNode ); + delete imageNode; + } } - - const auto newHash = hash(); - if ( ( newHash == 0 ) || ( newHash != m_hash ) ) + else { - m_hash = newHash; - isTextureDirty = true; + bool isDirty = false; + + 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 ) + { + if ( renderMode != QskTextureRenderer::Raster ) + { + if ( !qskHasOpenGLRenderer( window ) ) + renderMode = QskTextureRenderer::Raster; + } + + if ( renderMode == QskTextureRenderer::Raster ) + updateImageNode( window, rect ); + else + updateImageNodeGL( window, rect ); + } } - - auto textureId = QskTextureNode::textureId(); - - if ( isTextureDirty ) - { - PaintHelper helper( this ); - textureId = QskTextureRenderer::createTexture( - window, renderMode, rect.size(), &helper ); - } - - QskTextureNode::setTexture( window, rect, textureId ); +} + +void QskPaintedNode::updateImageNode( QQuickWindow* window, const QRectF& rect ) +{ + const auto ratio = window->effectiveDevicePixelRatio(); + const auto size = rect.size() * ratio; + + QImage image( size.toSize(), QImage::Format_RGBA8888_Premultiplied ); + image.fill( Qt::transparent ); + + { + QPainter painter( &image ); + + /* + setting a devicePixelRatio for the image only works for + value >= 1.0. So we have to scale manually. + */ + painter.scale( ratio, ratio ); + paint( &painter, rect.size() ); + } + + auto imageNode = findImageNode( this ); + + if ( imageNode == nullptr ) + { + imageNode = window->createImageNode(); + + imageNode->setOwnsTexture( true ); + QskSGNode::setNodeRole( imageNode, imageRole ); + + appendChildNode( imageNode ); + } + + if ( auto texture = qobject_cast< QSGPlainTexture* >( imageNode->texture() ) ) + texture->setImage( image ); + else + imageNode->setTexture( window->createTextureFromImage( image ) ); + + imageNode->setRect( rect ); +} + +void QskPaintedNode::updateImageNodeGL( QQuickWindow* window, const QRectF& rect ) +{ + const auto ratio = window->effectiveDevicePixelRatio(); + const QSize size( ratio * rect.width(), ratio * rect.height() ); + + auto imageNode = findImageNode( this ); + + if ( imageNode == nullptr ) + { + imageNode = window->createImageNode(); + + imageNode->setOwnsTexture( true ); + QskSGNode::setNodeRole( imageNode, imageRole ); + + appendChildNode( imageNode ); + } + + auto texture = qobject_cast< QSGPlainTexture* >( imageNode->texture() ); + if ( texture == nullptr ) + { + texture = new QSGPlainTexture; + texture->setHasAlphaChannel( true ); + texture->setOwnsTexture( true ); + + imageNode->setTexture( texture ); + } + + const auto textureId = createTexture( window, size ); + + auto rhi = QQuickWindowPrivate::get( window )->rhi; + +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) + texture->setTextureFromNativeTexture( + rhi, quint64( textureId ), 0, size, {}, {} ); +#else + if ( rhi ) + { + // enabled with: "export QSG_RHI=1" + texture->setTextureFromNativeObject( rhi, + QQuickWindow::NativeObjectTexture, &textureId, 0, size, false ); + } + else + { + texture->setTextureId( textureId ); + texture->setTextureSize( size ); + } +#endif + + imageNode->setRect( rect ); +} + +// this method will be moved to QskTextureRenderer. TODO ... +uint32_t QskPaintedNode::createTexture( QQuickWindow* window, const QSize& size ) +{ + auto context = QOpenGLContext::currentContext(); + + QOpenGLFramebufferObjectFormat format1; + format1.setAttachment( QOpenGLFramebufferObject::CombinedDepthStencil ); + + format1.setSamples( context->format().samples() ); + + QOpenGLFramebufferObject multisampledFbo( size, format1 ); + + QOpenGLPaintDevice pd( 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 ); + painter.scale( ratio, ratio ); + + painter.setCompositionMode( QPainter::CompositionMode_Source ); + painter.fillRect( 0, 0, size.width(), size.height(), Qt::transparent ); + painter.setCompositionMode( QPainter::CompositionMode_SourceOver ); + + paint( &painter, size ); + + gl->glBindBuffer( GL_ARRAY_BUFFER, bufferId ); + } + + QOpenGLFramebufferObjectFormat format2; + format2.setAttachment( QOpenGLFramebufferObject::NoAttachment ); + + QOpenGLFramebufferObject fbo( size, format2 ); + + const QRect fboRect( 0, 0, size.width(), size.height() ); + + QOpenGLFramebufferObject::blitFramebuffer( + &fbo, fboRect, &multisampledFbo, fboRect ); + + return qskTakeTexture( fbo ); } diff --git a/src/nodes/QskPaintedNode.h b/src/nodes/QskPaintedNode.h index 6b53ab6a..06065406 100644 --- a/src/nodes/QskPaintedNode.h +++ b/src/nodes/QskPaintedNode.h @@ -6,17 +6,17 @@ #ifndef QSK_PAINTED_NODE_H #define QSK_PAINTED_NODE_H -#include "QskTextureNode.h" #include "QskTextureRenderer.h" +#include -class QSK_EXPORT QskPaintedNode : public QskTextureNode +class QSK_EXPORT QskPaintedNode : public QSGNode { public: QskPaintedNode(); ~QskPaintedNode() override; void update( QQuickWindow*, - QskTextureRenderer::RenderMode, const QRect& ); + QskTextureRenderer::RenderMode, const QRectF& ); protected: virtual void paint( QPainter*, const QSizeF& ) = 0; @@ -25,10 +25,10 @@ class QSK_EXPORT QskPaintedNode : public QskTextureNode virtual QskHashValue hash() const = 0; private: - class PaintHelper; + void updateImageNode( QQuickWindow*, const QRectF& ); + void updateImageNodeGL( QQuickWindow*, const QRectF& ); - void setTexture( QQuickWindow*, - const QRectF&, uint id, Qt::Orientations ) = delete; + uint32_t createTexture( QQuickWindow*, const QSize& ); QskHashValue m_hash; }; diff --git a/src/src.pro b/src/src.pro index 366590c0..58d26aaa 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,7 +1,11 @@ TEMPLATE = lib TARGET = $$qskLibraryTarget(qskinny) -QT += quick quick-private +QT += quick quick-private opengl-private + +greaterThan( QT_MAJOR_VERSION, 5 ) { + QT += opengl-private +} contains(QSK_CONFIG, QskDll): DEFINES += QSK_MAKEDLL From c291cde259a07a0e16bcc8ec6c88a0d87a6fd8e5 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 1 Jun 2022 16:51:11 +0200 Subject: [PATCH 02/12] includes fixed --- src/controls/QskGraphicLabelSkinlet.cpp | 1 - src/controls/QskSkinlet.cpp | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/controls/QskGraphicLabelSkinlet.cpp b/src/controls/QskGraphicLabelSkinlet.cpp index ea85c8f8..e6a36707 100644 --- a/src/controls/QskGraphicLabelSkinlet.cpp +++ b/src/controls/QskGraphicLabelSkinlet.cpp @@ -10,7 +10,6 @@ #include "QskColorFilter.h" #include "QskFunctions.h" #include "QskGraphic.h" -#include "QskTextureNode.h" QskGraphicLabelSkinlet::QskGraphicLabelSkinlet( QskSkin* skin ) : Inherited( skin ) diff --git a/src/controls/QskSkinlet.cpp b/src/controls/QskSkinlet.cpp index 61ed5415..14319207 100644 --- a/src/controls/QskSkinlet.cpp +++ b/src/controls/QskSkinlet.cpp @@ -7,6 +7,7 @@ #include "QskArcNode.h" #include "QskAspect.h" +#include "QskArcMetrics.h" #include "QskBoxBorderColors.h" #include "QskBoxBorderMetrics.h" #include "QskBoxClipNode.h" @@ -24,6 +25,7 @@ #include "QskTextNode.h" #include "QskTextOptions.h" #include "QskSkinStateChanger.h" +#include "QskTextureRenderer.h" #include #include From e1a58f84ee48afefba72ca0a9a5b7ff7eeffafa5 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 1 Jun 2022 16:57:57 +0200 Subject: [PATCH 03/12] 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 From 86658ee2dc594f2eb7cc71c93c2dd396ae1c7fd2 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 1 Jun 2022 17:04:37 +0200 Subject: [PATCH 04/12] raster rendering does not solve the problems with the experimantel RHI state in Qt5 ( no problems with Qt6 RHI and Qt5 without RHI ) --- src/nodes/QskPaintedNode.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/nodes/QskPaintedNode.cpp b/src/nodes/QskPaintedNode.cpp index c0490fbd..6d09acbb 100644 --- a/src/nodes/QskPaintedNode.cpp +++ b/src/nodes/QskPaintedNode.cpp @@ -93,14 +93,6 @@ namespace if ( window == nullptr ) return false; -#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - if ( QQuickWindowPrivate::get( window )->rhi ) - { - // does not yet work with the experimental RHI implementation in Qt5 - return false; - } -#endif - const auto renderer = window->rendererInterface(); return renderer->graphicsApi() == QSGRendererInterface::OpenGL; } From 5ab536f0e33d95374efe6130506a7de75fc295aa Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 1 Jun 2022 17:41:33 +0200 Subject: [PATCH 05/12] fixed fot Qt5 --- src/src.pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src.pro b/src/src.pro index 58d26aaa..ce4d27a3 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,7 +1,7 @@ TEMPLATE = lib TARGET = $$qskLibraryTarget(qskinny) -QT += quick quick-private opengl-private +QT += quick quick-private greaterThan( QT_MAJOR_VERSION, 5 ) { QT += opengl-private From 86e438a0ed10d38a1614c0ae2af039368f6c4fbc Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 1 Jun 2022 17:50:16 +0200 Subject: [PATCH 06/12] QFrameBufferObject::takeTexture also leaks with Qt 5.15 --- src/nodes/QskPaintedNode.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/src/nodes/QskPaintedNode.cpp b/src/nodes/QskPaintedNode.cpp index 6d09acbb..e153b2bb 100644 --- a/src/nodes/QskPaintedNode.cpp +++ b/src/nodes/QskPaintedNode.cpp @@ -18,15 +18,12 @@ QSK_QT_PRIVATE_BEGIN #include #include +#include QSK_QT_PRIVATE_END #if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - #include - -QSK_QT_PRIVATE_BEGIN -#include -QSK_QT_PRIVATE_END +#endif static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo ) { @@ -67,15 +64,6 @@ static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo ) return textureId; } -#else - -static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo ) -{ - return fbo.takeTexture(); -} - -#endif - namespace { const quint8 imageRole = 250; // reserved for internal use From a35cc9936a1b8f19be617daa1e4f778a05aaaa65 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 1 Jun 2022 18:27:05 +0200 Subject: [PATCH 07/12] mirroring added --- src/nodes/QskPaintedNode.cpp | 45 ++++++++++++++++++++++++++++++++---- src/nodes/QskPaintedNode.h | 4 ++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/nodes/QskPaintedNode.cpp b/src/nodes/QskPaintedNode.cpp index e153b2bb..8f505caf 100644 --- a/src/nodes/QskPaintedNode.cpp +++ b/src/nodes/QskPaintedNode.cpp @@ -64,6 +64,20 @@ static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo ) return textureId; } +static inline QSGImageNode::TextureCoordinatesTransformMode + qskEffectiveTransformMode( const Qt::Orientations mirrored ) +{ + QSGImageNode::TextureCoordinatesTransformMode mode; + + if ( mirrored & Qt::Vertical ) + mode |= QSGImageNode::MirrorVertically; + + if ( mirrored & Qt::Horizontal ) + mode |= QSGImageNode::MirrorHorizontally; + + return mode; +} + namespace { const quint8 imageRole = 250; // reserved for internal use @@ -104,6 +118,25 @@ QskPaintedNode::RenderHint QskPaintedNode::renderHint() const return m_renderHint; } +void QskPaintedNode::setMirrored( Qt::Orientations orientations ) +{ + if ( orientations != m_mirrored ) + { + m_mirrored == orientations; + + if ( auto imageNode = findImageNode( this ) ) + { + imageNode->setTextureCoordinatesTransform( + qskEffectiveTransformMode( orientations ) ); + } + } +} + +Qt::Orientations QskPaintedNode::mirrored() const +{ + return m_mirrored; +} + QRectF QskPaintedNode::rect() const { const auto imageNode = findImageNode( this ); @@ -145,6 +178,14 @@ void QskPaintedNode::update( QQuickWindow* window, else updateImageNode( window, rect, nodeData ); } + + imageNode = findImageNode( this ); + if ( imageNode ) + { + imageNode->setRect( rect ); + imageNode->setTextureCoordinatesTransform( + qskEffectiveTransformMode( m_mirrored ) ); + } } void QskPaintedNode::updateImageNode( @@ -183,8 +224,6 @@ void QskPaintedNode::updateImageNode( texture->setImage( image ); else imageNode->setTexture( window->createTextureFromImage( image ) ); - - imageNode->setRect( rect ); } void QskPaintedNode::updateImageNodeGL( @@ -240,8 +279,6 @@ void QskPaintedNode::updateImageNodeGL( texture->setTextureSize( size ); } #endif - - imageNode->setRect( rect ); } uint32_t QskPaintedNode::createTexture( diff --git a/src/nodes/QskPaintedNode.h b/src/nodes/QskPaintedNode.h index 5ae43ec3..755b8dac 100644 --- a/src/nodes/QskPaintedNode.h +++ b/src/nodes/QskPaintedNode.h @@ -38,6 +38,9 @@ class QSK_EXPORT QskPaintedNode : public QSGNode void setRenderHint( RenderHint ); RenderHint renderHint() const; + void setMirrored( Qt::Orientations ); + Qt::Orientations mirrored() const; + QRectF rect() const; protected: @@ -55,6 +58,7 @@ class QSK_EXPORT QskPaintedNode : public QSGNode uint32_t createTexture( QQuickWindow*, const QSize&, const void* nodeData ); RenderHint m_renderHint = OpenGL; + Qt::Orientations m_mirrored; QskHashValue m_hash = 0; }; From 5dc4200cdc76059e27707f645ba6aa4b7b57b6e2 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Wed, 1 Jun 2022 18:29:05 +0200 Subject: [PATCH 08/12] typo fixed --- src/nodes/QskPaintedNode.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nodes/QskPaintedNode.cpp b/src/nodes/QskPaintedNode.cpp index 8f505caf..beedd175 100644 --- a/src/nodes/QskPaintedNode.cpp +++ b/src/nodes/QskPaintedNode.cpp @@ -122,7 +122,7 @@ void QskPaintedNode::setMirrored( Qt::Orientations orientations ) { if ( orientations != m_mirrored ) { - m_mirrored == orientations; + m_mirrored = orientations; if ( auto imageNode = findImageNode( this ) ) { From 54b55c0324087fc69a99ad130fe1cc6448991026 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 2 Jun 2022 16:02:42 +0200 Subject: [PATCH 09/12] default graphic pipeline without native OpenGL calls. code is QRHI compiant now --- examples/examples.pro | 1 - examples/gbenchmark/Benchmark.cpp | 178 ------------- examples/gbenchmark/Benchmark.h | 13 - examples/gbenchmark/gbenchmark.pro | 10 - examples/gbenchmark/main.cpp | 65 ----- src/controls/QskSkinlet.cpp | 11 +- src/graphic/QskGraphicTextureFactory.cpp | 26 +- src/nodes/QskArcNode.cpp | 4 +- src/nodes/QskArcNode.h | 2 +- src/nodes/QskGraphicNode.cpp | 97 ++++--- src/nodes/QskGraphicNode.h | 18 +- src/nodes/QskPaintedNode.cpp | 315 ++++++++--------------- src/nodes/QskPaintedNode.h | 14 +- src/nodes/QskTextureRenderer.cpp | 293 ++++++++++----------- src/nodes/QskTextureRenderer.h | 39 +-- 15 files changed, 338 insertions(+), 748 deletions(-) delete mode 100644 examples/gbenchmark/Benchmark.cpp delete mode 100644 examples/gbenchmark/Benchmark.h delete mode 100644 examples/gbenchmark/gbenchmark.pro delete mode 100644 examples/gbenchmark/main.cpp diff --git a/examples/examples.pro b/examples/examples.pro index 2148d806..76bb9b66 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -26,6 +26,5 @@ SUBDIRS += \ boxes \ buttons \ frames \ - gbenchmark \ glabels \ messageboxQml diff --git a/examples/gbenchmark/Benchmark.cpp b/examples/gbenchmark/Benchmark.cpp deleted file mode 100644 index eeef334a..00000000 --- a/examples/gbenchmark/Benchmark.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * This file may be used under the terms of the 3-clause BSD License - *****************************************************************************/ - -#include "Benchmark.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -bool Benchmark::run( const QString& dirName ) -{ - QDir svgDir( dirName ); - - QStringList svgFiles = svgDir.entryList( QStringList() << "*.svg", QDir::Files ); - if ( svgFiles.isEmpty() ) - return false; - - const char qvgPath[] = "/tmp/benchmark/qvg"; - if ( !svgDir.mkpath( qvgPath ) ) - return false; - - QStringList qvgFiles = svgFiles; - for ( int i = 0; i < qvgFiles.size(); i++ ) - { - svgFiles[ i ].prepend( "/" ); - svgFiles[ i ].prepend( dirName ); - - qvgFiles[ i ].replace( ".svg", ".qvg" ); - qvgFiles[ i ].prepend( "/" ); - qvgFiles[ i ].prepend( qvgPath ); - } - - QVector< QskGraphic > graphics( qvgFiles.size() ); - QVector< QSvgRenderer* > renderers( svgFiles.size() ); - - qint64 msElapsed[ 6 ]; - - QElapsedTimer timer; - - { - // compile step - - timer.start(); - - for ( int i = 0; i < svgFiles.size(); i++ ) - { - renderers[ i ] = new QSvgRenderer(); - if ( !renderers[ i ]->load( svgFiles[ i ] ) ) - { - qCritical() << "Can't load" << svgFiles[ i ]; - return false; - } - } - - msElapsed[ 0 ] = timer.elapsed(); - } - - { - // converting into graphics and storing to disk - - timer.start(); - - for ( int i = 0; i < renderers.size(); i++ ) - { - QPainter painter( &graphics[ i ] ); - renderers[ i ]->render( &painter ); - painter.end(); - } - - msElapsed[ 1 ] = timer.elapsed(); - } - - { - // writing them to disk - - timer.start(); - - for ( int i = 0; i < graphics.size(); i++ ) - { - QskGraphicIO::write( graphics[ i ], qvgFiles[ i ] ); - } - - msElapsed[ 2 ] = timer.elapsed(); - } - - { - // loading qvg files to memory - - timer.start(); - - for ( int i = 0; i < qvgFiles.size(); i++ ) - { - graphics[ i ] = QskGraphicIO::read( qvgFiles[ i ] ); - if ( graphics[ i ].isNull() ) - { - qCritical() << "Can't load" << qvgFiles[ i ]; - return false; - } - } - - msElapsed[ 3 ] = timer.elapsed(); - } - - { - // creating textures using OpenGL - - timer.start(); - - const QSize targetSize( 200, 200 ); - const QskColorFilter colorFilter; - - for ( int i = 0; i < qvgFiles.size(); i++ ) - { - using namespace QskTextureRenderer; - - const auto textureId = createTextureFromGraphic( - nullptr, OpenGL, targetSize, graphics[ i ], colorFilter, - Qt::IgnoreAspectRatio ); - - if ( textureId == 0 ) - { - qCritical() << "Can't render texture for" << qvgFiles[ i ]; - return false; - } - } - - msElapsed[ 4 ] = timer.elapsed(); - } - - { - // creating textures using Raster - - timer.start(); - - const QSize targetSize( 200, 200 ); - const QskColorFilter colorFilter; - - for ( int i = 0; i < qvgFiles.size(); i++ ) - { - using namespace QskTextureRenderer; - - const auto textureId = createTextureFromGraphic( - nullptr, Raster, targetSize, graphics[ i ], colorFilter, - Qt::IgnoreAspectRatio ); - - if ( textureId == 0 ) - { - qCritical() << "Can't render texture for" << qvgFiles[ i ]; - return false; - } - } - - msElapsed[ 5 ] = timer.elapsed(); - } - - qDebug() << "#Icons:" << svgFiles.count() << - "Compiled:" << msElapsed[ 0 ] << - "Converted:" << msElapsed[ 1 ] << - "Stored:" << msElapsed[ 2 ] << - "Loaded:" << msElapsed[ 3 ] << - "Rendered OpenGL:" << msElapsed[ 4 ] << - "Rendered Raster:" << msElapsed[ 5 ]; - - svgDir.rmdir( qvgPath ); - - return true; -} diff --git a/examples/gbenchmark/Benchmark.h b/examples/gbenchmark/Benchmark.h deleted file mode 100644 index 4a6efc78..00000000 --- a/examples/gbenchmark/Benchmark.h +++ /dev/null @@ -1,13 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * This file may be used under the terms of the 3-clause BSD License - *****************************************************************************/ - -#pragma once - -class QString; - -namespace Benchmark -{ - bool run( const QString& svgDir ); -} diff --git a/examples/gbenchmark/gbenchmark.pro b/examples/gbenchmark/gbenchmark.pro deleted file mode 100644 index 850763cf..00000000 --- a/examples/gbenchmark/gbenchmark.pro +++ /dev/null @@ -1,10 +0,0 @@ -CONFIG += qskexample - -QT += svg - -HEADERS += \ - Benchmark.h - -SOURCES += \ - Benchmark.cpp \ - main.cpp diff --git a/examples/gbenchmark/main.cpp b/examples/gbenchmark/main.cpp deleted file mode 100644 index 2a5b00a2..00000000 --- a/examples/gbenchmark/main.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * This file may be used under the terms of the 3-clause BSD License - *****************************************************************************/ - -#include "Benchmark.h" - -#include -#include -#include - -#include -#include - -class Button : public QskPushButton -{ - public: - Button( const QString& testDir ) - : m_testDir( testDir ) - { - setText( QString( "Run: " ) + testDir ); - setSizePolicy( QskSizePolicy::Fixed, QskSizePolicy::Fixed ); - } - - void run() - { - Benchmark::run( m_testDir ); - } - - private: - QString m_testDir; -}; - -int main( int argc, char* argv[] ) -{ - QGuiApplication app( argc, argv ); - - QCommandLineParser parser; - parser.setApplicationDescription( "Benchmark for creating textures from SVGs" ); - parser.addHelpOption(); - parser.addPositionalArgument( "svgdir", "Directory with SVG files.", "[pathname]" ); - - parser.process( app ); - - const QStringList args = parser.positionalArguments(); - - if ( args.count() != 1 ) - parser.showHelp( 1 ); - - auto button = new Button( args[ 0 ] ); - button->setLayoutAlignmentHint( Qt::AlignCenter ); - QObject::connect( button, &Button::clicked, button, &Button::run ); - - auto box = new QskLinearBox(); - box->addItem( button ); - - QskWindow window; - window.setColor( Qt::white ); - window.resize( 400, 400 ); - window.addItem( box ); - - window.show(); - - return app.exec(); -} diff --git a/src/controls/QskSkinlet.cpp b/src/controls/QskSkinlet.cpp index 14319207..29fc172b 100644 --- a/src/controls/QskSkinlet.cpp +++ b/src/controls/QskSkinlet.cpp @@ -113,18 +113,17 @@ static inline QSGNode* qskUpdateGraphicNode( if ( control == nullptr ) return nullptr; - auto mode = QskTextureRenderer::OpenGL; - auto graphicNode = static_cast< QskGraphicNode* >( node ); if ( graphicNode == nullptr ) graphicNode = new QskGraphicNode(); - if ( control->testUpdateFlag( QskControl::PreferRasterForTextures ) ) - mode = QskTextureRenderer::Raster; + const bool useRaster = control->testUpdateFlag( QskControl::PreferRasterForTextures ); + graphicNode->setRenderHint( useRaster ? QskPaintedNode::Raster : QskPaintedNode::OpenGL ); + + graphicNode->setMirrored( mirrored ); const auto r = qskSceneAlignedRect( control, rect ); - graphicNode->setGraphic( control->window(), graphic, - colorFilter, mode, r, mirrored ); + graphicNode->setGraphic( control->window(), graphic, colorFilter, r ); return graphicNode; } diff --git a/src/graphic/QskGraphicTextureFactory.cpp b/src/graphic/QskGraphicTextureFactory.cpp index 3a256448..25210b87 100644 --- a/src/graphic/QskGraphicTextureFactory.cpp +++ b/src/graphic/QskGraphicTextureFactory.cpp @@ -54,16 +54,30 @@ QSize QskGraphicTextureFactory::size() const return m_size; } - QSGTexture* QskGraphicTextureFactory::createTexture( QQuickWindow* window ) const { - using namespace QskTextureRenderer; + class PaintHelper : public QskTextureRenderer::PaintHelper + { + public: + PaintHelper( const QskGraphic& graphic, const QskColorFilter& filter ) + : m_graphic( graphic ) + , m_filter( filter ) + { + } - const uint textureId = createTextureFromGraphic( - window, QskTextureRenderer::OpenGL, m_size, m_graphic, m_colorFilter, - Qt::IgnoreAspectRatio ); + void paint( QPainter* painter, const QSize& size ) override + { + const QRect rect( 0, 0, size.width(), size.height() ); + m_graphic.render( painter, rect, m_filter ); + } - return textureFromId( window, textureId, m_size ); + private: + const QskGraphic& m_graphic; + const QskColorFilter& m_filter; + }; + + PaintHelper helper( m_graphic, m_colorFilter ); + return QskTextureRenderer::createPaintedTexture( window, m_size, &helper ); } QSize QskGraphicTextureFactory::textureSize() const diff --git a/src/nodes/QskArcNode.cpp b/src/nodes/QskArcNode.cpp index 3b9f3726..be8feb07 100644 --- a/src/nodes/QskArcNode.cpp +++ b/src/nodes/QskArcNode.cpp @@ -30,10 +30,10 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& metrics, const QskGradient& gradient, QQuickWindow* window ) { const ArcData arcData { metrics, gradient }; - update( window, rect.toRect(), &arcData ); + update( window, rect, QSizeF(), &arcData ); } -void QskArcNode::paint( QPainter* painter, const QSizeF& size, const void* nodeData ) +void QskArcNode::paint( QPainter* painter, const QSize& size, const void* nodeData ) { const auto arcData = reinterpret_cast< const ArcData* >( nodeData ); diff --git a/src/nodes/QskArcNode.h b/src/nodes/QskArcNode.h index 411c7582..24c66654 100644 --- a/src/nodes/QskArcNode.h +++ b/src/nodes/QskArcNode.h @@ -23,7 +23,7 @@ class QSK_EXPORT QskArcNode : public QskPaintedNode const QskGradient&, QQuickWindow* ); protected: - void paint( QPainter*, const QSizeF&, const void* nodeData ) override; + void paint( QPainter*, const QSize&, const void* nodeData ) override; QskHashValue hash( const void* nodeData ) const override; }; diff --git a/src/nodes/QskGraphicNode.cpp b/src/nodes/QskGraphicNode.cpp index 4e3e4d79..86023740 100644 --- a/src/nodes/QskGraphicNode.cpp +++ b/src/nodes/QskGraphicNode.cpp @@ -8,27 +8,17 @@ #include "QskColorFilter.h" #include "QskPainterCommand.h" -static inline QskHashValue qskHash( - const QskGraphic& graphic, const QskColorFilter& colorFilter, - QskTextureRenderer::RenderMode renderMode ) +namespace { - QskHashValue hash = 12000; - - const auto& substitutions = colorFilter.substitutions(); - if ( substitutions.size() > 0 ) + class GraphicData { - hash = qHashBits( substitutions.constData(), - substitutions.size() * sizeof( substitutions[ 0 ] ), hash ); - } - - hash = graphic.hash( hash ); - hash = qHash( renderMode, hash ); - - return hash; + public: + const QskGraphic& graphic; + const QskColorFilter& colorFilter; + }; } QskGraphicNode::QskGraphicNode() - : m_hash( 0 ) { } @@ -36,14 +26,10 @@ QskGraphicNode::~QskGraphicNode() { } -void QskGraphicNode::setGraphic( - QQuickWindow* window, const QskGraphic& graphic, const QskColorFilter& colorFilter, - QskTextureRenderer::RenderMode renderMode, const QRectF& rect, - Qt::Orientations mirrored ) +void QskGraphicNode::setGraphic( QQuickWindow* window, const QskGraphic& graphic, + const QskColorFilter& colorFilter, const QRectF& rect ) { - bool isTextureDirty = isNull(); - - QSize textureSize; + QSizeF size; if ( graphic.commandTypes() == QskGraphic::RasterData ) { @@ -52,34 +38,43 @@ void QskGraphicNode::setGraphic( There is no benefit in rescaling it into the target rectangle by the CPU and creating a new texture. */ - textureSize = graphic.defaultSize().toSize(); - } - else - { - textureSize = rect.size().toSize(); - - if ( !isTextureDirty ) - { - const auto oldRect = QskTextureNode::rect(); - isTextureDirty = ( rect.width() != static_cast< int >( oldRect.width() ) ) || - ( rect.height() != static_cast< int >( oldRect.height() ) ); - } + size = graphic.defaultSize(); } - const auto hash = qskHash( graphic, colorFilter, renderMode ); - if ( hash != m_hash ) - { - m_hash = hash; - isTextureDirty = true; - } - - auto textureId = QskTextureNode::textureId(); - - if ( isTextureDirty ) - { - textureId = QskTextureRenderer::createTextureFromGraphic( - window, renderMode, textureSize, graphic, colorFilter, Qt::IgnoreAspectRatio ); - } - - QskTextureNode::setTexture( window, rect, textureId, mirrored ); + const GraphicData graphicData { graphic, colorFilter }; + update( window, rect, size, &graphicData ); +} + +void QskGraphicNode::paint( QPainter* painter, const QSize& size, const void* nodeData ) +{ + const auto graphicData = reinterpret_cast< const GraphicData* >( nodeData ); + + const auto& graphic = graphicData->graphic; + const auto& colorFilter = graphicData->colorFilter; + + if ( graphic.commandTypes() == QskGraphic::RasterData ) + { + qDebug() << size; + } + + const QRectF rect( 0, 0, size.width(), size.height() ); + graphic.render( painter, rect, colorFilter, Qt::IgnoreAspectRatio ); +} + +QskHashValue QskGraphicNode::hash( const void* nodeData ) const +{ + const auto graphicData = reinterpret_cast< const GraphicData* >( nodeData ); + + const auto& graphic = graphicData->graphic; + + QskHashValue hash = 12000; + + const auto& substitutions = graphicData->colorFilter.substitutions(); + if ( substitutions.size() > 0 ) + { + hash = qHashBits( substitutions.constData(), + substitutions.size() * sizeof( substitutions[ 0 ] ), hash ); + } + + return graphic.hash( hash ); } diff --git a/src/nodes/QskGraphicNode.h b/src/nodes/QskGraphicNode.h index 3d5925b5..26b5930c 100644 --- a/src/nodes/QskGraphicNode.h +++ b/src/nodes/QskGraphicNode.h @@ -6,29 +6,23 @@ #ifndef QSK_GRAPHIC_NODE_H #define QSK_GRAPHIC_NODE_H -#include "QskTextureRenderer.h" -#include "QskTextureNode.h" +#include "QskPaintedNode.h" class QskGraphic; class QskColorFilter; -class QQuickWindow; -class QSK_EXPORT QskGraphicNode : public QskTextureNode +class QSK_EXPORT QskGraphicNode : public QskPaintedNode { public: QskGraphicNode(); ~QskGraphicNode() override; - void setGraphic( QQuickWindow*, - const QskGraphic&, const QskColorFilter&, - QskTextureRenderer::RenderMode, const QRectF&, - Qt::Orientations mirrored = Qt::Orientations() ); + void setGraphic( QQuickWindow*, const QskGraphic&, + const QskColorFilter&, const QRectF& ); private: - void setTexture( QQuickWindow*, - const QRectF&, uint id, Qt::Orientations ) = delete; - - QskHashValue m_hash; + virtual void paint( QPainter*, const QSize&, const void* nodeData ) override; + virtual QskHashValue hash( const void* nodeData ) const override; }; #endif diff --git a/src/nodes/QskPaintedNode.cpp b/src/nodes/QskPaintedNode.cpp index beedd175..101f9641 100644 --- a/src/nodes/QskPaintedNode.cpp +++ b/src/nodes/QskPaintedNode.cpp @@ -5,65 +5,17 @@ #include "QskPaintedNode.h" #include "QskSGNode.h" +#include "QskTextureRenderer.h" #include #include #include #include -#include -#include -#include - QSK_QT_PRIVATE_BEGIN #include -#include -#include QSK_QT_PRIVATE_END -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) -#include -#endif - -static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo ) -{ - /* - See https://bugreports.qt.io/browse/QTBUG-103929 - - As we create a FBO for each update of a node we can't live - without having this ( ugly ) workaround. - */ - class MyFBO - { - public: - virtual ~MyFBO() = default; - QScopedPointer< QOpenGLFramebufferObjectPrivate > d_ptr; - }; - - static_assert( sizeof( MyFBO ) == sizeof( QOpenGLFramebufferObject ), - "Bad cast: QOpenGLFramebufferObject does not match" ); - - auto& attachment = reinterpret_cast< MyFBO* >( &fbo )->d_ptr->colorAttachments[0]; - auto guard = attachment.guard; - - const auto textureId = fbo.takeTexture(); - - if ( guard ) - { - class MyGuard : public QOpenGLSharedResourceGuard - { - public: - void invalidateTexture() { invalidateResource(); } - }; - - reinterpret_cast< MyGuard* >( guard )->invalidateTexture(); - } - - attachment.guard = guard; - - return textureId; -} - static inline QSGImageNode::TextureCoordinatesTransformMode qskEffectiveTransformMode( const Qt::Orientations mirrored ) { @@ -89,15 +41,6 @@ namespace return static_cast< QSGImageNode* >( node ); } - - static inline bool qskHasOpenGLRenderer( QQuickWindow* window ) - { - if ( window == nullptr ) - return false; - - const auto renderer = window->rendererInterface(); - return renderer->graphicsApi() == QSGRendererInterface::OpenGL; - } } QskPaintedNode::QskPaintedNode() @@ -137,6 +80,17 @@ Qt::Orientations QskPaintedNode::mirrored() const return m_mirrored; } +QSize QskPaintedNode::textureSize() const +{ + if ( const auto imageNode = findImageNode( this ) ) + { + if ( auto texture = imageNode->texture() ) + return texture->textureSize(); + } + + return QSize(); +} + QRectF QskPaintedNode::rect() const { const auto imageNode = findImageNode( this ); @@ -144,7 +98,7 @@ QRectF QskPaintedNode::rect() const } void QskPaintedNode::update( QQuickWindow* window, - const QRectF& rect, const void* nodeData ) + const QRectF& rect, const QSizeF& size, const void* nodeData ) { auto imageNode = findImageNode( this ); @@ -159,186 +113,123 @@ void QskPaintedNode::update( QQuickWindow* window, return; } - bool isDirty = false; + if ( imageNode == nullptr ) + { + imageNode = window->createImageNode(); + + imageNode->setOwnsTexture( true ); + QskSGNode::setNodeRole( imageNode, imageRole ); + + appendChildNode( imageNode ); + } + + QSize imageSize; + + { + auto scaledSize = size.isEmpty() ? rect.size() : size; + scaledSize *= window->effectiveDevicePixelRatio(); + + imageSize = scaledSize.toSize(); + } + + bool isTextureDirty = false; const auto newHash = hash( nodeData ); if ( ( newHash == 0 ) || ( newHash != m_hash ) ) { m_hash = newHash; - isDirty = true; + isTextureDirty = true; + } + else + { + isTextureDirty = ( imageSize != textureSize() ); } - if ( !isDirty ) - isDirty = ( imageNode == nullptr ) || ( imageNode->rect() != rect ); - if ( isDirty ) + if ( isTextureDirty ) + updateTexture( window, imageSize, nodeData ); + + imageNode->setRect( rect ); + imageNode->setTextureCoordinatesTransform( + qskEffectiveTransformMode( m_mirrored ) ); +} + +void QskPaintedNode::updateTexture( QQuickWindow* window, + const QSize& size, const void* nodeData ) +{ + auto imageNode = findImageNode( this ); + + if ( ( m_renderHint == OpenGL ) && QskTextureRenderer::isOpenGLWindow( window ) ) { - if ( ( m_renderHint == OpenGL ) && qskHasOpenGLRenderer( window ) ) - updateImageNodeGL( window, rect, nodeData ); + const auto textureId = createTextureGL( window, size, nodeData ); + + auto texture = qobject_cast< QSGPlainTexture* >( imageNode->texture() ); + if ( texture == nullptr ) + { + texture = new QSGPlainTexture; + texture->setHasAlphaChannel( true ); + texture->setOwnsTexture( true ); + + imageNode->setTexture( texture ); + } + + QskTextureRenderer::setTextureId( window, textureId, size, texture ); + } + else + { + const auto image = createImage( window, size, nodeData ); + + if ( auto texture = qobject_cast< QSGPlainTexture* >( imageNode->texture() ) ) + texture->setImage( image ); else - updateImageNode( window, rect, nodeData ); - } - - imageNode = findImageNode( this ); - if ( imageNode ) - { - imageNode->setRect( rect ); - imageNode->setTextureCoordinatesTransform( - qskEffectiveTransformMode( m_mirrored ) ); + imageNode->setTexture( window->createTextureFromImage( image ) ); } } -void QskPaintedNode::updateImageNode( - QQuickWindow* window, const QRectF& rect, const void* nodeData ) +QImage QskPaintedNode::createImage( QQuickWindow* window, + const QSize& size, const void* nodeData ) { - const auto ratio = window->effectiveDevicePixelRatio(); - const auto size = rect.size() * ratio; - - QImage image( size.toSize(), QImage::Format_RGBA8888_Premultiplied ); + QImage image( size, QImage::Format_RGBA8888_Premultiplied ); image.fill( Qt::transparent ); - { - QPainter painter( &image ); - - /* - setting a devicePixelRatio for the image only works for - value >= 1.0. So we have to scale manually. - */ - painter.scale( ratio, ratio ); - paint( &painter, rect.size(), nodeData ); - } - - auto imageNode = findImageNode( this ); - - if ( imageNode == nullptr ) - { - imageNode = window->createImageNode(); - - imageNode->setOwnsTexture( true ); - QskSGNode::setNodeRole( imageNode, imageRole ); - - appendChildNode( imageNode ); - } - - if ( auto texture = qobject_cast< QSGPlainTexture* >( imageNode->texture() ) ) - texture->setImage( image ); - else - imageNode->setTexture( window->createTextureFromImage( image ) ); -} - -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() ); - - auto imageNode = findImageNode( this ); - - if ( imageNode == nullptr ) - { - imageNode = window->createImageNode(); - - imageNode->setOwnsTexture( true ); - QskSGNode::setNodeRole( imageNode, imageRole ); - - appendChildNode( imageNode ); - } - - auto texture = qobject_cast< QSGPlainTexture* >( imageNode->texture() ); - if ( texture == nullptr ) - { - texture = new QSGPlainTexture; - texture->setHasAlphaChannel( true ); - texture->setOwnsTexture( true ); - - imageNode->setTexture( texture ); - } + QPainter painter( &image ); /* - 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 ... + setting a devicePixelRatio for the image only works for + value >= 1.0. So we have to scale manually. */ - const auto textureId = createTexture( window, size, nodeData ); + const auto ratio = window->effectiveDevicePixelRatio(); + painter.scale( ratio, ratio ); - auto rhi = QQuickWindowPrivate::get( window )->rhi; + paint( &painter, size / ratio, nodeData ); -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - texture->setTextureFromNativeTexture( - rhi, quint64( textureId ), 0, size, {}, {} ); -#else - if ( rhi ) - { - // enabled with: "export QSG_RHI=1" - texture->setTextureFromNativeObject( rhi, - QQuickWindow::NativeObjectTexture, &textureId, 0, size, false ); - } - else - { - texture->setTextureId( textureId ); - texture->setTextureSize( size ); - } -#endif + painter.end(); + + return image; } -uint32_t QskPaintedNode::createTexture( +quint32 QskPaintedNode::createTextureGL( 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; - format1.setAttachment( QOpenGLFramebufferObject::CombinedDepthStencil ); - - format1.setSamples( context->format().samples() ); - - QOpenGLFramebufferObject multisampledFbo( size, format1 ); - - QOpenGLPaintDevice pd( size ); - pd.setPaintFlipped( true ); - + class PaintHelper : public QskTextureRenderer::PaintHelper { - const auto ratio = window->effectiveDevicePixelRatio(); + public: + PaintHelper( QskPaintedNode* node, const void* nodeData ) + : m_node( node ) + , m_nodeData( nodeData ) + { + } - QPainter painter( &pd ); - painter.scale( ratio, ratio ); + void paint( QPainter* painter, const QSize& size ) override + { + m_node->paint( painter, size, m_nodeData ); + } - painter.setCompositionMode( QPainter::CompositionMode_Source ); - painter.fillRect( 0, 0, size.width(), size.height(), Qt::transparent ); - painter.setCompositionMode( QPainter::CompositionMode_SourceOver ); + private: + QskPaintedNode* m_node; + const void* m_nodeData; + }; - paint( &painter, size, nodeData ); - } - - QOpenGLFramebufferObjectFormat format2; - format2.setAttachment( QOpenGLFramebufferObject::NoAttachment ); - - QOpenGLFramebufferObject fbo( size, format2 ); - - const QRect fboRect( 0, 0, size.width(), size.height() ); - - QOpenGLFramebufferObject::blitFramebuffer( - &fbo, fboRect, &multisampledFbo, fboRect ); - - window->endExternalCommands(); - - return qskTakeTexture( fbo ); + PaintHelper helper( this, nodeData ); + return createPaintedTextureGL( window, size, &helper ); } diff --git a/src/nodes/QskPaintedNode.h b/src/nodes/QskPaintedNode.h index 755b8dac..cda4250f 100644 --- a/src/nodes/QskPaintedNode.h +++ b/src/nodes/QskPaintedNode.h @@ -11,6 +11,7 @@ class QQuickWindow; class QPainter; +class QImage; class QSK_EXPORT QskPaintedNode : public QSGNode { @@ -42,20 +43,21 @@ class QSK_EXPORT QskPaintedNode : public QSGNode Qt::Orientations mirrored() const; QRectF rect() const; + QSize textureSize() const; + + virtual void paint( QPainter*, const QSize&, const void* nodeData ) = 0; protected: - void update( QQuickWindow*, const QRectF&, const void* nodeData ); - - virtual void paint( QPainter*, const QSizeF&, const void* nodeData ) = 0; + void update( QQuickWindow*, const QRectF&, const QSizeF&, const void* nodeData ); // a hash value of '0' always results in repainting virtual QskHashValue hash( const void* nodeData ) const = 0; private: - void updateImageNode( QQuickWindow*, const QRectF&, const void* nodeData ); - void updateImageNodeGL( QQuickWindow*, const QRectF&, const void* nodeData ); + void updateTexture( QQuickWindow*, const QSize&, const void* nodeData ); - uint32_t createTexture( QQuickWindow*, const QSize&, const void* nodeData ); + QImage createImage( QQuickWindow*, const QSize&, const void* nodeData ); + quint32 createTextureGL( QQuickWindow*, const QSize&, const void* nodeData ); RenderHint m_renderHint = OpenGL; Qt::Orientations m_mirrored; diff --git a/src/nodes/QskTextureRenderer.cpp b/src/nodes/QskTextureRenderer.cpp index 16bc8074..072b1dd1 100644 --- a/src/nodes/QskTextureRenderer.cpp +++ b/src/nodes/QskTextureRenderer.cpp @@ -4,92 +4,177 @@ *****************************************************************************/ #include "QskTextureRenderer.h" -#include "QskColorFilter.h" -#include "QskGraphic.h" -#include "QskSetup.h" #include -#include #include -#include #include -#include #include #include #include -#include + +QSK_QT_PRIVATE_BEGIN +#include +#include +QSK_QT_PRIVATE_END #if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - #include + #include #endif -static inline bool qskHasOpenGLRenderer( const QQuickWindow* window ) +static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo ) +{ + /* + See https://bugreports.qt.io/browse/QTBUG-103929 + + As we create a FBO for each update of a node we can't live + without having this ( ugly ) workaround. + */ + class MyFBO + { + public: + virtual ~MyFBO() = default; + QScopedPointer< QOpenGLFramebufferObjectPrivate > d_ptr; + }; + + static_assert( sizeof( MyFBO ) == sizeof( QOpenGLFramebufferObject ), + "Bad cast: QOpenGLFramebufferObject does not match" ); + + auto& attachment = reinterpret_cast< MyFBO* >( &fbo )->d_ptr->colorAttachments[0]; + auto guard = attachment.guard; + + const auto textureId = fbo.takeTexture(); + + if ( guard ) + { + class MyGuard : public QOpenGLSharedResourceGuard + { + public: + void invalidateTexture() { invalidateResource(); } + }; + + reinterpret_cast< MyGuard* >( guard )->invalidateTexture(); + } + + attachment.guard = guard; + + return textureId; +} + +bool QskTextureRenderer::isOpenGLWindow( const QQuickWindow* window ) { if ( window == nullptr ) return false; const auto renderer = window->rendererInterface(); - return renderer->graphicsApi() == QSGRendererInterface::OpenGL; + switch( renderer->graphicsApi() ) + { + case QSGRendererInterface::OpenGL: +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + case QSGRendererInterface::OpenGLRhi: +#endif + return true; + + default: + return false; + } } -static uint qskCreateTextureOpenGL( QQuickWindow* window, - const QSize& size, QskTextureRenderer::PaintHelper* helper ) +void QskTextureRenderer::setTextureId( QQuickWindow* window, + quint32 textureId, const QSize& size, QSGTexture* texture ) { - const auto ratio = window ? window->effectiveDevicePixelRatio() : 1.0; + auto plainTexture = qobject_cast< QSGPlainTexture* >( texture ); + if ( plainTexture == nullptr ) + return; - const int width = ratio * size.width(); - const int height = ratio * size.height(); + auto rhi = QQuickWindowPrivate::get( window )->rhi; + +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) + plainTexture->setTextureFromNativeTexture( + rhi, quint64( textureId ), 0, size, {}, {} ); +#else + if ( rhi ) + { + // enabled with: "export QSG_RHI=1" + plainTexture->setTextureFromNativeObject( rhi, + QQuickWindow::NativeObjectTexture, &textureId, 0, size, false ); + } + else + { + plainTexture->setTextureId( textureId ); + plainTexture->setTextureSize( size ); + } +#endif +} + +quint32 QskTextureRenderer::createPaintedTextureGL( + QQuickWindow* window, const QSize& size, QskTextureRenderer::PaintHelper* helper ) +{ + /* + 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. + + QQuickFramebufferObject does the FBO rendering early + ( QQuickWindow::beforeRendering ). But so far doing it below updatePaintNode + seems to work as well. Let's see if we run into issues ... + */ + + window->beginExternalCommands(); + +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) + QQuickOpenGLUtils::resetOpenGLState(); +#else + window->resetOpenGLState(); +#endif + + auto context = QOpenGLContext::currentContext(); QOpenGLFramebufferObjectFormat format1; format1.setAttachment( QOpenGLFramebufferObject::CombinedDepthStencil ); - // ### TODO: get samples from window instead - format1.setSamples( QOpenGLContext::currentContext()->format().samples() ); + format1.setSamples( context->format().samples() ); - QOpenGLFramebufferObject multisampledFbo( width, height, format1 ); + QOpenGLFramebufferObject multisampledFbo( size, format1 ); - QOpenGLPaintDevice pd( width, height ); + QOpenGLPaintDevice pd( size ); pd.setPaintFlipped( true ); { QPainter painter( &pd ); - painter.scale( ratio, ratio ); painter.setCompositionMode( QPainter::CompositionMode_Source ); - painter.fillRect( 0, 0, width, height, Qt::transparent ); + painter.fillRect( 0, 0, size.width(), size.height(), Qt::transparent ); painter.setCompositionMode( QPainter::CompositionMode_SourceOver ); - helper->paint( &painter, size ); + const auto ratio = window->effectiveDevicePixelRatio(); -#if 1 - if ( format1.samples() > 0 ) - { - /* - Multisampling in the window surface might get lost - as a side effect of rendering to the FBO. - weired, needs to be investigated more - */ - painter.setRenderHint( QPainter::Antialiasing, true ); - } -#endif + painter.scale( ratio, ratio ); + helper->paint( &painter, size / ratio ); } QOpenGLFramebufferObjectFormat format2; format2.setAttachment( QOpenGLFramebufferObject::NoAttachment ); - QOpenGLFramebufferObject fbo( width, height, format2 ); + QOpenGLFramebufferObject fbo( size, format2 ); - const QRect fboRect( 0, 0, width, height ); + const QRect fboRect( 0, 0, size.width(), size.height() ); QOpenGLFramebufferObject::blitFramebuffer( &fbo, fboRect, &multisampledFbo, fboRect ); - return fbo.takeTexture(); + window->endExternalCommands(); + + return qskTakeTexture( fbo ); } -static uint qskCreateTextureRaster( QQuickWindow* window, +static QSGTexture* qskCreateTextureRaster( QQuickWindow* window, const QSize& size, QskTextureRenderer::PaintHelper* helper ) { const auto ratio = window ? window->effectiveDevicePixelRatio() : 1.0; @@ -109,130 +194,26 @@ static uint qskCreateTextureRaster( QQuickWindow* window, helper->paint( &painter, size ); } - const auto target = QOpenGLTexture::Target2D; + return window->createTextureFromImage( image, QQuickWindow::TextureHasAlphaChannel ); +} - auto context = QOpenGLContext::currentContext(); - if ( context == nullptr ) - return 0; - - auto& f = *context->functions(); - - GLint oldTexture; // we can't rely on having OpenGL Direct State Access - f.glGetIntegerv( QOpenGLTexture::BindingTarget2D, &oldTexture ); - - GLuint textureId; - f.glGenTextures( 1, &textureId ); - - f.glBindTexture( target, textureId ); - - f.glTexParameteri( target, GL_TEXTURE_MIN_FILTER, QOpenGLTexture::Nearest ); - f.glTexParameteri( target, GL_TEXTURE_MAG_FILTER, QOpenGLTexture::Nearest ); - - f.glTexParameteri( target, GL_TEXTURE_WRAP_S, QOpenGLTexture::ClampToEdge ); - f.glTexParameteri( target, GL_TEXTURE_WRAP_T, QOpenGLTexture::ClampToEdge ); - - if ( QOpenGLTexture::hasFeature( QOpenGLTexture::ImmutableStorage ) ) +QSGTexture* QskTextureRenderer::createPaintedTexture( + QQuickWindow* window, const QSize& size, PaintHelper* helper ) +{ + if ( isOpenGLWindow( window ) ) { - auto& ef = *context->extraFunctions(); - ef.glTexStorage2D( target, 1, - QOpenGLTexture::RGBA8_UNorm, image.width(), image.height() ); + const auto textureId = createPaintedTextureGL( window, size, helper ); - f.glTexSubImage2D( target, 0, 0, 0, image.width(), image.height(), - QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, image.constBits() ); + auto texture = new QSGPlainTexture; + texture->setHasAlphaChannel( true ); + texture->setOwnsTexture( true ); + + setTextureId( window, textureId, size, texture ); + + return texture; } else { - f.glTexImage2D( target, 0, QOpenGLTexture::RGBA8_UNorm, - image.width(), image.height(), 0, - QOpenGLTexture::RGBA, QOpenGLTexture::UInt8, image.constBits() ); - } - - f.glBindTexture( target, oldTexture ); - - return textureId; -} - -QSGTexture* QskTextureRenderer::textureFromId( - QQuickWindow* window, uint textureId, const QSize& size ) -{ - const auto flags = static_cast< QQuickWindow::CreateTextureOptions >( - QQuickWindow::TextureHasAlphaChannel | QQuickWindow::TextureOwnsGLTexture ); - - QSGTexture* texture; - -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - - texture = QNativeInterface::QSGOpenGLTexture::fromNative( - textureId, window, size, flags ); - -#else - - const int nativeLayout = 0; // VkImageLayout in case of Vulkan - - texture = window->createTextureFromNativeObject( - QQuickWindow::NativeObjectTexture, &textureId, nativeLayout, size, flags ); -#endif - - return texture; -} - -uint QskTextureRenderer::createTexture( - QQuickWindow* window, RenderMode renderMode, - const QSize& size, PaintHelper* helper ) -{ -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - // Qt6.0.0 is buggy when using FBOs. So let's disable it for the moment TODO ... - renderMode = Raster; -#endif - - if ( renderMode != Raster ) - { - if ( !qskHasOpenGLRenderer( window ) ) - renderMode = Raster; - } - - if ( renderMode == AutoDetect ) - { - if ( qskSetup->testItemUpdateFlag( QskQuickItem::PreferRasterForTextures ) ) - renderMode = Raster; - else - renderMode = OpenGL; - } - - if ( renderMode == Raster ) return qskCreateTextureRaster( window, size, helper ); - else - return qskCreateTextureOpenGL( window, size, helper ); -} - -uint QskTextureRenderer::createTextureFromGraphic( - QQuickWindow* window, RenderMode renderMode, const QSize& size, - const QskGraphic& graphic, const QskColorFilter& colorFilter, - Qt::AspectRatioMode aspectRatioMode ) -{ - class PaintHelper : public QskTextureRenderer::PaintHelper - { - public: - PaintHelper( const QskGraphic& graphic, - const QskColorFilter& filter, Qt::AspectRatioMode aspectRatioMode ) - : m_graphic( graphic ) - , m_filter( filter ) - , m_aspectRatioMode( aspectRatioMode ) - { - } - - void paint( QPainter* painter, const QSize& size ) override - { - const QRect rect( 0, 0, size.width(), size.height() ); - m_graphic.render( painter, rect, m_filter, m_aspectRatioMode ); - } - - private: - const QskGraphic& m_graphic; - const QskColorFilter& m_filter; - const Qt::AspectRatioMode m_aspectRatioMode; - }; - - PaintHelper helper( graphic, colorFilter, aspectRatioMode ); - return createTexture( window, renderMode, size, &helper ); + } } diff --git a/src/nodes/QskTextureRenderer.h b/src/nodes/QskTextureRenderer.h index 4e83b6fd..afdec3ba 100644 --- a/src/nodes/QskTextureRenderer.h +++ b/src/nodes/QskTextureRenderer.h @@ -7,35 +7,15 @@ #define QSK_TEXTURE_RENDERER_H #include "QskGlobal.h" -#include -class QskGraphic; -class QskColorFilter; - -class QPainter; class QSize; +class QPainter; class QSGTexture; class QQuickWindow; namespace QskTextureRenderer { - /* - 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 ... - */ - enum RenderMode - { - AutoDetect, // depends on QskSetup::controlFlags() - - Raster, - OpenGL - }; - - class QSK_EXPORT PaintHelper + class PaintHelper { public: PaintHelper() = default; @@ -47,15 +27,16 @@ namespace QskTextureRenderer Q_DISABLE_COPY( PaintHelper ) }; - QSK_EXPORT uint createTexture( - QQuickWindow*, RenderMode, const QSize&, PaintHelper* ); + bool isOpenGLWindow( const QQuickWindow* ); - QSK_EXPORT uint createTextureFromGraphic( - QQuickWindow*, RenderMode, const QSize&, const QskGraphic&, - const QskColorFilter&, Qt::AspectRatioMode ); + void setTextureId( QQuickWindow*, + quint32 textureId, const QSize&, QSGTexture* ); - QSK_EXPORT QSGTexture* textureFromId( - QQuickWindow*, uint textureId, const QSize& ); + quint32 createPaintedTextureGL( + QQuickWindow*, const QSize&, QskTextureRenderer::PaintHelper* ); + + QSK_EXPORT QSGTexture* createPaintedTexture( + QQuickWindow* window, const QSize& size, PaintHelper* helper ); } #endif From c3c0b2cd3365908873b37ae7048029ba4b1738ec Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 2 Jun 2022 16:09:49 +0200 Subject: [PATCH 10/12] bye, bye QskTextureNode --- src/nodes/QskTextureNode.cpp | 393 ----------------------------------- src/nodes/QskTextureNode.h | 36 ---- src/src.pro | 2 - 3 files changed, 431 deletions(-) delete mode 100644 src/nodes/QskTextureNode.cpp delete mode 100644 src/nodes/QskTextureNode.h diff --git a/src/nodes/QskTextureNode.cpp b/src/nodes/QskTextureNode.cpp deleted file mode 100644 index ae90fd66..00000000 --- a/src/nodes/QskTextureNode.cpp +++ /dev/null @@ -1,393 +0,0 @@ -#include "QskTextureNode.h" -#include "QskFunctions.h" - -#include -#include -#include -#include - -QSK_QT_PRIVATE_BEGIN -#include -QSK_QT_PRIVATE_END - -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - -#include -#include -#include - -QSK_QT_PRIVATE_BEGIN -#include -#include -QSK_QT_PRIVATE_END - -static void qskUpdateGLTextureId( QRhiTexture* rhiTexture, uint textureId ) -{ - // hack time: we do not want to create a new QSGTexture object for each texture - - class Texture : public QRhiTexture - { - public: - GLuint texture; - // ... - }; - - GLuint id = rhiTexture->nativeTexture().object; - - if ( id ) - { - auto funcs = QOpenGLContext::currentContext()->functions(); - funcs->glDeleteTextures( 1, &id ); - } - - auto glTexture = static_cast< Texture* >( rhiTexture ); - glTexture->texture = textureId; - - Q_ASSERT( rhiTexture->nativeTexture().object == textureId ); -} - -using TextureMaterial = QSGTextureMaterial; -using OpaqueTextureMaterial = QSGOpaqueTextureMaterial; - -static inline void qskDeleteTexture( const TextureMaterial& material ) -{ - delete material.texture(); -} - -#endif - -#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - -namespace -{ - class MaterialShader final : public QSGMaterialShader - { - public: - MaterialShader( bool isOpaque ); - - char const* const* attributeNames() const override; - void updateState( const RenderState&, QSGMaterial*, QSGMaterial* ) override; - - protected: - void initialize() override; - - private: - int m_matrixId = -1; - int m_opacityId = -1; - - const bool m_isOpaque : 1; - }; - - class Material : public QSGMaterial - { - public: - Material( bool isOpaque ); - - QSGMaterialType* type() const override; - QSGMaterialShader* createShader() const override; - - void setTextureId( uint ); - uint textureId() const; - - int compare( const QSGMaterial* ) const override; - - private: - uint m_textureId = 0; - const bool m_isOpaque : 1; - }; - - class TextureMaterial final : public Material - { - public: - TextureMaterial() - : Material( false ) - { - } - }; - - class OpaqueTextureMaterial final : public Material - { - public: - OpaqueTextureMaterial() - : Material( true ) - { - } - }; - - MaterialShader::MaterialShader( bool isOpaque ) - : m_isOpaque( isOpaque ) - { - setShaderSourceFile( QOpenGLShader::Vertex, - QStringLiteral( ":/qt-project.org/scenegraph/shaders/opaquetexture.vert" ) ); - - const auto fragmentShaderFile = m_isOpaque - ? QStringLiteral( ":/qt-project.org/scenegraph/shaders/opaquetexture.frag" ) - : QStringLiteral( ":/qt-project.org/scenegraph/shaders/texture.frag" ); - - setShaderSourceFile( QOpenGLShader::Fragment, fragmentShaderFile ); - } - - char const* const* MaterialShader::attributeNames() const - { - static char const* const attr[] = { "qt_VertexPosition", "qt_VertexTexCoord", 0 }; - return attr; - } - - void MaterialShader::updateState( - const RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial ) - { - if ( !m_isOpaque && state.isOpacityDirty() ) - program()->setUniformValue( m_opacityId, state.opacity() ); - - auto* materialOld = static_cast< Material* >( oldMaterial ); - auto* materialNew = static_cast< Material* >( newMaterial ); - - if ( ( materialOld == nullptr ) || - ( materialOld->textureId() != materialNew->textureId() ) ) - { - auto funcs = QOpenGLContext::currentContext()->functions(); - funcs->glBindTexture( GL_TEXTURE_2D, materialNew->textureId() ); - } - - if ( state.isMatrixDirty() ) - program()->setUniformValue( m_matrixId, state.combinedMatrix() ); - } - - void MaterialShader::initialize() - { - m_matrixId = program()->uniformLocation( "qt_Matrix" ); - - if ( !m_isOpaque ) - m_opacityId = program()->uniformLocation( "opacity" ); - } - - Material::Material( bool isOpaque ) - : m_isOpaque( isOpaque ) - { - setFlag( Blending, true ); // alpha blending - } - - void Material::setTextureId( uint id ) - { - m_textureId = id; - } - - uint Material::textureId() const - { - return m_textureId; - } - - QSGMaterialType* Material::type() const - { - if ( m_isOpaque ) - { - static QSGMaterialType typeOpaque; - return &typeOpaque; - } - else - { - static QSGMaterialType type; - return &type; - } - } - - QSGMaterialShader* Material::createShader() const - { - return new MaterialShader( m_isOpaque ); - } - - int Material::compare( const QSGMaterial* other ) const - { - const auto otherMaterial = static_cast< const Material* >( other ); - - if ( m_textureId == otherMaterial->m_textureId ) - return 0; - - return ( m_textureId > otherMaterial->m_textureId ) ? 1 : -1; - } -} - -static inline void qskDeleteTexture( const TextureMaterial& material ) -{ - if ( material.textureId() > 0 ) - { - /* - In certain environments we have the effect, that at - program termination the context is already gone - */ - if ( auto context = QOpenGLContext::currentContext() ) - { - GLuint id = material.textureId(); - - auto funcs = context->functions(); - funcs->glDeleteTextures( 1, &id ); - } - } -} - -#endif - -class QskTextureNodePrivate final : public QSGGeometryNodePrivate -{ - public: - QskTextureNodePrivate() - : geometry( QSGGeometry::defaultAttributes_TexturedPoint2D(), 4 ) - { - } - - void setTextureId( QQuickWindow*, uint id ); - - void updateTextureGeometry() - { - QRectF r( 0, 0, 1, 1 ); - - if ( this->mirrored & Qt::Horizontal ) - { - r.setLeft( 1 ); - r.setRight( 0 ); - } - - if ( mirrored & Qt::Vertical ) - { - r.setTop( 1 ); - r.setBottom( 0 ); - } - - QSGGeometry::updateTexturedRectGeometry( &geometry, rect, r ); - } - - QSGGeometry geometry; - - OpaqueTextureMaterial opaqueMaterial; - TextureMaterial material; - - QRectF rect; - Qt::Orientations mirrored; -}; - -QskTextureNode::QskTextureNode() - : QSGGeometryNode( *new QskTextureNodePrivate ) -{ - Q_D( QskTextureNode ); - - setGeometry( &d->geometry ); - - setMaterial( &d->material ); - setOpaqueMaterial( &d->opaqueMaterial ); -} - -QskTextureNode::~QskTextureNode() -{ - Q_D( const QskTextureNode ); - qskDeleteTexture( d->material ); -} - -void QskTextureNode::setTexture( QQuickWindow* window, - const QRectF& rect, uint textureId, - Qt::Orientations mirrored ) -{ - Q_D( QskTextureNode ); - - if ( ( d->rect != rect ) || ( d->mirrored != mirrored ) ) - { - d->rect = rect; - d->mirrored = mirrored; - - d->updateTextureGeometry(); - markDirty( DirtyGeometry ); - } - - if ( textureId != this->textureId() ) - { - d->setTextureId( window, textureId ); - markDirty( DirtyMaterial ); - } -} - -#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - -void QskTextureNodePrivate::setTextureId( QQuickWindow*, uint textureId ) -{ - qskDeleteTexture( this->material ); - - this->material.setTextureId( textureId ); - this->opaqueMaterial.setTextureId( textureId ); -} - -uint QskTextureNode::textureId() const -{ - Q_D( const QskTextureNode ); - return d->material.textureId(); -} - -#endif - -#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) - -void QskTextureNodePrivate::setTextureId( QQuickWindow* window, uint textureId ) -{ - auto texture = this->material.texture(); - - if ( texture ) - { - // we do not want to create a new QSGTexture object only - // to replace the textureId - - switch( window->rendererInterface()->graphicsApi() ) - { - case QSGRendererInterface::OpenGL: - { - qskUpdateGLTextureId( texture->rhiTexture(), textureId ); - break; - } - default: - { - delete texture; - texture = nullptr; - } - } - } - - if ( textureId > 0 && texture == nullptr ) - { - texture = QNativeInterface::QSGOpenGLTexture::fromNative( - static_cast< GLuint >( textureId ), window, - this->rect.size().toSize(), QQuickWindow::TextureHasAlphaChannel ); - - } - - this->material.setTexture( texture ); - this->opaqueMaterial.setTexture( texture ); -} - -uint QskTextureNode::textureId() const -{ - Q_D( const QskTextureNode ); - - if ( auto texture = d->material.texture() ) - { - const auto nativeTexture = texture->rhiTexture()->nativeTexture(); - return nativeTexture.object; - } - - return 0; -} - -#endif - -bool QskTextureNode::isNull() const -{ - return textureId() == 0; -} - -QRectF QskTextureNode::rect() const -{ - Q_D( const QskTextureNode ); - return d->rect; -} - -Qt::Orientations QskTextureNode::mirrored() const -{ - Q_D( const QskTextureNode ); - return d->mirrored; -} diff --git a/src/nodes/QskTextureNode.h b/src/nodes/QskTextureNode.h deleted file mode 100644 index 39868a63..00000000 --- a/src/nodes/QskTextureNode.h +++ /dev/null @@ -1,36 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * This file may be used under the terms of the QSkinny License, Version 1.0 - *****************************************************************************/ - -#ifndef QSK_TEXTURE_NODE_H -#define QSK_TEXTURE_NODE_H - -#include "QskGlobal.h" - -#include -#include - -class QQuickWindow; -class QskTextureNodePrivate; - -class QSK_EXPORT QskTextureNode : public QSGGeometryNode -{ - public: - QskTextureNode(); - ~QskTextureNode() override; - - bool isNull() const; - - void setTexture( QQuickWindow*, const QRectF&, uint id, - Qt::Orientations mirrored = Qt::Orientations() ); - - uint textureId() const; - QRectF rect() const; - Qt::Orientations mirrored() const; - - private: - Q_DECLARE_PRIVATE( QskTextureNode ) -}; - -#endif diff --git a/src/src.pro b/src/src.pro index ce4d27a3..56a98910 100644 --- a/src/src.pro +++ b/src/src.pro @@ -111,7 +111,6 @@ HEADERS += \ nodes/QskSGNode.h \ nodes/QskTextNode.h \ nodes/QskTextRenderer.h \ - nodes/QskTextureNode.h \ nodes/QskTextureRenderer.h \ nodes/QskTickmarksNode.h \ nodes/QskVertex.h @@ -132,7 +131,6 @@ SOURCES += \ nodes/QskSGNode.cpp \ nodes/QskTextNode.cpp \ nodes/QskTextRenderer.cpp \ - nodes/QskTextureNode.cpp \ nodes/QskTextureRenderer.cpp \ nodes/QskTickmarksNode.cpp \ nodes/QskVertex.cpp From a66d4645e85b86d65de384a0112d370ffb403322 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Thu, 2 Jun 2022 16:15:25 +0200 Subject: [PATCH 11/12] Qt5 compiler error fixed --- src/nodes/QskTextureRenderer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/nodes/QskTextureRenderer.cpp b/src/nodes/QskTextureRenderer.cpp index 072b1dd1..908cc296 100644 --- a/src/nodes/QskTextureRenderer.cpp +++ b/src/nodes/QskTextureRenderer.cpp @@ -17,6 +17,7 @@ QSK_QT_PRIVATE_BEGIN #include #include +#include QSK_QT_PRIVATE_END #if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) From e5af877246d23c1abfe13a2a81ac981dac575f47 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Fri, 3 Jun 2022 16:56:09 +0200 Subject: [PATCH 12/12] pointless fil removed --- tests/checkboxes/target_wrapper.sh | 6 ------ 1 file changed, 6 deletions(-) delete mode 100755 tests/checkboxes/target_wrapper.sh diff --git a/tests/checkboxes/target_wrapper.sh b/tests/checkboxes/target_wrapper.sh deleted file mode 100755 index 88cc3928..00000000 --- a/tests/checkboxes/target_wrapper.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/sh -LD_LIBRARY_PATH=/home/uwe/qskinny/config/qsk1/Qt/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} -export LD_LIBRARY_PATH -QT_PLUGIN_PATH=/home/uwe/qskinny/config/qsk1/Qt/plugins${QT_PLUGIN_PATH:+:$QT_PLUGIN_PATH} -export QT_PLUGIN_PATH -exec "$@"