diff --git a/playground/parrots/CMakeLists.txt b/playground/parrots/CMakeLists.txt index 91bdbf46..1d509668 100644 --- a/playground/parrots/CMakeLists.txt +++ b/playground/parrots/CMakeLists.txt @@ -6,7 +6,6 @@ set(SOURCES Overlay.h Overlay.cpp BlurringNode.h BlurringNode.cpp - SceneTexture.h SceneTexture.cpp main.cpp) qt_add_resources(SOURCES images.qrc shaders.qrc) diff --git a/playground/parrots/Overlay.cpp b/playground/parrots/Overlay.cpp index 5a7e19b3..a9f2bc4b 100644 --- a/playground/parrots/Overlay.cpp +++ b/playground/parrots/Overlay.cpp @@ -5,8 +5,8 @@ #include "Overlay.h" #include "BlurringNode.h" -#include "SceneTexture.h" +#include #include #include @@ -112,9 +112,6 @@ QSGNode* Overlay::updatePaintNode( QSGNode* oldNode, UpdatePaintNodeData* ) auto grabbedNode = d->grabbedNode(); - if ( grabbedNode == nullptr ) - QMetaObject::invokeMethod( this, &QQuickItem::update ); - if ( grabbedNode == nullptr || size().isEmpty() ) { delete oldNode; @@ -127,26 +124,30 @@ QSGNode* Overlay::updatePaintNode( QSGNode* oldNode, UpdatePaintNodeData* ) { node = new BlurringNode(); - auto texture = new SceneTexture( d->sceneGraphRenderContext() ); + auto texture = new QskSceneTexture( d->sceneGraphRenderContext() ); texture->setDevicePixelRatio( window()->effectiveDevicePixelRatio() ); + connect( texture, &QskSceneTexture::updateRequested, + this, &QQuickItem::update ); + node->setTexture( texture ); } - auto texture = static_cast< SceneTexture* >( node->texture() ); + auto texture = qobject_cast< QskSceneTexture* >( node->texture() ); + Q_ASSERT( texture ); - if ( !texture->isRendering() ) - { - texture->setFiltering( smooth() ? QSGTexture::Linear : QSGTexture::Nearest ); + texture->setFiltering( smooth() ? QSGTexture::Linear : QSGTexture::Nearest ); - texture->render( grabbedNode, d->itemNode(), - QRectF( x(), y(), width(), height() ) ); - - QMetaObject::invokeMethod( this, &QQuickItem::update ); - } + texture->render( grabbedNode, d->itemNode(), + QRectF( x(), y(), width(), height() ) ); node->setRect( QRectF( 0, 0, width(), height() ) ); +#if 0 + // always updating ... + QMetaObject::invokeMethod( this, &QQuickItem::update ); +#endif + return node; } diff --git a/playground/parrots/SceneTexture.cpp b/playground/parrots/SceneTexture.cpp deleted file mode 100644 index e07a4cea..00000000 --- a/playground/parrots/SceneTexture.cpp +++ /dev/null @@ -1,263 +0,0 @@ -/****************************************************************************** - * QSkinny - Copyright (C) 2016 Uwe Rathmann - * SPDX-License-Identifier: BSD-3-Clause - *****************************************************************************/ - -#include "SceneTexture.h" - -#include -#include - -namespace -{ - class Renderer : public QSGBatchRenderer::Renderer - { - using Inherited = QSGBatchRenderer::Renderer; - - public: - Renderer( QSGDefaultRenderContext* context ) - : Inherited( context, renderMode( context ) ) - { - setClearColor( Qt::transparent ); - } - - ~Renderer() override - { - clearTarget(); - } - - void setFinalNode( QSGTransformNode* node ) - { - if ( node != m_finalNode ) - { - m_finalNode = node; - } - } - - QRhiTexture* texture() const { return m_rhiTexture; } - - void setProjection( const QRectF& rect ) - { - const auto rhi = context()->rhi(); - - auto r = rect; - - if ( rhi->isYUpInFramebuffer() ) - { - r.moveTop( r.bottom() ); - r.setHeight( -r.height() ); - } - - MatrixTransformFlags matrixFlags; - - if ( !rhi->isYUpInNDC() ) - matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY; - - setProjectionMatrixToRect( r, matrixFlags ); - } - - void setTextureSize( const QSize& size ) - { - if ( m_rt.rt && m_rt.rt->pixelSize() != size ) - clearTarget(); - - if ( m_rt.rt == nullptr ) - createTarget( size ); - - const QRect r( 0, 0, size.width(), size.height() ); - - setDeviceRect( r ); - setViewportRect( r ); - } - - void render() override - { - setNodesBlocked( m_finalNode, true ); -#if 0 - qDebug() << "==================="; - QSGNodeDumper::dump( rootNode() ); -#endif - Inherited::render(); - setNodesBlocked( m_finalNode, false ); - } - - private: - void setNodesBlocked( QSGNode* node, bool on ) - { - setSubtreeBlocked( node, on ); - - for ( auto sibling = node->nextSibling(); - sibling != nullptr; sibling = sibling->nextSibling() ) - { - setSubtreeBlocked( sibling, on ); - } - - if ( node != rootNode() ) - { - if ( auto upperNode = node->parent() ) - { - upperNode = upperNode->nextSibling(); - if ( upperNode ) - setNodesBlocked( upperNode, on ); - } - } - } - - void setSubtreeBlocked( QSGNode* node, bool on ) - { - if ( qskTrySubtreeBlocked( node, on, false ) ) - return; - - for ( auto child = node->firstChild(); - child != nullptr; child = child->nextSibling() ) - { - setSubtreeBlocked( child, on ); - } - } - - void createTarget( const QSize& size ) - { - const auto rhi = context()->rhi(); - - auto flags = QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource; - - m_rhiTexture = rhi->newTexture( QRhiTexture::RGBA8, size, 1, flags ); - m_rhiTexture->create(); - - QRhiColorAttachment color0( m_rhiTexture ); - auto target = rhi->newTextureRenderTarget( { color0 } ); - - target->setRenderPassDescriptor( - target->newCompatibleRenderPassDescriptor() ); - - target->create(); - - m_rt.rt = target; - m_rt.rpDesc = target->renderPassDescriptor(); - - auto defaultContext = qobject_cast< QSGDefaultRenderContext* >( context() ); - m_rt.cb = defaultContext->currentFrameCommandBuffer(); - } - - void clearTarget() - { - delete m_rt.rt; - m_rt.rt = nullptr; - - delete m_rt.rpDesc; - m_rt.rpDesc = nullptr; - - delete m_rhiTexture; - m_rhiTexture = nullptr; - } - - private: - inline QSGRendererInterface::RenderMode renderMode( - QSGDefaultRenderContext* context ) const - { - return context->useDepthBufferFor2D() - ? QSGRendererInterface::RenderMode2D - : QSGRendererInterface::RenderMode2DNoDepthBuffer; - } - - QRhiTexture* m_rhiTexture = nullptr; - QSGTransformNode* m_finalNode = nullptr; - }; -}; - -class SceneTexturePrivate final : public QSGTexturePrivate -{ - public: - SceneTexturePrivate( SceneTexture* texture ) - : QSGTexturePrivate( texture ) - { - } - - QRectF rect; - qreal devicePixelRatio = 1.0; - - Renderer* renderer = nullptr; - QSGDefaultRenderContext* context = nullptr; - - bool isRendering = false; -}; - -SceneTexture::SceneTexture( QSGRenderContext* context ) - : Inherited(*( new SceneTexturePrivate(this) ) ) -{ - d_func()->context = static_cast< QSGDefaultRenderContext* >( context ); -} - -SceneTexture::~SceneTexture() -{ - delete d_func()->renderer; -} - -QSize SceneTexture::textureSize() const -{ - Q_D( const SceneTexture ); - - QSize size( qCeil( d->rect.width() ), qCeil( d->rect.height() ) ); - size *= d->devicePixelRatio; - - const QSize minSize = d->context->sceneGraphContext()->minimumFBOSize(); - - while ( size.width() < minSize.width() ) - size.rwidth() *= 2; - - while ( size.height() < minSize.height() ) - size.rheight() *= 2; - - return size; -} - -qint64 SceneTexture::comparisonKey() const -{ - return qint64( rhiTexture() ); -} - -QRhiTexture* SceneTexture::rhiTexture() const -{ - Q_D( const SceneTexture ); - return d->renderer ? d->renderer->texture() : nullptr; -} - -void SceneTexture::render( QSGRootNode* rootNode, - QSGTransformNode* finalNode, const QRectF& rect ) -{ - Q_D( SceneTexture ); - - d->rect = rect; - - const auto pixelSize = textureSize(); - - if ( d->renderer == nullptr ) - { - d->renderer = new Renderer( d->context ); - d->renderer->setDevicePixelRatio( d->devicePixelRatio ); - } - - d->renderer->setRootNode( rootNode ); - d->renderer->setFinalNode( finalNode ); - d->renderer->setProjection( d->rect ); - d->renderer->setTextureSize( pixelSize ); - - d->isRendering = true; - d->renderer->renderScene(); - d->isRendering = false; -} - -bool SceneTexture::isRendering() const -{ - return d_func()->isRendering; -} - -void SceneTexture::setDevicePixelRatio( qreal ratio ) -{ - d_func()->devicePixelRatio = ratio; -} - -QRectF SceneTexture::normalizedTextureSubRect() const -{ - return QRectF( 0, 1, 1, -1 ); -} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 76df8470..373a7b72 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 @@ -158,6 +159,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 diff --git a/src/nodes/QskSceneTexture.cpp b/src/nodes/QskSceneTexture.cpp new file mode 100644 index 00000000..38b3f2dd --- /dev/null +++ b/src/nodes/QskSceneTexture.cpp @@ -0,0 +1,268 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskSceneTexture.h" +#include "QskTreeNode.h" + +QSK_QT_PRIVATE_BEGIN +#include +QSK_QT_PRIVATE_END + +namespace +{ + inline QSGRendererInterface::RenderMode contextRenderMode( + QSGDefaultRenderContext* context ) + { + return context->useDepthBufferFor2D() + ? QSGRendererInterface::RenderMode2D + : QSGRendererInterface::RenderMode2DNoDepthBuffer; + } + + class Renderer final : public QSGBatchRenderer::Renderer + { + using Inherited = QSGBatchRenderer::Renderer; + + public: + Renderer( QskSceneTexture*, QSGDefaultRenderContext* ); + ~Renderer() override; + + void setFinalNode( QSGTransformNode* ); + QRhiTexture* texture() const { return m_rhiTexture; } + + void setProjection( const QRectF& ); + void setTextureSize( const QSize& ); + + protected: + void nodeChanged( QSGNode*, QSGNode::DirtyState ) override; + void render() override; + + private: + void createTarget( const QSize& ); + void clearTarget(); + + private: + QRhiTexture* m_rhiTexture = nullptr; + QSGTransformNode* m_finalNode = nullptr; + + QskSceneTexture* m_texture = nullptr; + }; + + Renderer::Renderer( QskSceneTexture* texture, QSGDefaultRenderContext* context ) + : Inherited( context, contextRenderMode( context ) ) + , m_texture( texture ) + { + setClearColor( Qt::transparent ); + } + + Renderer::~Renderer() + { + clearTarget(); + } + + void Renderer::setFinalNode( QSGTransformNode* node ) + { + if ( node != m_finalNode ) + { + m_finalNode = node; + } + } + + void Renderer::setProjection( const QRectF& rect ) + { + const auto rhi = context()->rhi(); + + auto r = rect; + + if ( rhi->isYUpInFramebuffer() ) + { + r.moveTop( r.bottom() ); + r.setHeight( -r.height() ); + } + + MatrixTransformFlags matrixFlags; + + if ( !rhi->isYUpInNDC() ) + matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY; + + setProjectionMatrixToRect( r, matrixFlags ); + } + + void Renderer::setTextureSize( const QSize& size ) + { + if ( m_rt.rt && m_rt.rt->pixelSize() != size ) + clearTarget(); + + if ( m_rt.rt == nullptr ) + createTarget( size ); + + const QRect r( 0, 0, size.width(), size.height() ); + + setDeviceRect( r ); + setViewportRect( r ); + } + + void Renderer::render() + { + qskTryBlockTrailingNodes( m_finalNode, rootNode(), true, false ); +#if 0 + QSGNodeDumper::dump( rootNode() ); +#endif + Inherited::render(); + qskTryBlockTrailingNodes( m_finalNode, rootNode(), false, false ); + } + + void Renderer::nodeChanged( QSGNode* node, QSGNode::DirtyState state ) + { + // do not forward nodes that are blocked while rendering + + const bool isTrailingNode = false; // TODO ... + + if ( !isTrailingNode ) + { + Inherited::nodeChanged( node, state ); + Q_EMIT m_texture->updateRequested(); + } + } + + void Renderer::createTarget( const QSize& size ) + { + const auto rhi = context()->rhi(); + + auto flags = QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource; + + m_rhiTexture = rhi->newTexture( QRhiTexture::RGBA8, size, 1, flags ); + m_rhiTexture->create(); + + QRhiColorAttachment color0( m_rhiTexture ); + auto target = rhi->newTextureRenderTarget( { color0 } ); + + target->setRenderPassDescriptor( + target->newCompatibleRenderPassDescriptor() ); + + target->create(); + + m_rt.rt = target; + m_rt.rpDesc = target->renderPassDescriptor(); + + auto defaultContext = qobject_cast< QSGDefaultRenderContext* >( context() ); + m_rt.cb = defaultContext->currentFrameCommandBuffer(); + } + + void Renderer::clearTarget() + { + delete m_rt.rt; + m_rt.rt = nullptr; + + delete m_rt.rpDesc; + m_rt.rpDesc = nullptr; + + delete m_rhiTexture; + m_rhiTexture = nullptr; + } +} + +class QskSceneTexturePrivate final : public QSGTexturePrivate +{ + public: + QskSceneTexturePrivate( QskSceneTexture* texture ) + : QSGTexturePrivate( texture ) + { + } + + QRectF rect; + qreal devicePixelRatio = 1.0; + + Renderer* renderer = nullptr; + QSGDefaultRenderContext* context = nullptr; +}; + +QskSceneTexture::QskSceneTexture( QSGRenderContext* context ) + : Inherited(*( new QskSceneTexturePrivate(this) ) ) +{ + d_func()->context = static_cast< QSGDefaultRenderContext* >( context ); +} + +QskSceneTexture::~QskSceneTexture() +{ + delete d_func()->renderer; +} + +QSize QskSceneTexture::textureSize() const +{ + Q_D( const QskSceneTexture ); + + QSize size( qCeil( d->rect.width() ), qCeil( d->rect.height() ) ); + size *= d->devicePixelRatio; + + const QSize minSize = d->context->sceneGraphContext()->minimumFBOSize(); + + while ( size.width() < minSize.width() ) + size.rwidth() *= 2; + + while ( size.height() < minSize.height() ) + size.rheight() *= 2; + + 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( QSGRootNode* rootNode, + QSGTransformNode* finalNode, const QRectF& rect ) +{ + Q_D( QskSceneTexture ); + + d->rect = rect; + + const auto pixelSize = textureSize(); + + if ( d->renderer == nullptr ) + { + d->renderer = new Renderer( this, d->context ); + d->renderer->setDevicePixelRatio( d->devicePixelRatio ); + } + + d->renderer->setRootNode( rootNode ); + d->renderer->setFinalNode( finalNode ); + d->renderer->setProjection( d->rect ); + d->renderer->setTextureSize( pixelSize ); + d->renderer->renderScene(); +} + +void QskSceneTexture::setDevicePixelRatio( qreal ratio ) +{ + d_func()->devicePixelRatio = ratio; +} + +QRectF QskSceneTexture::normalizedTextureSubRect() const +{ + return QRectF( 0, 1, 1, -1 ); +} + +bool QskSceneTexture::hasAlphaChannel() const +{ + return false; +} + +bool QskSceneTexture::hasMipmaps() const +{ + return false; +} + +void QskSceneTexture::commitTextureOperations( QRhi*, QRhiResourceUpdateBatch* ) +{ + // what to do here ? +} + +#include "moc_QskSceneTexture.cpp" diff --git a/playground/parrots/SceneTexture.h b/src/nodes/QskSceneTexture.h similarity index 63% rename from playground/parrots/SceneTexture.h rename to src/nodes/QskSceneTexture.h index 943c4283..0b36d1c1 100644 --- a/playground/parrots/SceneTexture.h +++ b/src/nodes/QskSceneTexture.h @@ -3,23 +3,27 @@ * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ -#pragma once +#ifndef QSK_SCENE_TEXTURE_H +#define QSK_SCENE_TEXTURE_H +#include "QskGlobal.h" #include -class SceneTexturePrivate; +class QskSceneTexturePrivate; class QSGRenderContext; class QSGRootNode; class QSGTransformNode; -class SceneTexture : public QSGTexture +class QSK_EXPORT QskSceneTexture : public QSGTexture // QSGDynamicTexture: TODO ... { + Q_OBJECT + using Inherited = QSGTexture; public: - SceneTexture( QSGRenderContext* ); - ~SceneTexture(); + QskSceneTexture( QSGRenderContext* ); + ~QskSceneTexture(); void setDevicePixelRatio( qreal ); void render( QSGRootNode*, QSGTransformNode*, const QRectF& ); @@ -31,13 +35,16 @@ class SceneTexture : public QSGTexture QRectF normalizedTextureSubRect() const override; - bool isRendering() const; + // satisfy the QSGTexture API + bool hasAlphaChannel() const override; + bool hasMipmaps() const override; + void commitTextureOperations( QRhi*, QRhiResourceUpdateBatch* ) override; + + Q_SIGNALS: + void updateRequested(); private: - // satisfy the QSGTexture API - bool hasAlphaChannel() const override { return false; } - bool hasMipmaps() const override { return false; } - void commitTextureOperations( QRhi*, QRhiResourceUpdateBatch* ) override {} - - Q_DECLARE_PRIVATE( SceneTexture ) + Q_DECLARE_PRIVATE( QskSceneTexture ) }; + +#endif diff --git a/src/nodes/QskTreeNode.cpp b/src/nodes/QskTreeNode.cpp index b5c8386c..f8d4c408 100644 --- a/src/nodes/QskTreeNode.cpp +++ b/src/nodes/QskTreeNode.cpp @@ -97,7 +97,7 @@ const QskItemNode* qskItemNodeCast( const QSGNode* node ) qskCheckedNode( node, QSGNode::TransformNodeType ) ); } -bool qskIsNodeBlockable( const QSGNode* node ) +bool qskIsBlockableNode( const QSGNode* node ) { switch( node->type() ) { @@ -110,7 +110,7 @@ bool qskIsNodeBlockable( const QSGNode* node ) } } -bool qskTrySubtreeBlocked( QSGNode* node, bool on, bool notify ) +bool qskTryBlockNode( QSGNode* node, bool on, bool notify ) { if ( node && ( node->flags() & extraFlag ) ) { @@ -129,3 +129,37 @@ bool qskTrySubtreeBlocked( QSGNode* node, bool on, bool notify ) return false; } + +void qskTryBlockTree( QSGNode* node, bool on, bool notify ) +{ + if ( qskTryBlockNode( node, on, notify ) ) + return; + + for ( auto child = node->firstChild(); + child != nullptr; child = child->nextSibling() ) + { + qskTryBlockTree( child, on, notify ); + } +} + +void qskTryBlockTrailingNodes( + QSGNode* node, const QSGNode* ancestorNode, bool on, bool notify ) +{ + qskTryBlockTree( node, on, notify ); + + for ( auto sibling = node->nextSibling(); + sibling != nullptr; sibling = sibling->nextSibling() ) + { + qskTryBlockTree( sibling, on, notify ); + } + + if ( node != ancestorNode ) + { + if ( auto upperNode = node->parent() ) + { + upperNode = upperNode->nextSibling(); + if ( upperNode ) + qskTryBlockTrailingNodes( upperNode, ancestorNode, on, notify ); + } + } +} diff --git a/src/nodes/QskTreeNode.h b/src/nodes/QskTreeNode.h index 2d0cd09e..5adc141d 100644 --- a/src/nodes/QskTreeNode.h +++ b/src/nodes/QskTreeNode.h @@ -51,7 +51,13 @@ class QSK_EXPORT QskItemNode final : public QSGTransformNode QSK_EXPORT QskItemNode* qskItemNodeCast( QSGNode* ); QSK_EXPORT const QskItemNode* qskItemNodeCast( const QSGNode* ); -QSK_EXPORT bool qskIsNodeBlockable( const QSGNode* ); -QSK_EXPORT bool qskTrySubtreeBlocked( QSGNode*, bool on, bool notify = true ); +QSK_EXPORT bool qskIsBlockableNode( const QSGNode* ); +QSK_EXPORT bool qskTryBlockNode( QSGNode*, bool on, bool notify = true ); + +QSK_EXPORT void qskTryBlockTree( QSGNode*, bool on, bool notify = true ); + +// un/block a node and all its successors in the rendering pipeline +QSK_EXPORT void qskTryBlockTrailingNodes( + QSGNode*, const QSGNode* ancestorNode, bool on, bool notify = true ); #endif