diff --git a/playground/parrots/CMakeLists.txt b/playground/parrots/CMakeLists.txt index 1d509668..91bdbf46 100644 --- a/playground/parrots/CMakeLists.txt +++ b/playground/parrots/CMakeLists.txt @@ -6,6 +6,7 @@ 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 ca53b82a..8f3a4dbe 100644 --- a/playground/parrots/Overlay.cpp +++ b/playground/parrots/Overlay.cpp @@ -5,10 +5,9 @@ #include "Overlay.h" #include "BlurringNode.h" +#include "SceneTexture.h" #include -#include -#include #include #include @@ -43,22 +42,6 @@ class OverlayPrivate final : public QQuickItemPrivate, public QQuickItemChangeLi q_func()->update(); } - void setCovering( bool on ) - { - if ( on == covering ) - return; - - if ( grabbedItem ) - { - auto sd = QQuickItemPrivate::get( grabbedItem ); - - sd->refFromEffectItem( on ); - sd->derefFromEffectItem( covering ); - } - - covering = on; - } - void setAttached( bool on ) { if ( grabbedItem ) @@ -67,76 +50,28 @@ class OverlayPrivate final : public QQuickItemPrivate, public QQuickItemChangeLi if ( on ) { - d->refFromEffectItem( covering ); + d->refFromEffectItem( false ); d->addItemChangeListener( this, Geometry ); } else { d->removeItemChangeListener( this, Geometry ); - d->derefFromEffectItem( covering ); + d->derefFromEffectItem( false ); } } } - QSGLayer* createTexture() + QSGRootNode* grabbedNode() { - auto renderContext = sceneGraphRenderContext(); - - auto layer = renderContext->sceneGraphContext()->createLayer( renderContext ); - - layer->setMipmapFiltering( QSGTexture::None ); - layer->setHorizontalWrapMode( QSGTexture::ClampToEdge ); - layer->setVerticalWrapMode( QSGTexture::ClampToEdge ); - layer->setFormat( QSGLayer::RGBA8 ); - layer->setHasMipmaps( false ); - layer->setMirrorHorizontal( false ); - layer->setMirrorVertical( true ); - - if ( q_func()->window()->format().samples() > 2 ) - { - /* - We want to disable multisampling as it doesn't make any sense - in combination with blurring afterwards. Unfortunately - QSGLayer uses the samples from the window when setting samples - below 2 here. - */ - layer->setSamples( 2 ); - } - - return layer; + return grabbedItem ? get( grabbedItem )->rootNode() : nullptr; } - void updateTexture( QSGLayer* layer ) + QSGTransformNode* grabbedItemNode() { - Q_Q( Overlay ); - - const auto pixelRatio = q->window()->effectiveDevicePixelRatio(); - - layer->setLive( true ); - layer->setItem( QQuickItemPrivate::get( grabbedItem )->itemNode() ); - - const auto rect = QRectF( q->position(), q->size() ); - layer->setRect( rect ); - - QSize textureSize( qCeil( rect.width() ), qCeil( rect.height() ) ); - textureSize *= pixelRatio; - - const QSize minTextureSize = sceneGraphContext()->minimumFBOSize(); - - while ( textureSize.width() < minTextureSize.width() ) - textureSize.rwidth() *= 2; - - while ( textureSize.height() < minTextureSize.height() ) - textureSize.rheight() *= 2; - - layer->setDevicePixelRatio( pixelRatio ); - layer->setSize( textureSize ); - layer->setRecursive( false ); - layer->setFiltering( q->smooth() ? QSGTexture::Linear : QSGTexture::Nearest ); + return grabbedItem ? get( grabbedItem )->itemNode() : nullptr; } QPointer< QQuickItem > grabbedItem; - bool covering = false; Q_DECLARE_PUBLIC(Overlay) }; @@ -176,36 +111,30 @@ void Overlay::geometryChange( { Inherited::geometryChange( newGeometry, oldGeometry ); - /* - When newGeometry covers the grabbedItem completely we could - set covering to true. TODO ... - */ - if ( d_func()->grabbedItem ) update(); } QSGNode* Overlay::updatePaintNode( QSGNode* oldNode, UpdatePaintNodeData* ) { - if ( size().isEmpty() ) + Q_D( Overlay ); + + if ( d->grabbedItemNode() == nullptr || size().isEmpty() ) { delete oldNode; return nullptr; } - Q_D( Overlay ); - auto node = static_cast< BlurringNode* >( oldNode ); if ( node == nullptr ) { node = new BlurringNode(); - auto layer = d->createTexture(); - node->setTexture( layer ); + auto texture = new SceneTexture( d->sceneGraphRenderContext() ); + texture->setDevicePixelRatio( window()->effectiveDevicePixelRatio() ); - connect( layer, &QSGLayer::updateRequested, - this, &QQuickItem::update ); + node->setTexture( texture ); } auto itemNode = static_cast< TransformNode* >( d->itemNode() ); @@ -214,12 +143,14 @@ QSGNode* Overlay::updatePaintNode( QSGNode* oldNode, UpdatePaintNodeData* ) { itemNode->isBlocked = true; - auto layer = static_cast< QSGLayer* >( node->texture() ); + auto texture = static_cast< SceneTexture* >( node->texture() ); + texture->setFiltering( smooth() ? QSGTexture::Linear : QSGTexture::Nearest ); - d->updateTexture( layer ); + texture->render( d->grabbedNode(), itemNode, + QRectF( x(), y(), width(), height() ) ); - layer->updateTexture(); itemNode->isBlocked = false; + QMetaObject::invokeMethod( this, &QQuickItem::update ); } node->setRect( QRectF( 0, 0, width(), height() ) ); diff --git a/playground/parrots/SceneTexture.cpp b/playground/parrots/SceneTexture.cpp new file mode 100644 index 00000000..fcafdaf2 --- /dev/null +++ b/playground/parrots/SceneTexture.cpp @@ -0,0 +1,219 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "SceneTexture.h" +#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 ) + { + /* + we need to find a way how to block all nodes + that are rendered behing m_finalNode TODO ... + */ + + 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 renderScene() override + { + Inherited::renderScene(); + } + + private: + 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; + } + + void nodeChanged( QSGNode* node, QSGNode::DirtyState state ) override + { + Inherited::nodeChanged( node, state ); + } + + 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; +}; + +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->renderer->renderScene(); +} + +void SceneTexture::setDevicePixelRatio( qreal ratio ) +{ + d_func()->devicePixelRatio = ratio; +} + +QRectF SceneTexture::normalizedTextureSubRect() const +{ + return QRectF( 0, 1, 1, -1 ); +} diff --git a/playground/parrots/SceneTexture.h b/playground/parrots/SceneTexture.h new file mode 100644 index 00000000..7e27e8d3 --- /dev/null +++ b/playground/parrots/SceneTexture.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#pragma once + +#include + +class SceneTexturePrivate; +class QSGRenderContext; + +class QSGRootNode; +class QSGTransformNode; + +class SceneTexture : public QSGTexture +{ + using Inherited = QSGTexture; + + public: + SceneTexture( QSGRenderContext* ); + ~SceneTexture(); + + void setDevicePixelRatio( qreal ); + void render( QSGRootNode*, QSGTransformNode*, const QRectF& ); + + QSize textureSize() const override; + + qint64 comparisonKey() const override; + QRhiTexture* rhiTexture() const override; + + QRectF normalizedTextureSubRect() const override; + + private: + // nops to satisfy the QSGTexture API + bool hasAlphaChannel() const override { return true; } + bool hasMipmaps() const override { return false; } + void commitTextureOperations( QRhi*, QRhiResourceUpdateBatch* ) override {} + + Q_DECLARE_PRIVATE( SceneTexture ) +}; diff --git a/playground/parrots/main.cpp b/playground/parrots/main.cpp index 3895fae1..aef022e2 100644 --- a/playground/parrots/main.cpp +++ b/playground/parrots/main.cpp @@ -137,8 +137,13 @@ class MainView : public QskControl m_background = new BackgroundItem( this ); m_overlay = new OverlayBox( m_background ); - (void )new ButtonBox( m_overlay ); + +#if 0 + auto box = new QskBox( m_background ); + box->setGeometry( 50, 50, 400, 200 ); + box->setFillGradient( Qt::darkBlue ); +#endif } protected: