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:
parent
942e6cc13a
commit
c64d386477
@ -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 );
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user