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 <Friedemann.Kleint@digia.com>
This commit is contained in:
parent
fdcf66f10f
commit
bfa0d149f6
@ -58,7 +58,6 @@
|
||||
#include <QtGui/private/qfontengine_p.h>
|
||||
#include <QtGui/private/qstatictext_p.h>
|
||||
|
||||
#include <wrl.h>
|
||||
using Microsoft::WRL::ComPtr;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@ -346,6 +345,8 @@ public:
|
||||
|
||||
QPointF currentBrushOrigin;
|
||||
|
||||
QHash< QFont, ComPtr<IDWriteFontFace> > fontCache;
|
||||
|
||||
struct {
|
||||
bool emulate;
|
||||
QPen qpen;
|
||||
@ -1291,44 +1292,6 @@ void QWindowsDirect2DPaintEngine::drawPixmap(const QRectF &r,
|
||||
}
|
||||
}
|
||||
|
||||
static ComPtr<IDWriteFontFace> fontFaceFromFontEngine(QFontEngine *fe)
|
||||
{
|
||||
ComPtr<IDWriteFontFace> fontFace;
|
||||
|
||||
switch (fe->type()) {
|
||||
case QFontEngine::Win:
|
||||
{
|
||||
QWindowsFontEngine *wfe = static_cast<QWindowsFontEngine *>(fe);
|
||||
QSharedPointer<QWindowsFontEngineData> 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<QWindowsFontEngineDirectWrite *>(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<IDWriteFontFace> fontFace = fontFaceFromFontEngine(staticTextItem->fontEngine());
|
||||
ComPtr<IDWriteFontFace> 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<UINT16> glyphIndices(staticTextItem->numGlyphs);
|
||||
QVector<FLOAT> glyphAdvances(staticTextItem->numGlyphs);
|
||||
QVector<DWRITE_GLYPH_OFFSET> glyphOffsets(staticTextItem->numGlyphs);
|
||||
QVarLengthArray<UINT16> glyphIndices(staticTextItem->numGlyphs);
|
||||
QVarLengthArray<FLOAT> glyphAdvances(staticTextItem->numGlyphs);
|
||||
QVarLengthArray<DWRITE_GLYPH_OFFSET> 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<IDWriteFontFace> fontFace = fontFaceFromFontEngine(ti.fontEngine);
|
||||
ComPtr<IDWriteFontFace> 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<UINT16> glyphIndices(ti.glyphs.numGlyphs);
|
||||
QVector<FLOAT> glyphAdvances(ti.glyphs.numGlyphs);
|
||||
QVector<DWRITE_GLYPH_OFFSET> glyphOffsets(ti.glyphs.numGlyphs);
|
||||
QVarLengthArray<UINT16> glyphIndices(ti.glyphs.numGlyphs);
|
||||
QVarLengthArray<FLOAT> glyphAdvances(ti.glyphs.numGlyphs);
|
||||
QVarLengthArray<DWRITE_GLYPH_OFFSET> 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<IDWriteFontFace> QWindowsDirect2DPaintEngine::fontFaceFromFontEngine(const QFont &font, QFontEngine *fe)
|
||||
{
|
||||
Q_D(QWindowsDirect2DPaintEngine);
|
||||
|
||||
ComPtr<IDWriteFontFace> fontFace = d->fontCache.value(font);
|
||||
if (fontFace)
|
||||
return fontFace;
|
||||
|
||||
switch (fe->type()) {
|
||||
case QFontEngine::Win:
|
||||
{
|
||||
QWindowsFontEngine *wfe = static_cast<QWindowsFontEngine *>(fe);
|
||||
QSharedPointer<QWindowsFontEngineData> 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<QWindowsFontEngineDirectWrite *>(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
|
||||
|
@ -47,6 +47,7 @@
|
||||
|
||||
#include <d2d1_1.h>
|
||||
#include <dwrite_1.h>
|
||||
#include <wrl.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@ -114,6 +115,8 @@ private:
|
||||
bool antiAliasingEnabled() const;
|
||||
void adjustForAliasing(QRectF *rect);
|
||||
void adjustForAliasing(QPointF *point);
|
||||
|
||||
Microsoft::WRL::ComPtr<IDWriteFontFace> fontFaceFromFontEngine(const QFont &font, QFontEngine *fe);
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
Loading…
Reference in New Issue
Block a user