Skip to content

Commit

Permalink
A fix and an improvement for partial updates
Browse files Browse the repository at this point in the history
Partial updates had a bug, that updated textures could be loaded by hash. This could result in unexpected results, if a game had 2 textures where the base is identical, but they differ because of parts that are updated via partial updates. Since efb copies always write the same dummy data, both textures would have the same hash, and Dolphin would load one of them for both textures.

This pr also changes the logic about which textures are removed from the cache when they overlap new efb copies. Dolphin now keeps all textures that are partly overwritten and have the same stride as the efb copy. This is supposed to get efb2tex to the same texture as efb2ram, by applying the related efb copies as updates after each other, in the order of their creation.
  • Loading branch information
mimimi085181 committed Jun 28, 2017
1 parent e14a82a commit 110004c
Showing 1 changed file with 32 additions and 27 deletions.
59 changes: 32 additions & 27 deletions Source/Core/VideoCommon/TextureCacheBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::ApplyPaletteToEntry(TCacheEntry
decoded_entry->SetHashes(entry->base_hash, entry->hash);
decoded_entry->frameCount = FRAMECOUNT_INVALID;
decoded_entry->is_efb_copy = false;
decoded_entry->may_have_overlapping_textures = entry->may_have_overlapping_textures;

ConvertTexture(decoded_entry, entry, palette, static_cast<TlutFormat>(tlutfmt));
textures_by_address.emplace(entry->addr, decoded_entry);
Expand Down Expand Up @@ -406,6 +407,13 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, u8* pale
// Mark the texture update as used, as if it was loaded directly
entry->frameCount = FRAMECOUNT_INVALID;
}

// Do not load textures by hash, if they were updated using an efb copy
if (entry_to_update->textures_by_hash_iter != textures_by_hash.end())
{
textures_by_hash.erase(entry_to_update->textures_by_hash_iter);
entry_to_update->textures_by_hash_iter = textures_by_hash.end();
}
}
else
{
Expand Down Expand Up @@ -610,9 +618,10 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
while (iter != iter_range.second)
{
TCacheEntry* entry = iter->second;
// Do not load strided EFB copies, they are not meant to be used directly
// Do not load strided EFB copies, they are not meant to be used directly.
// Also do not directly load EFB copies, which were partly overwritten.
if (entry->IsEfbCopy() && entry->native_width == nativeW && entry->native_height == nativeH &&
entry->memory_stride == entry->BytesPerRow())
entry->memory_stride == entry->BytesPerRow() && !entry->may_have_overlapping_textures)
{
// EFB copies have slightly different rules as EFB copy formats have different
// meanings from texture formats.
Expand Down Expand Up @@ -646,9 +655,9 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
else
{
// For normal textures, all texture parameters need to match
if (entry->hash == full_hash && entry->format == full_format &&
entry->native_levels >= tex_levels && entry->native_width == nativeW &&
entry->native_height == nativeH)
if (!entry->IsEfbCopy() && entry->hash == full_hash &&
entry->format == full_format && entry->native_levels >= tex_levels &&
entry->native_width == nativeW && entry->native_height == nativeH)
{
entry = DoPartialTextureUpdates(iter->second, &texMem[tlutaddr], tlutfmt);

Expand Down Expand Up @@ -1226,22 +1235,6 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo
unsigned int scaled_tex_h =
g_ActiveConfig.bCopyEFBScaled ? g_renderer->EFBToScaledY(tex_h) : tex_h;

// 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.
{
auto iter_range = textures_by_address.equal_range(dstAddr);
TexAddrCache::iterator iter = iter_range.first;
while (iter != iter_range.second)
{
iter = InvalidateTexture(iter);
}
}

// Get the base (in memory) format of this efb copy.
int baseFormat = TexDecoder_GetEfbCopyBaseFormat(dstFormat);

Expand Down Expand Up @@ -1310,23 +1303,34 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo
copy_to_vram = false;
}

// Invalidate all textures that overlap the range of our efb copy.
// 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.
bool invalidate_textures = dstStride == bytes_per_row || !copy_to_vram;
// Invalidate all textures, if they are either fully overwritten by our efb copy, or if they
// have a different stride than our efb copy. Textures with the same stride and that are partly
// overwritten are marked to check them for partial texture updates.
// TODO: The logic to detect a full overlap of 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->OverlapsMemoryRange(dstAddr, covered_range))
{
if (invalidate_textures)
u32 overlap_range = std::min(entry->addr + entry->size_in_bytes, dstAddr + covered_range) -
std::max(entry->addr, dstAddr);
if (entry->memory_stride != dstStride || !copy_to_vram ||
(!strided_efb_copy && entry->size_in_bytes == overlap_range) ||
(strided_efb_copy && entry->size_in_bytes == overlap_range && entry->addr == dstAddr))
{
iter.first = InvalidateTexture(iter.first);
continue;
}
entry->may_have_overlapping_textures = true;

// Do not load textures by hash, if they were at least partly overwritten by an efb copy
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;
}
Expand All @@ -1349,6 +1353,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFo

entry->frameCount = FRAMECOUNT_INVALID;
entry->SetEfbCopy(dstStride);
entry->may_have_overlapping_textures = false;
entry->is_custom_tex = false;

CopyEFBToCacheEntry(entry, is_depth_copy, srcRect, scaleByHalf, cbufid, colmat);
Expand Down

0 comments on commit 110004c

Please sign in to comment.