Revision control

Copy as Markdown

// Copyright (C) 2018 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "qsgadaptationlayer_p.h"
#include <qmath.h>
#include <QtQuick/private/qsgdistancefieldglyphnode_p.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qrawfont_p.h>
#include <QtGui/qguiapplication.h>
#include <qdir.h>
#include <qsgrendernode.h>
#include <private/qquickprofiler_p.h>
#include <QElapsedTimer>
#include <qtquick_tracepoints_p.h>
QT_BEGIN_NAMESPACE
Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_update_entry, int count)
Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_update_exit)
Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphRender_entry)
Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphRender_exit)
Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphStore_entry)
Q_TRACE_POINT(qtquick, QSGDistanceFieldGlyphCache_glyphStore_exit)
static QElapsedTimer qsg_render_timer;
QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture;
QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(const QRawFont &font, int renderTypeQuality)
: m_renderTypeQuality(renderTypeQuality)
, m_pendingGlyphs(64)
{
Q_ASSERT(font.isValid());
QRawFontPrivate *fontD = QRawFontPrivate::get(font);
m_glyphCount = fontD->fontEngine->glyphCount();
m_doubleGlyphResolution = qt_fontHasNarrowOutlines(font) && m_glyphCount < QT_DISTANCEFIELD_HIGHGLYPHCOUNT();
m_referenceFont = font;
// we set the same pixel size as used by the distance field internally.
// this allows us to call pathForGlyph once and reuse the result.
m_referenceFont.setPixelSize(baseFontSize() * QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution));
Q_ASSERT(m_referenceFont.isValid());
}
QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache()
{
}
int QSGDistanceFieldGlyphCache::baseFontSize() const
{
return m_renderTypeQuality > 0 ? m_renderTypeQuality : QT_DISTANCEFIELD_BASEFONTSIZE(m_doubleGlyphResolution);
}
QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::emptyData(glyph_t glyph)
{
GlyphData gd;
gd.texture = &s_emptyTexture;
QHash<glyph_t, GlyphData>::iterator it = m_glyphsData.insert(glyph, gd);
return it.value();
}
QSGDistanceFieldGlyphCache::GlyphData &QSGDistanceFieldGlyphCache::glyphData(glyph_t glyph)
{
QHash<glyph_t, GlyphData>::iterator data = m_glyphsData.find(glyph);
if (data == m_glyphsData.end()) {
GlyphData &gd = emptyData(glyph);
gd.path = m_referenceFont.pathForGlyph(glyph);
// need bounding rect in base font size scale
qreal scaleFactor = qreal(1) / QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution);
QTransform scaleDown;
scaleDown.scale(scaleFactor, scaleFactor);
gd.boundingRect = scaleDown.mapRect(gd.path.boundingRect());
// Guard against corrupt fonts with excessively large glyph bounds, which would cause
// us to allocate huge distance fields and atlas textures. Treat them as empty glyphs.
const qreal pixelSize = baseFontSize();
if (QFontEngine::glyphBoundingBoxExceedsLimit(gd.boundingRect.width(),
gd.boundingRect.height(),
pixelSize * pixelSize)) {
gd.boundingRect = QRectF();
gd.path = QPainterPath();
}
return gd;
}
return data.value();
}
QSGDistanceFieldGlyphCache::Metrics QSGDistanceFieldGlyphCache::glyphMetrics(glyph_t glyph, qreal pixelSize)
{
GlyphData &gd = glyphData(glyph);
qreal scale = fontScale(pixelSize);
Metrics m;
m.width = gd.boundingRect.width() * scale;
m.height = gd.boundingRect.height() * scale;
m.baselineX = gd.boundingRect.x() * scale;
m.baselineY = -gd.boundingRect.y() * scale;
return m;
}
void QSGDistanceFieldGlyphCache::populate(const QList<glyph_t> &glyphs)
{
QSet<glyph_t> referencedGlyphs;
QSet<glyph_t> newGlyphs;
int count = glyphs.size();
for (int i = 0; i < count; ++i) {
glyph_t glyphIndex = glyphs.at(i);
const bool isValid = int(glyphIndex) < glyphCount() || glyphCount() <= 0;
if (!isValid) {
qWarning("Warning: distance-field glyph is not available with index %d (glyph count: %d, font: %s)",
glyphIndex,
glyphCount(),
qPrintable(m_referenceFont.familyName()));
}
GlyphData &gd = isValid ? glyphData(glyphIndex) : emptyData(glyphIndex);
++gd.ref;
referencedGlyphs.insert(glyphIndex);
if (gd.texCoord.isValid() || m_populatingGlyphs.contains(glyphIndex))
continue;
m_populatingGlyphs.insert(glyphIndex);
if (gd.boundingRect.isEmpty()) {
gd.texCoord.width = 0;
gd.texCoord.height = 0;
} else {
newGlyphs.insert(glyphIndex);
}
}
referenceGlyphs(referencedGlyphs);
if (!newGlyphs.isEmpty())
requestGlyphs(newGlyphs);
}
void QSGDistanceFieldGlyphCache::release(const QList<glyph_t> &glyphs)
{
QSet<glyph_t> unusedGlyphs;
for (glyph_t glyphIndex : glyphs) {
if (auto it = m_glyphsData.find(glyphIndex); it != m_glyphsData.end()) {
GlyphData &gd = it.value();
if (--gd.ref == 0)
unusedGlyphs.insert(glyphIndex);
}
}
releaseGlyphs(unusedGlyphs);
}
bool QSGDistanceFieldGlyphCache::isActive() const
{
return true;
}
void QSGDistanceFieldGlyphCache::update()
{
m_populatingGlyphs.clear();
if (m_pendingGlyphs.isEmpty())
return;
Q_TRACE_SCOPE(QSGDistanceFieldGlyphCache_update, m_pendingGlyphs.size());
bool profileFrames = QSG_LOG_TIME_GLYPH().isDebugEnabled();
if (profileFrames)
qsg_render_timer.start();
Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphAdaptationLayerFrame);
Q_TRACE(QSGDistanceFieldGlyphCache_glyphRender_entry);
QList<QDistanceField> distanceFields;
const int pendingGlyphsSize = m_pendingGlyphs.size();
distanceFields.reserve(pendingGlyphsSize);
for (int i = 0; i < pendingGlyphsSize; ++i) {
GlyphData &gd = glyphData(m_pendingGlyphs.at(i));
QSize size = QSize(qCeil(gd.texCoord.width + gd.texCoord.xMargin * 2),
qCeil(gd.texCoord.height + gd.texCoord.yMargin * 2));
distanceFields.append(QDistanceField(size,
gd.path,
m_pendingGlyphs.at(i),
m_doubleGlyphResolution));
gd.path = QPainterPath(); // no longer needed, so release memory used by the painter path
}
qint64 renderTime = 0;
int count = m_pendingGlyphs.size();
if (profileFrames)
renderTime = qsg_render_timer.nsecsElapsed();
Q_TRACE(QSGDistanceFieldGlyphCache_glyphRender_exit);
Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphAdaptationLayerFrame,
QQuickProfiler::SceneGraphAdaptationLayerGlyphRender);
Q_TRACE(QSGDistanceFieldGlyphCache_glyphStore_entry);
m_pendingGlyphs.reset();
storeGlyphs(distanceFields);
#if defined(QSG_DISTANCEFIELD_CACHE_DEBUG)
for (Texture texture : std::as_const(m_textures))
saveTexture(texture.texture, m_referenceFont.familyName());
#endif
if (QSG_LOG_TIME_GLYPH().isDebugEnabled()) {
quint64 now = qsg_render_timer.elapsed();
qCDebug(QSG_LOG_TIME_GLYPH,
"distancefield: %d glyphs prepared in %dms, rendering=%d, upload=%d",
count,
(int) now,
int(renderTime / 1000000),
int((now - (renderTime / 1000000))));
}
Q_TRACE(QSGDistanceFieldGlyphCache_glyphStore_exit);
Q_QUICK_SG_PROFILE_END_WITH_PAYLOAD(QQuickProfiler::SceneGraphAdaptationLayerFrame,
QQuickProfiler::SceneGraphAdaptationLayerGlyphStore,
(qint64)count);
}
void QSGDistanceFieldGlyphCache::setGlyphsPosition(const QList<GlyphPosition> &glyphs)
{
QList<quint32> invalidatedGlyphs;
int count = glyphs.size();
for (int i = 0; i < count; ++i) {
GlyphPosition glyph = glyphs.at(i);
GlyphData &gd = glyphData(glyph.glyph);
if (!gd.texCoord.isNull())
invalidatedGlyphs.append(glyph.glyph);
gd.texCoord.xMargin = QT_DISTANCEFIELD_RADIUS(m_doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution));
gd.texCoord.yMargin = QT_DISTANCEFIELD_RADIUS(m_doubleGlyphResolution) / qreal(QT_DISTANCEFIELD_SCALE(m_doubleGlyphResolution));
gd.texCoord.x = glyph.position.x();
gd.texCoord.y = glyph.position.y();
gd.texCoord.width = gd.boundingRect.width();
gd.texCoord.height = gd.boundingRect.height();
}
if (!invalidatedGlyphs.isEmpty()) {
for (QSGDistanceFieldGlyphConsumerList::iterator iter = m_registeredNodes.begin(); iter != m_registeredNodes.end(); ++iter) {
iter->invalidateGlyphs(invalidatedGlyphs);
}
}
}
void QSGDistanceFieldGlyphCache::processPendingGlyphs()
{
/* Intentionally empty */
}
void QSGDistanceFieldGlyphCache::setGlyphsTexture(const QList<glyph_t> &glyphs, const Texture &tex)
{
int i = m_textures.indexOf(tex);
if (i == -1) {
m_textures.append(tex);
i = m_textures.size() - 1;
} else {
m_textures[i].size = tex.size;
}
Texture *texture = &(m_textures[i]);
QList<quint32> invalidatedGlyphs;
int count = glyphs.size();
for (int j = 0; j < count; ++j) {
glyph_t glyphIndex = glyphs.at(j);
GlyphData &gd = glyphData(glyphIndex);
if (gd.texture != &s_emptyTexture)
invalidatedGlyphs.append(glyphIndex);
gd.texture = texture;
}
if (!invalidatedGlyphs.isEmpty()) {
for (QSGDistanceFieldGlyphConsumerList::iterator iter = m_registeredNodes.begin(); iter != m_registeredNodes.end(); ++iter) {
iter->invalidateGlyphs(invalidatedGlyphs);
}
}
}
void QSGDistanceFieldGlyphCache::markGlyphsToRender(const QList<glyph_t> &glyphs)
{
int count = glyphs.size();
for (int i = 0; i < count; ++i)
m_pendingGlyphs.add(glyphs.at(i));
}
void QSGDistanceFieldGlyphCache::updateRhiTexture(QRhiTexture *oldTex, QRhiTexture *newTex, const QSize &newTexSize)
{
int count = m_textures.size();
for (int i = 0; i < count; ++i) {
Texture &tex = m_textures[i];
if (tex.texture == oldTex) {
tex.texture = newTex;
tex.size = newTexSize;
return;
}
}
}
QSGNodeVisitorEx::~QSGNodeVisitorEx()
= default;
void QSGNodeVisitorEx::visitChildren(QSGNode *node)
{
for (QSGNode *child = node->firstChild(); child; child = child->nextSibling()) {
switch (child->type()) {
case QSGNode::ClipNodeType: {
QSGClipNode *c = static_cast<QSGClipNode*>(child);
if (visit(c))
visitChildren(c);
endVisit(c);
break;
}
case QSGNode::TransformNodeType: {
QSGTransformNode *c = static_cast<QSGTransformNode*>(child);
if (visit(c))
visitChildren(c);
endVisit(c);
break;
}
case QSGNode::OpacityNodeType: {
QSGOpacityNode *c = static_cast<QSGOpacityNode*>(child);
if (visit(c))
visitChildren(c);
endVisit(c);
break;
}
case QSGNode::GeometryNodeType: {
if (child->flags() & QSGNode::IsVisitableNode) {
QSGVisitableNode *v = static_cast<QSGVisitableNode*>(child);
v->accept(this);
} else {
QSGGeometryNode *c = static_cast<QSGGeometryNode*>(child);
if (visit(c))
visitChildren(c);
endVisit(c);
}
break;
}
case QSGNode::RootNodeType: {
QSGRootNode *root = static_cast<QSGRootNode*>(child);
if (visit(root))
visitChildren(root);
endVisit(root);
break;
}
case QSGNode::BasicNodeType: {
visitChildren(child);
break;
}
case QSGNode::RenderNodeType: {
QSGRenderNode *r = static_cast<QSGRenderNode*>(child);
if (visit(r))
visitChildren(r);
endVisit(r);
break;
}
default:
Q_UNREACHABLE();
break;
}
}
}
QSGVisitableNode::~QSGVisitableNode()
= default;
QSGInternalRectangleNode::~QSGInternalRectangleNode()
= default;
QSGInternalImageNode::~QSGInternalImageNode()
= default;
QSGPainterNode::~QSGPainterNode()
= default;
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug debug, const QSGGuiThreadShaderEffectManager::ShaderInfo::Variable &v)
{
QDebugStateSaver saver(debug);
debug.space();
debug << v.name;
switch (v.type) {
case QSGGuiThreadShaderEffectManager::ShaderInfo::Constant:
debug << "cvar" << "offset" << v.offset << "size" << v.size;
break;
case QSGGuiThreadShaderEffectManager::ShaderInfo::Sampler:
debug << "sampler" << "bindpoint" << v.bindPoint;
break;
case QSGGuiThreadShaderEffectManager::ShaderInfo::Texture:
debug << "texture" << "bindpoint" << v.bindPoint;
break;
default:
break;
}
return debug;
}
QDebug operator<<(QDebug debug, const QSGShaderEffectNode::VariableData &vd)
{
QDebugStateSaver saver(debug);
debug.space();
debug << vd.specialType;
return debug;
}
#endif
/*!
\internal
*/
QSGLayer::QSGLayer(QSGTexturePrivate &dd)
: QSGDynamicTexture(dd)
{
}
QSGLayer::~QSGLayer()
= default;
#if QT_CONFIG(quick_sprite)
QSGSpriteNode::~QSGSpriteNode()
= default;
#endif
QSGGuiThreadShaderEffectManager::~QSGGuiThreadShaderEffectManager()
= default;
QSGShaderEffectNode::~QSGShaderEffectNode()
= default;
QSGGlyphNode::~QSGGlyphNode()
= default;
void QSGGlyphNode::recycle()
{
m_bounding_rect = QRectF{};
setRenderOrder(0);
setInheritedOpacity(1);
setRendererMatrix(nullptr);
setRendererClipList(nullptr);
}
QSGDistanceFieldGlyphConsumer::~QSGDistanceFieldGlyphConsumer()
= default;
QT_END_NAMESPACE
#include "moc_qsgadaptationlayer_p.cpp"