From 7b61268f223f6dbda67114d143f73d1ef87bd1a3 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Mon, 19 Sep 2022 16:09:22 +0200 Subject: [PATCH] ux: Reduce file loading time to basically zero --- lib/libimhex/include/hex/api/task.hpp | 5 +- lib/libimhex/source/api/task.cpp | 21 +++- main/include/window.hpp | 1 + main/source/window/window.cpp | 20 ++-- .../content/views/view_pattern_data.cpp | 23 ++-- .../content/views/view_pattern_editor.cpp | 100 +++++++++--------- .../builtin/source/content/welcome_screen.cpp | 79 ++++++++------ 7 files changed, 143 insertions(+), 106 deletions(-) diff --git a/lib/libimhex/include/hex/api/task.hpp b/lib/libimhex/include/hex/api/task.hpp index 06dbc1a10..81d33024c 100644 --- a/lib/libimhex/include/hex/api/task.hpp +++ b/lib/libimhex/include/hex/api/task.hpp @@ -19,7 +19,7 @@ namespace hex { class Task { public: Task() = default; - Task(std::string unlocalizedName, u64 maxValue, std::function function); + Task(std::string unlocalizedName, u64 maxValue, bool background, std::function function); Task(const Task&) = delete; Task(Task &&other) noexcept; @@ -28,6 +28,7 @@ namespace hex { void update(u64 value = 0); void setMaxValue(u64 value); + [[nodiscard]] bool isBackgroundTask() const; [[nodiscard]] bool isFinished() const; [[nodiscard]] bool hadException() const; [[nodiscard]] bool wasInterrupted() const; @@ -56,6 +57,7 @@ namespace hex { std::function m_interruptCallback; bool m_shouldInterrupt = false; + bool m_background = true; bool m_interrupted = false; bool m_finished = false; @@ -89,6 +91,7 @@ namespace hex { constexpr static auto NoProgress = 0; static TaskHolder createTask(std::string name, u64 maxValue, std::function function); + static TaskHolder createBackgroundTask(std::string name, std::function function); static void collectGarbage(); static size_t getRunningTaskCount(); diff --git a/lib/libimhex/source/api/task.cpp b/lib/libimhex/source/api/task.cpp index c2c32128e..41b7aa55a 100644 --- a/lib/libimhex/source/api/task.cpp +++ b/lib/libimhex/source/api/task.cpp @@ -9,11 +9,11 @@ namespace hex { std::mutex TaskManager::s_deferredCallsMutex; - std::list> TaskManager::s_tasks; + std::list> TaskManager::s_tasks, s_backgroundTasks; std::list> TaskManager::s_deferredCalls; - Task::Task(std::string unlocalizedName, u64 maxValue, std::function function) - : m_unlocalizedName(std::move(unlocalizedName)), m_currValue(0), m_maxValue(maxValue) { + Task::Task(std::string unlocalizedName, u64 maxValue, bool background, std::function function) + : m_unlocalizedName(std::move(unlocalizedName)), m_currValue(0), m_maxValue(maxValue), m_background(background) { this->m_thread = std::thread([this, func = std::move(function)] { try { func(*this); @@ -83,6 +83,12 @@ namespace hex { 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 { std::scoped_lock lock(this->m_mutex); @@ -164,13 +170,20 @@ namespace hex { TaskHolder TaskManager::createTask(std::string name, u64 maxValue, std::function function) { - s_tasks.emplace_back(std::make_shared(std::move(name), maxValue, std::move(function))); + s_tasks.emplace_back(std::make_shared(std::move(name), maxValue, false, std::move(function))); return TaskHolder(s_tasks.back()); } + TaskHolder TaskManager::createBackgroundTask(std::string name, std::function function) { + s_backgroundTasks.emplace_back(std::make_shared(std::move(name), 0, true, std::move(function))); + + return TaskHolder(s_backgroundTasks.back()); + } + void TaskManager::collectGarbage() { 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> &TaskManager::getRunningTasks() { diff --git a/main/include/window.hpp b/main/include/window.hpp index 51395ad45..6959c5fb4 100644 --- a/main/include/window.hpp +++ b/main/include/window.hpp @@ -52,6 +52,7 @@ namespace hex { ImGui::Texture m_logoTexture = { nullptr }; + std::mutex m_popupMutex; std::list m_popupsToOpen; std::vector m_pressedKeys; diff --git a/main/source/window/window.cpp b/main/source/window/window.cpp index 6d1578b16..4aa597408 100644 --- a/main/source/window/window.cpp +++ b/main/source/window/window.cpp @@ -144,6 +144,8 @@ namespace hex { }); EventManager::subscribe(this, [this](auto name) { + std::scoped_lock lock(this->m_popupMutex); + this->m_popupsToOpen.push_back(name); }); @@ -424,15 +426,17 @@ namespace hex { 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) { - if (ImGui::IsPopupOpen(name.c_str())) - return true; - else - ImGui::OpenPopup(name.c_str()); - - return false; - }); + return false; + }); + } TaskManager::runDeferredCalls(); diff --git a/plugins/builtin/source/content/views/view_pattern_data.cpp b/plugins/builtin/source/content/views/view_pattern_data.cpp index a0cc1d33e..5e0f691a9 100644 --- a/plugins/builtin/source/content/views/view_pattern_data.cpp +++ b/plugins/builtin/source/content/views/view_pattern_data.cpp @@ -106,19 +106,22 @@ namespace hex::plugin::builtin { void ViewPatternData::drawContent() { 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() && provider->isReadable() && !ProviderExtraData::get(provider).patternLanguage.runtime->isRunning()) { + if (ImHexApi::Provider::isValid()) { + auto provider = ImHexApi::Provider::get(); + auto &runtime = ProviderExtraData::get(provider).patternLanguage.runtime; - auto &sortedPatterns = this->m_sortedPatterns[ImHexApi::Provider::get()]; - if (beginPatternTable(provider, ProviderExtraData::get(provider).patternLanguage.runtime->getAllPatterns(), sortedPatterns)) { - ImGui::TableHeadersRow(); - if (!sortedPatterns.empty()) { + if (provider->isReadable() && runtime != nullptr && !runtime->isRunning()) { + auto &sortedPatterns = this->m_sortedPatterns[ImHexApi::Provider::get()]; + if (beginPatternTable(provider, ProviderExtraData::get(provider).patternLanguage.runtime->getAllPatterns(), sortedPatterns)) { + ImGui::TableHeadersRow(); - for (auto &patterns : sortedPatterns) - patterns->accept(this->m_patternDrawer); + if (!sortedPatterns.empty()) { + for (auto &patterns : sortedPatterns) + patterns->accept(this->m_patternDrawer); + } + + ImGui::EndTable(); } - - ImGui::EndTable(); } } } diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index c1b61e559..b96e7dbfc 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -114,58 +114,60 @@ namespace hex::plugin::builtin { patternLanguageData.runtime = std::make_unique(); ContentRegistry::PatternLanguage::configureRuntime(*patternLanguageData.runtime, provider); - if (!this->m_autoLoadPatterns) - return; + TaskManager::createBackgroundTask("Analyzing file content", [this, provider, &data = patternLanguageData](auto &) { + if (!this->m_autoLoadPatterns) + return; - // Copy over current pattern source code to the new provider - if (!this->m_syncPatternSourceCode) { - patternLanguageData.sourceCode = this->m_textEditor.GetText(); - } - - auto &runtime = patternLanguageData.runtime; - - auto mimeType = magic::getMIMEType(provider); - - bool foundCorrectType = false; - runtime->addPragma("MIME", [&mimeType, &foundCorrectType](pl::PatternLanguage &runtime, const std::string &value) { - hex::unused(runtime); - - if (value == mimeType) { - foundCorrectType = true; - return true; + // Copy over current pattern source code to the new provider + if (!this->m_syncPatternSourceCode) { + data.sourceCode = this->m_textEditor.GetText(); + } + + auto &runtime = data.runtime; + + auto mimeType = magic::getMIMEType(provider); + + bool foundCorrectType = false; + runtime->addPragma("MIME", [&mimeType, &foundCorrectType](pl::PatternLanguage &runtime, const std::string &value) { + hex::unused(runtime); + + if (value == mimeType) { + foundCorrectType = 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("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("hex.builtin.view.pattern_editor.accept_pattern"_lang); - this->m_acceptPatternWindowOpen = true; - } }); EventManager::subscribe(this, [this](prv::Provider *oldProvider, prv::Provider *newProvider) { diff --git a/plugins/builtin/source/content/welcome_screen.cpp b/plugins/builtin/source/content/welcome_screen.cpp index f7101817d..8eb45e711 100644 --- a/plugins/builtin/source/content/welcome_screen.cpp +++ b/plugins/builtin/source/content/welcome_screen.cpp @@ -52,40 +52,49 @@ namespace hex::plugin::builtin { }; + static std::atomic s_recentProvidersUpdating = false; static std::list s_recentProviders; static void updateRecentProviders() { - s_recentProviders.clear(); + TaskManager::createBackgroundTask("Updating recent files", [](auto&){ + if (s_recentProvidersUpdating) + return; - // Query all recent providers - std::vector 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()); + s_recentProvidersUpdating = true; + ON_SCOPE_EXIT { s_recentProvidersUpdating = false; }; + + s_recentProviders.clear(); + + // Query all recent providers + std::vector 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 - 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); + // Sort recent provider files by last modified time + 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); + }); + + std::unordered_set 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 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) { @@ -227,13 +236,15 @@ namespace hex::plugin::builtin { ImGui::UnderlinedText("hex.builtin.welcome.start.recent"_lang); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5_scaled); { - for (const auto &recentProvider : s_recentProviders) { - ImGui::PushID(&recentProvider); - ON_SCOPE_EXIT { ImGui::PopID(); }; + if (!s_recentProvidersUpdating) { + for (const auto &recentProvider : s_recentProviders) { + ImGui::PushID(&recentProvider); + ON_SCOPE_EXIT { ImGui::PopID(); }; - if (ImGui::BulletHyperlink(recentProvider.displayName.c_str())) { - loadRecentProvider(recentProvider); - break; + if (ImGui::BulletHyperlink(recentProvider.displayName.c_str())) { + loadRecentProvider(recentProvider); + break; + } } } } @@ -500,7 +511,7 @@ namespace hex::plugin::builtin { }); 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 auto recentProviders = s_recentProviders; for (auto &recentProvider : recentProviders) {