1
0
mirror of synced 2025-02-07 14:41:21 +01:00

ux: Reduce file loading time to basically zero

This commit is contained in:
WerWolv 2022-09-19 16:09:22 +02:00
parent b11dbe4fe1
commit 7b61268f22
7 changed files with 143 additions and 106 deletions

View File

@ -19,7 +19,7 @@ namespace hex {
class Task { class Task {
public: public:
Task() = default; Task() = default;
Task(std::string unlocalizedName, u64 maxValue, std::function<void(Task &)> function); Task(std::string unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> function);
Task(const Task&) = delete; Task(const Task&) = delete;
Task(Task &&other) noexcept; Task(Task &&other) noexcept;
@ -28,6 +28,7 @@ namespace hex {
void update(u64 value = 0); void update(u64 value = 0);
void setMaxValue(u64 value); void setMaxValue(u64 value);
[[nodiscard]] bool isBackgroundTask() const;
[[nodiscard]] bool isFinished() const; [[nodiscard]] bool isFinished() const;
[[nodiscard]] bool hadException() const; [[nodiscard]] bool hadException() const;
[[nodiscard]] bool wasInterrupted() const; [[nodiscard]] bool wasInterrupted() const;
@ -56,6 +57,7 @@ namespace hex {
std::function<void()> m_interruptCallback; std::function<void()> m_interruptCallback;
bool m_shouldInterrupt = false; bool m_shouldInterrupt = false;
bool m_background = true;
bool m_interrupted = false; bool m_interrupted = false;
bool m_finished = false; bool m_finished = false;
@ -89,6 +91,7 @@ namespace hex {
constexpr static auto NoProgress = 0; constexpr static auto NoProgress = 0;
static TaskHolder createTask(std::string name, u64 maxValue, std::function<void(Task &)> function); static TaskHolder createTask(std::string name, u64 maxValue, std::function<void(Task &)> function);
static TaskHolder createBackgroundTask(std::string name, std::function<void(Task &)> function);
static void collectGarbage(); static void collectGarbage();
static size_t getRunningTaskCount(); static size_t getRunningTaskCount();

View File

@ -9,11 +9,11 @@ namespace hex {
std::mutex TaskManager::s_deferredCallsMutex; std::mutex TaskManager::s_deferredCallsMutex;
std::list<std::shared_ptr<Task>> TaskManager::s_tasks; std::list<std::shared_ptr<Task>> TaskManager::s_tasks, s_backgroundTasks;
std::list<std::function<void()>> TaskManager::s_deferredCalls; std::list<std::function<void()>> TaskManager::s_deferredCalls;
Task::Task(std::string unlocalizedName, u64 maxValue, std::function<void(Task &)> function) Task::Task(std::string unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> function)
: m_unlocalizedName(std::move(unlocalizedName)), m_currValue(0), m_maxValue(maxValue) { : m_unlocalizedName(std::move(unlocalizedName)), m_currValue(0), m_maxValue(maxValue), m_background(background) {
this->m_thread = std::thread([this, func = std::move(function)] { this->m_thread = std::thread([this, func = std::move(function)] {
try { try {
func(*this); func(*this);
@ -83,6 +83,12 @@ namespace hex {
this->m_interruptCallback = std::move(callback); this->m_interruptCallback = std::move(callback);
} }
bool Task::isBackgroundTask() const {
std::scoped_lock lock(this->m_mutex);
return this->m_background;
}
bool Task::isFinished() const { bool Task::isFinished() const {
std::scoped_lock lock(this->m_mutex); std::scoped_lock lock(this->m_mutex);
@ -164,13 +170,20 @@ namespace hex {
TaskHolder TaskManager::createTask(std::string name, u64 maxValue, std::function<void(Task &)> function) { TaskHolder TaskManager::createTask(std::string name, u64 maxValue, std::function<void(Task &)> function) {
s_tasks.emplace_back(std::make_shared<Task>(std::move(name), maxValue, std::move(function))); s_tasks.emplace_back(std::make_shared<Task>(std::move(name), maxValue, false, std::move(function)));
return TaskHolder(s_tasks.back()); return TaskHolder(s_tasks.back());
} }
TaskHolder TaskManager::createBackgroundTask(std::string name, std::function<void(Task &)> function) {
s_backgroundTasks.emplace_back(std::make_shared<Task>(std::move(name), 0, true, std::move(function)));
return TaskHolder(s_backgroundTasks.back());
}
void TaskManager::collectGarbage() { void TaskManager::collectGarbage() {
std::erase_if(s_tasks, [](const auto &task) { return task->isFinished() && !task->hadException(); }); std::erase_if(s_tasks, [](const auto &task) { return task->isFinished() && !task->hadException(); });
std::erase_if(s_backgroundTasks, [](const auto &task) { return task->isFinished(); });
} }
std::list<std::shared_ptr<Task>> &TaskManager::getRunningTasks() { std::list<std::shared_ptr<Task>> &TaskManager::getRunningTasks() {

View File

@ -52,6 +52,7 @@ namespace hex {
ImGui::Texture m_logoTexture = { nullptr }; ImGui::Texture m_logoTexture = { nullptr };
std::mutex m_popupMutex;
std::list<std::string> m_popupsToOpen; std::list<std::string> m_popupsToOpen;
std::vector<int> m_pressedKeys; std::vector<int> m_pressedKeys;

View File

@ -144,6 +144,8 @@ namespace hex {
}); });
EventManager::subscribe<RequestOpenPopup>(this, [this](auto name) { EventManager::subscribe<RequestOpenPopup>(this, [this](auto name) {
std::scoped_lock lock(this->m_popupMutex);
this->m_popupsToOpen.push_back(name); this->m_popupsToOpen.push_back(name);
}); });
@ -424,15 +426,17 @@ namespace hex {
ImGui::EndPopup(); ImGui::EndPopup();
} }
} }
{
std::scoped_lock lock(this->m_popupMutex);
this->m_popupsToOpen.remove_if([](const auto &name) {
if (ImGui::IsPopupOpen(name.c_str()))
return true;
else
ImGui::OpenPopup(name.c_str());
this->m_popupsToOpen.remove_if([](const auto &name) { return false;
if (ImGui::IsPopupOpen(name.c_str())) });
return true; }
else
ImGui::OpenPopup(name.c_str());
return false;
});
TaskManager::runDeferredCalls(); TaskManager::runDeferredCalls();

View File

@ -106,19 +106,22 @@ namespace hex::plugin::builtin {
void ViewPatternData::drawContent() { void ViewPatternData::drawContent() {
if (ImGui::Begin(View::toWindowName("hex.builtin.view.pattern_data.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) { if (ImGui::Begin(View::toWindowName("hex.builtin.view.pattern_data.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
auto provider = ImHexApi::Provider::get(); if (ImHexApi::Provider::isValid()) {
if (ImHexApi::Provider::isValid() && provider->isReadable() && !ProviderExtraData::get(provider).patternLanguage.runtime->isRunning()) { auto provider = ImHexApi::Provider::get();
auto &runtime = ProviderExtraData::get(provider).patternLanguage.runtime;
auto &sortedPatterns = this->m_sortedPatterns[ImHexApi::Provider::get()]; if (provider->isReadable() && runtime != nullptr && !runtime->isRunning()) {
if (beginPatternTable(provider, ProviderExtraData::get(provider).patternLanguage.runtime->getAllPatterns(), sortedPatterns)) { auto &sortedPatterns = this->m_sortedPatterns[ImHexApi::Provider::get()];
ImGui::TableHeadersRow(); if (beginPatternTable(provider, ProviderExtraData::get(provider).patternLanguage.runtime->getAllPatterns(), sortedPatterns)) {
if (!sortedPatterns.empty()) { ImGui::TableHeadersRow();
for (auto &patterns : sortedPatterns) if (!sortedPatterns.empty()) {
patterns->accept(this->m_patternDrawer); for (auto &patterns : sortedPatterns)
patterns->accept(this->m_patternDrawer);
}
ImGui::EndTable();
} }
ImGui::EndTable();
} }
} }
} }

View File

@ -114,58 +114,60 @@ namespace hex::plugin::builtin {
patternLanguageData.runtime = std::make_unique<pl::PatternLanguage>(); patternLanguageData.runtime = std::make_unique<pl::PatternLanguage>();
ContentRegistry::PatternLanguage::configureRuntime(*patternLanguageData.runtime, provider); ContentRegistry::PatternLanguage::configureRuntime(*patternLanguageData.runtime, provider);
if (!this->m_autoLoadPatterns) TaskManager::createBackgroundTask("Analyzing file content", [this, provider, &data = patternLanguageData](auto &) {
return; if (!this->m_autoLoadPatterns)
return;
// Copy over current pattern source code to the new provider // Copy over current pattern source code to the new provider
if (!this->m_syncPatternSourceCode) { if (!this->m_syncPatternSourceCode) {
patternLanguageData.sourceCode = this->m_textEditor.GetText(); data.sourceCode = this->m_textEditor.GetText();
} }
auto &runtime = patternLanguageData.runtime; auto &runtime = data.runtime;
auto mimeType = magic::getMIMEType(provider); auto mimeType = magic::getMIMEType(provider);
bool foundCorrectType = false; bool foundCorrectType = false;
runtime->addPragma("MIME", [&mimeType, &foundCorrectType](pl::PatternLanguage &runtime, const std::string &value) { runtime->addPragma("MIME", [&mimeType, &foundCorrectType](pl::PatternLanguage &runtime, const std::string &value) {
hex::unused(runtime); hex::unused(runtime);
if (value == mimeType) { if (value == mimeType) {
foundCorrectType = true; foundCorrectType = true;
return true; return true;
}
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
});
this->m_possiblePatternFiles.clear();
std::error_code errorCode;
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Patterns)) {
for (auto &entry : std::fs::recursive_directory_iterator(dir, errorCode)) {
foundCorrectType = false;
if (!entry.is_regular_file())
continue;
fs::File file(entry.path(), fs::File::Mode::Read);
if (!file.isValid())
continue;
runtime->getInternals().preprocessor->preprocess(*runtime, file.readString());
if (foundCorrectType)
this->m_possiblePatternFiles.push_back(entry.path());
runtime->reset();
}
}
runtime->addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) { return !value.empty(); });
if (!this->m_possiblePatternFiles.empty()) {
this->m_selectedPatternFile = 0;
EventManager::post<RequestOpenPopup>("hex.builtin.view.pattern_editor.accept_pattern"_lang);
this->m_acceptPatternWindowOpen = true;
} }
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
}); });
this->m_possiblePatternFiles.clear();
std::error_code errorCode;
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Patterns)) {
for (auto &entry : std::fs::recursive_directory_iterator(dir, errorCode)) {
foundCorrectType = false;
if (!entry.is_regular_file())
continue;
fs::File file(entry.path(), fs::File::Mode::Read);
if (!file.isValid())
continue;
runtime->getInternals().preprocessor->preprocess(*runtime, file.readString());
if (foundCorrectType)
this->m_possiblePatternFiles.push_back(entry.path());
runtime->reset();
}
}
runtime->addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) { return !value.empty(); });
if (!this->m_possiblePatternFiles.empty()) {
this->m_selectedPatternFile = 0;
EventManager::post<RequestOpenPopup>("hex.builtin.view.pattern_editor.accept_pattern"_lang);
this->m_acceptPatternWindowOpen = true;
}
}); });
EventManager::subscribe<EventProviderChanged>(this, [this](prv::Provider *oldProvider, prv::Provider *newProvider) { EventManager::subscribe<EventProviderChanged>(this, [this](prv::Provider *oldProvider, prv::Provider *newProvider) {

View File

@ -52,40 +52,49 @@ namespace hex::plugin::builtin {
}; };
static std::atomic<bool> s_recentProvidersUpdating = false;
static std::list<RecentProvider> s_recentProviders; static std::list<RecentProvider> s_recentProviders;
static void updateRecentProviders() { static void updateRecentProviders() {
s_recentProviders.clear(); TaskManager::createBackgroundTask("Updating recent files", [](auto&){
if (s_recentProvidersUpdating)
return;
// Query all recent providers s_recentProvidersUpdating = true;
std::vector<std::fs::path> recentFilePaths; ON_SCOPE_EXIT { s_recentProvidersUpdating = false; };
for (const auto &folder : fs::getDefaultPaths(fs::ImHexPath::Recent)) {
for (const auto &entry : std::fs::directory_iterator(folder)) { s_recentProviders.clear();
if (entry.is_regular_file())
recentFilePaths.push_back(entry.path()); // Query all recent providers
std::vector<std::fs::path> recentFilePaths;
for (const auto &folder : fs::getDefaultPaths(fs::ImHexPath::Recent)) {
for (const auto &entry : std::fs::directory_iterator(folder)) {
if (entry.is_regular_file())
recentFilePaths.push_back(entry.path());
}
} }
}
// Sort recent provider files by last modified time // Sort recent provider files by last modified time
std::sort(recentFilePaths.begin(), recentFilePaths.end(), [](const auto &a, const auto &b) { std::sort(recentFilePaths.begin(), recentFilePaths.end(), [](const auto &a, const auto &b) {
return std::fs::last_write_time(a) > std::fs::last_write_time(b); return std::fs::last_write_time(a) > std::fs::last_write_time(b);
});
std::unordered_set<RecentProvider, RecentProvider::HashFunction> uniqueProviders;
for (u32 i = 0; i < recentFilePaths.size() && uniqueProviders.size() < 5; i++) {
auto &path = recentFilePaths[i];
try {
auto jsonData = nlohmann::json::parse(fs::File(path, fs::File::Mode::Read).readString());
uniqueProviders.insert(RecentProvider {
.displayName = jsonData["displayName"],
.type = jsonData["type"],
.filePath = path,
.data = jsonData
});
} catch (...) { }
}
std::copy(uniqueProviders.begin(), uniqueProviders.end(), std::front_inserter(s_recentProviders));
}); });
std::unordered_set<RecentProvider, RecentProvider::HashFunction> uniqueProviders;
for (u32 i = 0; i < recentFilePaths.size() && uniqueProviders.size() < 5; i++) {
auto &path = recentFilePaths[i];
try {
auto jsonData = nlohmann::json::parse(fs::File(path, fs::File::Mode::Read).readString());
uniqueProviders.insert(RecentProvider {
.displayName = jsonData["displayName"],
.type = jsonData["type"],
.filePath = path,
.data = jsonData
});
} catch (...) { }
}
std::copy(uniqueProviders.begin(), uniqueProviders.end(), std::front_inserter(s_recentProviders));
} }
static void loadRecentProvider(const RecentProvider &recentProvider) { static void loadRecentProvider(const RecentProvider &recentProvider) {
@ -227,13 +236,15 @@ namespace hex::plugin::builtin {
ImGui::UnderlinedText("hex.builtin.welcome.start.recent"_lang); ImGui::UnderlinedText("hex.builtin.welcome.start.recent"_lang);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5_scaled); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5_scaled);
{ {
for (const auto &recentProvider : s_recentProviders) { if (!s_recentProvidersUpdating) {
ImGui::PushID(&recentProvider); for (const auto &recentProvider : s_recentProviders) {
ON_SCOPE_EXIT { ImGui::PopID(); }; ImGui::PushID(&recentProvider);
ON_SCOPE_EXIT { ImGui::PopID(); };
if (ImGui::BulletHyperlink(recentProvider.displayName.c_str())) { if (ImGui::BulletHyperlink(recentProvider.displayName.c_str())) {
loadRecentProvider(recentProvider); loadRecentProvider(recentProvider);
break; break;
}
} }
} }
} }
@ -500,7 +511,7 @@ namespace hex::plugin::builtin {
}); });
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1075, [&] { ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1075, [&] {
if (ImGui::BeginMenu("hex.builtin.menu.file.open_recent"_lang, !s_recentProviders.empty())) { if (ImGui::BeginMenu("hex.builtin.menu.file.open_recent"_lang, !s_recentProvidersUpdating && !s_recentProviders.empty())) {
// Copy to avoid changing list while iteration // Copy to avoid changing list while iteration
auto recentProviders = s_recentProviders; auto recentProviders = s_recentProviders;
for (auto &recentProvider : recentProviders) { for (auto &recentProvider : recentProviders) {