From fb3d09430f9fbe15ecd8c56f5b2e7d81995d4092 Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Sun, 17 Dec 2023 17:32:07 +0100 Subject: [PATCH] QskSceneTexture added --- src/CMakeLists.txt | 2 + src/controls/QskQuick.cpp | 44 ++++++ src/controls/QskQuick.h | 7 +- src/nodes/QskSceneTexture.cpp | 268 ++++++++++++++++++++++++++++++++++ src/nodes/QskSceneTexture.h | 49 +++++++ 5 files changed, 369 insertions(+), 1 deletion(-) create mode 100644 src/nodes/QskSceneTexture.cpp create mode 100644 src/nodes/QskSceneTexture.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1211c078..313e1b93 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 diff --git a/src/controls/QskQuick.cpp b/src/controls/QskQuick.cpp index 6a8f045f..c2888db9 100644 --- a/src/controls/QskQuick.cpp +++ b/src/controls/QskQuick.cpp @@ -12,6 +12,8 @@ QSK_QT_PRIVATE_BEGIN #include +#include +#include QSK_QT_PRIVATE_END #include @@ -387,6 +389,48 @@ const QSGNode* qskPaintNode( const QQuickItem* item ) return QQuickItemPrivate::get( item )->paintNode; } +const QSGRootNode* qskScenegraphAnchorNode( const QQuickItem* item ) +{ + if ( item == nullptr ) + return nullptr; + + return QQuickItemPrivate::get( item )->rootNode(); +} + +const QSGRootNode* qskScenegraphAnchorNode( const QQuickWindow* window ) +{ + if ( window ) + { + if ( auto renderer = QQuickWindowPrivate::get( window )->renderer ) + return renderer->rootNode(); + } + + return nullptr; +} + +void qskSetScenegraphAnchor( QQuickItem* item, bool on ) +{ + /* + For setting up a subtree renderer ( f.e in QskSceneTexture ) we need + to insert a QSGRootNode above the paintNode. + + ( In Qt this feature is exlusively used in the Qt/Quick Effects module + what lead to the not very intuitive name "refFromEffectItem" ) + + refFromEffectItem also allows to insert a opacity node of 0 to + hide the subtree from the main renderer by setting its parameter to + true. We have QskItemNode to achieve the same. + */ + if ( item ) + { + auto d = QQuickItemPrivate::get( item ); + if ( on ) + d->refFromEffectItem( false ); + else + d->derefFromEffectItem( false ); + } +} + QSizeF qskEffectiveSizeHint( const QQuickItem* item, Qt::SizeHint whichHint, const QSizeF& constraint ) { diff --git a/src/controls/QskQuick.h b/src/controls/QskQuick.h index 6b2ae2df..622b108d 100644 --- a/src/controls/QskQuick.h +++ b/src/controls/QskQuick.h @@ -14,10 +14,11 @@ class QskSizePolicy; -class QQuickItem; class QSGNode; class QSGTransformNode; +class QSGRootNode; class QRectF; + template< typename T > class QList; /* @@ -71,6 +72,10 @@ QSK_EXPORT void qskInputMethodSetVisible( const QQuickItem*, bool ); QSK_EXPORT const QSGTransformNode* qskItemNode( const QQuickItem* ); QSK_EXPORT const QSGNode* qskPaintNode( const QQuickItem* ); +QSK_EXPORT const QSGRootNode* qskScenegraphAnchorNode( const QQuickItem* ); +QSK_EXPORT const QSGRootNode* qskScenegraphAnchorNode( const QQuickWindow* ); +QSK_EXPORT void qskSetScenegraphAnchor( QQuickItem*, bool on, bool hide = false ); + QSK_EXPORT void qskItemUpdateRecursive( QQuickItem* ); QSK_EXPORT bool qskGrabMouse( QQuickItem* ); diff --git a/src/nodes/QskSceneTexture.cpp b/src/nodes/QskSceneTexture.cpp new file mode 100644 index 00000000..582488b6 --- /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 +#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( const QQuickWindow* window, QskSceneTexture* texture ) + : QSGTexturePrivate( texture ) + , devicePixelRatio( window->effectiveDevicePixelRatio() ) + { + context = dynamic_cast< QSGDefaultRenderContext* >( + QQuickWindowPrivate::get( window )->context ); + } + + QRectF rect; + const qreal devicePixelRatio; + + Renderer* renderer = nullptr; + QSGDefaultRenderContext* context = nullptr; +}; + +QskSceneTexture::QskSceneTexture( const QQuickWindow* window ) + : Inherited(*( new QskSceneTexturePrivate( window, this ) ) ) +{ + Q_ASSERT( d_func()->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( const QSGRootNode* rootNode, + const 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( const_cast< QSGRootNode* >( rootNode ) ); + d->renderer->setFinalNode( const_cast< QSGTransformNode* >( finalNode ) ); + + d->renderer->setProjection( d->rect ); + d->renderer->setTextureSize( pixelSize ); + d->renderer->renderScene(); +} + +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/src/nodes/QskSceneTexture.h b/src/nodes/QskSceneTexture.h new file mode 100644 index 00000000..1a66f931 --- /dev/null +++ b/src/nodes/QskSceneTexture.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_SCENE_TEXTURE_H +#define QSK_SCENE_TEXTURE_H + +#include "QskGlobal.h" +#include + +class QskSceneTexturePrivate; + +class QSGRootNode; +class QSGTransformNode; +class QQuickWindow; + +class QSK_EXPORT QskSceneTexture : public QSGTexture +{ + Q_OBJECT + + using Inherited = QSGTexture; + + public: + QskSceneTexture( const QQuickWindow* ); + ~QskSceneTexture(); + + void render( const QSGRootNode*, const QSGTransformNode*, const QRectF& ); + + QSize textureSize() const override; + + qint64 comparisonKey() const override; + QRhiTexture* rhiTexture() const override; + + QRectF normalizedTextureSubRect() const override; + + // satisfy the QSGTexture API + bool hasAlphaChannel() const override; + bool hasMipmaps() const override; + void commitTextureOperations( QRhi*, QRhiResourceUpdateBatch* ) override; + + Q_SIGNALS: + void updateRequested(); + + private: + Q_DECLARE_PRIVATE( QskSceneTexture ) +}; + +#endif