Get rid of preprocess in QQuickText

Preprocessing in the scene graph is actually pretty expensive,
so we want to avoid using it for something like text, where you
can often have a lot of nodes in a UI.

For text it was previously used for two purposes:

1. To make sure the distance field glyph cache was updated with
potential new glyphs before the frame was rendered, but *after*
all new glyph nodes had been created (this is an optimization to
avoid having to resize the cache multiple times in one frame,
but rather wait until we know the exact amount of new glyphs
that need to be added)

2. Update the coordinates of the glyph based on their position
in the glyph cache if the glyph cache says they have moved (or
if they have just been added for the first time).

Point #1 can actually be handled a lot simpler, by having a
separate loop for the existing glyph caches where we ask them
to preprocess. There is typically only a few glyph caches, but
can be hundreds of glyph nodes in a scene, so in the previous
code we would be calling the update() functions on the same
glyph cache over and over again every frame.

Point #2 does not need preprocessing every frame, but can be
reduced to only preprocess when the geometry is marked as
dirty. We then do a pass on the glyph node in question to
update its geometry and turn the flag off again so that we
don't pay the cost every frame.

Pick-to: 5.15
Fixes: QTBUG-84351
Change-Id: If8c492acaef9c512c2db61b64bcab2103db2d997
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2020-06-12 09:53:17 +02:00
parent 10fa487754
commit 1f3dd3f5db
6 changed files with 23 additions and 6 deletions

View File

@ -252,6 +252,8 @@ void QSGRenderer::preprocess()
// For the default case, when this does not happen, the cost is negligible.
QSet<QSGNode *> items = m_nodes_to_preprocess;
m_context->preprocess();
for (QSet<QSGNode *>::const_iterator it = items.constBegin();
it != items.constEnd(); ++it) {
QSGNode *n = *it;

View File

@ -394,6 +394,13 @@ void QSGRenderContext::endSync()
m_texturesToDelete.clear();
}
/*!
Do necessary preprocessing before the frame
*/
void QSGRenderContext::preprocess()
{
}
/*!
Factory function for scene graph backends of the distance-field glyph cache.
*/

View File

@ -192,6 +192,7 @@ public:
virtual void endSync();
virtual void preprocess();
virtual QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font);
QSGTexture *textureForFactory(QQuickTextureFactory *factory, QQuickWindow *window);

View File

@ -282,6 +282,14 @@ bool QSGDefaultRenderContext::separateIndexBuffer() const
return m_separateIndexBuffer;
}
void QSGDefaultRenderContext::preprocess()
{
for (auto it = m_glyphCaches.begin(); it != m_glyphCaches.end(); ++it) {
it.value()->processPendingGlyphs();
it.value()->update();
}
}
QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font)
{
QString key = fontKey(font);

View File

@ -106,6 +106,7 @@ public:
void renderNextRhiFrame(QSGRenderer *renderer) override;
void endNextRhiFrame(QSGRenderer *renderer) override;
void preprocess() override;
QSGDistanceFieldGlyphCache *distanceFieldGlyphCache(const QRawFont &font) override;
QSGTexture *createTexture(const QImage &image, uint flags) const override;

View File

@ -57,7 +57,6 @@ QSGDistanceFieldGlyphNode::QSGDistanceFieldGlyphNode(QSGRenderContext *context)
{
m_geometry.setDrawingMode(GL_TRIANGLES);
setGeometry(&m_geometry);
setFlag(UsePreprocess);
#ifdef QSG_RUNTIME_DESCRIPTION
qsgnode_set_description(this, QLatin1String("glyphs"));
#endif
@ -105,6 +104,7 @@ void QSGDistanceFieldGlyphNode::setGlyphs(const QPointF &position, const QGlyphR
m_dirtyGeometry = true;
m_dirtyMaterial = true;
setFlag(UsePreprocess);
QSGDistanceFieldGlyphCache *oldCache = m_glyph_cache;
m_glyph_cache = m_context->distanceFieldGlyphCache(m_glyphs.rawFont());
@ -152,13 +152,10 @@ void QSGDistanceFieldGlyphNode::update()
void QSGDistanceFieldGlyphNode::preprocess()
{
Q_ASSERT(m_glyph_cache);
m_glyph_cache->processPendingGlyphs();
m_glyph_cache->update();
if (m_dirtyGeometry)
updateGeometry();
setFlag(UsePreprocess, false);
}
void QSGDistanceFieldGlyphNode::invalidateGlyphs(const QVector<quint32> &glyphs)
@ -169,6 +166,7 @@ void QSGDistanceFieldGlyphNode::invalidateGlyphs(const QVector<quint32> &glyphs)
for (int i = 0; i < glyphs.count(); ++i) {
if (m_allGlyphIndexesLookup.contains(glyphs.at(i))) {
m_dirtyGeometry = true;
setFlag(UsePreprocess);
return;
}
}