async texture loading
This commit is contained in:
parent
2391ab7c13
commit
41c638c050
7
TODO.md
7
TODO.md
@ -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
|
||||||
|
@ -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',
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user