Merge pull request #3902 from phire/lets_just_release_5_0_already
TextureCache: Track efb copies used in a partially updated texture (fixes regression for 5.0)
This commit is contained in:
commit
847cb540d9
|
@ -162,7 +162,7 @@ void TextureCacheBase::Cleanup(int _frameCount)
|
||||||
if ((_frameCount - iter->second->frameCount) % TEXTURE_KILL_THRESHOLD == 1 &&
|
if ((_frameCount - iter->second->frameCount) % TEXTURE_KILL_THRESHOLD == 1 &&
|
||||||
iter->second->hash != iter->second->CalculateHash())
|
iter->second->hash != iter->second->CalculateHash())
|
||||||
{
|
{
|
||||||
iter = FreeTexture(iter);
|
iter = InvalidateTexture(iter);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -171,7 +171,7 @@ void TextureCacheBase::Cleanup(int _frameCount)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
iter = FreeTexture(iter);
|
iter = InvalidateTexture(iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -282,7 +282,7 @@ void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntryBas
|
||||||
newentry->textures_by_hash_iter = textures_by_hash.emplace((*entry)->hash, newentry);
|
newentry->textures_by_hash_iter = textures_by_hash.emplace((*entry)->hash, newentry);
|
||||||
}
|
}
|
||||||
|
|
||||||
FreeTexture(GetTexCacheIter(*entry));
|
InvalidateTexture(GetTexCacheIter(*entry));
|
||||||
|
|
||||||
*entry = newentry;
|
*entry = newentry;
|
||||||
textures_by_address.emplace((*entry)->addr, *entry);
|
textures_by_address.emplace((*entry)->addr, *entry);
|
||||||
|
@ -319,6 +319,7 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::DoPartialTextureUpdates(Tex
|
||||||
TCacheEntryBase* entry = iter->second;
|
TCacheEntryBase* entry = iter->second;
|
||||||
if (entry != entry_to_update
|
if (entry != entry_to_update
|
||||||
&& entry->IsEfbCopy()
|
&& entry->IsEfbCopy()
|
||||||
|
&& entry->references.count(entry_to_update) == 0
|
||||||
&& entry->OverlapsMemoryRange(entry_to_update->addr, entry_to_update->size_in_bytes)
|
&& entry->OverlapsMemoryRange(entry_to_update->addr, entry_to_update->size_in_bytes)
|
||||||
&& entry->memory_stride == numBlocksX * block_size)
|
&& entry->memory_stride == numBlocksX * block_size)
|
||||||
{
|
{
|
||||||
|
@ -329,6 +330,8 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::DoPartialTextureUpdates(Tex
|
||||||
TCacheEntryBase *decoded_entry = entry->ApplyPalette(palette, tlutfmt);
|
TCacheEntryBase *decoded_entry = entry->ApplyPalette(palette, tlutfmt);
|
||||||
if (decoded_entry)
|
if (decoded_entry)
|
||||||
{
|
{
|
||||||
|
// Link the efb copy with the partially updated texture, so we won't apply this partial update again
|
||||||
|
entry->CreateReference(entry_to_update);
|
||||||
// Mark the texture update as used, as if it was loaded directly
|
// Mark the texture update as used, as if it was loaded directly
|
||||||
entry->frameCount = FRAMECOUNT_INVALID;
|
entry->frameCount = FRAMECOUNT_INVALID;
|
||||||
entry = decoded_entry;
|
entry = decoded_entry;
|
||||||
|
@ -394,17 +397,26 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::DoPartialTextureUpdates(Tex
|
||||||
dstrect.right = (dst_x + copy_width);
|
dstrect.right = (dst_x + copy_width);
|
||||||
dstrect.bottom = (dst_y + copy_height);
|
dstrect.bottom = (dst_y + copy_height);
|
||||||
entry_to_update->CopyRectangleFromTexture(entry, srcrect, dstrect);
|
entry_to_update->CopyRectangleFromTexture(entry, srcrect, dstrect);
|
||||||
// Mark the texture update as used, as if it was loaded directly
|
|
||||||
entry->frameCount = FRAMECOUNT_INVALID;
|
|
||||||
|
|
||||||
// Remove the converted texture, it won't be used anywhere else
|
|
||||||
if (isPaletteTexture)
|
if (isPaletteTexture)
|
||||||
FreeTexture(GetTexCacheIter(entry));
|
{
|
||||||
|
// Remove the temporary converted texture, it won't be used anywhere else
|
||||||
|
// TODO: It would be nice to convert and copy in one step, but this code path isn't common
|
||||||
|
InvalidateTexture(GetTexCacheIter(entry));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Link the two textures together, so we won't apply this partial update again
|
||||||
|
entry->CreateReference(entry_to_update);
|
||||||
|
// Mark the texture update as used, as if it was loaded directly
|
||||||
|
entry->frameCount = FRAMECOUNT_INVALID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If the hash does not match, this EFB copy will not be used for anything, so remove it
|
// If the hash does not match, this EFB copy will not be used for anything, so remove it
|
||||||
iter = FreeTexture(iter);
|
iter = InvalidateTexture(iter);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -612,7 +624,7 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage)
|
||||||
// never be useful again. It's theoretically possible for a game to do
|
// never be useful again. It's theoretically possible for a game to do
|
||||||
// something weird where the copy could become useful in the future, but in
|
// something weird where the copy could become useful in the future, but in
|
||||||
// practice it doesn't happen.
|
// practice it doesn't happen.
|
||||||
iter = FreeTexture(iter);
|
iter = InvalidateTexture(iter);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -680,7 +692,7 @@ TextureCacheBase::TCacheEntryBase* TextureCacheBase::Load(const u32 stage)
|
||||||
if (temp_frameCount != 0x7fffffff)
|
if (temp_frameCount != 0x7fffffff)
|
||||||
{
|
{
|
||||||
// pool this texture and make a new one later
|
// pool this texture and make a new one later
|
||||||
FreeTexture(oldest_entry);
|
InvalidateTexture(oldest_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<HiresTexture> hires_tex;
|
std::shared_ptr<HiresTexture> hires_tex;
|
||||||
|
@ -1128,13 +1140,17 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo
|
||||||
unsigned int scaled_tex_w = g_ActiveConfig.bCopyEFBScaled ? Renderer::EFBToScaledX(tex_w) : tex_w;
|
unsigned int scaled_tex_w = g_ActiveConfig.bCopyEFBScaled ? Renderer::EFBToScaledX(tex_w) : tex_w;
|
||||||
unsigned int scaled_tex_h = g_ActiveConfig.bCopyEFBScaled ? Renderer::EFBToScaledY(tex_h) : tex_h;
|
unsigned int scaled_tex_h = g_ActiveConfig.bCopyEFBScaled ? Renderer::EFBToScaledY(tex_h) : tex_h;
|
||||||
|
|
||||||
// remove all texture cache entries at dstAddr
|
// Remove all texture cache entries at dstAddr
|
||||||
|
// It's not possible to have two EFB copies at the same address, this makes sure any old efb copies
|
||||||
|
// (or normal textures) are removed from texture cache. They are also un-linked from any partially
|
||||||
|
// updated textures, which forces that partially updated texture to be updated.
|
||||||
|
// TODO: This also wipes out non-efb copies, which is counterproductive.
|
||||||
{
|
{
|
||||||
std::pair<TexCache::iterator, TexCache::iterator> iter_range = textures_by_address.equal_range((u64)dstAddr);
|
std::pair<TexCache::iterator, TexCache::iterator> iter_range = textures_by_address.equal_range((u64)dstAddr);
|
||||||
TexCache::iterator iter = iter_range.first;
|
TexCache::iterator iter = iter_range.first;
|
||||||
while (iter != iter_range.second)
|
while (iter != iter_range.second)
|
||||||
{
|
{
|
||||||
iter = FreeTexture(iter);
|
iter = InvalidateTexture(iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1215,6 +1231,8 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo
|
||||||
// Invalidate all textures that overlap the range of our efb copy.
|
// Invalidate all textures that overlap the range of our efb copy.
|
||||||
// Unless our efb copy has a weird stride, then we want avoid invalidating textures which
|
// Unless our efb copy has a weird stride, then we want avoid invalidating textures which
|
||||||
// we might be able to do a partial texture update on.
|
// we might be able to do a partial texture update on.
|
||||||
|
// TODO: This also invalidates partial overlaps, which we currently don't have a better way
|
||||||
|
// of dealing with.
|
||||||
if (dstStride == bytes_per_row || !copy_to_vram)
|
if (dstStride == bytes_per_row || !copy_to_vram)
|
||||||
{
|
{
|
||||||
TexCache::iterator iter = textures_by_address.begin();
|
TexCache::iterator iter = textures_by_address.begin();
|
||||||
|
@ -1223,7 +1241,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo
|
||||||
if (iter->second->addr + iter->second->size_in_bytes <= dstAddr || iter->second->addr >= dstAddr + num_blocks_y * dstStride)
|
if (iter->second->addr + iter->second->size_in_bytes <= dstAddr || iter->second->addr >= dstAddr + num_blocks_y * dstStride)
|
||||||
++iter;
|
++iter;
|
||||||
else
|
else
|
||||||
iter = FreeTexture(iter);
|
iter = InvalidateTexture(iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1301,7 +1319,7 @@ TextureCacheBase::TexCache::iterator TextureCacheBase::GetTexCacheIter(TextureCa
|
||||||
return textures_by_address.end();
|
return textures_by_address.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureCacheBase::TexCache::iterator TextureCacheBase::FreeTexture(TexCache::iterator iter)
|
TextureCacheBase::TexCache::iterator TextureCacheBase::InvalidateTexture(TexCache::iterator iter)
|
||||||
{
|
{
|
||||||
if (iter == textures_by_address.end())
|
if (iter == textures_by_address.end())
|
||||||
return textures_by_address.end();
|
return textures_by_address.end();
|
||||||
|
@ -1314,6 +1332,8 @@ TextureCacheBase::TexCache::iterator TextureCacheBase::FreeTexture(TexCache::ite
|
||||||
entry->textures_by_hash_iter = textures_by_hash.end();
|
entry->textures_by_hash_iter = textures_by_hash.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
entry->DestroyAllReferences();
|
||||||
|
|
||||||
entry->frameCount = FRAMECOUNT_INVALID;
|
entry->frameCount = FRAMECOUNT_INVALID;
|
||||||
texture_pool.emplace(entry->config, entry);
|
texture_pool.emplace(entry->config, entry);
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "VideoCommon/BPMemory.h"
|
#include "VideoCommon/BPMemory.h"
|
||||||
|
@ -68,6 +69,11 @@ public:
|
||||||
// Keep an iterator to the entry in textures_by_hash, so it does not need to be searched when removing the cache entry
|
// Keep an iterator to the entry in textures_by_hash, so it does not need to be searched when removing the cache entry
|
||||||
std::multimap<u64, TCacheEntryBase*>::iterator textures_by_hash_iter;
|
std::multimap<u64, TCacheEntryBase*>::iterator textures_by_hash_iter;
|
||||||
|
|
||||||
|
// This is used to keep track of both:
|
||||||
|
// * efb copies used by this partially updated texture
|
||||||
|
// * partially updated textures which refer to this efb copy
|
||||||
|
std::unordered_set<TCacheEntryBase*> references;
|
||||||
|
|
||||||
void SetGeneralParameters(u32 _addr, u32 _size, u32 _format)
|
void SetGeneralParameters(u32 _addr, u32 _size, u32 _format)
|
||||||
{
|
{
|
||||||
addr = _addr;
|
addr = _addr;
|
||||||
|
@ -89,6 +95,22 @@ public:
|
||||||
hash = _hash;
|
hash = _hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This texture entry is used by the other entry as a sub-texture
|
||||||
|
void CreateReference(TCacheEntryBase* other_entry)
|
||||||
|
{
|
||||||
|
// References are two-way, so they can easily be destroyed later
|
||||||
|
this->references.emplace(other_entry);
|
||||||
|
other_entry->references.emplace(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DestroyAllReferences()
|
||||||
|
{
|
||||||
|
for (auto& reference : references)
|
||||||
|
reference->references.erase(this);
|
||||||
|
|
||||||
|
references.clear();
|
||||||
|
}
|
||||||
|
|
||||||
void SetEfbCopy(u32 stride);
|
void SetEfbCopy(u32 stride);
|
||||||
|
|
||||||
TCacheEntryBase(const TCacheEntryConfig& c) : config(c) {}
|
TCacheEntryBase(const TCacheEntryConfig& c) : config(c) {}
|
||||||
|
@ -164,7 +186,9 @@ private:
|
||||||
|
|
||||||
static TCacheEntryBase* AllocateTexture(const TCacheEntryConfig& config);
|
static TCacheEntryBase* AllocateTexture(const TCacheEntryConfig& config);
|
||||||
static TexCache::iterator GetTexCacheIter(TCacheEntryBase* entry);
|
static TexCache::iterator GetTexCacheIter(TCacheEntryBase* entry);
|
||||||
static TexCache::iterator FreeTexture(TexCache::iterator t_iter);
|
|
||||||
|
// Removes and unlinks texture from texture cache and returns it to the pool
|
||||||
|
static TexCache::iterator InvalidateTexture(TexCache::iterator t_iter);
|
||||||
|
|
||||||
static TCacheEntryBase* ReturnEntry(unsigned int stage, TCacheEntryBase* entry);
|
static TCacheEntryBase* ReturnEntry(unsigned int stage, TCacheEntryBase* entry);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue