From b6d09c61ed328259f2e43bc9e14ad725d8ba0017 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Thu, 6 Oct 2016 00:27:02 +1000 Subject: [PATCH] TextureCache: Don't re-use pooled textures within the same frame This is an issue because a driver may have to maintain two copies of a texture if it batches all uploads together at the start of a frame. In the Vulkan backend, we do something similar to avoid breaking out of a render pass to copy a texture from the streaming buffer to the destination image. This was causing issues in the sms-bubbles fifolog, where an EFB copy to the same address of a previously-used texture caused the previous texture to be re-used again for a different image later on in the frame, causing the original contents to be discarded. --- Source/Core/VideoCommon/TextureCacheBase.cpp | 15 ++++++++++++++- Source/Core/VideoCommon/TextureCacheBase.h | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 39af7f155c..0f8a39b341 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -1353,7 +1353,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo TextureCacheBase::TCacheEntryBase* TextureCacheBase::AllocateTexture(const TCacheEntryConfig& config) { - TexPool::iterator iter = texture_pool.find(config); + TexPool::iterator iter = FindMatchingTextureFromPool(config); TextureCacheBase::TCacheEntryBase* entry; if (iter != texture_pool.end()) { @@ -1373,6 +1373,19 @@ TextureCacheBase::AllocateTexture(const TCacheEntryConfig& config) return entry; } +TextureCacheBase::TexPool::iterator +TextureCacheBase::FindMatchingTextureFromPool(const TCacheEntryConfig& config) +{ + // Find a texture from the pool that does not have a frameCount of FRAMECOUNT_INVALID. + // This prevents a texture from being used twice in a single frame with different data, + // which potentially means that a driver has to maintain two copies of the texture anyway. + auto range = texture_pool.equal_range(config); + auto matching_iter = std::find_if(range.first, range.second, [](const auto& iter) { + return iter.second->frameCount != FRAMECOUNT_INVALID; + }); + return matching_iter != range.second ? matching_iter : texture_pool.end(); +} + TextureCacheBase::TexCache::iterator TextureCacheBase::GetTexCacheIter(TextureCacheBase::TCacheEntryBase* entry) { diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index e10a0439f1..437392569f 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -193,6 +193,7 @@ private: static void CheckTempSize(size_t required_size); static TCacheEntryBase* AllocateTexture(const TCacheEntryConfig& config); + static TexPool::iterator FindMatchingTextureFromPool(const TCacheEntryConfig& config); static TexCache::iterator GetTexCacheIter(TCacheEntryBase* entry); // Removes and unlinks texture from texture cache and returns it to the pool