aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>2018-07-08 11:23:24 +0200
committerEskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>2018-08-17 11:06:20 +0000
commit36c6b727c5f433ce3f22e18d93e2502df1c526aa (patch)
tree4c57debdf593897440d9d012935bfca16ba7cb66
parent9fbdc8c4c2c028427b19907d636a7a83d79cbd09 (diff)
Load pregenerated glyph cache in default DF cache
In order to support quick loading of scenes with lots of text, we support preloading the contents of the distance field cache from a generated file instead of creating all the distance fields on startup. The idea is that when creating a distance field cache for a specific font, the data will be prepopulated. This is stored in a table in the font file and is picked up automatically by Qt when available. [ChangeLog][Text] Support pregenerated loading distance field glyph caches to decrease startup time for applications with large amounts of text. Task-number: QTBUG-69356 Change-Id: I7cff0c4c782f819b1c893041405970ea4553fb8d Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer.cpp15
-rw-r--r--src/quick/scenegraph/qsgadaptationlayer_p.h9
-rw-r--r--src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp295
-rw-r--r--src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h4
-rw-r--r--src/quick/scenegraph/util/qsgareaallocator.cpp144
-rw-r--r--src/quick/scenegraph/util/qsgareaallocator_p.h4
6 files changed, 458 insertions, 13 deletions
diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp
index 46af0f28f0..c99e149aa5 100644
--- a/src/quick/scenegraph/qsgadaptationlayer.cpp
+++ b/src/quick/scenegraph/qsgadaptationlayer.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -82,19 +82,26 @@ QSGDistanceFieldGlyphCache::~QSGDistanceFieldGlyphCache()
{
}
+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;
- gd.texture = &s_emptyTexture;
+ 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());
- data = m_glyphsData.insert(glyph, gd);
+ return gd;
}
return data.value();
}
diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h
index 5a465831cb..ba5c4353b2 100644
--- a/src/quick/scenegraph/qsgadaptationlayer_p.h
+++ b/src/quick/scenegraph/qsgadaptationlayer_p.h
@@ -83,6 +83,7 @@ class QSGGlyphNode;
class QSGRootNode;
class QSGSpriteNode;
class QSGRenderNode;
+class QSGRenderContext;
class Q_QUICK_PRIVATE_EXPORT QSGNodeVisitorEx
{
@@ -507,6 +508,7 @@ protected:
uint textureIdForGlyph(glyph_t glyph) const;
GlyphData &glyphData(glyph_t glyph);
+ GlyphData &emptyData(glyph_t glyph);
#if defined(QSG_DISTANCEFIELD_CACHE_DEBUG)
void saveTexture(GLuint textureId, int width, int height) const;
@@ -514,11 +516,14 @@ protected:
inline bool isCoreProfile() const { return m_coreProfile; }
-private:
+ bool m_doubleGlyphResolution;
+
+protected:
QRawFont m_referenceFont;
+
+private:
int m_glyphCount;
- bool m_doubleGlyphResolution;
bool m_coreProfile;
QList<Texture> m_textures;
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
index ef189ba461..ccc57b0b86 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
@@ -39,12 +39,18 @@
#include "qsgdefaultdistancefieldglyphcache_p.h"
+#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qbuffer.h>
+#include <QtQml/qqmlfile.h>
+
#include <QtGui/private/qdistancefield_p.h>
#include <QtGui/private/qopenglcontext_p.h>
#include <QtQml/private/qqmlglobal_p.h>
#include <qopenglfunctions.h>
#include <qopenglframebufferobject.h>
#include <qmath.h>
+#include "qsgcontext_p.h"
+
#if !defined(QT_OPENGL_ES_2)
#include <QtGui/qopenglfunctions_3_2_core.h>
@@ -59,10 +65,12 @@ DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSI
# define QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING 2
#endif
-QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font)
+QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c,
+ const QRawFont &font)
: QSGDistanceFieldGlyphCache(c, font)
, m_maxTextureSize(0)
, m_maxTextureCount(3)
+ , m_areaAllocator(nullptr)
, m_blitProgram(nullptr)
, m_blitBuffer(QOpenGLBuffer::VertexBuffer)
, m_fboGuard(nullptr)
@@ -81,7 +89,8 @@ QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLCont
qWarning("Buffer creation failed");
}
- m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize()));
+ // Load a pregenerated cache if the font contains one
+ loadPregeneratedCache(font);
}
QSGDefaultDistanceFieldGlyphCache::~QSGDefaultDistanceFieldGlyphCache()
@@ -101,6 +110,9 @@ void QSGDefaultDistanceFieldGlyphCache::requestGlyphs(const QSet<glyph_t> &glyph
QList<GlyphPosition> glyphPositions;
QVector<glyph_t> glyphsToRender;
+ if (m_areaAllocator == nullptr)
+ m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize()));
+
for (QSet<glyph_t>::const_iterator it = glyphs.constBegin(); it != glyphs.constEnd() ; ++it) {
glyph_t glyphIndex = *it;
@@ -237,10 +249,23 @@ void QSGDefaultDistanceFieldGlyphCache::releaseGlyphs(const QSet<glyph_t> &glyph
m_unusedGlyphs += glyphs;
}
-void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int width, int height)
+void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
+ int width,
+ int height)
+{
+ QByteArray zeroBuf(width * height, 0);
+ createTexture(texInfo, width, height, zeroBuf.constData());
+}
+
+void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo,
+ int width,
+ int height,
+ const void *pixels)
{
- if (useTextureResizeWorkaround() && texInfo->image.isNull())
+ if (useTextureResizeWorkaround() && texInfo->image.isNull()) {
texInfo->image = QDistanceField(width, height);
+ memcpy(texInfo->image.bits(), pixels, width * height);
+ }
while (m_funcs->glGetError() != GL_NO_ERROR) { }
@@ -261,8 +286,7 @@ void QSGDefaultDistanceFieldGlyphCache::createTexture(TextureInfo *texInfo, int
const GLenum format = GL_ALPHA;
#endif
- QByteArray zeroBuf(width * height, 0);
- m_funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, zeroBuf.constData());
+ m_funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, pixels);
texInfo->size = QSize(width, height);
@@ -520,4 +544,261 @@ int QSGDefaultDistanceFieldGlyphCache::maxTextureSize() const
return m_maxTextureSize;
}
+namespace {
+ struct Qtdf {
+ // We need these structs to be tightly packed, but some compilers we use do not
+ // support #pragma pack(1), so we need to hardcode the offsets/sizes in the
+ // file format
+ enum TableSize {
+ HeaderSize = 14,
+ GlyphRecordSize = 46,
+ TextureRecordSize = 17
+ };
+
+ enum Offset {
+ // Header
+ majorVersion = 0,
+ minorVersion = 1,
+ pixelSize = 2,
+ textureSize = 4,
+ flags = 8,
+ headerPadding = 9,
+ numGlyphs = 10,
+
+ // Glyph record
+ glyphIndex = 0,
+ textureOffsetX = 4,
+ textureOffsetY = 8,
+ textureWidth = 12,
+ textureHeight = 16,
+ xMargin = 20,
+ yMargin = 24,
+ boundingRectX = 28,
+ boundingRectY = 32,
+ boundingRectWidth = 36,
+ boundingRectHeight = 40,
+ textureIndex = 44,
+
+ // Texture record
+ allocatedX = 0,
+ allocatedY = 4,
+ allocatedWidth = 8,
+ allocatedHeight = 12,
+ texturePadding = 16
+
+ };
+
+ template <typename T>
+ static inline T fetch(const char *data, Offset offset)
+ {
+ return qFromBigEndian<T>(data + int(offset));
+ }
+ };
+}
+
+bool QSGDefaultDistanceFieldGlyphCache::loadPregeneratedCache(const QRawFont &font)
+{
+ // The pregenerated data must be loaded first, otherwise the area allocator
+ // will be wrong
+ if (m_areaAllocator != nullptr) {
+ qWarning("Font cache must be loaded before cache is used");
+ return false;
+ }
+
+ static QElapsedTimer timer;
+
+ bool profile = QSG_LOG_TIME_GLYPH().isDebugEnabled();
+ if (profile)
+ timer.start();
+
+ QByteArray qtdfTable = font.fontTable("qtdf");
+ if (qtdfTable.isEmpty())
+ return false;
+
+ typedef QHash<TextureInfo *, QVector<glyph_t> > GlyphTextureHash;
+
+ GlyphTextureHash glyphTextures;
+
+ if (uint(qtdfTable.size()) < Qtdf::HeaderSize) {
+ qWarning("Invalid qtdf table in font '%s'",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ const char *qtdfTableStart = qtdfTable.constData();
+ const char *qtdfTableEnd = qtdfTableStart + qtdfTable.size();
+
+ int padding = 0;
+ int textureCount = 0;
+ {
+ quint8 majorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::majorVersion);
+ quint8 minorVersion = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::minorVersion);
+ if (majorVersion != 5 || minorVersion != 12) {
+ qWarning("Invalid version of qtdf table %d.%d in font '%s'",
+ majorVersion,
+ minorVersion,
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ qreal pixelSize = qreal(Qtdf::fetch<quint16>(qtdfTableStart, Qtdf::pixelSize));
+ m_maxTextureSize = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::textureSize);
+ m_doubleGlyphResolution = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::flags) == 1;
+ padding = Qtdf::fetch<quint8>(qtdfTableStart, Qtdf::headerPadding);
+
+ if (pixelSize <= 0.0) {
+ qWarning("Invalid pixel size in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ if (m_maxTextureSize <= 0) {
+ qWarning("Invalid texture size in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ int systemMaxTextureSize;
+ m_funcs->glGetIntegerv(GL_MAX_TEXTURE_SIZE, &systemMaxTextureSize);
+
+ if (m_maxTextureSize > systemMaxTextureSize) {
+ qWarning("System maximum texture size is %d. This is lower than the value in '%s', which is %d",
+ systemMaxTextureSize,
+ qPrintable(font.familyName()),
+ m_maxTextureSize);
+ }
+
+ if (padding != QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING) {
+ qWarning("Padding mismatch in '%s'. Font requires %d, but Qt is compiled with %d.",
+ qPrintable(font.familyName()),
+ padding,
+ QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING);
+ }
+
+ m_referenceFont.setPixelSize(pixelSize);
+
+ quint32 glyphCount = Qtdf::fetch<quint32>(qtdfTableStart, Qtdf::numGlyphs);
+ m_unusedGlyphs.reserve(glyphCount);
+
+ const char *allocatorData = qtdfTableStart + Qtdf::HeaderSize;
+ {
+ m_areaAllocator = new QSGAreaAllocator(QSize(0, 0));
+ allocatorData = m_areaAllocator->deserialize(allocatorData, qtdfTableEnd - allocatorData);
+ if (allocatorData == nullptr)
+ return false;
+ }
+
+ if (m_areaAllocator->size().height() % m_maxTextureSize != 0) {
+ qWarning("Area allocator size mismatch in '%s'", qPrintable(font.familyName()));
+ return false;
+ }
+
+ textureCount = m_areaAllocator->size().height() / m_maxTextureSize;
+ m_maxTextureCount = qMax(m_maxTextureCount, textureCount);
+
+ const char *textureRecord = allocatorData;
+ for (int i = 0; i < textureCount; ++i, textureRecord += Qtdf::TextureRecordSize) {
+ if (textureRecord + Qtdf::TextureRecordSize > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ TextureInfo *tex = textureInfo(i);
+ tex->allocatedArea.setX(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedX));
+ tex->allocatedArea.setY(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedY));
+ tex->allocatedArea.setWidth(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedWidth));
+ tex->allocatedArea.setHeight(Qtdf::fetch<quint32>(textureRecord, Qtdf::allocatedHeight));
+ tex->padding = Qtdf::fetch<quint8>(textureRecord, Qtdf::texturePadding);
+ }
+
+ const char *glyphRecord = textureRecord;
+ for (quint32 i = 0; i < glyphCount; ++i, glyphRecord += Qtdf::GlyphRecordSize) {
+ if (glyphRecord + Qtdf::GlyphRecordSize > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ glyph_t glyph = Qtdf::fetch<quint32>(glyphRecord, Qtdf::glyphIndex);
+ m_unusedGlyphs.insert(glyph);
+
+ GlyphData &glyphData = emptyData(glyph);
+
+#define FROM_FIXED_POINT(value) \
+(((qreal)value)/(qreal)65536)
+
+ glyphData.texCoord.x = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetX));
+ glyphData.texCoord.y = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureOffsetY));
+ glyphData.texCoord.width = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureWidth));
+ glyphData.texCoord.height = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::textureHeight));
+ glyphData.texCoord.xMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::xMargin));
+ glyphData.texCoord.yMargin = FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::yMargin));
+ glyphData.boundingRect.setX(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectX)));
+ glyphData.boundingRect.setY(FROM_FIXED_POINT(Qtdf::fetch<qint32>(glyphRecord, Qtdf::boundingRectY)));
+ glyphData.boundingRect.setWidth(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectWidth)));
+ glyphData.boundingRect.setHeight(FROM_FIXED_POINT(Qtdf::fetch<quint32>(glyphRecord, Qtdf::boundingRectHeight)));
+
+#undef FROM_FIXED_POINT
+
+ int textureIndex = Qtdf::fetch<quint16>(glyphRecord, Qtdf::textureIndex);
+ if (textureIndex < 0 || textureIndex >= textureCount) {
+ qWarning("Invalid texture index %d (texture count == %d) in '%s'",
+ textureIndex,
+ textureCount,
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+
+ TextureInfo *texInfo = textureInfo(textureIndex);
+ m_glyphsTexture.insert(glyph, texInfo);
+
+ glyphTextures[texInfo].append(glyph);
+ }
+
+ GLint alignment = 4; // default value
+ m_funcs->glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment);
+
+ m_funcs->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+
+ const uchar *textureData = reinterpret_cast<const uchar *>(glyphRecord);
+ for (int i = 0; i < textureCount; ++i) {
+
+ TextureInfo *texInfo = textureInfo(i);
+
+ int width = texInfo->allocatedArea.width();
+ int height = texInfo->allocatedArea.height();
+ qint64 size = width * height;
+ if (reinterpret_cast<const char *>(textureData + size) > qtdfTableEnd) {
+ qWarning("qtdf table too small in font '%s'.",
+ qPrintable(font.familyName()));
+ return false;
+ }
+
+ createTexture(texInfo, width, height, textureData);
+
+ QVector<glyph_t> glyphs = glyphTextures.value(texInfo);
+
+ Texture t;
+ t.textureId = texInfo->texture;
+ t.size = texInfo->size;
+
+ setGlyphsTexture(glyphs, t);
+
+ textureData += size;
+ }
+
+ m_funcs->glPixelStorei(GL_UNPACK_ALIGNMENT, alignment);
+ }
+
+ if (profile) {
+ quint64 now = timer.elapsed();
+ qCDebug(QSG_LOG_TIME_GLYPH,
+ "distancefield: %d pre-generated glyphs loaded in %dms",
+ m_unusedGlyphs.size(),
+ (int) now);
+ }
+
+ return true;
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
index 76c0d20647..a0e4387af9 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
+++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h
@@ -85,7 +85,10 @@ public:
void setMaxTextureCount(int max) { m_maxTextureCount = max; }
int maxTextureCount() const { return m_maxTextureCount; }
+
private:
+ bool loadPregeneratedCache(const QRawFont &font);
+
struct TextureInfo {
GLuint texture;
QSize size;
@@ -96,6 +99,7 @@ private:
TextureInfo(const QRect &preallocRect = QRect()) : texture(0), allocatedArea(preallocRect) { }
};
+ void createTexture(TextureInfo * texInfo, int width, int height, const void *pixels);
void createTexture(TextureInfo * texInfo, int width, int height);
void resizeTexture(TextureInfo * texInfo, int width, int height);
diff --git a/src/quick/scenegraph/util/qsgareaallocator.cpp b/src/quick/scenegraph/util/qsgareaallocator.cpp
index cd270a1d63..9a8c8e333b 100644
--- a/src/quick/scenegraph/util/qsgareaallocator.cpp
+++ b/src/quick/scenegraph/util/qsgareaallocator.cpp
@@ -42,6 +42,9 @@
#include <QtCore/qglobal.h>
#include <QtCore/qrect.h>
#include <QtCore/qpoint.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qstack.h>
+#include <QtCore/qendian.h>
QT_BEGIN_NAMESPACE
@@ -285,4 +288,145 @@ void QSGAreaAllocator::mergeNodeWithNeighbors(QSGAreaAllocatorNode *node)
} // end while(!done)
}
+namespace {
+ struct AreaAllocatorTable
+ {
+ enum TableSize {
+ HeaderSize = 10,
+ NodeSize = 9
+ };
+
+ enum Offset {
+ // Header
+ majorVersion = 0,
+ minorVersion = 1,
+ width = 2,
+ height = 6,
+
+ // Node
+ split = 0,
+ splitType = 4,
+ flags = 8
+ };
+
+ enum Flags {
+ IsOccupied = 1,
+ HasLeft = 2,
+ HasRight = 4
+ };
+
+ template <typename T>
+ static inline T fetch(const char *data, Offset offset)
+ {
+ return qFromBigEndian<T>(data + int(offset));
+ }
+
+ template <typename T>
+ static inline void put(char *data, Offset offset, T value)
+ {
+ qToBigEndian(value, data + int(offset));
+ }
+ };
+}
+
+QByteArray QSGAreaAllocator::serialize()
+{
+ QVarLengthArray<QSGAreaAllocatorNode *> nodesToProcess;
+
+ QStack<QSGAreaAllocatorNode *> nodes;
+ nodes.push(m_root);
+ while (!nodes.isEmpty()) {
+ QSGAreaAllocatorNode *node = nodes.pop();
+
+ nodesToProcess.append(node);
+ if (node->left != nullptr)
+ nodes.push(node->left);
+ if (node->right != nullptr)
+ nodes.push(node->right);
+ }
+
+ QByteArray ret;
+ ret.resize(AreaAllocatorTable::HeaderSize + AreaAllocatorTable::NodeSize * nodesToProcess.size());
+
+ char *data = ret.data();
+ AreaAllocatorTable::put(data, AreaAllocatorTable::majorVersion, quint8(5));
+ AreaAllocatorTable::put(data, AreaAllocatorTable::minorVersion, quint8(12));
+ AreaAllocatorTable::put(data, AreaAllocatorTable::width, quint32(m_size.width()));
+ AreaAllocatorTable::put(data, AreaAllocatorTable::height, quint32(m_size.height()));
+
+ data += AreaAllocatorTable::HeaderSize;
+ for (QSGAreaAllocatorNode *node : nodesToProcess) {
+ AreaAllocatorTable::put(data, AreaAllocatorTable::split, qint32(node->split));
+ AreaAllocatorTable::put(data, AreaAllocatorTable::splitType, quint32(node->splitType));
+
+ quint8 flags =
+ (node->isOccupied ? AreaAllocatorTable::IsOccupied : 0)
+ | (node->left != nullptr ? AreaAllocatorTable::HasLeft : 0)
+ | (node->right != nullptr ? AreaAllocatorTable::HasRight : 0);
+ AreaAllocatorTable::put(data, AreaAllocatorTable::flags, flags);
+ data += AreaAllocatorTable::NodeSize;
+ }
+
+ return ret;
+}
+
+const char *QSGAreaAllocator::deserialize(const char *data, int size)
+{
+ if (uint(size) < AreaAllocatorTable::HeaderSize) {
+ qWarning("QSGAreaAllocator::deserialize: Data not long enough to fit header");
+ return nullptr;
+ }
+
+ const char *end = data + size;
+
+ quint8 majorVersion = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::majorVersion);
+ quint8 minorVersion = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::minorVersion);
+ if (majorVersion != 5 || minorVersion != 12) {
+ qWarning("Unrecognized version %d.%d of QSGAreaAllocator",
+ majorVersion,
+ minorVersion);
+ return nullptr;
+ }
+
+ m_size = QSize(AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::width),
+ AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::height));
+
+ Q_ASSERT(m_root != nullptr);
+ Q_ASSERT(m_root->left == nullptr);
+ Q_ASSERT(m_root->right == nullptr);
+
+ QStack<QSGAreaAllocatorNode *> nodes;
+ nodes.push(m_root);
+
+ data += AreaAllocatorTable::HeaderSize;
+ while (!nodes.isEmpty()) {
+ if (data + AreaAllocatorTable::NodeSize > end) {
+ qWarning("QSGAreaAllocator::deseriable: Data not long enough for nodes");
+ return nullptr;
+ }
+
+ QSGAreaAllocatorNode *node = nodes.pop();
+
+ node->split = AreaAllocatorTable::fetch<qint32>(data, AreaAllocatorTable::split);
+ node->splitType = SplitType(AreaAllocatorTable::fetch<quint32>(data, AreaAllocatorTable::splitType));
+
+ quint8 flags = AreaAllocatorTable::fetch<quint8>(data, AreaAllocatorTable::flags);
+ node->isOccupied = flags & AreaAllocatorTable::IsOccupied;
+
+ if (flags & AreaAllocatorTable::HasLeft) {
+ node->left = new QSGAreaAllocatorNode(node);
+ nodes.push(node->left);
+ }
+
+ if (flags & AreaAllocatorTable::HasRight) {
+ node->right = new QSGAreaAllocatorNode(node);
+ nodes.push(node->right);
+ }
+
+ data += AreaAllocatorTable::NodeSize;
+ }
+
+ return data;
+}
+
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/util/qsgareaallocator_p.h b/src/quick/scenegraph/util/qsgareaallocator_p.h
index 8bc05a5a5b..300a8128c0 100644
--- a/src/quick/scenegraph/util/qsgareaallocator_p.h
+++ b/src/quick/scenegraph/util/qsgareaallocator_p.h
@@ -69,6 +69,10 @@ public:
bool deallocate(const QRect &rect);
bool isEmpty() const { return m_root == nullptr; }
QSize size() const { return m_size; }
+
+ QByteArray serialize();
+ const char *deserialize(const char *data, int size);
+
private:
bool allocateInNode(const QSize &size, QPoint &result, const QRect &currentRect, QSGAreaAllocatorNode *node);
bool deallocateInNode(const QPoint &pos, QSGAreaAllocatorNode *node);