QPen/QBrush: add optimized assignment operators for color and style

Assigning a color or style to an existing pen or brush (which we do
frequently in QPainter and elsewhere in Qt) used to implicitly create a
new QPen/QBrush instance and then move-assign that. This involves an
allocation of the new private data, and often also a deallocation of the
old pen or brush private if that wasn't shared (which it often isn't, as
we usually set a temporary object with no further copies).

By implementing specific assignment operators from color and style we
can shortcut this and avoid the allocations.

With this set of changes, the setPen/setBrush benchmarks now compare
to Qt 6.8 as follows:

setPen
6.8: 0.00016 msecs per iteration (total: 89, iterations: 524288)
6.9: 0.000032 msecs per iteration (total: 69, iterations: 2097152)

setBrush
6.8: 0.000078 msecs per iteration (total: 82, iterations: 1048576)
6.9: 0.000017 msecs per iteration (total: 73, iterations: 4194304)

i.e. improve by a factor of >4.5.

Change-Id: Ia9c0229b0f540ecf9afba2a598973c6931712f64
Reviewed-by: Christian Ehrlicher <ch.ehrlicher@gmx.de>
This commit is contained in:
Volker Hilsheimer 2024-11-27 17:58:32 +01:00
parent 17ac447f46
commit ab53500cfa
4 changed files with 84 additions and 6 deletions

View File

@ -598,19 +598,49 @@ void QBrush::detach(Qt::BrushStyle newStyle)
/*!
\fn QBrush &QBrush::operator=(const QBrush &brush)
Assigns the given \a brush to \e this brush and returns a
reference to \e this brush.
*/
QBrush &QBrush::operator=(const QBrush &b)
QBrush &QBrush::operator=(const QBrush &brush)
{
if (d == b.d)
if (d == brush.d)
return *this;
b.d->ref.ref();
d.reset(b.d.get());
brush.d->ref.ref();
d.reset(brush.d.get());
return *this;
}
/*!
\fn QBrush &QBrush::operator=(QColor color)
\fn QBrush &QBrush::operator=(Qt::GlobalColor color)
\overload
\since 6.9
Makes this brush a solid pattern brush of the given \a color,
and returns a reference to \e this brush.
*/
QBrush &QBrush::operator=(QColor color)
{
detach(Qt::SolidPattern);
d->color = color;
d->transform = {};
return *this;
}
/*!
\overload
\since 6.9
Makes this brush a black brush of the given \a style,
and returns a reference to \e this brush.
*/
QBrush &QBrush::operator=(Qt::BrushStyle style)
{
detach(style);
d->color = Qt::black;
d->transform = {};
return *this;
}

View File

@ -49,6 +49,10 @@ public:
inline void swap(QBrush &other) noexcept
{ d.swap(other.d); }
QBrush &operator=(Qt::BrushStyle style);
QBrush &operator=(QColor color);
QBrush &operator=(Qt::GlobalColor color) { return operator=(QColor(color)); }
operator QVariant() const;
inline Qt::BrushStyle style() const;

View File

@ -344,6 +344,47 @@ QPen &QPen::operator=(const QPen &p) noexcept
\memberswap{pen}
*/
/*!
\overload
\since 6.9
Makes this pen a solid pen with the given color, and default
cap and join styles, and returns a reference to \e this pen.
*/
QPen &QPen::operator=(QColor color)
{
detach();
d->brush = color;
d->width = 1;
d->style = Qt::SolidLine;
d->capStyle = qpen_default_cap;
d->joinStyle = qpen_default_join;
return *this;
}
/*!
\overload
\since 6.9
Makes this pen a solid, black pen with default cap and join styles,
and returns a reference to \e this pen.
*/
QPen &QPen::operator=(Qt::PenStyle style)
{
detach();
if (style == Qt::NoPen) {
d = nullPenInstance()->pen;
} else {
d->brush = Qt::black;
d->width = 1;
d->style = style;
d->capStyle = qpen_default_cap;
d->joinStyle = qpen_default_join;
}
return *this;
}
/*!
Returns the pen as a QVariant.
*/

View File

@ -41,6 +41,9 @@ public:
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QPen)
void swap(QPen &other) noexcept { d.swap(other.d); }
QPen &operator=(QColor color);
QPen &operator=(Qt::PenStyle style);
Qt::PenStyle style() const;
void setStyle(Qt::PenStyle);