Merge pull request #7494 from wesnoth/image-cache-cleanup

Overhaul image cache to be hash-based rather than index-based
This commit is contained in:
Charles Dang 2023-03-30 01:01:41 -04:00 committed by GitHub
commit e4c050f92c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 65 additions and 117 deletions

View File

@ -53,29 +53,8 @@ static lg::log_domain log_config("config");
using game_config::tile_size;
template<typename T>
struct cache_item
{
cache_item()
: item()
, loaded(false)
{
}
cache_item(const T& item)
: item(item)
, loaded(true)
{
}
T item;
bool loaded;
};
namespace std
{
template<>
struct hash<image::locator::value>
struct std::hash<image::locator::value>
{
std::size_t operator()(const image::locator::value& val) const
{
@ -96,24 +75,32 @@ struct hash<image::locator::value>
return hash;
}
};
}
namespace image
{
template<typename T>
class cache_type
{
public:
cache_type()
: content_()
{
}
using Key = locator::value;
cache_item<T>& get_element(int index)
public:
struct cache_item
{
if(static_cast<unsigned>(index) >= content_.size())
content_.resize(index + 1);
return content_[index];
T item {};
bool loaded = false;
void populate(T&& value)
{
item = value;
loaded = true;
}
};
cache_type() = default;
cache_item& get_element(const Key& item)
{
return content_[item];
}
void flush()
@ -122,41 +109,47 @@ public:
}
private:
std::vector<cache_item<T>> content_;
std::unordered_map<Key, cache_item> content_;
};
std::size_t locator::hash() const
{
return std::hash<value>{}(val_);
}
template<typename T>
bool locator::in_cache(cache_type<T>& cache) const
{
return index_ < 0 ? false : cache.get_element(index_).loaded;
return cache.get_element(val_).loaded;
}
template<typename T>
const T& locator::locate_in_cache(cache_type<T>& cache) const
{
static T dummy;
return index_ < 0 ? dummy : cache.get_element(index_).item;
return cache.get_element(val_).item;
}
template<typename T>
T& locator::access_in_cache(cache_type<T>& cache) const
{
static T dummy;
return index_ < 0 ? dummy : cache.get_element(index_).item;
return cache.get_element(val_).item;
}
template<typename T>
void locator::add_to_cache(cache_type<T>& cache, const T& data) const
std::optional<T> locator::copy_from_cache(cache_type<T>& cache) const
{
if(index_ >= 0) {
cache.get_element(index_) = cache_item<T>(data);
}
const auto& elem = cache.get_element(val_);
return elem.loaded ? std::make_optional(elem.item) : std::nullopt;
}
template<typename T>
void locator::add_to_cache(cache_type<T>& cache, T data) const
{
cache.get_element(val_).populate(std::move(data));
}
namespace
{
image::locator::locator_finder_t locator_finder;
/** Definition of all image maps */
std::array<surface_cache, NUM_TYPES> surfaces_;
@ -229,8 +222,6 @@ mini_terrain_cache_map mini_terrain_cache;
mini_terrain_cache_map mini_fogged_terrain_cache;
mini_terrain_cache_map mini_highlighted_terrain_cache;
static int last_index_ = 0;
void flush_cache()
{
for(surface_cache& cache : surfaces_) {
@ -250,22 +241,6 @@ void flush_cache()
mini_highlighted_terrain_cache.clear();
image_existence_map.clear();
precached_dirs.clear();
/* We can't reset last_index_, since some locators are still alive
when using :refresh. That would cause them to point to the wrong
images. Not resetting the variable causes a memory leak, though. */
// last_index_ = 0;
}
void locator::init_index()
{
auto i = locator_finder.find(val_);
if(i == locator_finder.end()) {
index_ = last_index_++;
locator_finder.emplace(val_, index_);
} else {
index_ = i->second;
}
}
void locator::parse_arguments()
@ -297,52 +272,39 @@ void locator::parse_arguments()
}
locator::locator()
: index_(-1)
, val_()
: val_()
{
}
locator::locator(const locator& a, const std::string& mods)
: index_(-1)
, val_(a.val_)
: val_(a.val_)
{
if(!mods.empty()) {
val_.modifications_ += mods;
val_.type_ = SUB_FILE;
init_index();
} else {
index_ = a.index_;
}
}
locator::locator(const char* filename)
: index_(-1)
, val_(filename)
: val_(filename)
{
parse_arguments();
init_index();
}
locator::locator(const std::string& filename)
: index_(-1)
, val_(filename)
: val_(filename)
{
parse_arguments();
init_index();
}
locator::locator(const std::string& filename, const std::string& modifications)
: index_(-1)
, val_(filename, modifications)
: val_(filename, modifications)
{
init_index();
}
locator::locator(const char* filename, const char* modifications)
: index_(-1)
, val_(filename, modifications)
: val_(filename, modifications)
{
init_index();
}
locator::locator(const std::string& filename,
@ -350,17 +312,13 @@ locator::locator(const std::string& filename,
int center_x,
int center_y,
const std::string& modifications)
: index_(-1)
, val_(filename, loc, center_x, center_y, modifications)
: val_(filename, loc, center_x, center_y, modifications)
{
init_index();
}
locator& locator::operator=(const locator& a)
{
index_ = a.index_;
val_ = a.val_;
return *this;
}
@ -453,8 +411,8 @@ bool locator::value::operator==(const value& a) const
} else if(type_ == FILE) {
return filename_ == a.filename_;
} else if(type_ == SUB_FILE) {
return filename_ == a.filename_ && loc_ == a.loc_ && modifications_ == a.modifications_
&& center_x_ == a.center_x_ && center_y_ == a.center_y_;
return std::tie(filename_, loc_, modifications_, center_x_, center_y_) ==
std::tie(a.filename_, a.loc_, a.modifications_, a.center_x_, a.center_y_);
}
return false;
@ -467,15 +425,8 @@ bool locator::value::operator<(const value& a) const
} else if(type_ == FILE) {
return filename_ < a.filename_;
} else if(type_ == SUB_FILE) {
if(filename_ != a.filename_)
return filename_ < a.filename_;
if(loc_ != a.loc_)
return loc_ < a.loc_;
if(center_x_ != a.center_x_)
return center_x_ < a.center_x_;
if(center_y_ != a.center_y_)
return center_y_ < a.center_y_;
return (modifications_ < a.modifications_);
return std::tie(filename_, loc_, modifications_, center_x_, center_y_) <
std::tie(a.filename_, a.loc_, a.modifications_, a.center_x_, a.center_y_);
}
return false;
@ -842,8 +793,8 @@ surface get_surface(
surface_cache& imap = surfaces_[type];
// return the image if already cached
if (i_locator.in_cache(imap)) {
return i_locator.locate_in_cache(imap);
if(auto cached_item = i_locator.copy_from_cache(imap)) {
return *cached_item;
}
DBG_IMG << "surface cache [" << type << "] miss: " << i_locator;
@ -1156,11 +1107,8 @@ texture get_texture(const image::locator& i_locator, scale_quality quality, TYPE
//
// Now attempt to find a cached texture. If found, return it.
//
bool in_cache = i_locator.in_cache(*cache);
if(in_cache) {
res = i_locator.locate_in_cache(*cache);
return res;
if(auto cached_item = i_locator.copy_from_cache(*cache)) {
return *cached_item;
}
DBG_IMG << "texture cache [" << type << "] miss: " << i_locator;

View File

@ -18,6 +18,7 @@
#include "map/location.hpp"
#include "terrain/translation.hpp"
#include <optional>
#include <unordered_map>
class surface;
@ -74,9 +75,8 @@ public:
locator& operator=(const locator& a);
bool operator==(const locator& a) const { return index_ == a.index_; }
bool operator!=(const locator& a) const { return index_ != a.index_; }
bool operator<(const locator& a) const { return index_ < a.index_; }
bool operator==(const locator& a) const { return val_ == a.val_; }
bool operator!=(const locator& a) const { return !operator==(a); }
const std::string& get_filename() const { return val_.filename_; }
bool is_data_uri() const { return val_.is_data_uri_; }
@ -85,7 +85,6 @@ public:
int get_center_y() const { return val_.center_y_; }
const std::string& get_modifications() const {return val_.modifications_;}
type get_type() const { return val_.type_; }
// const int get_index() const { return index_; };
/**
* Returns @a true if the locator does not correspond to an actual image.
@ -115,12 +114,12 @@ public:
const T& locate_in_cache(cache_type<T>& cache) const;
template<typename T>
void add_to_cache(cache_type<T>& cache, const T& data) const;
std::optional<T> copy_from_cache(cache_type<T>& cache) const;
template<typename T>
void add_to_cache(cache_type<T>& cache, T data) const;
private:
// Called by each constructor after actual construction to
// initialize the index_ field
void init_index();
void parse_arguments();
struct value
@ -144,14 +143,15 @@ private:
int center_y_;
};
public:
typedef std::unordered_map<value, int> locator_finder_t;
value val_;
private:
public:
friend struct std::hash<value>;
int index_;
value val_;
template<typename T>
friend class cache_type;
std::size_t hash() const;
};
// write a readable representation of a locator, mostly for debugging