QskPaintedNode reimplemented to work with RHI and software renderer

Code will be moved in parts to QskTextureRenderer to do something
similar with QskTextureNode.
This commit is contained in:
Uwe Rathmann 2022-05-31 17:56:49 +02:00
parent 942e6cc13a
commit c64d386477
3 changed files with 272 additions and 39 deletions

View File

@ -4,24 +4,101 @@
*****************************************************************************/ *****************************************************************************/
#include "QskPaintedNode.h" #include "QskPaintedNode.h"
#include "QskTextureRenderer.h" #include "QskSGNode.h"
class QskPaintedNode::PaintHelper : public QskTextureRenderer::PaintHelper #include <qsgimagenode.h>
#include <qquickwindow.h>
#include <qimage.h>
#include <qpainter.h>
#include <qopenglframebufferobject.h>
#include <qopenglpaintdevice.h>
#include <qopenglfunctions.h>
QSK_QT_PRIVATE_BEGIN
#include <private/qsgplaintexture_p.h>
#include <private/qquickwindow_p.h>
QSK_QT_PRIVATE_END
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
#include <private/qopenglframebufferobject_p.h>
static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo )
{ {
public: /*
inline PaintHelper( QskPaintedNode* node ) See https://bugreports.qt.io/browse/QTBUG-103929
: m_node( node )
As we create a FBO for each update of a node we can't live
without having this ( ugly ) workaround.
*/
class MyFBO
{ {
public:
virtual ~MyFBO() = default;
QScopedPointer< QOpenGLFramebufferObjectPrivate > d_ptr;
};
static_assert( sizeof( MyFBO ) == sizeof( QOpenGLFramebufferObject ),
"Bad cast: QOpenGLFramebufferObject does not match" );
auto& attachment = reinterpret_cast< MyFBO* >( &fbo )->d_ptr->colorAttachments[0];
auto guard = attachment.guard;
const auto textureId = fbo.takeTexture();
if ( guard )
{
class MyGuard : public QOpenGLSharedResourceGuard
{
public:
void invalidateTexture() { invalidateResource(); }
};
reinterpret_cast< MyGuard* >( guard )->invalidateTexture();
} }
void paint( QPainter* painter, const QSize& size ) override attachment.guard = guard;
return textureId;
}
#else
static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo )
{
return fbo.takeTexture();
}
#endif
namespace
{
const quint8 imageRole = 250; // reserved for internal use
inline QSGImageNode* findImageNode( QSGNode* parentNode )
{ {
m_node->paint( painter, size ); return static_cast< QSGImageNode* >(
QskSGNode::findChildNode( parentNode, imageRole ) );
} }
private: static inline bool qskHasOpenGLRenderer( QQuickWindow* window )
QskPaintedNode* m_node; {
}; if ( window == nullptr )
return false;
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
if ( QQuickWindowPrivate::get( window )->rhi )
{
// does not yet work with the experimental RHI implementation in Qt5
return false;
}
#endif
const auto renderer = window->rendererInterface();
return renderer->graphicsApi() == QSGRendererInterface::OpenGL;
}
}
QskPaintedNode::QskPaintedNode() QskPaintedNode::QskPaintedNode()
{ {
@ -32,32 +109,184 @@ QskPaintedNode::~QskPaintedNode()
} }
void QskPaintedNode::update( QQuickWindow* window, void QskPaintedNode::update( QQuickWindow* window,
QskTextureRenderer::RenderMode renderMode, const QRect& rect ) QskTextureRenderer::RenderMode renderMode, const QRectF& rect )
{ {
bool isTextureDirty = isNull(); auto imageNode = findImageNode( this );
if ( !isTextureDirty ) if ( rect.isEmpty() )
{ {
const auto oldRect = QskTextureNode::rect(); if ( imageNode )
isTextureDirty = ( rect.width() != static_cast< int >( oldRect.width() ) ) || {
( rect.height() != static_cast< int >( oldRect.height() ) ); removeChildNode( imageNode );
delete imageNode;
}
} }
else
const auto newHash = hash();
if ( ( newHash == 0 ) || ( newHash != m_hash ) )
{ {
m_hash = newHash; bool isDirty = false;
isTextureDirty = true;
const auto newHash = hash();
if ( ( newHash == 0 ) || ( newHash != m_hash ) )
{
m_hash = newHash;
isDirty = true;
}
if ( !isDirty )
isDirty = ( imageNode == nullptr ) || ( imageNode->rect() != rect );
if ( isDirty )
{
if ( renderMode != QskTextureRenderer::Raster )
{
if ( !qskHasOpenGLRenderer( window ) )
renderMode = QskTextureRenderer::Raster;
}
if ( renderMode == QskTextureRenderer::Raster )
updateImageNode( window, rect );
else
updateImageNodeGL( window, rect );
}
} }
}
auto textureId = QskTextureNode::textureId();
void QskPaintedNode::updateImageNode( QQuickWindow* window, const QRectF& rect )
if ( isTextureDirty ) {
{ const auto ratio = window->effectiveDevicePixelRatio();
PaintHelper helper( this ); const auto size = rect.size() * ratio;
textureId = QskTextureRenderer::createTexture(
window, renderMode, rect.size(), &helper ); QImage image( size.toSize(), QImage::Format_RGBA8888_Premultiplied );
} image.fill( Qt::transparent );
QskTextureNode::setTexture( window, rect, textureId ); {
QPainter painter( &image );
/*
setting a devicePixelRatio for the image only works for
value >= 1.0. So we have to scale manually.
*/
painter.scale( ratio, ratio );
paint( &painter, rect.size() );
}
auto imageNode = findImageNode( this );
if ( imageNode == nullptr )
{
imageNode = window->createImageNode();
imageNode->setOwnsTexture( true );
QskSGNode::setNodeRole( imageNode, imageRole );
appendChildNode( imageNode );
}
if ( auto texture = qobject_cast< QSGPlainTexture* >( imageNode->texture() ) )
texture->setImage( image );
else
imageNode->setTexture( window->createTextureFromImage( image ) );
imageNode->setRect( rect );
}
void QskPaintedNode::updateImageNodeGL( QQuickWindow* window, const QRectF& rect )
{
const auto ratio = window->effectiveDevicePixelRatio();
const QSize size( ratio * rect.width(), ratio * rect.height() );
auto imageNode = findImageNode( this );
if ( imageNode == nullptr )
{
imageNode = window->createImageNode();
imageNode->setOwnsTexture( true );
QskSGNode::setNodeRole( imageNode, imageRole );
appendChildNode( imageNode );
}
auto texture = qobject_cast< QSGPlainTexture* >( imageNode->texture() );
if ( texture == nullptr )
{
texture = new QSGPlainTexture;
texture->setHasAlphaChannel( true );
texture->setOwnsTexture( true );
imageNode->setTexture( texture );
}
const auto textureId = createTexture( window, size );
auto rhi = QQuickWindowPrivate::get( window )->rhi;
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
texture->setTextureFromNativeTexture(
rhi, quint64( textureId ), 0, size, {}, {} );
#else
if ( rhi )
{
// enabled with: "export QSG_RHI=1"
texture->setTextureFromNativeObject( rhi,
QQuickWindow::NativeObjectTexture, &textureId, 0, size, false );
}
else
{
texture->setTextureId( textureId );
texture->setTextureSize( size );
}
#endif
imageNode->setRect( rect );
}
// this method will be moved to QskTextureRenderer. TODO ...
uint32_t QskPaintedNode::createTexture( QQuickWindow* window, const QSize& size )
{
auto context = QOpenGLContext::currentContext();
QOpenGLFramebufferObjectFormat format1;
format1.setAttachment( QOpenGLFramebufferObject::CombinedDepthStencil );
format1.setSamples( context->format().samples() );
QOpenGLFramebufferObject multisampledFbo( size, format1 );
QOpenGLPaintDevice pd( size );
pd.setPaintFlipped( true );
{
int bufferId;
auto gl = context->functions();
gl->glGetIntegerv( GL_ARRAY_BUFFER_BINDING, &bufferId);
gl->glBindBuffer( GL_ARRAY_BUFFER, 0 );
const auto ratio = window->effectiveDevicePixelRatio();
QPainter painter( &pd );
painter.scale( ratio, ratio );
painter.setCompositionMode( QPainter::CompositionMode_Source );
painter.fillRect( 0, 0, size.width(), size.height(), Qt::transparent );
painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
paint( &painter, size );
gl->glBindBuffer( GL_ARRAY_BUFFER, bufferId );
}
QOpenGLFramebufferObjectFormat format2;
format2.setAttachment( QOpenGLFramebufferObject::NoAttachment );
QOpenGLFramebufferObject fbo( size, format2 );
const QRect fboRect( 0, 0, size.width(), size.height() );
QOpenGLFramebufferObject::blitFramebuffer(
&fbo, fboRect, &multisampledFbo, fboRect );
return qskTakeTexture( fbo );
} }

View File

@ -6,17 +6,17 @@
#ifndef QSK_PAINTED_NODE_H #ifndef QSK_PAINTED_NODE_H
#define QSK_PAINTED_NODE_H #define QSK_PAINTED_NODE_H
#include "QskTextureNode.h"
#include "QskTextureRenderer.h" #include "QskTextureRenderer.h"
#include <qsgnode.h>
class QSK_EXPORT QskPaintedNode : public QskTextureNode class QSK_EXPORT QskPaintedNode : public QSGNode
{ {
public: public:
QskPaintedNode(); QskPaintedNode();
~QskPaintedNode() override; ~QskPaintedNode() override;
void update( QQuickWindow*, void update( QQuickWindow*,
QskTextureRenderer::RenderMode, const QRect& ); QskTextureRenderer::RenderMode, const QRectF& );
protected: protected:
virtual void paint( QPainter*, const QSizeF& ) = 0; virtual void paint( QPainter*, const QSizeF& ) = 0;
@ -25,10 +25,10 @@ class QSK_EXPORT QskPaintedNode : public QskTextureNode
virtual QskHashValue hash() const = 0; virtual QskHashValue hash() const = 0;
private: private:
class PaintHelper; void updateImageNode( QQuickWindow*, const QRectF& );
void updateImageNodeGL( QQuickWindow*, const QRectF& );
void setTexture( QQuickWindow*, uint32_t createTexture( QQuickWindow*, const QSize& );
const QRectF&, uint id, Qt::Orientations ) = delete;
QskHashValue m_hash; QskHashValue m_hash;
}; };

View File

@ -1,7 +1,11 @@
TEMPLATE = lib TEMPLATE = lib
TARGET = $$qskLibraryTarget(qskinny) TARGET = $$qskLibraryTarget(qskinny)
QT += quick quick-private QT += quick quick-private opengl-private
greaterThan( QT_MAJOR_VERSION, 5 ) {
QT += opengl-private
}
contains(QSK_CONFIG, QskDll): DEFINES += QSK_MAKEDLL contains(QSK_CONFIG, QskDll): DEFINES += QSK_MAKEDLL