mirror of
https://github.com/OpenMW/openmw.git
synced 2025-05-01 01:41:24 +00:00
Merge branch 'cs_optimize_saving' into 'master'
Fix editor saving performance See merge request OpenMW/openmw!2741
This commit is contained in:
commit
d846a9fc15
8 changed files with 117 additions and 60 deletions
|
@ -6,12 +6,40 @@
|
||||||
|
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include <components/debug/debuglog.hpp>
|
||||||
|
|
||||||
#include <apps/opencs/model/doc/messages.hpp>
|
#include <apps/opencs/model/doc/messages.hpp>
|
||||||
|
|
||||||
#include "../world/universalid.hpp"
|
#include "../world/universalid.hpp"
|
||||||
|
|
||||||
#include "stage.hpp"
|
#include "stage.hpp"
|
||||||
|
|
||||||
|
namespace CSMDoc
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
std::string_view operationToString(State value)
|
||||||
|
{
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case State_Saving:
|
||||||
|
return "Saving";
|
||||||
|
case State_Merging:
|
||||||
|
return "Merging";
|
||||||
|
case State_Verifying:
|
||||||
|
return "Verifying";
|
||||||
|
case State_Searching:
|
||||||
|
return "Searching";
|
||||||
|
case State_Loading:
|
||||||
|
return "Loading";
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CSMDoc::Operation::prepareStages()
|
void CSMDoc::Operation::prepareStages()
|
||||||
{
|
{
|
||||||
mCurrentStage = mStages.begin();
|
mCurrentStage = mStages.begin();
|
||||||
|
@ -27,7 +55,7 @@ void CSMDoc::Operation::prepareStages()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMDoc::Operation::Operation(int type, bool ordered, bool finalAlways)
|
CSMDoc::Operation::Operation(State type, bool ordered, bool finalAlways)
|
||||||
: mType(type)
|
: mType(type)
|
||||||
, mStages(std::vector<std::pair<Stage*, int>>())
|
, mStages(std::vector<std::pair<Stage*, int>>())
|
||||||
, mCurrentStage(mStages.begin())
|
, mCurrentStage(mStages.begin())
|
||||||
|
@ -61,6 +89,7 @@ void CSMDoc::Operation::run()
|
||||||
}
|
}
|
||||||
|
|
||||||
mPrepared = false;
|
mPrepared = false;
|
||||||
|
mStart = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
mTimer->start(0);
|
mTimer->start(0);
|
||||||
}
|
}
|
||||||
|
@ -140,7 +169,17 @@ void CSMDoc::Operation::executeStage()
|
||||||
emit reportMessage(*iter, mType);
|
emit reportMessage(*iter, mType);
|
||||||
|
|
||||||
if (mCurrentStage == mStages.end())
|
if (mCurrentStage == mStages.end())
|
||||||
|
{
|
||||||
|
if (mStart.has_value())
|
||||||
|
{
|
||||||
|
const auto duration = std::chrono::steady_clock::now() - *mStart;
|
||||||
|
Log(Debug::Verbose) << operationToString(mType) << " operation is completed in "
|
||||||
|
<< std::chrono::duration_cast<std::chrono::duration<double>>(duration).count() << 's';
|
||||||
|
mStart.reset();
|
||||||
|
}
|
||||||
|
|
||||||
operationDone();
|
operationDone();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CSMDoc::Operation::operationDone()
|
void CSMDoc::Operation::operationDone()
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
#ifndef CSM_DOC_OPERATION_H
|
#ifndef CSM_DOC_OPERATION_H
|
||||||
#define CSM_DOC_OPERATION_H
|
#define CSM_DOC_OPERATION_H
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "messages.hpp"
|
#include "messages.hpp"
|
||||||
|
#include "state.hpp"
|
||||||
|
|
||||||
class QTimer;
|
class QTimer;
|
||||||
|
|
||||||
|
@ -18,7 +21,7 @@ namespace CSMDoc
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
int mType;
|
State mType;
|
||||||
std::vector<std::pair<Stage*, int>> mStages; // stage, number of steps
|
std::vector<std::pair<Stage*, int>> mStages; // stage, number of steps
|
||||||
std::vector<std::pair<Stage*, int>>::iterator mCurrentStage;
|
std::vector<std::pair<Stage*, int>>::iterator mCurrentStage;
|
||||||
int mCurrentStep;
|
int mCurrentStep;
|
||||||
|
@ -31,11 +34,12 @@ namespace CSMDoc
|
||||||
QTimer* mTimer;
|
QTimer* mTimer;
|
||||||
bool mPrepared;
|
bool mPrepared;
|
||||||
Message::Severity mDefaultSeverity;
|
Message::Severity mDefaultSeverity;
|
||||||
|
std::optional<std::chrono::steady_clock::time_point> mStart;
|
||||||
|
|
||||||
void prepareStages();
|
void prepareStages();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Operation(int type, bool ordered, bool finalAlways = false);
|
Operation(State type, bool ordered, bool finalAlways = false);
|
||||||
///< \param ordered Stages must be executed in the given order.
|
///< \param ordered Stages must be executed in the given order.
|
||||||
/// \param finalAlways Execute last stage even if an error occurred during earlier stages.
|
/// \param finalAlways Execute last stage even if an error occurred during earlier stages.
|
||||||
|
|
||||||
|
|
|
@ -125,6 +125,7 @@ CSMDoc::WriteDialogueCollectionStage::WriteDialogueCollectionStage(Document& doc
|
||||||
|
|
||||||
int CSMDoc::WriteDialogueCollectionStage::setup()
|
int CSMDoc::WriteDialogueCollectionStage::setup()
|
||||||
{
|
{
|
||||||
|
mInfosByTopic = mInfos.getInfosByTopic();
|
||||||
return mTopics.getSize();
|
return mTopics.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,14 +146,17 @@ void CSMDoc::WriteDialogueCollectionStage::perform(int stage, Messages& messages
|
||||||
|
|
||||||
// Test, if we need to save anything associated info records.
|
// Test, if we need to save anything associated info records.
|
||||||
bool infoModified = false;
|
bool infoModified = false;
|
||||||
const auto infos = mInfos.getTopicInfos(topic.get().mId);
|
const auto topicInfos = mInfosByTopic.find(topic.get().mId);
|
||||||
|
|
||||||
for (const auto& record : infos)
|
if (topicInfos != mInfosByTopic.end())
|
||||||
{
|
{
|
||||||
if (record->isModified() || record->mState == CSMWorld::RecordBase::State_Deleted)
|
for (const auto& record : topicInfos->second)
|
||||||
{
|
{
|
||||||
infoModified = true;
|
if (record->isModified() || record->mState == CSMWorld::RecordBase::State_Deleted)
|
||||||
break;
|
{
|
||||||
|
infoModified = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,31 +177,36 @@ void CSMDoc::WriteDialogueCollectionStage::perform(int stage, Messages& messages
|
||||||
}
|
}
|
||||||
|
|
||||||
// write modified selected info records
|
// write modified selected info records
|
||||||
for (auto iter = infos.begin(); iter != infos.end(); ++iter)
|
if (topicInfos != mInfosByTopic.end())
|
||||||
{
|
{
|
||||||
const CSMWorld::Record<CSMWorld::Info>& record = **iter;
|
const std::vector<const CSMWorld::Record<CSMWorld::Info>*>& infos = topicInfos->second;
|
||||||
|
|
||||||
if (record.isModified() || record.mState == CSMWorld::RecordBase::State_Deleted)
|
for (auto iter = infos.begin(); iter != infos.end(); ++iter)
|
||||||
{
|
{
|
||||||
ESM::DialInfo info = record.get();
|
const CSMWorld::Record<CSMWorld::Info>& record = **iter;
|
||||||
info.mId = record.get().mOriginalId;
|
|
||||||
|
|
||||||
info.mPrev = ESM::RefId::sEmpty;
|
if (record.isModified() || record.mState == CSMWorld::RecordBase::State_Deleted)
|
||||||
if (iter != infos.begin())
|
|
||||||
{
|
{
|
||||||
const auto prev = std::prev(iter);
|
ESM::DialInfo info = record.get();
|
||||||
info.mPrev = (*prev)->get().mOriginalId;
|
info.mId = record.get().mOriginalId;
|
||||||
|
|
||||||
|
info.mPrev = ESM::RefId::sEmpty;
|
||||||
|
if (iter != infos.begin())
|
||||||
|
{
|
||||||
|
const auto prev = std::prev(iter);
|
||||||
|
info.mPrev = (*prev)->get().mOriginalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto next = std::next(iter);
|
||||||
|
|
||||||
|
info.mNext = ESM::RefId::sEmpty;
|
||||||
|
if (next != infos.end())
|
||||||
|
info.mNext = (*next)->get().mOriginalId;
|
||||||
|
|
||||||
|
writer.startRecord(info.sRecordId);
|
||||||
|
info.save(writer, record.mState == CSMWorld::RecordBase::State_Deleted);
|
||||||
|
writer.endRecord(info.sRecordId);
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto next = std::next(iter);
|
|
||||||
|
|
||||||
info.mNext = ESM::RefId::sEmpty;
|
|
||||||
if (next != infos.end())
|
|
||||||
info.mNext = (*next)->get().mOriginalId;
|
|
||||||
|
|
||||||
writer.startRecord(info.sRecordId);
|
|
||||||
info.save(writer, record.mState == CSMWorld::RecordBase::State_Deleted);
|
|
||||||
writer.endRecord(info.sRecordId);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
#include "stage.hpp"
|
#include "stage.hpp"
|
||||||
|
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "../world/idcollection.hpp"
|
#include "../world/idcollection.hpp"
|
||||||
|
#include "../world/infocollection.hpp"
|
||||||
#include "../world/record.hpp"
|
#include "../world/record.hpp"
|
||||||
#include "../world/scope.hpp"
|
#include "../world/scope.hpp"
|
||||||
|
|
||||||
|
@ -118,6 +120,7 @@ namespace CSMDoc
|
||||||
SavingState& mState;
|
SavingState& mState;
|
||||||
const CSMWorld::IdCollection<ESM::Dialogue>& mTopics;
|
const CSMWorld::IdCollection<ESM::Dialogue>& mTopics;
|
||||||
CSMWorld::InfoCollection& mInfos;
|
CSMWorld::InfoCollection& mInfos;
|
||||||
|
CSMWorld::InfosRecordPtrByTopic mInfosByTopic;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WriteDialogueCollectionStage(Document& document, SavingState& state, bool journal);
|
WriteDialogueCollectionStage(Document& document, SavingState& state, bool journal);
|
||||||
|
|
|
@ -32,7 +32,7 @@ CSMTools::JournalCheckStage::JournalCheckStage(
|
||||||
int CSMTools::JournalCheckStage::setup()
|
int CSMTools::JournalCheckStage::setup()
|
||||||
{
|
{
|
||||||
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
|
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
|
||||||
|
mInfosByTopic = mJournalInfos.getInfosByTopic();
|
||||||
return mJournals.getSize();
|
return mJournals.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,38 +50,39 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages)
|
||||||
int totalInfoCount = 0;
|
int totalInfoCount = 0;
|
||||||
std::set<int> questIndices;
|
std::set<int> questIndices;
|
||||||
|
|
||||||
for (const CSMWorld::Record<CSMWorld::Info>* record : mJournalInfos.getTopicInfos(journal.mId))
|
if (const auto infos = mInfosByTopic.find(journal.mId); infos != mInfosByTopic.end())
|
||||||
{
|
{
|
||||||
if (record->isDeleted())
|
for (const CSMWorld::Record<CSMWorld::Info>* record : infos->second)
|
||||||
continue;
|
|
||||||
|
|
||||||
const CSMWorld::Info& journalInfo = record->get();
|
|
||||||
|
|
||||||
totalInfoCount += 1;
|
|
||||||
|
|
||||||
if (journalInfo.mQuestStatus == ESM::DialInfo::QS_Name)
|
|
||||||
{
|
{
|
||||||
statusNamedCount += 1;
|
if (record->isDeleted())
|
||||||
}
|
continue;
|
||||||
|
|
||||||
// Skip "Base" records (setting!)
|
const CSMWorld::Info& journalInfo = record->get();
|
||||||
if (mIgnoreBaseRecords && record->mState == CSMWorld::RecordBase::State_BaseOnly)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (journalInfo.mResponse.empty())
|
totalInfoCount += 1;
|
||||||
{
|
|
||||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
|
|
||||||
messages.add(id, "Missing journal entry text", "", CSMDoc::Message::Severity_Warning);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<std::set<int>::iterator, bool> result = questIndices.insert(journalInfo.mData.mJournalIndex);
|
if (journalInfo.mQuestStatus == ESM::DialInfo::QS_Name)
|
||||||
|
{
|
||||||
|
statusNamedCount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Duplicate index
|
// Skip "Base" records (setting!)
|
||||||
if (!result.second)
|
if (mIgnoreBaseRecords && record->mState == CSMWorld::RecordBase::State_BaseOnly)
|
||||||
{
|
continue;
|
||||||
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
|
|
||||||
messages.add(id, "Duplicated quest index " + std::to_string(journalInfo.mData.mJournalIndex), "",
|
if (journalInfo.mResponse.empty())
|
||||||
CSMDoc::Message::Severity_Error);
|
{
|
||||||
|
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
|
||||||
|
messages.add(id, "Missing journal entry text", "", CSMDoc::Message::Severity_Warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplicate index
|
||||||
|
if (!questIndices.insert(journalInfo.mData.mJournalIndex).second)
|
||||||
|
{
|
||||||
|
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);
|
||||||
|
messages.add(id, "Duplicated quest index " + std::to_string(journalInfo.mData.mJournalIndex), "",
|
||||||
|
CSMDoc::Message::Severity_Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define CSM_TOOLS_JOURNALCHECK_H
|
#define CSM_TOOLS_JOURNALCHECK_H
|
||||||
|
|
||||||
#include "../world/idcollection.hpp"
|
#include "../world/idcollection.hpp"
|
||||||
|
#include "../world/infocollection.hpp"
|
||||||
|
|
||||||
#include "../doc/stage.hpp"
|
#include "../doc/stage.hpp"
|
||||||
|
|
||||||
|
@ -39,6 +40,7 @@ namespace CSMTools
|
||||||
const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;
|
const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;
|
||||||
const CSMWorld::InfoCollection& mJournalInfos;
|
const CSMWorld::InfoCollection& mJournalInfos;
|
||||||
bool mIgnoreBaseRecords;
|
bool mIgnoreBaseRecords;
|
||||||
|
CSMWorld::InfosRecordPtrByTopic mInfosByTopic;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,11 +82,10 @@ void CSMWorld::InfoCollection::load(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<CSMWorld::Record<CSMWorld::Info>*> CSMWorld::InfoCollection::getTopicInfos(const ESM::RefId& topic) const
|
CSMWorld::InfosRecordPtrByTopic CSMWorld::InfoCollection::getInfosByTopic() const
|
||||||
{
|
{
|
||||||
std::vector<CSMWorld::Record<CSMWorld::Info>*> result;
|
InfosRecordPtrByTopic result;
|
||||||
for (const std::unique_ptr<Record<Info>>& record : getRecords())
|
for (const std::unique_ptr<Record<Info>>& record : getRecords())
|
||||||
if (record->mBase.mTopicId == topic)
|
result[record->mBase.mTopicId].push_back(record.get());
|
||||||
result.push_back(record.get());
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
#include "collection.hpp"
|
#include "collection.hpp"
|
||||||
#include "info.hpp"
|
#include "info.hpp"
|
||||||
#include "record.hpp"
|
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
@ -18,6 +17,7 @@ namespace ESM
|
||||||
namespace CSMWorld
|
namespace CSMWorld
|
||||||
{
|
{
|
||||||
using InfosByTopic = std::unordered_map<ESM::RefId, std::vector<ESM::RefId>>;
|
using InfosByTopic = std::unordered_map<ESM::RefId, std::vector<ESM::RefId>>;
|
||||||
|
using InfosRecordPtrByTopic = std::unordered_map<ESM::RefId, std::vector<const Record<Info>*>>;
|
||||||
|
|
||||||
class InfoCollection : public Collection<Info, IdAccessor<Info>>
|
class InfoCollection : public Collection<Info, IdAccessor<Info>>
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,7 @@ namespace CSMWorld
|
||||||
public:
|
public:
|
||||||
void load(ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue, InfosByTopic& infosByTopic);
|
void load(ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue, InfosByTopic& infosByTopic);
|
||||||
|
|
||||||
std::vector<Record<Info>*> getTopicInfos(const ESM::RefId& topic) const;
|
InfosRecordPtrByTopic getInfosByTopic() const;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue