From 41c638c0507263890797f2688bcee514ed79a1e8 Mon Sep 17 00:00:00 2001 From: Stepland <16676308+Stepland@users.noreply.github.com> Date: Sat, 8 Feb 2020 15:25:49 +0100 Subject: [PATCH] async texture loading --- TODO.md | 7 +++- meson.build | 4 +- src/Resources/Autoloader.cpp | 65 ++++++++++++++++++++++++------- src/Resources/Autoloader.hpp | 16 ++++++-- src/Screens/MusicSelect/Panel.cpp | 8 ++-- 5 files changed, 78 insertions(+), 22 deletions(-) diff --git a/TODO.md b/TODO.md index c240b1f..5f3f8ee 100644 --- a/TODO.md +++ b/TODO.md @@ -2,7 +2,7 @@ ## Music Select Screen ### TODO - Song Panel draw - - async song cover loading + - Song Panel click - Chart Panel - Visible controls @@ -12,6 +12,8 @@ ### Done - Category Panel click +- Song Panel draw + - async song cover loading ## Gameplay Screen @@ -25,6 +27,9 @@ # v1.1.0 ## FB9 Support +## Music Select Screen +- bound memory usage of Textures::Autoloader + # v1.2.0 ## P4IO Support diff --git a/meson.build b/meson.build index 728efe7..d02144b 100644 --- a/meson.build +++ b/meson.build @@ -10,6 +10,8 @@ foreach module : ['system', 'window', 'graphics', 'audio'] sfml += [dependency('sfml-'+module, version : '>=2.5.1')] endforeach +thread_dep = dependency('threads') + add_project_link_arguments(['-lstdc++', '-lstdc++fs', '-lm', '-lGL'], language : 'cpp') sources = [ @@ -53,7 +55,7 @@ sources = [ executable( 'jujube', sources, - dependencies: [sfml], + dependencies: [sfml, thread_dep], include_directories : include_directories('include', 'include/imgui', 'include/imgui-sfml'), cpp_args : [ '-Wall', diff --git a/src/Resources/Autoloader.cpp b/src/Resources/Autoloader.cpp index dd8dc25..6a00a5f 100644 --- a/src/Resources/Autoloader.cpp +++ b/src/Resources/Autoloader.cpp @@ -1,27 +1,66 @@ #include "Autoloader.hpp" #include +#include +#include #include -void Textures::Autoloader::load(const fs::path& t_path) { - if (m_mapping.find(t_path) != m_mapping.end()) { +void Textures::Autoloader::load(const fs::path& path) { + if (has(path) or is_loading(path)) { return; } + { + std::unique_lock lock{m_is_loading_mutex}; + m_is_loading.insert(path); + } auto texture = std::make_shared(); - if (!texture->loadFromFile(t_path)) { - throw std::invalid_argument("Unable to load cover image : "+t_path.string()); + if (!texture->loadFromFile(path)) { + throw std::invalid_argument("Unable to load cover image : "+path.string()); } texture->setSmooth(true); - m_mapping[t_path] = texture; -} - -std::shared_ptr Textures::Autoloader::get(const fs::path& t_path) { - if (m_mapping.find(t_path) == m_mapping.end()) { - this->load(t_path); + std::this_thread::sleep_for(std::chrono::seconds(2)); + { + std::unique_lock lock_mapping{m_mapping_mutex, std::defer_lock}; + std::unique_lock lock_is_loading{m_is_loading_mutex, std::defer_lock}; + std::lock(lock_mapping, lock_is_loading); + m_mapping.emplace(path, texture); + m_is_loading.erase(path); } - return m_mapping.at(t_path); + std::cout << "Loaded " << path << std::endl; } -bool Textures::Autoloader::has(const fs::path& t_path) { - return m_mapping.find(t_path) != m_mapping.end(); +void Textures::Autoloader::async_load(const fs::path& path) { + std::thread t(&Textures::Autoloader::load, this, path); + t.detach(); +} + +std::optional> Textures::Autoloader::async_get(const fs::path& path) { + if (not has(path)) { + if (not is_loading(path)) { + async_load(path); + } + return {}; + } else { + return get(path); + } +} + +std::optional> Textures::Autoloader::get(const fs::path& path) { + std::shared_lock lock{m_mapping_mutex}; + if (has(path)) { + return m_mapping.at(path); + } else { + return {}; + } +} + + +bool Textures::Autoloader::has(const fs::path& path) { + std::shared_lock lock{m_mapping_mutex}; + return m_mapping.find(path) != m_mapping.end(); +} + +bool Textures::Autoloader::is_loading(const fs::path& path) { + std::shared_lock lock{m_is_loading_mutex}; + return m_is_loading.find(path) != m_is_loading.end(); } diff --git a/src/Resources/Autoloader.hpp b/src/Resources/Autoloader.hpp index 5966fde..8415d4c 100644 --- a/src/Resources/Autoloader.hpp +++ b/src/Resources/Autoloader.hpp @@ -3,8 +3,10 @@ #include #include #include +#include #include #include +#include #include @@ -21,14 +23,20 @@ namespace std { } namespace Textures { - // Loads textures on demand and stores them in a map for easy path-based access + // Loads textures asynchronously (in the background) on demand and stores them in a map for easy path-based access class Autoloader { public: Autoloader() = default; - std::shared_ptr get(const fs::path& t_path); - void load(const fs::path& t_path); - bool has(const fs::path& t_path); + std::optional> async_get(const fs::path& path); + std::optional> get(const fs::path& path); + void load(const fs::path& path); + void async_load(const fs::path& path); + bool has(const fs::path& path); + bool is_loading(const fs::path& path); private: std::unordered_map> m_mapping; + std::shared_mutex m_mapping_mutex; + std::unordered_set m_is_loading; + std::shared_mutex m_is_loading_mutex; }; } \ No newline at end of file diff --git a/src/Screens/MusicSelect/Panel.cpp b/src/Screens/MusicSelect/Panel.cpp index cc27b06..2c557c9 100644 --- a/src/Screens/MusicSelect/Panel.cpp +++ b/src/Screens/MusicSelect/Panel.cpp @@ -68,11 +68,13 @@ void MusicSelect::SongPanel::click(Ribbon& ribbon, std::size_t from_button_index void MusicSelect::SongPanel::draw(Resources& resources, sf::RenderTarget& target, sf::FloatRect area) { sf::Sprite cover; - sf::Texture& cover_texture = resources.fallback_cover; + cover.setTexture(resources.fallback_cover); if (m_song.cover) { - cover_texture = *resources.covers.get(m_song.folder/m_song.cover.value()); + auto loaded_texture = resources.covers.async_get(m_song.folder/m_song.cover.value()); + if (loaded_texture) { + cover.setTexture(**loaded_texture, true); + } } - cover.setTexture(cover_texture); auto bounds = cover.getGlobalBounds(); cover.setPosition(area.left, area.top); cover.setScale(area.width / bounds.width, area.width / bounds.width);