QmlDesigner: Add cache for instances

When detaching the NodeInstanceView from a model
we insert all instances for this model into a cache.
The cache currently takes a maximum of 20 models.
If the model is reattached we use the existing instances, instead
of creating new ones.
We also recycle the state previews. Outdated data will be overridden by
new data once the puppet is sending the respective commands.

Task-number: QDS-6121
Change-Id: I15b5628afc5579ba8a03dca23ba5809e55022f3d
Reviewed-by: Marco Bubke <marco.bubke@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Thomas Hartmann
2022-02-09 13:37:05 +01:00
parent 3bd96e7e73
commit cea31a2c4f
3 changed files with 141 additions and 4 deletions

View File

@@ -0,0 +1,81 @@
/****************************************************************************
**
** Copyright (C) 2022 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <qmldesignercorelib_global.h>
#include <model.h>
#include <utils/optional.h>
#include <QHash>
#include <QQueue>
namespace QmlDesigner {
template<class DataType>
class ModelCache
{
public:
ModelCache(int max = 20)
: m_maxEntries(max)
{}
void insert(Model *model, const DataType &data)
{
QObject::connect(model, &Model::destroyed, [this](QObject *o) {
QObject *deletedModel = o;
if (deletedModel) {
m_content.remove(deletedModel);
m_queue.removeAll(deletedModel);
}
});
m_content.insert(model, data);
if (!m_queue.contains(model))
m_queue.append(model);
if (m_queue.length() > m_maxEntries) {
QObject *first = m_queue.takeFirst();
m_content.remove(first);
}
}
Utils::optional<DataType> take(Model *model)
{
if (!m_content.contains(model))
return {};
m_queue.removeOne(model);
return m_content.take(model);
}
private:
QHash<QObject *, DataType> m_content;
QQueue<QObject *> m_queue;
int m_maxEntries = 20;
};
} // namespace QmlDesigner

View File

@@ -27,6 +27,7 @@
#include "qmldesignercorelib_global.h" #include "qmldesignercorelib_global.h"
#include "abstractview.h" #include "abstractview.h"
#include "modelcache.h"
#include <modelnode.h> #include <modelnode.h>
#include <nodeinstance.h> #include <nodeinstance.h>
@@ -232,11 +233,29 @@ private: // functions
PropertyChangeFlags flags); PropertyChangeFlags flags);
private: private:
struct NodeInstanceCacheData
{
NodeInstanceCacheData(const QHash<ModelNode, NodeInstance> &i,
const QHash<ModelNode, QImage> &p)
: instances(i)
, previewImages(p)
{}
NodeInstanceCacheData() = default;
QHash<ModelNode, NodeInstance> instances;
QHash<ModelNode, QImage> previewImages;
};
QList<NodeInstance> loadInstancesFromCache(const QList<ModelNode> &nodeList,
const NodeInstanceCacheData &cache);
QHash<QString, ModelNodePreviewImageData> m_imageDataMap; QHash<QString, ModelNodePreviewImageData> m_imageDataMap;
NodeInstance m_rootNodeInstance; NodeInstance m_rootNodeInstance;
NodeInstance m_activeStateInstance; NodeInstance m_activeStateInstance;
QHash<ModelNode, NodeInstance> m_nodeInstanceHash; QHash<ModelNode, NodeInstance> m_nodeInstanceHash;
ModelCache<NodeInstanceCacheData> m_nodeInstanceCache;
QHash<ModelNode, QImage> m_statePreviewImage; QHash<ModelNode, QImage> m_statePreviewImage;
ConnectionManagerInterface &m_connectionManager; ConnectionManagerInterface &m_connectionManager;
std::unique_ptr<NodeInstanceServerProxy> m_nodeInstanceServer; std::unique_ptr<NodeInstanceServerProxy> m_nodeInstanceServer;

View File

@@ -265,6 +265,9 @@ void NodeInstanceView::modelAboutToBeDetached(Model * model)
{ {
m_connectionManager.setCrashCallback({}); m_connectionManager.setCrashCallback({});
m_nodeInstanceCache.insert(model,
NodeInstanceCacheData(m_nodeInstanceHash, m_statePreviewImage));
removeAllInstanceNodeRelationships(); removeAllInstanceNodeRelationships();
if (m_nodeInstanceServer) { if (m_nodeInstanceServer) {
m_nodeInstanceServer->clearScene(createClearSceneCommand()); m_nodeInstanceServer->clearScene(createClearSceneCommand());
@@ -935,10 +938,16 @@ CreateSceneCommand NodeInstanceView::createCreateSceneCommand()
QList<ModelNode> nodeList = allModelNodes(); QList<ModelNode> nodeList = allModelNodes();
QList<NodeInstance> instanceList; QList<NodeInstance> instanceList;
for (const ModelNode &node : std::as_const(nodeList)) { Utils::optional oldNodeInstanceHash = m_nodeInstanceCache.take(model());
NodeInstance instance = loadNode(node); if (oldNodeInstanceHash
if (!isSkippedNode(node)) && oldNodeInstanceHash->instances.value(rootModelNode()).isValid()) {
instanceList.append(instance); instanceList = loadInstancesFromCache(nodeList, oldNodeInstanceHash.value());
} else {
for (const ModelNode &node : std::as_const(nodeList)) {
NodeInstance instance = loadNode(node);
if (!isSkippedNode(node))
instanceList.append(instance);
}
} }
nodeList = filterNodesForSkipItems(nodeList); nodeList = filterNodesForSkipItems(nodeList);
@@ -1942,4 +1951,32 @@ void NodeInstanceView::maybeResetOnPropertyChange(const PropertyName &name, cons
resetPuppet(); resetPuppet();
} }
QList<NodeInstance> NodeInstanceView::loadInstancesFromCache(const QList<ModelNode> &nodeList,
const NodeInstanceCacheData &cache)
{
QList<NodeInstance> instanceList;
auto previews = cache.previewImages;
auto iterator = previews.begin();
while (iterator != previews.end()) {
if (iterator.key().isValid())
m_statePreviewImage.insert(iterator.key(), iterator.value());
iterator++;
}
for (const ModelNode &node : std::as_const(nodeList)) {
NodeInstance instance = cache.instances.value(node);
if (instance.isValid())
insertInstanceRelationships(instance);
else
instance = loadNode(node);
if (node.isRootNode())
m_rootNodeInstance = instance;
if (!isSkippedNode(node))
instanceList.append(instanceForModelNode(node));
}
return instanceList;
} }
} // namespace QmlDesigner