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{};
|
||||
};
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,6 +69,17 @@ public:
|
|||
|
||||
/// @brief Return average color of a texture string
|
||||
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
|
||||
|
@ -88,8 +99,8 @@ public:
|
|||
|
||||
/**
|
||||
* Rebuilds all textures (in case-source images have changed)
|
||||
* @note This won't invalidate old ITexture's, but you have to retrieve them
|
||||
* again to see changes.
|
||||
* @note This won't invalidate old ITexture's, but may or may not reuse them.
|
||||
* So you have to re-get all textures anyway.
|
||||
*/
|
||||
virtual void rebuildImagesAndTextures()=0;
|
||||
};
|
||||
|
|
|
@ -761,10 +761,6 @@ static bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType d
|
|||
void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc,
|
||||
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
|
||||
TileDef tdef[6];
|
||||
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);
|
||||
|
||||
// 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[])
|
||||
bool any_polygon_offset = false;
|
||||
for (u16 j = 0; j < 6; j++) {
|
||||
|
@ -1457,13 +1457,16 @@ void NodeDefManager::updateTextures(IGameDef *gamedef, void *progress_callback_a
|
|||
TextureSettings tsettings;
|
||||
tsettings.readSettings();
|
||||
|
||||
u32 size = m_content_features.size();
|
||||
tsrc->setImageCaching(true);
|
||||
|
||||
u32 size = m_content_features.size();
|
||||
for (u32 i = 0; i < size; i++) {
|
||||
ContentFeatures *f = &(m_content_features[i]);
|
||||
f->updateTextures(tsrc, shdsrc, meshmanip, client, tsettings);
|
||||
client->showUpdateProgressTexture(progress_callback_args, i, size);
|
||||
}
|
||||
|
||||
tsrc->setImageCaching(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue