1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-08-11 17:51:04 +00:00

Add caching of generated textures as image

This commit is contained in:
sfan5 2025-04-20 14:27:14 +02:00
parent 9cb78f2dc5
commit 486fb7cc4d
3 changed files with 86 additions and 20 deletions

View file

@ -24,6 +24,13 @@ struct TextureInfo
std::set<std::string> sourceImages{};
};
// Stores internal information about a texture image.
struct ImageInfo
{
video::IImage *image = nullptr;
std::set<std::string> sourceImages;
};
// TextureSource
class TextureSource final : public IWritableTextureSource
{
@ -123,7 +130,13 @@ public:
video::SColor getTextureAverageColor(const std::string &name);
void setImageCaching(bool enabled);
private:
// Gets or generates an image for a texture string
// Caller needs to drop the returned image
video::IImage *getOrGenerateImage(const std::string &name,
std::set<std::string> &source_image_names);
// The id of the thread that is allowed to use irrlicht directly
std::thread::id m_main_thread;
@ -132,6 +145,12 @@ private:
// This should be only accessed from the main thread
ImageSource m_imagesource;
// Is the image cache enabled?
bool m_image_cache_enabled = false;
// Caches finished texture images before they are uploaded to the GPU
// (main thread use only)
std::unordered_map<std::string, ImageInfo> m_image_cache;
// Rebuild images and textures from the current set of source images
// Shall be called from the main thread.
// You ARE expected to be holding m_textureinfo_cache_mutex
@ -193,15 +212,18 @@ TextureSource::~TextureSource()
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
u32 textures_before = driver->getTextureCount();
for (const auto &it : m_image_cache) {
assert(it.second.image);
it.second.image->drop();
}
for (const auto &iter : m_textureinfo_cache) {
// cleanup texture
if (iter.texture)
driver->removeTexture(iter.texture);
}
m_textureinfo_cache.clear();
for (auto t : m_texture_trash) {
// cleanup trashed texture
driver->removeTexture(t);
}
@ -209,6 +231,26 @@ TextureSource::~TextureSource()
<< " after: " << driver->getTextureCount() << std::endl;
}
video::IImage *TextureSource::getOrGenerateImage(const std::string &name,
std::set<std::string> &source_image_names)
{
auto it = m_image_cache.find(name);
if (it != m_image_cache.end()) {
source_image_names = it->second.sourceImages;
it->second.image->grab();
return it->second.image;
}
std::set<std::string> tmp;
auto *img = m_imagesource.generateImage(name, tmp);
if (img && m_image_cache_enabled) {
img->grab();
m_image_cache[name] = {img, tmp};
}
source_image_names = std::move(tmp);
return img;
}
u32 TextureSource::getTextureId(const std::string &name)
{
{ // See if texture already exists
@ -280,7 +322,7 @@ u32 TextureSource::generateTexture(const std::string &name)
// passed into texture info for dynamic media tracking
std::set<std::string> source_image_names;
video::IImage *img = m_imagesource.generateImage(name, source_image_names);
video::IImage *img = getOrGenerateImage(name, source_image_names);
video::ITexture *tex = nullptr;
@ -356,7 +398,7 @@ Palette* TextureSource::getPalette(const std::string &name)
if (it == m_palettes.end()) {
// Create palette
std::set<std::string> source_image_names; // unused, sadly.
video::IImage *img = m_imagesource.generateImage(name, source_image_names);
video::IImage *img = getOrGenerateImage(name, source_image_names);
if (!img) {
warningstream << "TextureSource::getPalette(): palette \"" << name
<< "\" could not be loaded." << std::endl;
@ -458,6 +500,8 @@ void TextureSource::rebuildImagesAndTextures()
infostream << "TextureSource: recreating " << m_textureinfo_cache.size()
<< " textures" << std::endl;
assert(!m_image_cache_enabled || m_image_cache.empty());
// Recreate textures
for (TextureInfo &ti : m_textureinfo_cache) {
if (ti.name.empty())
@ -474,7 +518,7 @@ void TextureSource::rebuildTexture(video::IVideoDriver *driver, TextureInfo &ti)
sanity_check(std::this_thread::get_id() == m_main_thread);
std::set<std::string> source_image_names;
video::IImage *img = m_imagesource.generateImage(ti.name, source_image_names);
video::IImage *img = getOrGenerateImage(ti.name, source_image_names);
// Create texture from resulting image
video::ITexture *t = nullptr, *t_old = ti.texture;
@ -510,14 +554,10 @@ void TextureSource::rebuildTexture(video::IVideoDriver *driver, TextureInfo &ti)
video::SColor TextureSource::getTextureAverageColor(const std::string &name)
{
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
video::ITexture *texture = getTexture(name);
if (!texture)
return {0, 0, 0, 0};
// Note: this downloads the texture back from the GPU, which is pointless
video::IImage *image = driver->createImage(texture,
core::position2d<s32>(0, 0),
texture->getOriginalSize());
assert(std::this_thread::get_id() == m_main_thread);
std::set<std::string> unused;
auto *image = getOrGenerateImage(name, unused);
if (!image)
return {0, 0, 0, 0};
@ -526,3 +566,15 @@ video::SColor TextureSource::getTextureAverageColor(const std::string &name)
return c;
}
void TextureSource::setImageCaching(bool enabled)
{
m_image_cache_enabled = enabled;
if (!enabled) {
for (const auto &it : m_image_cache) {
assert(it.second.image);
it.second.image->drop();
}
m_image_cache.clear();
}
}