summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon/TextureCacheBase.cpp
diff options
context:
space:
mode:
authorAdmiral H. Curtiss <pikachu025@gmail.com>2018-09-12 23:53:40 +0200
committerAdmiral H. Curtiss <pikachu025@gmail.com>2018-09-14 19:41:27 +0200
commit3e95d3c477aac0e186ff2cbe4804273162a13f1f (patch)
treec848c85f722199cf026efe8f83bcfb90a01d458a /Source/Core/VideoCommon/TextureCacheBase.cpp
parentce9e9186f715408af8d9abb60b73ccb1e4d3c474 (diff)
Stitch together overlapping XFB regions in order of XFB copy creation.
Diffstat (limited to 'Source/Core/VideoCommon/TextureCacheBase.cpp')
-rw-r--r--Source/Core/VideoCommon/TextureCacheBase.cpp214
1 files changed, 115 insertions, 99 deletions
diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp
index cd8a024fbc..dfbd83db87 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.cpp
+++ b/Source/Core/VideoCommon/TextureCacheBase.cpp
@@ -8,6 +8,7 @@
#include <memory>
#include <string>
#include <utility>
+#include <vector>
#if defined(_M_X86) || defined(_M_X86_64)
#include <pmmintrin.h>
#endif
@@ -1296,6 +1297,15 @@ bool TextureCacheBase::LoadTextureFromOverlappingTextures(TCacheEntry* entry_to_
u32 numBlocksX = entry_to_update->native_width / tex_info.block_width;
+ // It is possible that some of the overlapping textures overlap each other.
+ // This behavior has been seen with XFB copies in Rogue Leader.
+ // To get the correct result, we apply the texture updates in the order the textures were
+ // originally loaded. This ensures that the parts of the texture that would have been overwritten
+ // in memory on real hardware get overwritten the same way here too.
+ // This should work, but it may be a better idea to keep track of partial XFB copy invalidations
+ // instead, which would reduce the amount of copying work here.
+ std::vector<TCacheEntry*> candidates;
+
auto iter = FindOverlappingTextures(entry_to_update->addr, entry_to_update->size_in_bytes);
while (iter.first != iter.second)
{
@@ -1307,115 +1317,121 @@ bool TextureCacheBase::LoadTextureFromOverlappingTextures(TCacheEntry* entry_to_
{
if (entry->hash == entry->CalculateHash())
{
- if (tex_info.is_palette_texture)
- {
- TCacheEntry* decoded_entry =
- ApplyPaletteToEntry(entry, nullptr, tex_info.full_format.tlutfmt);
- if (decoded_entry)
- {
- // Link the efb copy with the partially updated texture, so we won't apply this partial
- // update again
- entry->CreateReference(entry_to_update);
- // Mark the texture update as used, as if it was loaded directly
- entry->frameCount = FRAMECOUNT_INVALID;
- entry = decoded_entry;
- }
- else
- {
- ++iter.first;
- continue;
- }
- }
+ candidates.emplace_back(entry);
+ }
+ else
+ {
+ // If the hash does not match, this EFB copy will not be used for anything, so remove it
+ iter.first = InvalidateTexture(iter.first);
+ continue;
+ }
+ }
+ ++iter.first;
+ }
- s32 src_x, src_y, dst_x, dst_y;
+ std::sort(candidates.begin(), candidates.end(),
+ [](const TCacheEntry* a, const TCacheEntry* b) { return a->id < b->id; });
- // Note for understanding the math:
- // Normal textures can't be strided, so the 2 missing cases with src_x > 0 don't exist
- if (entry->addr >= entry_to_update->addr)
- {
- s32 block_offset = (entry->addr - entry_to_update->addr) / tex_info.bytes_per_block;
- s32 block_x = block_offset % numBlocksX;
- s32 block_y = block_offset / numBlocksX;
- src_x = 0;
- src_y = 0;
- dst_x = block_x * tex_info.block_width;
- dst_y = block_y * tex_info.block_height;
- }
- else
- {
- s32 block_offset = (entry_to_update->addr - entry->addr) / tex_info.bytes_per_block;
- s32 block_x = block_offset % numBlocksX;
- s32 block_y = block_offset / numBlocksX;
- src_x = block_x * tex_info.block_width;
- src_y = block_y * tex_info.block_height;
- dst_x = 0;
- dst_y = 0;
- }
+ for (TCacheEntry* entry : candidates)
+ {
+ if (tex_info.is_palette_texture)
+ {
+ TCacheEntry* decoded_entry =
+ ApplyPaletteToEntry(entry, nullptr, tex_info.full_format.tlutfmt);
+ if (decoded_entry)
+ {
+ // Link the efb copy with the partially updated texture, so we won't apply this partial
+ // update again
+ entry->CreateReference(entry_to_update);
+ // Mark the texture update as used, as if it was loaded directly
+ entry->frameCount = FRAMECOUNT_INVALID;
+ entry = decoded_entry;
+ }
+ else
+ {
+ continue;
+ }
+ }
- u32 copy_width =
- std::min(entry->native_width - src_x, entry_to_update->native_width - dst_x);
- u32 copy_height =
- std::min(entry->native_height - src_y, entry_to_update->native_height - dst_y);
+ s32 src_x, src_y, dst_x, dst_y;
- // If one of the textures is scaled, scale both with the current efb scaling factor
- if (entry_to_update->native_width != entry_to_update->GetWidth() ||
- entry_to_update->native_height != entry_to_update->GetHeight() ||
- entry->native_width != entry->GetWidth() || entry->native_height != entry->GetHeight())
- {
- ScaleTextureCacheEntryTo(entry_to_update,
- g_renderer->EFBToScaledX(entry_to_update->native_width),
- g_renderer->EFBToScaledY(entry_to_update->native_height));
- ScaleTextureCacheEntryTo(entry, g_renderer->EFBToScaledX(entry->native_width),
- g_renderer->EFBToScaledY(entry->native_height));
+ // Note for understanding the math:
+ // Normal textures can't be strided, so the 2 missing cases with src_x > 0 don't exist
+ if (entry->addr >= entry_to_update->addr)
+ {
+ s32 block_offset = (entry->addr - entry_to_update->addr) / tex_info.bytes_per_block;
+ s32 block_x = block_offset % numBlocksX;
+ s32 block_y = block_offset / numBlocksX;
+ src_x = 0;
+ src_y = 0;
+ dst_x = block_x * tex_info.block_width;
+ dst_y = block_y * tex_info.block_height;
+ }
+ else
+ {
+ s32 block_offset = (entry_to_update->addr - entry->addr) / tex_info.bytes_per_block;
+ s32 block_x = block_offset % numBlocksX;
+ s32 block_y = block_offset / numBlocksX;
+ src_x = block_x * tex_info.block_width;
+ src_y = block_y * tex_info.block_height;
+ dst_x = 0;
+ dst_y = 0;
+ }
- src_x = g_renderer->EFBToScaledX(src_x);
- src_y = g_renderer->EFBToScaledY(src_y);
- dst_x = g_renderer->EFBToScaledX(dst_x);
- dst_y = g_renderer->EFBToScaledY(dst_y);
- copy_width = g_renderer->EFBToScaledX(copy_width);
- copy_height = g_renderer->EFBToScaledY(copy_height);
- }
+ u32 copy_width = std::min(entry->native_width - src_x, entry_to_update->native_width - dst_x);
+ u32 copy_height =
+ std::min(entry->native_height - src_y, entry_to_update->native_height - dst_y);
- MathUtil::Rectangle<int> srcrect, dstrect;
- srcrect.left = src_x;
- srcrect.top = src_y;
- srcrect.right = (src_x + copy_width);
- srcrect.bottom = (src_y + copy_height);
+ // If one of the textures is scaled, scale both with the current efb scaling factor
+ if (entry_to_update->native_width != entry_to_update->GetWidth() ||
+ entry_to_update->native_height != entry_to_update->GetHeight() ||
+ entry->native_width != entry->GetWidth() || entry->native_height != entry->GetHeight())
+ {
+ ScaleTextureCacheEntryTo(entry_to_update,
+ g_renderer->EFBToScaledX(entry_to_update->native_width),
+ g_renderer->EFBToScaledY(entry_to_update->native_height));
+ ScaleTextureCacheEntryTo(entry, g_renderer->EFBToScaledX(entry->native_width),
+ g_renderer->EFBToScaledY(entry->native_height));
+
+ src_x = g_renderer->EFBToScaledX(src_x);
+ src_y = g_renderer->EFBToScaledY(src_y);
+ dst_x = g_renderer->EFBToScaledX(dst_x);
+ dst_y = g_renderer->EFBToScaledY(dst_y);
+ copy_width = g_renderer->EFBToScaledX(copy_width);
+ copy_height = g_renderer->EFBToScaledY(copy_height);
+ }
- dstrect.left = dst_x;
- dstrect.top = dst_y;
- dstrect.right = (dst_x + copy_width);
- dstrect.bottom = (dst_y + copy_height);
+ MathUtil::Rectangle<int> srcrect, dstrect;
+ srcrect.left = src_x;
+ srcrect.top = src_y;
+ srcrect.right = (src_x + copy_width);
+ srcrect.bottom = (src_y + copy_height);
- for (u32 layer = 0; layer < entry->texture->GetConfig().layers; layer++)
- {
- entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, layer,
- 0, dstrect, layer, 0);
- }
- updated_entry = true;
+ dstrect.left = dst_x;
+ dstrect.top = dst_y;
+ dstrect.right = (dst_x + copy_width);
+ dstrect.bottom = (dst_y + copy_height);
- if (tex_info.is_palette_texture)
- {
- // Remove the temporary converted texture, it won't be used anywhere else
- // TODO: It would be nice to convert and copy in one step, but this code path isn't common
- InvalidateTexture(GetTexCacheIter(entry));
- }
- else
- {
- // Link the two textures together, so we won't apply this partial update again
- entry->CreateReference(entry_to_update);
- // Mark the texture update as used, as if it was loaded directly
- entry->frameCount = FRAMECOUNT_INVALID;
- }
- }
- else
- {
- // If the hash does not match, this EFB copy will not be used for anything, so remove it
- iter.first = InvalidateTexture(iter.first);
- continue;
- }
+ for (u32 layer = 0; layer < entry->texture->GetConfig().layers; layer++)
+ {
+ entry_to_update->texture->CopyRectangleFromTexture(entry->texture.get(), srcrect, layer, 0,
+ dstrect, layer, 0);
+ }
+ updated_entry = true;
+
+ if (tex_info.is_palette_texture)
+ {
+ // Remove the temporary converted texture, it won't be used anywhere else
+ // TODO: It would be nice to convert and copy in one step, but this code path isn't common
+ InvalidateTexture(GetTexCacheIter(entry));
+ }
+ else
+ {
+ // Link the two textures together, so we won't apply this partial update again
+ entry->CreateReference(entry_to_update);
+ // Mark the texture update as used, as if it was loaded directly
+ entry->frameCount = FRAMECOUNT_INVALID;
}
- ++iter.first;
}
return updated_entry;