summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEirik Aavitsland <eirik.aavitsland@qt.io>2023-01-31 10:57:26 +0100
committerEirik Aavitsland <eirik.aavitsland@qt.io>2023-02-10 23:50:21 +0100
commitce7b4c734b78d24b75ecb389cf799ce85d0cc3bf (patch)
treef3f953ccf6a438be941d7f04e7a8c7eb366e3dfa
parent08c201070817be73c17fa7e4e790ba08e94532c6 (diff)
Fix Qt 6 performance regression when painting outside device
Painting wide lines and filling would be clipped to cliprect (by default, the device rect) only if the bounding rect coordinates exceeded QT_RASTER_COORD_LIMIT. In Qt 6, that limit was raised from 2^15 to 2^23, so a lot of time could be spent on rasterizing elements that would anyway be outside the rendering area. Fix by instead clipping whenever the path to be painted overshoots the cliprect by a significant margin. At this point, the path is already flattened to straight lines, so clipping is quick and precise. Testing indicates that this solution improves performance a lot when large portions of the elements to be painted fall outside the cliprect, while not causing significant performance hits otherwise. As a side effect, it is then no longer necessary to test the bounding rect explicitly against QT_RASTER_COORD_LIMIT, since we already make sure that the clip rect we check against is within that limit. Fixes: QTBUG-110595 Pick-to: 6.5 6.4 6.2 Change-Id: Iaf1afbb481c2d7059405f334278796ad46f5bcb6 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
-rw-r--r--src/gui/painting/qoutlinemapper.cpp30
-rw-r--r--src/gui/painting/qoutlinemapper_p.h3
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp8
3 files changed, 24 insertions, 17 deletions
diff --git a/src/gui/painting/qoutlinemapper.cpp b/src/gui/painting/qoutlinemapper.cpp
index 0dfb310ee9..93eac5cced 100644
--- a/src/gui/painting/qoutlinemapper.cpp
+++ b/src/gui/painting/qoutlinemapper.cpp
@@ -37,6 +37,24 @@ static const QRectF boundingRect(const QPointF *points, int pointCount)
return QRectF(QPointF(minx, miny), QPointF(maxx, maxy));
}
+void QOutlineMapper::setClipRect(QRect clipRect)
+{
+ auto limitCoords = [](QRect r) {
+ const QRect limitRect(QPoint(-QT_RASTER_COORD_LIMIT, -QT_RASTER_COORD_LIMIT),
+ QPoint(QT_RASTER_COORD_LIMIT, QT_RASTER_COORD_LIMIT));
+ r &= limitRect;
+ r.setWidth(qMin(r.width(), QT_RASTER_COORD_LIMIT));
+ r.setHeight(qMin(r.height(), QT_RASTER_COORD_LIMIT));
+ return r;
+ };
+
+ if (clipRect != m_clip_rect) {
+ m_clip_rect = limitCoords(clipRect);
+ const int mw = 64; // margin width. No need to trigger clipping for slight overshooting
+ m_clip_trigger_rect = QRectF(limitCoords(m_clip_rect.adjusted(-mw, -mw, mw, mw)));
+ }
+}
+
void QOutlineMapper::curveTo(const QPointF &cp1, const QPointF &cp2, const QPointF &ep) {
#ifdef QT_DEBUG_CONVERT
printf("QOutlineMapper::curveTo() (%f, %f)\n", ep.x(), ep.y());
@@ -200,16 +218,8 @@ void QOutlineMapper::endOutline()
m_clip_rect.x(), m_clip_rect.y(), m_clip_rect.width(), m_clip_rect.height());
#endif
-
- // Check for out of dev bounds...
- const bool do_clip = !m_in_clip_elements && ((controlPointRect.left() < -QT_RASTER_COORD_LIMIT
- || controlPointRect.right() > QT_RASTER_COORD_LIMIT
- || controlPointRect.top() < -QT_RASTER_COORD_LIMIT
- || controlPointRect.bottom() > QT_RASTER_COORD_LIMIT
- || controlPointRect.width() > QT_RASTER_COORD_LIMIT
- || controlPointRect.height() > QT_RASTER_COORD_LIMIT));
-
- if (do_clip) {
+ // Avoid rasterizing outside cliprect: faster, and ensures coords < QT_RASTER_COORD_LIMIT
+ if (!m_in_clip_elements && !m_clip_trigger_rect.contains(controlPointRect)) {
clipElements(elements, elementTypes(), m_elements.size());
} else {
convertElements(elements, elementTypes(), m_elements.size());
diff --git a/src/gui/painting/qoutlinemapper_p.h b/src/gui/painting/qoutlinemapper_p.h
index 1f4c3f6670..ff2fff6bac 100644
--- a/src/gui/painting/qoutlinemapper_p.h
+++ b/src/gui/painting/qoutlinemapper_p.h
@@ -79,6 +79,8 @@ public:
m_curve_threshold = scale == 0 ? qreal(0.25) : (qreal(0.25) / scale);
}
+ void setClipRect(QRect clipRect);
+
void beginOutline(Qt::FillRule fillRule)
{
#ifdef QT_DEBUG_CONVERT
@@ -163,6 +165,7 @@ public:
QDataBuffer<int> m_contours;
QRect m_clip_rect;
+ QRectF m_clip_trigger_rect;
QRectF controlPointRect; // only valid after endOutline()
QT_FT_Outline m_outline;
diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
index 9cabef4a95..ac9af070f1 100644
--- a/src/gui/painting/qpaintengine_raster.cpp
+++ b/src/gui/painting/qpaintengine_raster.cpp
@@ -406,13 +406,7 @@ bool QRasterPaintEngine::begin(QPaintDevice *device)
QRasterPaintEngineState *s = state();
ensureOutlineMapper();
- d->outlineMapper->m_clip_rect = d->deviceRect;
-
- if (d->outlineMapper->m_clip_rect.width() > QT_RASTER_COORD_LIMIT)
- d->outlineMapper->m_clip_rect.setWidth(QT_RASTER_COORD_LIMIT);
- if (d->outlineMapper->m_clip_rect.height() > QT_RASTER_COORD_LIMIT)
- d->outlineMapper->m_clip_rect.setHeight(QT_RASTER_COORD_LIMIT);
-
+ d->outlineMapper->setClipRect(d->deviceRect);
d->rasterizer->setClipRect(d->deviceRect);
s->penData.init(d->rasterBuffer.data(), this);