From d0a73c1d90200ba39368bf0905816be4031dd940 Mon Sep 17 00:00:00 2001 From: degasus Date: Mon, 26 Dec 2016 18:41:34 +0100 Subject: [PATCH] TextureCache: Add a dirty bit for partial updates on overlapping EFB copies. --- Source/Core/VideoCommon/TextureCacheBase.cpp | 70 +++++++++++++------- Source/Core/VideoCommon/TextureCacheBase.h | 8 ++- 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index b70cfd8391..755602f19a 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -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 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::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) { diff --git a/Source/Core/VideoCommon/TextureCacheBase.h b/Source/Core/VideoCommon/TextureCacheBase.h index 0d101ea616..bffeb2aad7 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.h +++ b/Source/Core/VideoCommon/TextureCacheBase.h @@ -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 + 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);