From 502c4c0e87976d183a2e2b299201f3b9771ebe79 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 13 Nov 2018 12:48:27 +1000 Subject: [PATCH] TextureCache: Recompute overlapping XFB copy hashes after copying to RAM --- Source/Core/VideoCommon/TextureCacheBase.cpp | 161 +++++++++++-------- 1 file changed, 90 insertions(+), 71 deletions(-) diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index d6b746ed9d..e0406708ba 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -1695,18 +1695,6 @@ void TextureCacheBase::CopyRenderTargetToTexture( const u32 bytes_per_row = num_blocks_x * bytes_per_block; const u32 covered_range = num_blocks_y * dstStride; - if (g_bRecordFifoData) - { - // Mark the memory behind this efb copy as dynamicly generated for the Fifo log - u32 address = dstAddr; - for (u32 i = 0; i < num_blocks_y; i++) - { - FifoRecorder::GetInstance().UseMemory(address, bytes_per_row, MemoryUpdate::TEXTURE_MAP, - true); - address += dstStride; - } - } - if (dstStride < bytes_per_row) { // This kind of efb copy results in a scrambled image. @@ -1722,63 +1710,6 @@ void TextureCacheBase::CopyRenderTargetToTexture( copy_to_vram = false; } - // Invalidate all textures, if they are either fully overwritten by our efb copy, or if they - // have a different stride than our efb copy. Partly overwritten textures with the same stride - // as our efb copy are marked to check them for partial texture updates. - // TODO: The logic to detect overlapping strided efb copies is not 100% accurate. - bool strided_efb_copy = dstStride != bytes_per_row; - auto iter = FindOverlappingTextures(dstAddr, covered_range); - while (iter.first != iter.second) - { - TCacheEntry* entry = iter.first->second; - - if (entry->addr == dstAddr && entry->is_xfb_copy) - { - for (auto& reference : entry->references) - { - reference->reference_changed = true; - } - } - - if (entry->OverlapsMemoryRange(dstAddr, covered_range)) - { - u32 overlap_range = std::min(entry->addr + entry->size_in_bytes, dstAddr + covered_range) - - std::max(entry->addr, dstAddr); - if (!copy_to_vram || entry->memory_stride != dstStride || - (!strided_efb_copy && entry->size_in_bytes == overlap_range) || - (strided_efb_copy && entry->size_in_bytes == overlap_range && entry->addr == dstAddr)) - { - // Pending EFB copies which are completely covered by this new copy can simply be tossed, - // instead of having to flush them later on, since this copy will write over everything. - iter.first = InvalidateTexture(iter.first, true); - continue; - } - entry->may_have_overlapping_textures = true; - - // There are cases (Rogue Squadron 2 / Texas Holdem on Wiiware) where - // for xfb copies the textures overlap which causes the hash of the first copy - // to be different (from when it was originally created). This has no implications - // for XFB2Tex because the underlying memory doesn't change (dummy values) but - // can affect XFB2Ram when we compare the texture cache copy hash with the - // newly computed hash - // By calculating the hash when we receive overlapping xfbs, we are able - // to mitigate this - if (entry->is_xfb_copy && copy_to_ram) - { - entry->hash = entry->CalculateHash(); - } - - // Do not load textures by hash, if they were at least partly overwritten by an efb copy. - // In this case, comparing the hash is not enough to check, if two textures are identical. - if (entry->textures_by_hash_iter != textures_by_hash.end()) - { - textures_by_hash.erase(entry->textures_by_hash_iter); - entry->textures_by_hash_iter = textures_by_hash.end(); - } - } - ++iter.first; - } - TCacheEntry* entry = nullptr; if (copy_to_vram) { @@ -1828,8 +1759,6 @@ void TextureCacheBase::CopyRenderTargetToTexture( xfb_count++), 0); } - - textures_by_address.emplace(dstAddr, entry); } } @@ -1884,12 +1813,84 @@ void TextureCacheBase::CopyRenderTargetToTexture( } } + // Invalidate all textures, if they are either fully overwritten by our efb copy, or if they + // have a different stride than our efb copy. Partly overwritten textures with the same stride + // as our efb copy are marked to check them for partial texture updates. + // TODO: The logic to detect overlapping strided efb copies is not 100% accurate. + bool strided_efb_copy = dstStride != bytes_per_row; + auto iter = FindOverlappingTextures(dstAddr, covered_range); + while (iter.first != iter.second) + { + TCacheEntry* overlapping_entry = iter.first->second; + + if (overlapping_entry->addr == dstAddr && overlapping_entry->is_xfb_copy) + { + for (auto& reference : overlapping_entry->references) + { + reference->reference_changed = true; + } + } + + if (overlapping_entry->OverlapsMemoryRange(dstAddr, covered_range)) + { + u32 overlap_range = std::min(overlapping_entry->addr + overlapping_entry->size_in_bytes, + dstAddr + covered_range) - + std::max(overlapping_entry->addr, dstAddr); + if (!copy_to_vram || overlapping_entry->memory_stride != dstStride || + (!strided_efb_copy && overlapping_entry->size_in_bytes == overlap_range) || + (strided_efb_copy && overlapping_entry->size_in_bytes == overlap_range && + overlapping_entry->addr == dstAddr)) + { + // Pending EFB copies which are completely covered by this new copy can simply be tossed, + // instead of having to flush them later on, since this copy will write over everything. + iter.first = InvalidateTexture(iter.first, true); + continue; + } + overlapping_entry->may_have_overlapping_textures = true; + + // There are cases (Rogue Squadron 2 / Texas Holdem on Wiiware) where + // for xfb copies the textures overlap which causes the hash of the first copy + // to be different (from when it was originally created). This has no implications + // for XFB2Tex because the underlying memory doesn't change (dummy values) but + // can affect XFB2Ram when we compare the texture cache copy hash with the + // newly computed hash + // By calculating the hash when we receive overlapping xfbs, we are able + // to mitigate this + if (overlapping_entry->is_xfb_copy && copy_to_ram) + { + overlapping_entry->hash = overlapping_entry->CalculateHash(); + } + + // Do not load textures by hash, if they were at least partly overwritten by an efb copy. + // In this case, comparing the hash is not enough to check, if two textures are identical. + if (overlapping_entry->textures_by_hash_iter != textures_by_hash.end()) + { + textures_by_hash.erase(overlapping_entry->textures_by_hash_iter); + overlapping_entry->textures_by_hash_iter = textures_by_hash.end(); + } + } + ++iter.first; + } + + if (g_bRecordFifoData) + { + // Mark the memory behind this efb copy as dynamicly generated for the Fifo log + u32 address = dstAddr; + for (u32 i = 0; i < num_blocks_y; i++) + { + FifoRecorder::GetInstance().UseMemory(address, bytes_per_row, MemoryUpdate::TEXTURE_MAP, + true); + address += dstStride; + } + } + // Even if the copy is deferred, still compute the hash. This way if the copy is used as a texture // in a subsequent draw before it is flushed, it will have the same hash. if (entry) { const u64 hash = entry->CalculateHash(); entry->SetHashes(hash, hash); + textures_by_address.emplace(dstAddr, entry); } } @@ -1936,6 +1937,24 @@ void TextureCacheBase::FlushEFBCopy(TCacheEntry* entry) // This should be safe because we'll catch any writes before the game can modify it. const u64 hash = entry->CalculateHash(); entry->SetHashes(hash, hash); + + // Check for any overlapping XFB copies which now need the hash recomputed. + // See the comment above regarding Rogue Squadron 2. + if (entry->is_xfb_copy) + { + const u32 covered_range = entry->pending_efb_copy_height * entry->memory_stride; + auto range = FindOverlappingTextures(entry->addr, covered_range); + for (auto iter = range.first; iter != range.second; ++iter) + { + TCacheEntry* overlapping_entry = iter->second; + if (overlapping_entry->may_have_overlapping_textures && overlapping_entry->is_xfb_copy && + overlapping_entry->OverlapsMemoryRange(entry->addr, covered_range)) + { + const u64 overlapping_hash = overlapping_entry->CalculateHash(); + entry->SetHashes(overlapping_hash, overlapping_hash); + } + } + } } std::unique_ptr TextureCacheBase::GetEFBCopyStagingTexture()