Skip to content

Commit 339b504

Browse files
committed
AutoMapping: Load rule maps on-demand
This optimizes AutoMapping operations by not loading rule maps which do not apply to the current map based on file name filters. Also, re-parsing of the rules.txt file(s) now only happens when any of such files were changed, not when rule maps have changed.
1 parent 4cf1972 commit 339b504

File tree

5 files changed

+67
-57
lines changed

5 files changed

+67
-57
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
* Added support for SVG 1.2 / CSS blending modes to layers (#3932)
44
* AutoMapping: Don't match rules based on empty input indexes
5+
* AutoMapping: Optimized reloading of rule maps and load rule maps on-demand
56
* Raised minimum supported Qt version from 5.12 to 5.15
67

78
### Tiled 1.11.2 (28 Jan 2025)

src/tiled/automapper.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,9 @@ AutoMappingContext::AutoMappingContext(MapDocument *mapDocument)
143143
}
144144

145145

146-
AutoMapper::AutoMapper(std::unique_ptr<Map> rulesMap, const QRegularExpression &mapNameFilter)
146+
AutoMapper::AutoMapper(std::unique_ptr<Map> rulesMap)
147147
: mRulesMap(std::move(rulesMap))
148148
, mRulesMapRenderer(MapRenderer::create(mRulesMap.get()))
149-
, mMapNameFilter(mapNameFilter)
150149
{
151150
setupRuleMapProperties();
152151

src/tiled/automapper.h

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
#include <QList>
3232
#include <QMap>
3333
#include <QRegion>
34-
#include <QRegularExpression>
3534
#include <QSet>
3635
#include <QString>
3736
#include <QVector>
@@ -333,11 +332,10 @@ class TILED_EDITOR_EXPORT AutoMapper
333332
* @param rulesMap The map containing the AutoMapping rules. The
334333
* AutoMapper takes ownership of this map.
335334
*/
336-
AutoMapper(std::unique_ptr<Map> rulesMap, const QRegularExpression &mapNameFilter = {});
335+
explicit AutoMapper(std::unique_ptr<Map> rulesMap);
337336
~AutoMapper();
338337

339338
QString rulesMapFileName() const;
340-
const QRegularExpression &mapNameFilter() const;
341339

342340
/**
343341
* Checks if the passed \a ruleLayerName is used as input layer in this
@@ -467,7 +465,6 @@ class TILED_EDITOR_EXPORT AutoMapper
467465
*/
468466
const std::unique_ptr<Map> mRulesMap;
469467
const std::unique_ptr<MapRenderer> mRulesMapRenderer;
470-
const QRegularExpression mMapNameFilter;
471468

472469
RuleMapSetup mRuleMapSetup;
473470

@@ -490,9 +487,4 @@ class TILED_EDITOR_EXPORT AutoMapper
490487
const TileLayer dummy; // used in case input layers are missing
491488
};
492489

493-
inline const QRegularExpression &AutoMapper::mapNameFilter() const
494-
{
495-
return mMapNameFilter;
496-
}
497-
498490
} // namespace Tiled

src/tiled/automappingmanager.cpp

Lines changed: 52 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -109,21 +109,6 @@ void AutomappingManager::autoMapInternal(const QRegion &where,
109109

110110
const bool automatic = touchedLayer != nullptr;
111111

112-
if (!mLoaded) {
113-
if (mRulesFile.isEmpty()) {
114-
mError = tr("No AutoMapping rules provided. Save the map or refer to a rule file in the project properties.");
115-
emit errorsOccurred(automatic);
116-
return;
117-
}
118-
119-
if (loadFile(mRulesFile)) {
120-
mLoaded = true;
121-
} else {
122-
emit errorsOccurred(automatic);
123-
return;
124-
}
125-
}
126-
127112
// Even if no AutoMapper instance will be executed, we still want to report
128113
// any warnings or errors that might have been reported while interpreting
129114
// the rule maps.
@@ -135,14 +120,29 @@ void AutomappingManager::autoMapInternal(const QRegion &where,
135120
emit errorsOccurred(automatic);
136121
});
137122

123+
if (!mLoaded) {
124+
if (mRulesFile.isEmpty()) {
125+
mError = tr("No AutoMapping rules provided. Save the map or refer to a rule file in the project properties.");
126+
return;
127+
}
128+
129+
if (!loadFile(mRulesFile))
130+
return;
131+
132+
mLoaded = true;
133+
}
134+
138135
// Determine the list of AutoMappers that is relevant for this map
139136
const QString mapFileName = QFileInfo(mMapDocument->fileName()).fileName();
137+
140138
QVector<const AutoMapper*> autoMappers;
141-
autoMappers.reserve(mActiveAutoMappers.size());
142-
for (auto autoMapper : mActiveAutoMappers) {
143-
const auto &mapNameFilter = autoMapper->mapNameFilter();
139+
autoMappers.reserve(mRuleMapReferences.size());
140+
141+
for (auto &ruleMap : std::as_const(mRuleMapReferences)) {
142+
const auto &mapNameFilter = ruleMap.mapNameFilter;
144143
if (!mapNameFilter.isValid() || mapNameFilter.match(mapFileName).hasMatch())
145-
autoMappers.append(autoMapper);
144+
if (const AutoMapper *autoMapper = findAutoMapper(ruleMap.filePath))
145+
autoMappers.append(autoMapper);
146146
}
147147

148148
if (autoMappers.isEmpty())
@@ -164,6 +164,24 @@ void AutomappingManager::autoMapInternal(const QRegion &where,
164164
mMapDocument->undoStack()->push(aw);
165165
}
166166

167+
/**
168+
* Returns the AutoMapper instance for the given rules file, loading it if
169+
* necessary. Returns nullptr if the file could not be loaded.
170+
*/
171+
const AutoMapper *AutomappingManager::findAutoMapper(const QString &filePath)
172+
{
173+
auto it = mLoadedAutoMappers.find(filePath);
174+
if (it != mLoadedAutoMappers.end())
175+
return it->second.get();
176+
177+
auto autoMapper = loadRuleMap(filePath);
178+
if (!autoMapper)
179+
return nullptr;
180+
181+
auto result = mLoadedAutoMappers.emplace(filePath, std::move(autoMapper));
182+
return result.first->second.get();
183+
}
184+
167185
/**
168186
* This function parses a rules file or loads a rules map file.
169187
*
@@ -183,7 +201,8 @@ bool AutomappingManager::loadFile(const QString &filePath)
183201
return loadRulesFile(filePath);
184202
}
185203

186-
return loadRuleMap(filePath);
204+
mRuleMapReferences.append(RuleMapReference { filePath, mMapNameFilter });
205+
return true;
187206
}
188207

189208
bool AutomappingManager::loadRulesFile(const QString &filePath)
@@ -251,41 +270,30 @@ bool AutomappingManager::loadRulesFile(const QString &filePath)
251270
return ret;
252271
}
253272

254-
bool AutomappingManager::loadRuleMap(const QString &filePath)
273+
std::unique_ptr<AutoMapper> AutomappingManager::loadRuleMap(const QString &filePath)
255274
{
256-
auto it = mLoadedAutoMappers.find(filePath);
257-
if (it != mLoadedAutoMappers.end()) {
258-
mActiveAutoMappers.push_back(it->second.get());
259-
return true;
260-
}
261-
262275
QString errorString;
263-
std::unique_ptr<Map> rules { readMap(filePath, &errorString) };
264-
265-
if (!rules) {
276+
auto rulesMap = readMap(filePath, &errorString);
277+
if (!rulesMap) {
266278
QString error = tr("Opening rules map '%1' failed: %2")
267279
.arg(filePath, errorString);
268280
ERROR(error);
269281

270282
mError += error;
271283
mError += QLatin1Char('\n');
272-
return false;
284+
return {};
273285
}
274286

275-
std::unique_ptr<AutoMapper> autoMapper { new AutoMapper(std::move(rules), mMapNameFilter) };
287+
mWatcher.addPath(filePath);
288+
289+
auto autoMapper = std::make_unique<AutoMapper>(std::move(rulesMap));
276290

277291
mWarning += autoMapper->warningString();
278292
const QString error = autoMapper->errorString();
279-
if (error.isEmpty()) {
280-
auto autoMapperPtr = autoMapper.get();
281-
mLoadedAutoMappers.insert(std::make_pair(filePath, std::move(autoMapper)));
282-
mActiveAutoMappers.push_back(autoMapperPtr);
283-
mWatcher.addPath(filePath);
284-
} else {
293+
if (!error.isEmpty())
285294
mError += error;
286-
}
287295

288-
return true;
296+
return autoMapper;
289297
}
290298

291299
/**
@@ -346,7 +354,7 @@ void AutomappingManager::refreshRulesFile(const QString &ruleFileOverride)
346354

347355
void AutomappingManager::cleanUp()
348356
{
349-
mActiveAutoMappers.clear();
357+
mRuleMapReferences.clear();
350358
mLoaded = false;
351359
}
352360

@@ -358,7 +366,9 @@ void AutomappingManager::onFileChanged(const QString &path)
358366
// File will be re-added when it is still relevant
359367
mWatcher.removePath(path);
360368

361-
cleanUp();
369+
// Re-parse the rules file(s) when any of them changed
370+
if (path.endsWith(QLatin1String(".txt"), Qt::CaseInsensitive))
371+
cleanUp();
362372
}
363373

364374
#include "moc_automappingmanager.cpp"

src/tiled/automappingmanager.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
#include <QString>
3131

3232
#include <memory>
33-
#include <vector>
33+
#include <unordered_map>
3434

3535
namespace Tiled {
3636

@@ -39,6 +39,12 @@ class TileLayer;
3939
class AutoMapper;
4040
class MapDocument;
4141

42+
struct RuleMapReference
43+
{
44+
QString filePath;
45+
QRegularExpression mapNameFilter;
46+
};
47+
4248
/**
4349
* This class is a superior class to the AutoMapper and AutoMapperWrapper class.
4450
* It uses these classes to do the whole automapping process.
@@ -84,9 +90,11 @@ class AutomappingManager : public QObject
8490
void onMapFileNameChanged();
8591
void onFileChanged(const QString &path);
8692

93+
const AutoMapper *findAutoMapper(const QString &filePath);
94+
8795
bool loadFile(const QString &filePath);
8896
bool loadRulesFile(const QString &filePath);
89-
bool loadRuleMap(const QString &filePath);
97+
std::unique_ptr<AutoMapper> loadRuleMap(const QString &filePath);
9098

9199
/**
92100
* Applies automapping to the region \a where.
@@ -113,13 +121,13 @@ class AutomappingManager : public QObject
113121
std::unordered_map<QString, std::unique_ptr<AutoMapper>> mLoadedAutoMappers;
114122

115123
/**
116-
* The active list of AutoMapper instances, in the order they were
124+
* The active list of rule map references, in the order they were
117125
* encountered in the rules file.
118126
*
119127
* Some loaded rule maps might not be active, and some might be active
120128
* multiple times.
121129
*/
122-
std::vector<const AutoMapper*> mActiveAutoMappers;
130+
QVector<RuleMapReference> mRuleMapReferences;
123131

124132
/**
125133
* This tells you if the rules for the current map document were already

0 commit comments

Comments
 (0)