Qt5 implementation of QskSceneTexture

This commit is contained in:
Uwe Rathmann 2023-12-22 13:52:01 +01:00
parent 101492fff7
commit 972f839cad
19 changed files with 238 additions and 69 deletions

View File

@ -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

View File

@ -19,6 +19,14 @@ QSK_QT_PRIVATE_END
#include <qpa/qplatforminputcontext.h>
#include <qpa/qplatformintegration.h>
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 );

View File

@ -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 );

View File

@ -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 ) )

View File

@ -22,7 +22,7 @@
#endif
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
#include <QSGMaterialRhiShader>
#include <qsgmaterialrhishader.h>
using RhiShader = QSGMaterialRhiShader;
#else
using RhiShader = QSGMaterialShader;

View File

@ -10,11 +10,28 @@
QSK_QT_PRIVATE_BEGIN
#include <private/qquickwindow_p.h>
#include <private/qsgtexture_p.h>
#define QT_BUILD_QUICK_LIB // suppress Qt5 warnings
#include <private/qsgbatchrenderer_p.h>
#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 <qopenglframebufferobject.h>
#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"

View File

@ -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();

View File

@ -4,6 +4,7 @@
*****************************************************************************/
#include "QskTextureRenderer.h"
#include "QskQuick.h"
#include <qopenglcontext.h>
#include <qopenglframebufferobject.h>
@ -17,7 +18,6 @@
QSK_QT_PRIVATE_BEGIN
#include <private/qsgplaintexture_p.h>
#include <private/qopenglframebufferobject_p.h>
#include <private/qquickwindow_p.h>
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 )

Binary file not shown.

Binary file not shown.

View File

@ -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