QskPaintedNode improved

This commit is contained in:
Uwe Rathmann 2022-06-01 16:57:57 +02:00
parent c291cde259
commit e1a58f84ee
4 changed files with 145 additions and 71 deletions

View File

@ -4,7 +4,19 @@
*****************************************************************************/
#include "QskArcNode.h"
#include "QskArcMetrics.h"
#include "QskArcRenderer.h"
#include "QskGradient.h"
namespace
{
class ArcData
{
public:
const QskArcMetrics& metrics;
const QskGradient& gradient;
};
}
QskArcNode::QskArcNode()
{
@ -17,26 +29,28 @@ QskArcNode::~QskArcNode()
void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& metrics,
const QskGradient& gradient, QQuickWindow* window )
{
m_metrics = metrics;
m_gradient = gradient;
update( window, QskTextureRenderer::AutoDetect, rect.toRect() );
const ArcData arcData { metrics, gradient };
update( window, rect.toRect(), &arcData );
}
void QskArcNode::paint( QPainter* painter, const QSizeF& size )
void QskArcNode::paint( QPainter* painter, const QSizeF& size, const void* nodeData )
{
const qreal w = m_metrics.width();
const auto arcData = reinterpret_cast< const ArcData* >( nodeData );
const qreal w = arcData->metrics.width();
const QRectF rect( 0.5 * w, 0.5 * w, size.width() - w, size.height() - w );
QskArcRenderer renderer;
renderer.renderArc( rect, m_metrics, m_gradient, painter );
renderer.renderArc( rect, arcData->metrics, arcData->gradient, painter );
}
QskHashValue QskArcNode::hash() const
QskHashValue QskArcNode::hash( const void* nodeData ) const
{
auto h = m_metrics.hash();
const auto arcData = reinterpret_cast< const ArcData* >( nodeData );
for( const auto& stop : qAsConst( m_gradient.stops() ) )
auto h = arcData->metrics.hash();
for( const auto& stop : qAsConst( arcData->gradient.stops() ) )
h = stop.hash( h );
return h;

View File

@ -6,10 +6,13 @@
#ifndef QSK_ARC_NODE_H
#define QSK_ARC_NODE_H
#include "QskArcMetrics.h"
#include "QskGradient.h"
#include "QskPaintedNode.h"
class QskArcMetrics;
class QskGradient;
// should be a QSGGeometryNode, TODO ..
class QSK_EXPORT QskArcNode : public QskPaintedNode
{
public:
@ -19,12 +22,9 @@ class QSK_EXPORT QskArcNode : public QskPaintedNode
void setArcData( const QRectF&, const QskArcMetrics&,
const QskGradient&, QQuickWindow* );
void paint( QPainter* painter, const QSizeF& size ) override;
QskHashValue hash() const override;
private:
QskArcMetrics m_metrics;
QskGradient m_gradient;
protected:
void paint( QPainter*, const QSizeF&, const void* nodeData ) override;
QskHashValue hash( const void* nodeData ) const override;
};
#endif

View File

@ -22,7 +22,11 @@ QSK_QT_PRIVATE_END
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
#include <qquickopenglutils.h>
QSK_QT_PRIVATE_BEGIN
#include <private/qopenglframebufferobject_p.h>
QSK_QT_PRIVATE_END
static GLuint qskTakeTexture( QOpenGLFramebufferObject& fbo )
{
@ -76,10 +80,12 @@ namespace
{
const quint8 imageRole = 250; // reserved for internal use
inline QSGImageNode* findImageNode( QSGNode* parentNode )
inline QSGImageNode* findImageNode( const QSGNode* parentNode )
{
return static_cast< QSGImageNode* >(
QskSGNode::findChildNode( parentNode, imageRole ) );
auto node = QskSGNode::findChildNode(
const_cast< QSGNode* >( parentNode ), imageRole );
return static_cast< QSGImageNode* >( node );
}
static inline bool qskHasOpenGLRenderer( QQuickWindow* window )
@ -108,8 +114,24 @@ QskPaintedNode::~QskPaintedNode()
{
}
void QskPaintedNode::setRenderHint( RenderHint renderHint )
{
m_renderHint = renderHint;
}
QskPaintedNode::RenderHint QskPaintedNode::renderHint() const
{
return m_renderHint;
}
QRectF QskPaintedNode::rect() const
{
const auto imageNode = findImageNode( this );
return imageNode ? imageNode->rect() : QRectF();
}
void QskPaintedNode::update( QQuickWindow* window,
QskTextureRenderer::RenderMode renderMode, const QRectF& rect )
const QRectF& rect, const void* nodeData )
{
auto imageNode = findImageNode( this );
@ -120,38 +142,33 @@ void QskPaintedNode::update( QQuickWindow* window,
removeChildNode( imageNode );
delete imageNode;
}
return;
}
else
bool isDirty = false;
const auto newHash = hash( nodeData );
if ( ( newHash == 0 ) || ( newHash != m_hash ) )
{
bool isDirty = false;
m_hash = newHash;
isDirty = 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 )
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 );
}
if ( isDirty )
{
if ( ( m_renderHint == OpenGL ) && qskHasOpenGLRenderer( window ) )
updateImageNodeGL( window, rect, nodeData );
else
updateImageNode( window, rect, nodeData );
}
}
void QskPaintedNode::updateImageNode( QQuickWindow* window, const QRectF& rect )
void QskPaintedNode::updateImageNode(
QQuickWindow* window, const QRectF& rect, const void* nodeData )
{
const auto ratio = window->effectiveDevicePixelRatio();
const auto size = rect.size() * ratio;
@ -167,7 +184,7 @@ void QskPaintedNode::updateImageNode( QQuickWindow* window, const QRectF& rect )
value >= 1.0. So we have to scale manually.
*/
painter.scale( ratio, ratio );
paint( &painter, rect.size() );
paint( &painter, rect.size(), nodeData );
}
auto imageNode = findImageNode( this );
@ -190,7 +207,8 @@ void QskPaintedNode::updateImageNode( QQuickWindow* window, const QRectF& rect )
imageNode->setRect( rect );
}
void QskPaintedNode::updateImageNodeGL( QQuickWindow* window, const QRectF& rect )
void QskPaintedNode::updateImageNodeGL(
QQuickWindow* window, const QRectF& rect, const void* nodeData )
{
const auto ratio = window->effectiveDevicePixelRatio();
const QSize size( ratio * rect.width(), ratio * rect.height() );
@ -217,7 +235,12 @@ void QskPaintedNode::updateImageNodeGL( QQuickWindow* window, const QRectF& rect
imageNode->setTexture( texture );
}
const auto textureId = createTexture( window, size );
/*
QQuickFramebufferObject does the FBO rendering early
( QQuickWindow::beforeRendering ). However doing it below updatePaintNode
seems to work as well. Let's see if we run into issues ...
*/
const auto textureId = createTexture( window, size, nodeData );
auto rhi = QQuickWindowPrivate::get( window )->rhi;
@ -241,9 +264,28 @@ void QskPaintedNode::updateImageNodeGL( QQuickWindow* window, const QRectF& rect
imageNode->setRect( rect );
}
// this method will be moved to QskTextureRenderer. TODO ...
uint32_t QskPaintedNode::createTexture( QQuickWindow* window, const QSize& size )
uint32_t QskPaintedNode::createTexture(
QQuickWindow* window, const QSize& size, const void* nodeData )
{
/*
Binding GL_ARRAY_BUFFER/GL_ELEMENT_ARRAY_BUFFER to 0 seems to be enough.
However - as we do not know what is finally painted and what the
OpenGL paint engine is doing with better reinitialize everything.
Hope this has no side effects as the context will leave the function
in a modified state. Otherwise we could try to change the buffers
only and reset them, before leaving.
*/
window->beginExternalCommands();
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
QQuickOpenGLUtils::resetOpenGLState();
#else
window->resetOpenGLState();
#endif
auto context = QOpenGLContext::currentContext();
QOpenGLFramebufferObjectFormat format1;
@ -257,13 +299,6 @@ uint32_t QskPaintedNode::createTexture( QQuickWindow* window, const QSize& 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 );
@ -273,9 +308,7 @@ uint32_t QskPaintedNode::createTexture( QQuickWindow* window, const QSize& size
painter.fillRect( 0, 0, size.width(), size.height(), Qt::transparent );
painter.setCompositionMode( QPainter::CompositionMode_SourceOver );
paint( &painter, size );
gl->glBindBuffer( GL_ARRAY_BUFFER, bufferId );
paint( &painter, size, nodeData );
}
QOpenGLFramebufferObjectFormat format2;
@ -288,5 +321,7 @@ uint32_t QskPaintedNode::createTexture( QQuickWindow* window, const QSize& size
QOpenGLFramebufferObject::blitFramebuffer(
&fbo, fboRect, &multisampledFbo, fboRect );
window->endExternalCommands();
return qskTakeTexture( fbo );
}

View File

@ -6,31 +6,56 @@
#ifndef QSK_PAINTED_NODE_H
#define QSK_PAINTED_NODE_H
#include "QskTextureRenderer.h"
#include "QskGlobal.h"
#include <qsgnode.h>
class QQuickWindow;
class QPainter;
class QSK_EXPORT QskPaintedNode : public QSGNode
{
public:
/*
Raster usually provides a better antialiasing and is less buggy,
while OpenGL might be faster - depending on the content that has
to be painted.
Since Qt 5.10 X11 is back and could be an interesting option
with good quality and hardware accelerated performance. TODO ...
OpenGL might be ignored depending on the backend used by the
application.
*/
enum RenderHint
{
Raster,
OpenGL
};
QskPaintedNode();
~QskPaintedNode() override;
void update( QQuickWindow*,
QskTextureRenderer::RenderMode, const QRectF& );
void setRenderHint( RenderHint );
RenderHint renderHint() const;
QRectF rect() const;
protected:
virtual void paint( QPainter*, const QSizeF& ) = 0;
void update( QQuickWindow*, const QRectF&, const void* nodeData );
virtual void paint( QPainter*, const QSizeF&, const void* nodeData ) = 0;
// a hash value of '0' always results in repainting
virtual QskHashValue hash() const = 0;
virtual QskHashValue hash( const void* nodeData ) const = 0;
private:
void updateImageNode( QQuickWindow*, const QRectF& );
void updateImageNodeGL( QQuickWindow*, const QRectF& );
void updateImageNode( QQuickWindow*, const QRectF&, const void* nodeData );
void updateImageNodeGL( QQuickWindow*, const QRectF&, const void* nodeData );
uint32_t createTexture( QQuickWindow*, const QSize& );
uint32_t createTexture( QQuickWindow*, const QSize&, const void* nodeData );
QskHashValue m_hash;
RenderHint m_renderHint = OpenGL;
QskHashValue m_hash = 0;
};
#endif