diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp index ea008dc898..41af7ac946 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.cpp @@ -30,6 +30,8 @@ unsigned int TextureCache::temp_size; TextureCache::TexCache TextureCache::textures; +TextureCache::TexPool TextureCache::texPool; + TextureCache::BackupConfig TextureCache::backup_config; bool invalidate_texture_cache_requested; @@ -68,6 +70,13 @@ void TextureCache::Invalidate() delete iter->second; textures.clear(); + + TexPool::iterator + iter2 = texPool.begin(), + tcend2 = texPool.end(); + for (; iter2 != tcend2; ++iter2) + delete iter2->second; + texPool.clear(); } TextureCache::~TextureCache() @@ -135,7 +144,7 @@ void TextureCache::Cleanup() // EFB copies living on the host GPU are unrecoverable and thus shouldn't be deleted && ! iter->second->IsEfbCopy() ) { - delete iter->second; + PoolTexture(iter->second); textures.erase(iter++); } else @@ -143,6 +152,21 @@ void TextureCache::Cleanup() ++iter; } } + + TexPool::iterator iter2 = texPool.begin(); + TexPool::iterator tcend2 = texPool.end(); + while (iter2 != tcend2) + { + if (frameCount > TEXTURE_KILL_THRESHOLD + iter2->second->frameCount) + { + delete iter2->second; + texPool.erase(iter2++); + } + else + { + ++iter2; + } + } } void TextureCache::InvalidateRange(u32 start_address, u32 size) @@ -155,7 +179,7 @@ void TextureCache::InvalidateRange(u32 start_address, u32 size) const int rangePosition = iter->second->IntersectsMemoryRange(start_address, size); if (0 == rangePosition) { - delete iter->second; + PoolTexture(iter->second); textures.erase(iter++); } else @@ -215,7 +239,7 @@ void TextureCache::ClearRenderTargets() { if (iter->second->type == TCET_EC_VRAM) { - delete iter->second; + PoolTexture(iter->second); textures.erase(iter++); } else @@ -426,8 +450,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage, } else { - // delete the texture and make a new one - delete entry; + // pool the texture and make a new one + PoolTexture(entry); entry = NULL; } } @@ -445,8 +469,8 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage, expandedWidth = width; expandedHeight = height; - // If we thought we could reuse the texture before, make sure to delete it now! - delete entry; + // If we thought we could reuse the texture before, make sure to pool it now! + PoolTexture(entry); entry = NULL; } using_custom_texture = true; @@ -473,6 +497,15 @@ TextureCache::TCacheEntryBase* TextureCache::Load(unsigned int const stage, const bool use_native_mips = use_mipmaps && !using_custom_lods && (width == nativeW && height == nativeH); texLevels = (use_native_mips || using_custom_lods) ? texLevels : 1; // TODO: Should be forced to 1 for non-pow2 textures (e.g. efb copies with automatically adjusted IR) + // try to search for a pooled texture + if (NULL == entry) + { + // Try to find a matching texture in the pool. We pool unused texture as they often just change the type. + // This happens in eg efb2ram which overwrites half of a texture. So most of this textures are only pooled + // for some frames. + textures[texID] = entry = GetPooledTexture ( width, height, full_format, texLevels, false ); + } + // create the entry/texture if (NULL == entry) { @@ -821,16 +854,23 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat else if (!(entry->type == TCET_EC_VRAM && entry->virtual_width == scaled_tex_w && entry->virtual_height == scaled_tex_h)) { // remove it and recreate it as a render target - delete entry; + PoolTexture(entry); entry = NULL; } } if (NULL == entry) { - // create the texture - textures[dstAddr] = entry = g_texture_cache->CreateRenderTargetTexture(scaled_tex_w, scaled_tex_h); - + // search for a compatible pooled texture + entry = GetPooledTexture(scaled_tex_w, scaled_tex_h, 0, 0, true); + + if (NULL == entry) + { + // create the texture + entry = g_texture_cache->CreateRenderTargetTexture(scaled_tex_w, scaled_tex_h); + } + textures[dstAddr] = entry; + // TODO: Using the wrong dstFormat, dumb... entry->SetGeneralParameters(dstAddr, 0, dstFormat, 1); entry->SetDimensions(tex_w, tex_h, scaled_tex_w, scaled_tex_h); @@ -842,3 +882,33 @@ void TextureCache::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat entry->FromRenderTarget(dstAddr, dstFormat, srcFormat, srcRect, isIntensity, scaleByHalf, cbufid, colmat); } + +TextureCache::TCacheEntryBase* TextureCache::GetPooledTexture ( u32 width, u32 height, u32 full_format, u32 maxlevel, bool isEfbCopy ) +{ + TCacheEntryBase* entry = NULL; + std::pair bounds; + bounds = texPool.equal_range(std::make_pair(width, height)); + while(!entry && bounds.first != bounds.second) { + entry = bounds.first->second; + if ( + (isEfbCopy && entry->IsEfbCopy()) || + (!isEfbCopy && entry->type == TCET_NORMAL && full_format == entry->format && entry->num_mipmaps == maxlevel) || + (!isEfbCopy && entry->type == TCET_EC_DYNAMIC) + ) + { + texPool.erase(bounds.first); + } + else + { + entry = NULL; + bounds.first++; + } + } + return entry; +} + +void TextureCache::PoolTexture(TextureCache::TCacheEntryBase* entry) +{ + entry->frameCount = frameCount; + texPool.insert(std::make_pair(std::make_pair(entry->virtual_width, entry->virtual_height), entry)); +} diff --git a/Source/Core/VideoCommon/Src/TextureCacheBase.h b/Source/Core/VideoCommon/Src/TextureCacheBase.h index 1afcb8cd2d..c035ee8d23 100644 --- a/Source/Core/VideoCommon/Src/TextureCacheBase.h +++ b/Source/Core/VideoCommon/Src/TextureCacheBase.h @@ -121,8 +121,12 @@ private: static void DumpTexture(TCacheEntryBase* entry, unsigned int level); typedef std::map TexCache; - static TexCache textures; + + static TCacheEntryBase* GetPooledTexture(u32 width, u32 height, u32 full_format, u32 maxlevel, bool isEfbCopy); + static void PoolTexture(TCacheEntryBase *entry); + typedef std::multimap, TCacheEntryBase*> TexPool; + static TexPool texPool; // Backup configuration values static struct BackupConfig