1
0
mirror of synced 2025-02-08 22:59:41 +01:00

async texture loading

This commit is contained in:
Stepland 2020-02-08 15:25:49 +01:00
parent 2391ab7c13
commit 41c638c050
5 changed files with 78 additions and 22 deletions

View File

@ -2,7 +2,7 @@
## Music Select Screen ## Music Select Screen
### TODO ### TODO
- Song Panel draw - Song Panel draw
- async song cover loading
- Song Panel click - Song Panel click
- Chart Panel - Chart Panel
- Visible controls - Visible controls
@ -12,6 +12,8 @@
### Done ### Done
- Category Panel click - Category Panel click
- Song Panel draw
- async song cover loading
## Gameplay Screen ## Gameplay Screen
@ -25,6 +27,9 @@
# v1.1.0 # v1.1.0
## FB9 Support ## FB9 Support
## Music Select Screen
- bound memory usage of Textures::Autoloader
# v1.2.0 # v1.2.0
## P4IO Support ## P4IO Support

View File

@ -10,6 +10,8 @@ foreach module : ['system', 'window', 'graphics', 'audio']
sfml += [dependency('sfml-'+module, version : '>=2.5.1')] sfml += [dependency('sfml-'+module, version : '>=2.5.1')]
endforeach endforeach
thread_dep = dependency('threads')
add_project_link_arguments(['-lstdc++', '-lstdc++fs', '-lm', '-lGL'], language : 'cpp') add_project_link_arguments(['-lstdc++', '-lstdc++fs', '-lm', '-lGL'], language : 'cpp')
sources = [ sources = [
@ -53,7 +55,7 @@ sources = [
executable( executable(
'jujube', 'jujube',
sources, sources,
dependencies: [sfml], dependencies: [sfml, thread_dep],
include_directories : include_directories('include', 'include/imgui', 'include/imgui-sfml'), include_directories : include_directories('include', 'include/imgui', 'include/imgui-sfml'),
cpp_args : [ cpp_args : [
'-Wall', '-Wall',

View File

@ -1,27 +1,66 @@
#include "Autoloader.hpp" #include "Autoloader.hpp"
#include <chrono> #include <chrono>
#include <iostream>
#include <mutex>
#include <thread> #include <thread>
void Textures::Autoloader::load(const fs::path& t_path) { void Textures::Autoloader::load(const fs::path& path) {
if (m_mapping.find(t_path) != m_mapping.end()) { if (has(path) or is_loading(path)) {
return; return;
} }
{
std::unique_lock lock{m_is_loading_mutex};
m_is_loading.insert(path);
}
auto texture = std::make_shared<sf::Texture>(); auto texture = std::make_shared<sf::Texture>();
if (!texture->loadFromFile(t_path)) { if (!texture->loadFromFile(path)) {
throw std::invalid_argument("Unable to load cover image : "+t_path.string()); throw std::invalid_argument("Unable to load cover image : "+path.string());
} }
texture->setSmooth(true); texture->setSmooth(true);
m_mapping[t_path] = texture; std::this_thread::sleep_for(std::chrono::seconds(2));
} {
std::unique_lock<std::shared_mutex> lock_mapping{m_mapping_mutex, std::defer_lock};
std::shared_ptr<sf::Texture> Textures::Autoloader::get(const fs::path& t_path) { std::unique_lock<std::shared_mutex> lock_is_loading{m_is_loading_mutex, std::defer_lock};
if (m_mapping.find(t_path) == m_mapping.end()) { std::lock(lock_mapping, lock_is_loading);
this->load(t_path); 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) { void Textures::Autoloader::async_load(const fs::path& path) {
return m_mapping.find(t_path) != m_mapping.end(); std::thread t(&Textures::Autoloader::load, this, path);
t.detach();
}
std::optional<std::shared_ptr<sf::Texture>> 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<std::shared_ptr<sf::Texture>> 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();
} }

View File

@ -3,8 +3,10 @@
#include <filesystem> #include <filesystem>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <shared_mutex>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <SFML/Graphics.hpp> #include <SFML/Graphics.hpp>
@ -21,14 +23,20 @@ namespace std {
} }
namespace Textures { 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 { class Autoloader {
public: public:
Autoloader() = default; Autoloader() = default;
std::shared_ptr<sf::Texture> get(const fs::path& t_path); std::optional<std::shared_ptr<sf::Texture>> async_get(const fs::path& path);
void load(const fs::path& t_path); std::optional<std::shared_ptr<sf::Texture>> get(const fs::path& path);
bool has(const fs::path& t_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: private:
std::unordered_map<fs::path, std::shared_ptr<sf::Texture>> m_mapping; std::unordered_map<fs::path, std::shared_ptr<sf::Texture>> m_mapping;
std::shared_mutex m_mapping_mutex;
std::unordered_set<fs::path> m_is_loading;
std::shared_mutex m_is_loading_mutex;
}; };
} }

View File

@ -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) { void MusicSelect::SongPanel::draw(Resources& resources, sf::RenderTarget& target, sf::FloatRect area) {
sf::Sprite cover; sf::Sprite cover;
sf::Texture& cover_texture = resources.fallback_cover; cover.setTexture(resources.fallback_cover);
if (m_song.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(); auto bounds = cover.getGlobalBounds();
cover.setPosition(area.left, area.top); cover.setPosition(area.left, area.top);
cover.setScale(area.width / bounds.width, area.width / bounds.width); cover.setScale(area.width / bounds.width, area.width / bounds.width);