From 4e415802fa2e20d516da6133286f85fbf91a63e0 Mon Sep 17 00:00:00 2001 From: matto Date: Sun, 4 Oct 2015 17:41:25 -0400 Subject: [PATCH] Changing the texture cache to be a proper LRU cache I change the currently used 'map' of textures to a queue (implemented as a list), and then remove from the end of the queue when textures need removing from the cache, and adding to the beginning of the queue. The other operation that is needed is moving the texture to the front of the list whenever it is used, so that frequently used textures are not deleted. In order to make that last operation effecient, I created a map of the locations of these textures, and keep that properly updated. This makes the accessing of a texture still O(1). Then, in order to have the iterators remain valid through insertions and deletions, I needed to implement the queue as a list. The map and queue implementation is a standard way to implement an LRU cache, just FYI. Not something I thought of all on my own. Fixes issue #744 --- src/Textures.cpp | 48 ++++++++++++++++++++++++++++++------------------ src/Textures.h | 7 +++++-- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/Textures.cpp b/src/Textures.cpp index 5e889d3d..313753d5 100644 --- a/src/Textures.cpp +++ b/src/Textures.cpp @@ -508,10 +508,11 @@ void TextureCache::destroy() current[0] = current[1] = NULL; for (Textures::const_iterator cur = m_textures.cbegin(); cur != m_textures.cend(); ++cur) - glDeleteTextures( 1, &cur->second.glName ); + glDeleteTextures( 1, &cur->glName ); m_textures.clear(); + m_lruTextureLocations.clear(); - for (Textures::const_iterator cur = m_fbTextures.cbegin(); cur != m_fbTextures.cend(); ++cur) + for (FBTextures::const_iterator cur = m_fbTextures.cbegin(); cur != m_fbTextures.cend(); ++cur) glDeleteTextures( 1, &cur->second.glName ); m_fbTextures.clear(); @@ -522,11 +523,14 @@ void TextureCache::_checkCacheSize() { if (m_cachedBytes <= m_maxBytes) return; + Textures::const_iterator iter = m_textures.cend(); do { --iter; - m_cachedBytes -= iter->second.textureBytes; - glDeleteTextures( 1, &iter->second.glName ); + const CachedTexture& tex = *iter; + m_cachedBytes -= tex.textureBytes; + glDeleteTextures(1, &tex.glName); + m_lruTextureLocations.erase(tex.crc); } while (m_cachedBytes > m_maxBytes && iter != m_textures.cbegin()); m_textures.erase(iter, m_textures.cend()); } @@ -538,16 +542,17 @@ CachedTexture * TextureCache::_addTexture(u32 _crc32) _checkCacheSize(); GLuint glName; glGenTextures(1, &glName); - m_textures.emplace(_crc32, glName); - CachedTexture & texture = m_textures.at(_crc32); - texture.crc = _crc32; - return &texture; + m_textures.emplace_front(glName); + Textures::iterator new_iter = m_textures.begin(); + new_iter->crc = _crc32; + m_lruTextureLocations.insert(std::pair(_crc32, new_iter)); + return &(*new_iter); } void TextureCache::removeFrameBufferTexture(CachedTexture * _pTexture) { - Textures::const_iterator iter = m_fbTextures.find(_pTexture->glName); - assert(iter != m_fbTextures.end()); + FBTextures::const_iterator iter = m_fbTextures.find(_pTexture->glName); + assert(iter != m_fbTextures.cend()); m_cachedBytes -= iter->second.textureBytes; glDeleteTextures( 1, &iter->second.glName ); m_fbTextures.erase(iter); @@ -1265,9 +1270,12 @@ void TextureCache::_updateBackground() u32 params[4] = {gSP.bgImage.width, gSP.bgImage.height, gSP.bgImage.format, gSP.bgImage.size}; crc = CRC_Calculate(crc, params, sizeof(u32)*4); - Textures::iterator iter = m_textures.find(crc); - if (iter != m_textures.end()) { - CachedTexture & current = iter->second; + Texture_Locations::iterator locations_iter = m_lruTextureLocations.find(crc); + if (locations_iter != m_lruTextureLocations.end()) { + Textures::iterator iter = locations_iter->second; + CachedTexture & current = *iter; + m_textures.splice(m_textures.begin(), m_textures, iter); + assert(current.width == gSP.bgImage.width); assert(current.height == gSP.bgImage.height); assert(current.format == gSP.bgImage.format); @@ -1331,10 +1339,11 @@ void TextureCache::_clear() current[0] = current[1] = NULL; for (Textures::const_iterator cur = m_textures.cbegin(); cur != m_textures.cend(); ++cur) { - m_cachedBytes -= cur->second.textureBytes; - glDeleteTextures(1, &cur->second.glName); + m_cachedBytes -= cur->textureBytes; + glDeleteTextures(1, &cur->glName); } m_textures.clear(); + m_lruTextureLocations.clear(); } void TextureCache::update(u32 _t) @@ -1414,9 +1423,12 @@ void TextureCache::update(u32 _t) return; } - Textures::iterator iter = m_textures.find(crc); - if (iter != m_textures.end()) { - CachedTexture & current = iter->second; + Texture_Locations::iterator locations_iter = m_lruTextureLocations.find(crc); + if (locations_iter != m_lruTextureLocations.end()) { + Textures::iterator iter = locations_iter->second; + CachedTexture & current = *iter; + m_textures.splice(m_textures.begin(), m_textures, iter); + assert(current.width == sizes.width); assert(current.height == sizes.height); assert(current.clampWidth == sizes.clampWidth); diff --git a/src/Textures.h b/src/Textures.h index 2ccddf5e..9737089a 100644 --- a/src/Textures.h +++ b/src/Textures.h @@ -82,9 +82,12 @@ private: void _initDummyTexture(CachedTexture * _pDummy); void _getTextureDestData(CachedTexture& tmptex, u32* pDest, GLuint glInternalFormat, GetTexelFunc GetTexel, u16* pLine); - typedef std::map Textures; + typedef std::list Textures; + typedef std::map Texture_Locations; + typedef std::map FBTextures; Textures m_textures; - Textures m_fbTextures; + Texture_Locations m_lruTextureLocations; + FBTextures m_fbTextures; CachedTexture * m_pDummy; CachedTexture * m_pMSDummy; u32 m_hits, m_misses;