TextureCache: Add a dirty bit for partial updates on overlapping EFB copies.

This commit is contained in:
degasus 2016-12-26 18:41:34 +01:00
parent cd29d565c4
commit d0a73c1d90
2 changed files with 53 additions and 25 deletions

View File

@ -40,8 +40,6 @@ static const int TEXTURE_KILL_THRESHOLD =
64; // Sonic the Fighters (inside Sonic Gems Collection) loops a 64 frames animation
static const int TEXTURE_POOL_KILL_THRESHOLD = 3;
static const int FRAMECOUNT_INVALID = 0;
static const u64 MAX_TEXTURE_BINARY_SIZE =
1024 * 1024 * 4; // 1024 x 1024 texel times 8 nibbles per texel
std::unique_ptr<TextureCacheBase> g_texture_cache;
@ -297,6 +295,13 @@ TextureCacheBase::TCacheEntryBase*
TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8* palette,
u32 tlutfmt)
{
// If the flag may_have_overlapping_textures is cleared, there are no overlapping EFB copies,
// which aren't applied already. It is set for new textures, and for the affected range
// on each EFB copy.
if (!entry_to_update->may_have_overlapping_textures)
return entry_to_update;
entry_to_update->may_have_overlapping_textures = false;
const bool isPaletteTexture =
(entry_to_update->format == GX_TF_C4 || entry_to_update->format == GX_TF_C8 ||
entry_to_update->format == GX_TF_C14X2 || entry_to_update->format >= 0x10000);
@ -313,15 +318,10 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8*
u32 numBlocksX = (entry_to_update->native_width + block_width - 1) / block_width;
TexAddrCache::iterator iter =
textures_by_address.lower_bound(entry_to_update->addr > MAX_TEXTURE_BINARY_SIZE ?
entry_to_update->addr - MAX_TEXTURE_BINARY_SIZE :
0);
TexAddrCache::iterator iterend =
textures_by_address.upper_bound(entry_to_update->addr + entry_to_update->size_in_bytes);
while (iter != iterend)
auto iter = FindOverlappingTextures(entry_to_update->addr, entry_to_update->size_in_bytes);
while (iter.first != iter.second)
{
TCacheEntryBase* entry = iter->second;
TCacheEntryBase* entry = iter.first->second;
if (entry != entry_to_update && entry->IsEfbCopy() &&
entry->references.count(entry_to_update) == 0 &&
entry->OverlapsMemoryRange(entry_to_update->addr, entry_to_update->size_in_bytes) &&
@ -343,7 +343,7 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8*
}
else
{
++iter;
++iter.first;
continue;
}
}
@ -426,11 +426,11 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntryBase* entry_to_update, u8*
else
{
// If the hash does not match, this EFB copy will not be used for anything, so remove it
iter = InvalidateTexture(iter);
iter.first = InvalidateTexture(iter.first);
continue;
}
}
++iter;
++iter.first;
}
return entry_to_update;
}
@ -1232,7 +1232,8 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo
// RGBA takes two cache lines per block; all others take one
const u32 bytes_per_block = baseFormat == GX_TF_RGBA8 ? 64 : 32;
u32 bytes_per_row = num_blocks_x * bytes_per_block;
const u32 bytes_per_row = num_blocks_x * bytes_per_block;
const u32 covered_range = num_blocks_y * dstStride;
bool copy_to_ram = !g_ActiveConfig.bSkipEFBCopyToRam;
bool copy_to_vram = true;
@ -1283,21 +1284,24 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo
}
// 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
// we might be able to do a partial texture update on.
// Unless our efb copy has a weird stride, then we mark them to check for partial texture updates.
// 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)
bool invalidate_textures = dstStride == bytes_per_row || !copy_to_vram;
auto iter = FindOverlappingTextures(dstAddr, covered_range);
while (iter.first != iter.second)
{
TexAddrCache::iterator iter = textures_by_address.begin();
while (iter != textures_by_address.end())
TCacheEntryBase* entry = iter.first->second;
if (entry->OverlapsMemoryRange(dstAddr, covered_range))
{
if (iter->second->addr + iter->second->size_in_bytes <= dstAddr ||
iter->second->addr >= dstAddr + num_blocks_y * dstStride)
++iter;
else
iter = InvalidateTexture(iter);
if (invalidate_textures)
{
iter.first = InvalidateTexture(iter.first);
continue;
}
entry->may_have_overlapping_textures = true;
}
++iter.first;
}
if (copy_to_vram)
@ -1358,6 +1362,7 @@ TextureCacheBase::AllocateTexture(const TCacheEntryConfig& config)
}
entry->textures_by_hash_iter = textures_by_hash.end();
entry->may_have_overlapping_textures = true;
return entry;
}
@ -1390,6 +1395,23 @@ TextureCacheBase::GetTexCacheIter(TextureCacheBase::TCacheEntryBase* entry)
return textures_by_address.end();
}
std::pair<TextureCacheBase::TexAddrCache::iterator, TextureCacheBase::TexAddrCache::iterator>
TextureCacheBase::FindOverlappingTextures(u32 addr, u32 size_in_bytes)
{
// We index by the starting address only, so there is no way to query all textures
// which end after the given addr. But the GC textures have a limited size, so we
// look for all textures which have a start address bigger than addr minus the maximal
// texture size. But this yields false-positives which must be checked later on.
// 1024 x 1024 texel times 8 nibbles per texel
constexpr u32 max_texture_size = 1024 * 1024 * 4;
u32 lower_addr = addr > max_texture_size ? addr - max_texture_size : 0;
auto begin = textures_by_address.lower_bound(lower_addr);
auto end = textures_by_address.upper_bound(addr + size_in_bytes);
return std::make_pair(begin, end);
}
TextureCacheBase::TexAddrCache::iterator
TextureCacheBase::InvalidateTexture(TexAddrCache::iterator iter)
{

View File

@ -57,9 +57,10 @@ public:
u64 base_hash;
u64 hash; // for paletted textures, hash = base_hash ^ palette_hash
u32 format; // bits 0-3 will contain the in-memory format.
u32 memory_stride;
bool is_efb_copy;
bool is_custom_tex;
u32 memory_stride;
bool may_have_overlapping_textures;
unsigned int native_width,
native_height; // Texture dimensions from the GameCube's point of view
@ -198,6 +199,11 @@ private:
TexPool::iterator FindMatchingTextureFromPool(const TCacheEntryConfig& config);
TexAddrCache::iterator GetTexCacheIter(TCacheEntryBase* entry);
// Return all possible overlapping textures. As addr+size of the textures is not
// indexed, this may return false positives.
std::pair<TexAddrCache::iterator, TexAddrCache::iterator>
FindOverlappingTextures(u32 addr, u32 size_in_bytes);
// Removes and unlinks texture from texture cache and returns it to the pool
TexAddrCache::iterator InvalidateTexture(TexAddrCache::iterator t_iter);