mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
Add caching of generated textures as image
This commit is contained in:
parent
9cb78f2dc5
commit
486fb7cc4d
3 changed files with 86 additions and 20 deletions
|
@ -24,6 +24,13 @@ struct TextureInfo
|
||||||
std::set<std::string> sourceImages{};
|
std::set<std::string> sourceImages{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Stores internal information about a texture image.
|
||||||
|
struct ImageInfo
|
||||||
|
{
|
||||||
|
video::IImage *image = nullptr;
|
||||||
|
std::set<std::string> sourceImages;
|
||||||
|
};
|
||||||
|
|
||||||
// TextureSource
|
// TextureSource
|
||||||
class TextureSource final : public IWritableTextureSource
|
class TextureSource final : public IWritableTextureSource
|
||||||
{
|
{
|
||||||
|
@ -123,7 +130,13 @@ public:
|
||||||
|
|
||||||
video::SColor getTextureAverageColor(const std::string &name);
|
video::SColor getTextureAverageColor(const std::string &name);
|
||||||
|
|
||||||
|
void setImageCaching(bool enabled);
|
||||||
|
|
||||||
private:
|
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
|
// The id of the thread that is allowed to use irrlicht directly
|
||||||
std::thread::id m_main_thread;
|
std::thread::id m_main_thread;
|
||||||
|
@ -132,6 +145,12 @@ private:
|
||||||
// This should be only accessed from the main thread
|
// This should be only accessed from the main thread
|
||||||
ImageSource m_imagesource;
|
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
|
// Rebuild images and textures from the current set of source images
|
||||||
// Shall be called from the main thread.
|
// Shall be called from the main thread.
|
||||||
// You ARE expected to be holding m_textureinfo_cache_mutex
|
// You ARE expected to be holding m_textureinfo_cache_mutex
|
||||||
|
@ -193,15 +212,18 @@ TextureSource::~TextureSource()
|
||||||
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
|
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
|
||||||
u32 textures_before = driver->getTextureCount();
|
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) {
|
for (const auto &iter : m_textureinfo_cache) {
|
||||||
// cleanup texture
|
|
||||||
if (iter.texture)
|
if (iter.texture)
|
||||||
driver->removeTexture(iter.texture);
|
driver->removeTexture(iter.texture);
|
||||||
}
|
}
|
||||||
m_textureinfo_cache.clear();
|
m_textureinfo_cache.clear();
|
||||||
|
|
||||||
for (auto t : m_texture_trash) {
|
for (auto t : m_texture_trash) {
|
||||||
// cleanup trashed texture
|
|
||||||
driver->removeTexture(t);
|
driver->removeTexture(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,6 +231,26 @@ TextureSource::~TextureSource()
|
||||||
<< " after: " << driver->getTextureCount() << std::endl;
|
<< " 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)
|
u32 TextureSource::getTextureId(const std::string &name)
|
||||||
{
|
{
|
||||||
{ // See if texture already exists
|
{ // See if texture already exists
|
||||||
|
@ -280,7 +322,7 @@ u32 TextureSource::generateTexture(const std::string &name)
|
||||||
|
|
||||||
// passed into texture info for dynamic media tracking
|
// passed into texture info for dynamic media tracking
|
||||||
std::set<std::string> source_image_names;
|
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;
|
video::ITexture *tex = nullptr;
|
||||||
|
|
||||||
|
@ -356,7 +398,7 @@ Palette* TextureSource::getPalette(const std::string &name)
|
||||||
if (it == m_palettes.end()) {
|
if (it == m_palettes.end()) {
|
||||||
// Create palette
|
// Create palette
|
||||||
std::set<std::string> source_image_names; // unused, sadly.
|
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) {
|
if (!img) {
|
||||||
warningstream << "TextureSource::getPalette(): palette \"" << name
|
warningstream << "TextureSource::getPalette(): palette \"" << name
|
||||||
<< "\" could not be loaded." << std::endl;
|
<< "\" could not be loaded." << std::endl;
|
||||||
|
@ -458,6 +500,8 @@ void TextureSource::rebuildImagesAndTextures()
|
||||||
infostream << "TextureSource: recreating " << m_textureinfo_cache.size()
|
infostream << "TextureSource: recreating " << m_textureinfo_cache.size()
|
||||||
<< " textures" << std::endl;
|
<< " textures" << std::endl;
|
||||||
|
|
||||||
|
assert(!m_image_cache_enabled || m_image_cache.empty());
|
||||||
|
|
||||||
// Recreate textures
|
// Recreate textures
|
||||||
for (TextureInfo &ti : m_textureinfo_cache) {
|
for (TextureInfo &ti : m_textureinfo_cache) {
|
||||||
if (ti.name.empty())
|
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);
|
sanity_check(std::this_thread::get_id() == m_main_thread);
|
||||||
|
|
||||||
std::set<std::string> source_image_names;
|
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
|
// Create texture from resulting image
|
||||||
video::ITexture *t = nullptr, *t_old = ti.texture;
|
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::SColor TextureSource::getTextureAverageColor(const std::string &name)
|
||||||
{
|
{
|
||||||
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
|
assert(std::this_thread::get_id() == m_main_thread);
|
||||||
video::ITexture *texture = getTexture(name);
|
|
||||||
if (!texture)
|
std::set<std::string> unused;
|
||||||
return {0, 0, 0, 0};
|
auto *image = getOrGenerateImage(name, unused);
|
||||||
// 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());
|
|
||||||
if (!image)
|
if (!image)
|
||||||
return {0, 0, 0, 0};
|
return {0, 0, 0, 0};
|
||||||
|
|
||||||
|
@ -526,3 +566,15 @@ video::SColor TextureSource::getTextureAverageColor(const std::string &name)
|
||||||
|
|
||||||
return c;
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -69,6 +69,17 @@ public:
|
||||||
|
|
||||||
/// @brief Return average color of a texture string
|
/// @brief Return average color of a texture string
|
||||||
virtual video::SColor getTextureAverageColor(const std::string &name)=0;
|
virtual video::SColor getTextureAverageColor(const std::string &name)=0;
|
||||||
|
|
||||||
|
// Note: this method is here because caching is the decision of the
|
||||||
|
// API user, even if his access is read-only.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables or disables the caching of finished texture images.
|
||||||
|
* This can be useful if you want to call getTextureAverageColor without
|
||||||
|
* duplicating work.
|
||||||
|
* @note Disabling caching will flush the cache.
|
||||||
|
*/
|
||||||
|
virtual void setImageCaching(bool enabled) {};
|
||||||
};
|
};
|
||||||
|
|
||||||
class IWritableTextureSource : public ITextureSource
|
class IWritableTextureSource : public ITextureSource
|
||||||
|
@ -88,8 +99,8 @@ public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rebuilds all textures (in case-source images have changed)
|
* Rebuilds all textures (in case-source images have changed)
|
||||||
* @note This won't invalidate old ITexture's, but you have to retrieve them
|
* @note This won't invalidate old ITexture's, but may or may not reuse them.
|
||||||
* again to see changes.
|
* So you have to re-get all textures anyway.
|
||||||
*/
|
*/
|
||||||
virtual void rebuildImagesAndTextures()=0;
|
virtual void rebuildImagesAndTextures()=0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -761,10 +761,6 @@ static bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType d
|
||||||
void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
|
void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
|
||||||
scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
|
scene::IMeshManipulator *meshmanip, Client *client, const TextureSettings &tsettings)
|
||||||
{
|
{
|
||||||
// minimap pixel color - the average color of a texture
|
|
||||||
if (tsettings.enable_minimap && !tiledef[0].name.empty())
|
|
||||||
minimap_color = tsrc->getTextureAverageColor(tiledef[0].name);
|
|
||||||
|
|
||||||
// Figure out the actual tiles to use
|
// Figure out the actual tiles to use
|
||||||
TileDef tdef[6];
|
TileDef tdef[6];
|
||||||
for (u32 j = 0; j < 6; j++) {
|
for (u32 j = 0; j < 6; j++) {
|
||||||
|
@ -909,6 +905,10 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
||||||
|
|
||||||
u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
|
u32 overlay_shader = shdsrc->getShader("nodes_shader", overlay_material, drawtype);
|
||||||
|
|
||||||
|
// minimap pixel color = average color of top tile
|
||||||
|
if (tsettings.enable_minimap && !tdef[0].name.empty() && drawtype != NDT_AIRLIKE)
|
||||||
|
minimap_color = tsrc->getTextureAverageColor(tdef[0].name);
|
||||||
|
|
||||||
// Tiles (fill in f->tiles[])
|
// Tiles (fill in f->tiles[])
|
||||||
bool any_polygon_offset = false;
|
bool any_polygon_offset = false;
|
||||||
for (u16 j = 0; j < 6; j++) {
|
for (u16 j = 0; j < 6; j++) {
|
||||||
|
@ -1457,13 +1457,16 @@ void NodeDefManager::updateTextures(IGameDef *gamedef, void *progress_callback_a
|
||||||
TextureSettings tsettings;
|
TextureSettings tsettings;
|
||||||
tsettings.readSettings();
|
tsettings.readSettings();
|
||||||
|
|
||||||
u32 size = m_content_features.size();
|
tsrc->setImageCaching(true);
|
||||||
|
|
||||||
|
u32 size = m_content_features.size();
|
||||||
for (u32 i = 0; i < size; i++) {
|
for (u32 i = 0; i < size; i++) {
|
||||||
ContentFeatures *f = &(m_content_features[i]);
|
ContentFeatures *f = &(m_content_features[i]);
|
||||||
f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
|
f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
|
||||||
client->showUpdateProgressTexture(progress_callback_args, i, size);
|
client->showUpdateProgressTexture(progress_callback_args, i, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tsrc->setImageCaching(false);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue