From ffe4e11b21c334f28085e4ae18f7bb3e00043a5f Mon Sep 17 00:00:00 2001 From: Uwe Rathmann Date: Tue, 2 May 2023 18:04:23 +0200 Subject: [PATCH] QskStrokeNode with colored matarial for better batching. option for simple polygons added without line joining added --- src/nodes/QskStrokeNode.cpp | 184 +++++++++++++++++++++++++++++++++--- src/nodes/QskStrokeNode.h | 19 ++++ 2 files changed, 191 insertions(+), 12 deletions(-) diff --git a/src/nodes/QskStrokeNode.cpp b/src/nodes/QskStrokeNode.cpp index b5d85192..2af7f73b 100644 --- a/src/nodes/QskStrokeNode.cpp +++ b/src/nodes/QskStrokeNode.cpp @@ -4,24 +4,77 @@ *****************************************************************************/ #include "QskStrokeNode.h" +#include "QskVertex.h" + #include +#include +#include +#include QSK_QT_PRIVATE_BEGIN #include #include QSK_QT_PRIVATE_END +Q_GLOBAL_STATIC( QSGVertexColorMaterial, qskMaterialColorVertex ) + +static inline void qskMapPolygon( const QPolygonF& polygon, + const QTransform& transform, const QColor& color, QSGGeometry& geometry ) +{ + const QskVertex::Color c( color ); + + auto p = geometry.vertexDataAsColoredPoint2D(); + + if ( transform.isIdentity() ) + { + for ( int i = 0; i < polygon.count(); i++ ) + { + const auto& pos = polygon[i]; + p[i].set( pos.x(), pos.y(), c.r, c.g, c.b, c.a ); + } + } + else + { + for ( int i = 0; i < polygon.count(); i++ ) + { + const auto pos = transform.map( polygon[i] ); + p[i].set( pos.x(), pos.y(), c.r, c.g, c.b, c.a ); + } + } +} + +static inline void qskMapPolygon( const QPolygonF& polygon, + const QTransform& transform, QSGGeometry& geometry ) +{ + auto p = geometry.vertexDataAsPoint2D(); + + if ( transform.isIdentity() ) + { + for ( int i = 0; i < polygon.count(); i++ ) + { + const auto& pos = polygon[i]; + p[i].set( pos.x(), pos.y() ); + } + } + else + { + for ( int i = 0; i < polygon.count(); i++ ) + { + const auto pos = transform.map( polygon[i] ); + p[i].set( pos.x(), pos.y() ); + } + } +} + class QskStrokeNodePrivate final : public QSGGeometryNodePrivate { public: QskStrokeNodePrivate() - : geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) + : geometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ) { - geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); } QSGGeometry geometry; - QSGFlatColorMaterial material; }; QskStrokeNode::QskStrokeNode() @@ -30,7 +83,41 @@ QskStrokeNode::QskStrokeNode() Q_D( QskStrokeNode ); setGeometry( &d->geometry ); - setMaterial( &d->material ); + setMaterial( qskMaterialColorVertex ); +} + +void QskStrokeNode::setRenderHint( RenderHint renderHint ) +{ + Q_D( QskStrokeNode ); + + const auto material = this->material(); + + if ( renderHint == Colored ) + { + if ( material != qskMaterialColorVertex ) + { + setMaterial( qskMaterialColorVertex ); + delete material; + + const QSGGeometry g( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 ); + memcpy( ( void* ) &d->geometry, ( void* ) &g, sizeof( QSGGeometry ) ); + } + } + else + { + if ( material == qskMaterialColorVertex ) + { + setMaterial( new QSGFlatColorMaterial() ); + + const QSGGeometry g( QSGGeometry::defaultAttributes_Point2D(), 0 ); + memcpy( ( void* ) &d->geometry, ( void* ) &g, sizeof( QSGGeometry ) ); + } + } +} + +QskStrokeNode::RenderHint QskStrokeNode::renderHint() const +{ + return ( material() == qskMaterialColorVertex ) ? Colored : Flat; } void QskStrokeNode::updateNode( @@ -39,7 +126,7 @@ void QskStrokeNode::updateNode( Q_D( QskStrokeNode ); if ( path.isEmpty() || ( pen.style() == Qt::NoPen ) || - !pen.color().isValid() || ( pen.color().alpha() == 0 ) ) + !pen.color().isValid() || ( pen.color().alpha() == 0 ) ) { if ( d->geometry.vertexCount() > 0 ) { @@ -91,19 +178,92 @@ void QskStrokeNode::updateNode( } // 2 vertices for each point + d->geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); d->geometry.allocate( stroker.vertexCount() / 2 ); - memcpy( d->geometry.vertexData(), stroker.vertices(), - stroker.vertexCount() * sizeof( float ) ); + if ( material() == qskMaterialColorVertex ) + { + const QskVertex::Color c( pen.color() ); + + const auto v = stroker.vertices(); + auto points = d->geometry.vertexDataAsColoredPoint2D(); + + for ( int i = 0; i < d->geometry.vertexCount(); i++ ) + { + const auto j = 2 * i; + points[i].set( v[j], v[j + 1], c.r, c.g, c.b, c.a ); + } + } + else + { + memcpy( d->geometry.vertexData(), stroker.vertices(), + stroker.vertexCount() * sizeof( float ) ); + } markDirty( QSGNode::DirtyGeometry ); } - const auto color = pen.color().toRgb(); - - if ( d->material.color() != color ) + if ( material() != qskMaterialColorVertex ) { - d->material.setColor( color ); - markDirty( QSGNode::DirtyMaterial ); + const auto color = pen.color().toRgb(); + + auto flatMaterial = static_cast< QSGFlatColorMaterial* >( material() ); + if ( flatMaterial->color() != color ) + { + flatMaterial->setColor( color ); + markDirty( QSGNode::DirtyMaterial ); + } + } +} + +/* + For polygons with a small lineWidth ( < 2 ) or a line without + connections we might get away with a simple and fast implementation + using DrawLineStrip/DrawLineLoop + */ +void QskStrokeNode::updateNode( const QPolygonF& polygon, + const QTransform& transform, qreal lineWidth, const QColor& color ) +{ + Q_D( QskStrokeNode ); + + if ( polygon.isEmpty() || !color.isValid() || ( color.alpha() == 0 ) ) + { + if ( d->geometry.vertexCount() > 0 ) + { + d->geometry.allocate( 0 ); + markDirty( QSGNode::DirtyGeometry ); + } + + return; + } + + d->geometry.setDrawingMode( QSGGeometry::DrawLineStrip ); + + const float lineWidthF = lineWidth; + + if( lineWidthF != d->geometry.lineWidth() ) + d->geometry.setLineWidth( lineWidthF ); + + if ( true ) // TODO + { + d->geometry.allocate( polygon.count() ); + + if ( material() == qskMaterialColorVertex ) + { + qskMapPolygon( polygon, transform, color, d->geometry ); + markDirty( QSGNode::DirtyGeometry ); + } + else + { + qskMapPolygon( polygon, transform, d->geometry ); + markDirty( QSGNode::DirtyGeometry ); + + auto flatMaterial = static_cast< QSGFlatColorMaterial* >( material() ); + if ( flatMaterial->color() != color ) + { + flatMaterial->setColor( color ); + markDirty( QSGNode::DirtyMaterial ); + } + } } } diff --git a/src/nodes/QskStrokeNode.h b/src/nodes/QskStrokeNode.h index c203eb3e..7b891919 100644 --- a/src/nodes/QskStrokeNode.h +++ b/src/nodes/QskStrokeNode.h @@ -11,6 +11,7 @@ class QPen; class QPainterPath; +class QPolygonF; class QskStrokeNodePrivate; @@ -19,8 +20,26 @@ class QSK_EXPORT QskStrokeNode : public QSGGeometryNode public: QskStrokeNode(); + /* + We only support monochrome pens ( QPen::color() ) and using a + QSGFlatColorMaterial is good enough. However QSGVertexColorMaterial + usually allows better batching. So Flat vs. Colored is a tradeoff + between memory and performance. + */ + enum RenderHint + { + Flat, + Colored + }; + + void setRenderHint( RenderHint ); + RenderHint renderHint() const; + void updateNode( const QPainterPath&, const QTransform&, const QPen& ); + void updateNode( const QPolygonF&, const QTransform&, + qreal lineWidth, const QColor& ); + private: Q_DECLARE_PRIVATE( QskStrokeNode ) };