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,19 +134,32 @@ namespace
MatrixTransformFlags matrixFlags;
if ( !rhi->isYUpInNDC() )
if ( flipMatrix )
matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY;
setProjectionMatrixToRect( r, matrixFlags );
}
void Renderer::setTextureSize( const QSize& size )
{
if ( const auto rhi = context()->rhi() )
{
if ( m_rt.rt && m_rt.rt->pixelSize() != size )
clearTarget();
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();
@ -117,26 +183,35 @@ namespace
}
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 );
/*
We want to limit updates to nodes, that are actually rendered. TODO ...
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() ) )
{
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;
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
QRhiColorAttachment color0( m_rhiTexture );
auto target = rhi->newTextureRenderTarget( { color0 } );
@ -144,16 +219,40 @@ namespace
target->setRenderPassDescriptor(
target->newCompatibleRenderPassDescriptor() );
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
target->build();
#else
target->create();
#endif
m_rt.rt = target;
m_rt.rpDesc = target->renderPassDescriptor();
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()
{
if ( const auto rhi = context()->rhi() )
{
delete m_rt.rt;
m_rt.rt = nullptr;
@ -164,28 +263,53 @@ namespace
delete m_rhiTexture;
m_rhiTexture = nullptr;
}
else
{
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
delete m_fbo;
m_fbo = nullptr;
#endif
}
}
}
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