diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 43feec2c..3906ad94 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -120,6 +120,7 @@ list(APPEND HEADERS nodes/QskPaintedNode.h nodes/QskPlainTextRenderer.h nodes/QskRichTextRenderer.h + nodes/QskSceneTexture.h nodes/QskSGNode.h nodes/QskStrokeNode.h nodes/QskStippledLineRenderer.h @@ -157,6 +158,7 @@ list(APPEND SOURCES nodes/QskPlainTextRenderer.cpp nodes/QskRectangleNode.cpp nodes/QskRichTextRenderer.cpp + nodes/QskSceneTexture.cpp nodes/QskSGNode.cpp nodes/QskStrokeNode.cpp nodes/QskStippledLineRenderer.cpp @@ -169,11 +171,6 @@ list(APPEND SOURCES nodes/QskVertex.cpp ) -if (QT_VERSION_MAJOR VERSION_GREATER 5) - list(APPEND HEADERS nodes/QskSceneTexture.h) - list(APPEND SOURCES nodes/QskSceneTexture.cpp) -endif() - qt_add_resources(SOURCES nodes/shaders.qrc) list(APPEND HEADERS diff --git a/src/controls/QskQuick.cpp b/src/controls/QskQuick.cpp index 7760e916..9d223a36 100644 --- a/src/controls/QskQuick.cpp +++ b/src/controls/QskQuick.cpp @@ -19,6 +19,14 @@ QSK_QT_PRIVATE_END #include #include +QRhi* qskRenderingHardwareInterface( const QQuickWindow* window ) +{ + if ( auto w = const_cast< QQuickWindow* >( window ) ) + return QQuickWindowPrivate::get( w )->rhi; + + return nullptr; +} + QRectF qskItemRect( const QQuickItem* item ) { auto d = QQuickItemPrivate::get( item ); diff --git a/src/controls/QskQuick.h b/src/controls/QskQuick.h index 622b108d..c8afaafd 100644 --- a/src/controls/QskQuick.h +++ b/src/controls/QskQuick.h @@ -18,14 +18,16 @@ class QSGNode; class QSGTransformNode; class QSGRootNode; class QRectF; +class QRhi; template< typename T > class QList; /* - Exporting methods from QQuickItemPrivate, that should be part - of QQuickItem. + Exporting useful methods from QQuickItemPrivate/QQuickWindowPrivate */ +QSK_EXPORT QRhi* qskRenderingHardwareInterface( const QQuickWindow* ); + QSK_EXPORT bool qskIsItemInDestructor( const QQuickItem* ); QSK_EXPORT bool qskIsItemComplete( const QQuickItem* ); QSK_EXPORT bool qskIsAncestorOf( const QQuickItem* item, const QQuickItem* child ); diff --git a/src/nodes/QskBoxNode.cpp b/src/nodes/QskBoxNode.cpp index 58329d1b..4de2242c 100644 --- a/src/nodes/QskBoxNode.cpp +++ b/src/nodes/QskBoxNode.cpp @@ -78,11 +78,9 @@ void QskBoxNode::updateNode( const QRectF& rect, } /* - QskBoxRectangleNode supports vertical/horizontal and many tilted - linear gradients. If our gradient doesn't fall into this category - we use a QskBoxFillNode. - - However the border is always done with a QskBoxRectangleNode + QskBoxRectangleNode is more efficient and creates batchable geometries. + So we prefer using it where possible. + Note, that the border is always done with a QskBoxRectangleNode */ if ( QskBoxRenderer::isGradientSupported( shape, gradient ) ) diff --git a/src/nodes/QskGradientMaterial.cpp b/src/nodes/QskGradientMaterial.cpp index 7be7d994..29771012 100644 --- a/src/nodes/QskGradientMaterial.cpp +++ b/src/nodes/QskGradientMaterial.cpp @@ -22,7 +22,7 @@ #endif #if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) - #include + #include using RhiShader = QSGMaterialRhiShader; #else using RhiShader = QSGMaterialShader; diff --git a/src/nodes/QskSceneTexture.cpp b/src/nodes/QskSceneTexture.cpp index 8660fdcb..9fa8af44 100644 --- a/src/nodes/QskSceneTexture.cpp +++ b/src/nodes/QskSceneTexture.cpp @@ -10,11 +10,28 @@ QSK_QT_PRIVATE_BEGIN #include +#include + +#define QT_BUILD_QUICK_LIB // suppress Qt5 warnings #include +#undef QT_BUILD_QUICK_LIB + QSK_QT_PRIVATE_END +/* + With Qt 5.15 Rhi can optionally be enbled by setting "export QSG_RHI=1". + So we need to have a native QOpenGL implementation and one using + the Rhi abstraction layer. For Qt6 we can rely on Rhi. + Once Qt5 support has been dropped we can eliminate this #ifdef jungle + */ + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) +#include +#endif + namespace { +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) inline QSGRendererInterface::RenderMode contextRenderMode( QSGDefaultRenderContext* context ) { @@ -22,6 +39,7 @@ namespace ? QSGRendererInterface::RenderMode2D : QSGRendererInterface::RenderMode2DNoDepthBuffer; } +#endif class Renderer final : public QSGBatchRenderer::Renderer { @@ -31,8 +49,16 @@ namespace Renderer( QskSceneTexture*, QSGDefaultRenderContext* ); ~Renderer() override; +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + inline int textureId() const { return m_fbo ? m_fbo->texture() : 0; } + inline void renderScene() { Inherited::renderScene( textureId() ); } +#endif + + inline QRhiTexture* rhiTexture() const { return m_rhiTexture; } + + inline bool isDirty() const { return m_dirty; } + void setFinalNode( QSGTransformNode* ); - QRhiTexture* texture() const { return m_rhiTexture; } void setProjection( const QRectF& ); void setTextureSize( const QSize& ); @@ -45,15 +71,30 @@ namespace void createTarget( const QSize& ); void clearTarget(); - private: - QRhiTexture* m_rhiTexture = nullptr; QSGTransformNode* m_finalNode = nullptr; - QskSceneTexture* m_texture = nullptr; + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QOpenGLFramebufferObject* m_fbo; + + struct RenderTarget + { + QRhiRenderTarget* rt = nullptr; + QRhiRenderPassDescriptor* rpDesc = nullptr; + QRhiCommandBuffer* cb = nullptr; + } m_rt; +#endif + QRhiTexture* m_rhiTexture = nullptr; + + bool m_dirty = true; }; Renderer::Renderer( QskSceneTexture* texture, QSGDefaultRenderContext* context ) +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + : Inherited( context ) +#else : Inherited( context, contextRenderMode( context ) ) +#endif , m_texture( texture ) { setClearColor( Qt::transparent ); @@ -74,11 +115,18 @@ namespace void Renderer::setProjection( const QRectF& rect ) { - const auto rhi = context()->rhi(); + bool flipFramebuffer = true; + bool flipMatrix = false; + + if ( const auto rhi = context()->rhi() ) + { + flipFramebuffer = rhi->isYUpInFramebuffer(); + flipMatrix = !rhi->isYUpInNDC(); + } auto r = rect; - if ( rhi->isYUpInFramebuffer() ) + if ( flipFramebuffer ) { r.moveTop( r.bottom() ); r.setHeight( -r.height() ); @@ -86,7 +134,7 @@ namespace MatrixTransformFlags matrixFlags; - if ( !rhi->isYUpInNDC() ) + if ( flipMatrix ) matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY; setProjectionMatrixToRect( r, matrixFlags ); @@ -94,11 +142,24 @@ namespace void Renderer::setTextureSize( const QSize& size ) { - if ( m_rt.rt && m_rt.rt->pixelSize() != size ) - clearTarget(); + if ( const auto rhi = context()->rhi() ) + { + if ( m_rt.rt && m_rt.rt->pixelSize() != size ) + clearTarget(); - if ( m_rt.rt == nullptr ) - createTarget( size ); + if ( m_rt.rt == nullptr ) + createTarget( size ); + } + else + { +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + if ( m_fbo && m_fbo->size() != size ) + clearTarget(); + + if ( m_fbo == nullptr ) + createTarget( size ); +#endif + } const QRect r( 0, 0, size.width(), size.height() ); @@ -108,8 +169,13 @@ namespace void Renderer::render() { + m_dirty = false; + qskTryBlockTrailingNodes( m_finalNode, rootNode(), true, false ); + #if 0 + static int counter = 0; + qDebug() << ++counter; QSGNodeDumper::dump( rootNode() ); #endif Inherited::render(); @@ -118,51 +184,92 @@ namespace void Renderer::nodeChanged( QSGNode* node, QSGNode::DirtyState state ) { - // do not forward nodes that are blocked while rendering + Inherited::nodeChanged( node, state ); - const bool isTrailingNode = false; // TODO ... + /* + We want to limit updates to nodes, that are actually rendered. TODO ... - if ( !isTrailingNode ) + In any case we need to block update requests, when the textureNode reports + that it has been updated by us to the renderer of the window. + */ + if ( ( state != QSGNode::DirtyMaterial ) + || ( node != m_texture->textureNode() ) ) { - Inherited::nodeChanged( node, state ); + m_dirty = true; Q_EMIT m_texture->updateRequested(); } } void Renderer::createTarget( const QSize& size ) { - const auto rhi = context()->rhi(); + if ( const auto rhi = context()->rhi() ) + { + auto flags = QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource; - auto flags = QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource; + m_rhiTexture = rhi->newTexture( QRhiTexture::RGBA8, size, 1, flags ); +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + m_rhiTexture->build(); +#else + m_rhiTexture->create(); +#endif - m_rhiTexture = rhi->newTexture( QRhiTexture::RGBA8, size, 1, flags ); - m_rhiTexture->create(); + QRhiColorAttachment color0( m_rhiTexture ); + auto target = rhi->newTextureRenderTarget( { color0 } ); - QRhiColorAttachment color0( m_rhiTexture ); - auto target = rhi->newTextureRenderTarget( { color0 } ); + target->setRenderPassDescriptor( + target->newCompatibleRenderPassDescriptor() ); - target->setRenderPassDescriptor( - target->newCompatibleRenderPassDescriptor() ); +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + target->build(); +#else + target->create(); +#endif - target->create(); + m_rt.rt = target; + m_rt.rpDesc = target->renderPassDescriptor(); - m_rt.rt = target; - m_rt.rpDesc = target->renderPassDescriptor(); + auto defaultContext = qobject_cast< QSGDefaultRenderContext* >( context() ); + m_rt.cb = defaultContext->currentFrameCommandBuffer(); - auto defaultContext = qobject_cast< QSGDefaultRenderContext* >( context() ); - m_rt.cb = defaultContext->currentFrameCommandBuffer(); +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + setRenderTarget( m_rt.rt ); + setCommandBuffer( m_rt.cb ); + setRenderPassDescriptor( m_rt.rpDesc ); +#endif + } + else + { +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QOpenGLFramebufferObjectFormat format; + format.setInternalTextureFormat( GL_RGBA8 ); + format.setSamples( 0 ); + format.setAttachment( QOpenGLFramebufferObject::CombinedDepthStencil ); + + m_fbo = new QOpenGLFramebufferObject( size, format ); +#endif + } } void Renderer::clearTarget() { - delete m_rt.rt; - m_rt.rt = nullptr; + if ( const auto rhi = context()->rhi() ) + { + delete m_rt.rt; + m_rt.rt = nullptr; - delete m_rt.rpDesc; - m_rt.rpDesc = nullptr; + delete m_rt.rpDesc; + m_rt.rpDesc = nullptr; - delete m_rhiTexture; - m_rhiTexture = nullptr; + delete m_rhiTexture; + m_rhiTexture = nullptr; + } + else + { +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + delete m_fbo; + m_fbo = nullptr; +#endif + } } } @@ -170,22 +277,39 @@ class QskSceneTexturePrivate final : public QSGTexturePrivate { public: QskSceneTexturePrivate( const QQuickWindow* window, QskSceneTexture* texture ) +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + : QSGTexturePrivate() +#else : QSGTexturePrivate( texture ) +#endif , devicePixelRatio( window->effectiveDevicePixelRatio() ) { - context = dynamic_cast< QSGDefaultRenderContext* >( - QQuickWindowPrivate::get( window )->context ); + Q_UNUSED( texture ); + + // Qt5 needs the extra const_cast + auto dw = QQuickWindowPrivate::get( const_cast< QQuickWindow* >( window ) ); + context = dynamic_cast< QSGDefaultRenderContext* >( dw->context ); } +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + int comparisonKey() const override + { return int( qintptr( rhiTexture() ) ); } + + QRhiTexture *rhiTexture() const override + { return renderer ? renderer->rhiTexture() : nullptr; } +#endif + QRectF rect; const qreal devicePixelRatio; Renderer* renderer = nullptr; QSGDefaultRenderContext* context = nullptr; + + const QSGGeometryNode* textureNode = nullptr; }; QskSceneTexture::QskSceneTexture( const QQuickWindow* window ) - : Inherited(*( new QskSceneTexturePrivate( window, this ) ) ) + : Inherited( *new QskSceneTexturePrivate( window, this ) ) { Q_ASSERT( d_func()->context ); } @@ -195,6 +319,16 @@ QskSceneTexture::~QskSceneTexture() delete d_func()->renderer; } +void QskSceneTexture::setTextureNode( const QSGGeometryNode* node ) +{ + d_func()->textureNode = node; +} + +const QSGGeometryNode* QskSceneTexture::textureNode() const +{ + return d_func()->textureNode; +} + QSize QskSceneTexture::textureSize() const { Q_D( const QskSceneTexture ); @@ -213,17 +347,6 @@ QSize QskSceneTexture::textureSize() const return size; } -qint64 QskSceneTexture::comparisonKey() const -{ - return qint64( rhiTexture() ); -} - -QRhiTexture* QskSceneTexture::rhiTexture() const -{ - Q_D( const QskSceneTexture ); - return d->renderer ? d->renderer->texture() : nullptr; -} - void QskSceneTexture::render( const QSGRootNode* rootNode, const QSGTransformNode* finalNode, const QRectF& rect ) { @@ -231,8 +354,6 @@ void QskSceneTexture::render( const QSGRootNode* rootNode, d->rect = rect; - const auto pixelSize = textureSize(); - if ( d->renderer == nullptr ) { d->renderer = new Renderer( this, d->context ); @@ -243,7 +364,7 @@ void QskSceneTexture::render( const QSGRootNode* rootNode, d->renderer->setFinalNode( const_cast< QSGTransformNode* >( finalNode ) ); d->renderer->setProjection( d->rect ); - d->renderer->setTextureSize( pixelSize ); + d->renderer->setTextureSize( textureSize() ); d->renderer->renderScene(); } @@ -262,9 +383,38 @@ bool QskSceneTexture::hasMipmaps() const return false; } -void QskSceneTexture::commitTextureOperations( QRhi*, QRhiResourceUpdateBatch* ) +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + +void QskSceneTexture::bind() { - // what to do here ? + if ( d_func()->rhiTexture() == nullptr ) + { + auto funcs = QOpenGLContext::currentContext()->functions(); + funcs->glBindTexture( GL_TEXTURE_2D, textureId() ); + + updateBindOptions(); + } } +int QskSceneTexture::textureId() const +{ + Q_D( const QskSceneTexture ); + return d->renderer ? d->renderer->textureId() : 0; +} + +#else + +qint64 QskSceneTexture::comparisonKey() const +{ + return qint64( rhiTexture() ); +} + +QRhiTexture* QskSceneTexture::rhiTexture() const +{ + Q_D( const QskSceneTexture ); + return d->renderer ? d->renderer->rhiTexture() : nullptr; +} + +#endif + #include "moc_QskSceneTexture.cpp" diff --git a/src/nodes/QskSceneTexture.h b/src/nodes/QskSceneTexture.h index 1a66f931..56acdb3e 100644 --- a/src/nodes/QskSceneTexture.h +++ b/src/nodes/QskSceneTexture.h @@ -13,6 +13,7 @@ class QskSceneTexturePrivate; class QSGRootNode; class QSGTransformNode; +class QSGGeometryNode; class QQuickWindow; class QSK_EXPORT QskSceneTexture : public QSGTexture @@ -25,19 +26,31 @@ class QSK_EXPORT QskSceneTexture : public QSGTexture QskSceneTexture( const QQuickWindow* ); ~QskSceneTexture(); +#if 1 + // to avoid recursive update - need to find a better solution TODO + void setTextureNode( const QSGGeometryNode* ); + const QSGGeometryNode* textureNode() const; +#endif + void render( const QSGRootNode*, const QSGTransformNode*, const QRectF& ); QSize textureSize() const override; +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + void bind() override; + int textureId() const override; +#else qint64 comparisonKey() const override; QRhiTexture* rhiTexture() const override; +#endif QRectF normalizedTextureSubRect() const override; // satisfy the QSGTexture API bool hasAlphaChannel() const override; bool hasMipmaps() const override; - void commitTextureOperations( QRhi*, QRhiResourceUpdateBatch* ) override; + + bool isDirty() const; Q_SIGNALS: void updateRequested(); diff --git a/src/nodes/QskTextureRenderer.cpp b/src/nodes/QskTextureRenderer.cpp index d7279b90..4d545736 100644 --- a/src/nodes/QskTextureRenderer.cpp +++ b/src/nodes/QskTextureRenderer.cpp @@ -4,6 +4,7 @@ *****************************************************************************/ #include "QskTextureRenderer.h" +#include "QskQuick.h" #include #include @@ -17,7 +18,6 @@ QSK_QT_PRIVATE_BEGIN #include #include -#include QSK_QT_PRIVATE_END #if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) @@ -89,7 +89,7 @@ void QskTextureRenderer::setTextureId( QQuickWindow* window, if ( plainTexture == nullptr ) return; - auto rhi = QQuickWindowPrivate::get( window )->rhi; + auto rhi = qskRenderingHardwareInterface( window ); #if QT_VERSION >= QT_VERSION_CHECK( 6, 4, 0 ) diff --git a/src/nodes/shaders/boxshadow.frag.qsb b/src/nodes/shaders/boxshadow.frag.qsb index 9a799078..8ecfc828 100644 Binary files a/src/nodes/shaders/boxshadow.frag.qsb and b/src/nodes/shaders/boxshadow.frag.qsb differ diff --git a/src/nodes/shaders/boxshadow.vert.qsb b/src/nodes/shaders/boxshadow.vert.qsb index 4d371492..d9831605 100644 Binary files a/src/nodes/shaders/boxshadow.vert.qsb and b/src/nodes/shaders/boxshadow.vert.qsb differ diff --git a/src/nodes/shaders/crisplines.frag.qsb b/src/nodes/shaders/crisplines.frag.qsb index fb01e7de..274f8d55 100644 Binary files a/src/nodes/shaders/crisplines.frag.qsb and b/src/nodes/shaders/crisplines.frag.qsb differ diff --git a/src/nodes/shaders/crisplines.vert.qsb b/src/nodes/shaders/crisplines.vert.qsb index c25564dc..674b357c 100644 Binary files a/src/nodes/shaders/crisplines.vert.qsb and b/src/nodes/shaders/crisplines.vert.qsb differ diff --git a/src/nodes/shaders/gradientconic.frag.qsb b/src/nodes/shaders/gradientconic.frag.qsb index c2a0848e..8a7e83b9 100644 Binary files a/src/nodes/shaders/gradientconic.frag.qsb and b/src/nodes/shaders/gradientconic.frag.qsb differ diff --git a/src/nodes/shaders/gradientconic.vert.qsb b/src/nodes/shaders/gradientconic.vert.qsb index e1b0505c..f807bce7 100644 Binary files a/src/nodes/shaders/gradientconic.vert.qsb and b/src/nodes/shaders/gradientconic.vert.qsb differ diff --git a/src/nodes/shaders/gradientlinear.frag.qsb b/src/nodes/shaders/gradientlinear.frag.qsb index 0d042678..be5fa92c 100644 Binary files a/src/nodes/shaders/gradientlinear.frag.qsb and b/src/nodes/shaders/gradientlinear.frag.qsb differ diff --git a/src/nodes/shaders/gradientlinear.vert.qsb b/src/nodes/shaders/gradientlinear.vert.qsb index 55c4d629..4a4a0b36 100644 Binary files a/src/nodes/shaders/gradientlinear.vert.qsb and b/src/nodes/shaders/gradientlinear.vert.qsb differ diff --git a/src/nodes/shaders/gradientradial.frag.qsb b/src/nodes/shaders/gradientradial.frag.qsb index 5af5a391..9759ed4c 100644 Binary files a/src/nodes/shaders/gradientradial.frag.qsb and b/src/nodes/shaders/gradientradial.frag.qsb differ diff --git a/src/nodes/shaders/gradientradial.vert.qsb b/src/nodes/shaders/gradientradial.vert.qsb index c81c721d..b530364f 100644 Binary files a/src/nodes/shaders/gradientradial.vert.qsb and b/src/nodes/shaders/gradientradial.vert.qsb differ diff --git a/src/nodes/shaders/vulkan2qsb.sh b/src/nodes/shaders/vulkan2qsb.sh index 22e28e10..765f5127 100755 --- a/src/nodes/shaders/vulkan2qsb.sh +++ b/src/nodes/shaders/vulkan2qsb.sh @@ -3,6 +3,7 @@ function qsbcompile { qsbfile=`echo $1 | sed 's/-vulkan//'` qsb --glsl 100es,120,150 --hlsl 50 --msl 12 -b -o ${qsbfile}.qsb $1 + # qsb --qt6 -b -o ${qsbfile}.qsb $1 } qsbcompile boxshadow-vulkan.vert