From bfa0d149f68eadf5cb1cd6eab8327850dc0bbbf9 Mon Sep 17 00:00:00 2001 From: Louai Al-Khanji Date: Mon, 5 May 2014 16:36:37 +0300 Subject: Direct2D QPA: Speed up text rendering After analysing text drawing performance two things seem to take up most of the time. The first is font lookup, the second is QVector initialization. To address the first point a per paint engine instance font cache is introduced. At the moment no mechanism exists to clear this cache and it is unbounded. To address the second point, we simply switch to using QVarLengthArray instead of QVector. In an artificial benchmark that draws text in a tight loop, the first change raised fps from ~70 to ~100. The second change further raised this number to ~115 fps. Change-Id: Iafa25c3e35bc42bd7c1582b0636e721c5193b494 Reviewed-by: Friedemann Kleint --- .../direct2d/qwindowsdirect2dpaintengine.cpp | 110 +++++++++++---------- .../direct2d/qwindowsdirect2dpaintengine.h | 3 + 2 files changed, 60 insertions(+), 53 deletions(-) (limited to 'src/plugins') diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index 58c30b6eeb..6e8d9b0baf 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -58,7 +58,6 @@ #include #include -#include using Microsoft::WRL::ComPtr; QT_BEGIN_NAMESPACE @@ -346,6 +345,8 @@ public: QPointF currentBrushOrigin; + QHash< QFont, ComPtr > fontCache; + struct { bool emulate; QPen qpen; @@ -1291,44 +1292,6 @@ void QWindowsDirect2DPaintEngine::drawPixmap(const QRectF &r, } } -static ComPtr fontFaceFromFontEngine(QFontEngine *fe) -{ - ComPtr fontFace; - - switch (fe->type()) { - case QFontEngine::Win: - { - QWindowsFontEngine *wfe = static_cast(fe); - QSharedPointer wfed = wfe->fontEngineData(); - - HGDIOBJ oldfont = wfe->selectDesignFont(); - HRESULT hr = QWindowsDirect2DContext::instance()->dwriteGdiInterop()->CreateFontFaceFromHdc(wfed->hdc, &fontFace); - DeleteObject(SelectObject(wfed->hdc, oldfont)); - if (FAILED(hr)) - qWarning("%s: Could not create DirectWrite fontface from HDC: %#x", __FUNCTION__, hr); - - } - break; - -#ifndef QT_NO_DIRECTWRITE - - case QFontEngine::DirectWrite: - { - QWindowsFontEngineDirectWrite *wfedw = static_cast(fe); - fontFace = wfedw->directWriteFontFace(); - } - break; - -#endif // QT_NO_DIRECTWRITE - - default: - qWarning("%s: Unknown font engine!", __FUNCTION__); - break; - } - - return fontFace; -} - void QWindowsDirect2DPaintEngine::drawStaticTextItem(QStaticTextItem *staticTextItem) { Q_D(QWindowsDirect2DPaintEngine); @@ -1340,24 +1303,22 @@ void QWindowsDirect2DPaintEngine::drawStaticTextItem(QStaticTextItem *staticText ensurePen(); // If we can't support the current configuration with Direct2D, fall back to slow path - // Most common cases are perspective transform and gradient brush as pen - if ((state()->transform().isAffine() == false) || d->pen.emulate) { + if (emulationRequired(PenEmulation)) { QPaintEngineEx::drawStaticTextItem(staticTextItem); return; } - ComPtr fontFace = fontFaceFromFontEngine(staticTextItem->fontEngine()); + ComPtr fontFace = fontFaceFromFontEngine(staticTextItem->font, staticTextItem->fontEngine()); if (!fontFace) { qWarning("%s: Could not find font - falling back to slow text rendering path.", __FUNCTION__); QPaintEngineEx::drawStaticTextItem(staticTextItem); return; } - QVector glyphIndices(staticTextItem->numGlyphs); - QVector glyphAdvances(staticTextItem->numGlyphs); - QVector glyphOffsets(staticTextItem->numGlyphs); + QVarLengthArray glyphIndices(staticTextItem->numGlyphs); + QVarLengthArray glyphAdvances(staticTextItem->numGlyphs); + QVarLengthArray glyphOffsets(staticTextItem->numGlyphs); - // XXX Are we generating a lot of cache misses here? for (int i = 0; i < staticTextItem->numGlyphs; i++) { glyphIndices[i] = UINT16(staticTextItem->glyphs[i]); // Imperfect conversion here @@ -1390,24 +1351,22 @@ void QWindowsDirect2DPaintEngine::drawTextItem(const QPointF &p, const QTextItem ensurePen(); // If we can't support the current configuration with Direct2D, fall back to slow path - // Most common cases are perspective transform and gradient brush as pen - if ((state()->transform().isAffine() == false) || d->pen.emulate) { + if (emulationRequired(PenEmulation)) { QPaintEngine::drawTextItem(p, textItem); return; } - ComPtr fontFace = fontFaceFromFontEngine(ti.fontEngine); + ComPtr fontFace = fontFaceFromFontEngine(*ti.f, ti.fontEngine); if (!fontFace) { qWarning("%s: Could not find font - falling back to slow text rendering path.", __FUNCTION__); QPaintEngine::drawTextItem(p, textItem); return; } - QVector glyphIndices(ti.glyphs.numGlyphs); - QVector glyphAdvances(ti.glyphs.numGlyphs); - QVector glyphOffsets(ti.glyphs.numGlyphs); + QVarLengthArray glyphIndices(ti.glyphs.numGlyphs); + QVarLengthArray glyphAdvances(ti.glyphs.numGlyphs); + QVarLengthArray glyphOffsets(ti.glyphs.numGlyphs); - // XXX Are we generating a lot of cache misses here? for (int i = 0; i < ti.glyphs.numGlyphs; i++) { glyphIndices[i] = UINT16(ti.glyphs.glyphs[i]); // Imperfect conversion here glyphAdvances[i] = ti.glyphs.effectiveAdvance(i).toReal(); @@ -1618,4 +1577,49 @@ void QWindowsDirect2DPaintEngine::adjustForAliasing(QPointF *point) (*point) += adjustment; } +Microsoft::WRL::ComPtr QWindowsDirect2DPaintEngine::fontFaceFromFontEngine(const QFont &font, QFontEngine *fe) +{ + Q_D(QWindowsDirect2DPaintEngine); + + ComPtr fontFace = d->fontCache.value(font); + if (fontFace) + return fontFace; + + switch (fe->type()) { + case QFontEngine::Win: + { + QWindowsFontEngine *wfe = static_cast(fe); + QSharedPointer wfed = wfe->fontEngineData(); + + HGDIOBJ oldfont = wfe->selectDesignFont(); + HRESULT hr = QWindowsDirect2DContext::instance()->dwriteGdiInterop()->CreateFontFaceFromHdc(wfed->hdc, &fontFace); + DeleteObject(SelectObject(wfed->hdc, oldfont)); + if (FAILED(hr)) + qWarning("%s: Could not create DirectWrite fontface from HDC: %#x", __FUNCTION__, hr); + + } + break; + +#ifndef QT_NO_DIRECTWRITE + + case QFontEngine::DirectWrite: + { + QWindowsFontEngineDirectWrite *wfedw = static_cast(fe); + fontFace = wfedw->directWriteFontFace(); + } + break; + +#endif // QT_NO_DIRECTWRITE + + default: + qWarning("%s: Unknown font engine!", __FUNCTION__); + break; + } + + if (fontFace) + d->fontCache.insert(font, fontFace); + + return fontFace; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h index badd7a7688..fb9b7acec3 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h @@ -47,6 +47,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -114,6 +115,8 @@ private: bool antiAliasingEnabled() const; void adjustForAliasing(QRectF *rect); void adjustForAliasing(QPointF *point); + + Microsoft::WRL::ComPtr fontFaceFromFontEngine(const QFont &font, QFontEngine *fe); }; QT_END_NAMESPACE -- cgit v1.2.3