QskStrokeNode with colored matarial for better batching. option for

simple polygons added without line joining added
This commit is contained in:
Uwe Rathmann 2023-05-02 18:04:23 +02:00
parent 585f763542
commit ffe4e11b21
2 changed files with 191 additions and 12 deletions

View File

@ -4,24 +4,77 @@
*****************************************************************************/ *****************************************************************************/
#include "QskStrokeNode.h" #include "QskStrokeNode.h"
#include "QskVertex.h"
#include <qsgflatcolormaterial.h> #include <qsgflatcolormaterial.h>
#include <qsgvertexcolormaterial.h>
#include <qpainterpath.h>
#include <qpolygon.h>
QSK_QT_PRIVATE_BEGIN QSK_QT_PRIVATE_BEGIN
#include <private/qsgnode_p.h> #include <private/qsgnode_p.h>
#include <private/qtriangulatingstroker_p.h> #include <private/qtriangulatingstroker_p.h>
QSK_QT_PRIVATE_END 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 class QskStrokeNodePrivate final : public QSGGeometryNodePrivate
{ {
public: public:
QskStrokeNodePrivate() QskStrokeNodePrivate()
: geometry( QSGGeometry::defaultAttributes_Point2D(), 0 ) : geometry( QSGGeometry::defaultAttributes_ColoredPoint2D(), 0 )
{ {
geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
} }
QSGGeometry geometry; QSGGeometry geometry;
QSGFlatColorMaterial material;
}; };
QskStrokeNode::QskStrokeNode() QskStrokeNode::QskStrokeNode()
@ -30,7 +83,41 @@ QskStrokeNode::QskStrokeNode()
Q_D( QskStrokeNode ); Q_D( QskStrokeNode );
setGeometry( &d->geometry ); 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( void QskStrokeNode::updateNode(
@ -91,19 +178,92 @@ void QskStrokeNode::updateNode(
} }
// 2 vertices for each point // 2 vertices for each point
d->geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip );
d->geometry.allocate( stroker.vertexCount() / 2 ); d->geometry.allocate( stroker.vertexCount() / 2 );
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(), memcpy( d->geometry.vertexData(), stroker.vertices(),
stroker.vertexCount() * sizeof( float ) ); stroker.vertexCount() * sizeof( float ) );
}
markDirty( QSGNode::DirtyGeometry ); markDirty( QSGNode::DirtyGeometry );
} }
if ( material() != qskMaterialColorVertex )
{
const auto color = pen.color().toRgb(); const auto color = pen.color().toRgb();
if ( d->material.color() != color ) auto flatMaterial = static_cast< QSGFlatColorMaterial* >( material() );
if ( flatMaterial->color() != color )
{ {
d->material.setColor( color ); flatMaterial->setColor( color );
markDirty( QSGNode::DirtyMaterial ); 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 );
}
}
}
}

View File

@ -11,6 +11,7 @@
class QPen; class QPen;
class QPainterPath; class QPainterPath;
class QPolygonF;
class QskStrokeNodePrivate; class QskStrokeNodePrivate;
@ -19,8 +20,26 @@ class QSK_EXPORT QskStrokeNode : public QSGGeometryNode
public: public:
QskStrokeNode(); 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 QPainterPath&, const QTransform&, const QPen& );
void updateNode( const QPolygonF&, const QTransform&,
qreal lineWidth, const QColor& );
private: private:
Q_DECLARE_PRIVATE( QskStrokeNode ) Q_DECLARE_PRIVATE( QskStrokeNode )
}; };