Merge pull request #7570 from stenzek/defer-overlap-xfb-copies
TextureCache: Recompute overlapping XFB copy hashes after copying to RAM
This commit is contained in:
commit
3153b9e94f
|
@ -1695,18 +1695,6 @@ void TextureCacheBase::CopyRenderTargetToTexture(
|
||||||
const 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;
|
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)
|
if (dstStride < bytes_per_row)
|
||||||
{
|
{
|
||||||
// This kind of efb copy results in a scrambled image.
|
// This kind of efb copy results in a scrambled image.
|
||||||
|
@ -1722,63 +1710,6 @@ void TextureCacheBase::CopyRenderTargetToTexture(
|
||||||
copy_to_vram = false;
|
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;
|
TCacheEntry* entry = nullptr;
|
||||||
if (copy_to_vram)
|
if (copy_to_vram)
|
||||||
{
|
{
|
||||||
|
@ -1828,8 +1759,6 @@ void TextureCacheBase::CopyRenderTargetToTexture(
|
||||||
xfb_count++),
|
xfb_count++),
|
||||||
0);
|
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
|
// 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.
|
// in a subsequent draw before it is flushed, it will have the same hash.
|
||||||
if (entry)
|
if (entry)
|
||||||
{
|
{
|
||||||
const u64 hash = entry->CalculateHash();
|
const u64 hash = entry->CalculateHash();
|
||||||
entry->SetHashes(hash, hash);
|
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.
|
// This should be safe because we'll catch any writes before the game can modify it.
|
||||||
const u64 hash = entry->CalculateHash();
|
const u64 hash = entry->CalculateHash();
|
||||||
entry->SetHashes(hash, hash);
|
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<AbstractStagingTexture> TextureCacheBase::GetEFBCopyStagingTexture()
|
std::unique_ptr<AbstractStagingTexture> TextureCacheBase::GetEFBCopyStagingTexture()
|
||||||
|
|
Loading…
Reference in New Issue