Merge pull request #5726 from mimimi085181/minimal-tmem-cache-emulation

Implement minimal emulation of TMEM caching
This commit is contained in:
Anthony 2017-07-11 22:49:19 -07:00 committed by GitHub
commit ab5a5ee3ea
5 changed files with 60 additions and 16 deletions

View File

@ -16,7 +16,3 @@ EmulationIssues = Needs efb to Ram for proper lighting.
[ActionReplay] [ActionReplay]
# Add action replay cheats here. # Add action replay cheats here.
[Video_Hacks]
EFBToTextureEnable = False

View File

@ -285,6 +285,8 @@ static void BPWritten(const BPCmd& bp)
if (g_bRecordFifoData) if (g_bRecordFifoData)
FifoRecorder::GetInstance().UseMemory(addr, tlutXferCount, MemoryUpdate::TMEM); FifoRecorder::GetInstance().UseMemory(addr, tlutXferCount, MemoryUpdate::TMEM);
TextureCacheBase::InvalidateAllBindPoints();
return; return;
} }
case BPMEM_FOGRANGE: // Fog Settings Control case BPMEM_FOGRANGE: // Fog Settings Control
@ -397,6 +399,7 @@ static void BPWritten(const BPCmd& bp)
return; return;
case BPMEM_TEXINVALIDATE: case BPMEM_TEXINVALIDATE:
// TODO: Needs some restructuring in TextureCacheBase. // TODO: Needs some restructuring in TextureCacheBase.
TextureCacheBase::InvalidateAllBindPoints();
return; return;
case BPMEM_ZCOMPARE: // Set the Z-Compare and EFB pixel format case BPMEM_ZCOMPARE: // Set the Z-Compare and EFB pixel format
@ -499,6 +502,8 @@ static void BPWritten(const BPCmd& bp)
if (g_bRecordFifoData) if (g_bRecordFifoData)
FifoRecorder::GetInstance().UseMemory(src_addr, bytes_read, MemoryUpdate::TMEM); FifoRecorder::GetInstance().UseMemory(src_addr, bytes_read, MemoryUpdate::TMEM);
TextureCacheBase::InvalidateAllBindPoints();
} }
return; return;
@ -582,10 +587,12 @@ static void BPWritten(const BPCmd& bp)
// ------------------------ // ------------------------
case BPMEM_TX_SETMODE0: // (0x90 for linear) case BPMEM_TX_SETMODE0: // (0x90 for linear)
case BPMEM_TX_SETMODE0_4: case BPMEM_TX_SETMODE0_4:
TextureCacheBase::InvalidateAllBindPoints();
return; return;
case BPMEM_TX_SETMODE1: case BPMEM_TX_SETMODE1:
case BPMEM_TX_SETMODE1_4: case BPMEM_TX_SETMODE1_4:
TextureCacheBase::InvalidateAllBindPoints();
return; return;
// -------------------------------------------- // --------------------------------------------
// BPMEM_TX_SETIMAGE0 - Texture width, height, format // BPMEM_TX_SETIMAGE0 - Texture width, height, format
@ -602,6 +609,7 @@ static void BPWritten(const BPCmd& bp)
case BPMEM_TX_SETIMAGE2_4: case BPMEM_TX_SETIMAGE2_4:
case BPMEM_TX_SETIMAGE3: case BPMEM_TX_SETIMAGE3:
case BPMEM_TX_SETIMAGE3_4: case BPMEM_TX_SETIMAGE3_4:
TextureCacheBase::InvalidateAllBindPoints();
return; return;
// ------------------------------- // -------------------------------
// Set a TLUT // Set a TLUT
@ -609,6 +617,7 @@ static void BPWritten(const BPCmd& bp)
// ------------------------------- // -------------------------------
case BPMEM_TX_SETTLUT: case BPMEM_TX_SETTLUT:
case BPMEM_TX_SETTLUT_4: case BPMEM_TX_SETTLUT_4:
TextureCacheBase::InvalidateAllBindPoints();
return; return;
default: default:

View File

@ -42,6 +42,8 @@ static const int TEXTURE_POOL_KILL_THRESHOLD = 3;
std::unique_ptr<TextureCacheBase> g_texture_cache; std::unique_ptr<TextureCacheBase> g_texture_cache;
std::bitset<8> TextureCacheBase::valid_bind_points;
TextureCacheBase::TCacheEntry::TCacheEntry(std::unique_ptr<AbstractTexture> tex) TextureCacheBase::TCacheEntry::TCacheEntry(std::unique_ptr<AbstractTexture> tex)
: texture(std::move(tex)) : texture(std::move(tex))
{ {
@ -76,11 +78,17 @@ TextureCacheBase::TextureCacheBase()
HiresTexture::Init(); HiresTexture::Init();
SetHash64Function(); SetHash64Function();
InvalidateAllBindPoints();
} }
void TextureCacheBase::Invalidate() void TextureCacheBase::Invalidate()
{ {
UnbindTextures(); InvalidateAllBindPoints();
for (size_t i = 0; i < bound_textures.size(); ++i)
{
bound_textures[i] = nullptr;
}
for (auto& tex : textures_by_address) for (auto& tex : textures_by_address)
{ {
@ -138,7 +146,11 @@ void TextureCacheBase::Cleanup(int _frameCount)
TexAddrCache::iterator tcend = textures_by_address.end(); TexAddrCache::iterator tcend = textures_by_address.end();
while (iter != tcend) while (iter != tcend)
{ {
if (iter->second->frameCount == FRAMECOUNT_INVALID) if (iter->second->tmem_only)
{
iter = InvalidateTexture(iter);
}
else if (iter->second->frameCount == FRAMECOUNT_INVALID)
{ {
iter->second->frameCount = _frameCount; iter->second->frameCount = _frameCount;
++iter; ++iter;
@ -307,7 +319,7 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, u8* pale
while (iter.first != iter.second) while (iter.first != iter.second)
{ {
TCacheEntry* entry = iter.first->second; TCacheEntry* entry = iter.first->second;
if (entry != entry_to_update && entry->IsEfbCopy() && if (entry != entry_to_update && entry->IsEfbCopy() && !entry->tmem_only &&
entry->references.count(entry_to_update) == 0 && entry->references.count(entry_to_update) == 0 &&
entry->OverlapsMemoryRange(entry_to_update->addr, entry_to_update->size_in_bytes) && entry->OverlapsMemoryRange(entry_to_update->addr, entry_to_update->size_in_bytes) &&
entry->memory_stride == numBlocksX * block_size) entry->memory_stride == numBlocksX * block_size)
@ -450,6 +462,9 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::ReturnEntry(unsigned int stage,
GFX_DEBUGGER_PAUSE_AT(NEXT_TEXTURE_CHANGE, true); GFX_DEBUGGER_PAUSE_AT(NEXT_TEXTURE_CHANGE, true);
// We need to keep track of invalided textures until they have actually been replaced or re-loaded
valid_bind_points.set(stage);
return entry; return entry;
} }
@ -457,18 +472,19 @@ void TextureCacheBase::BindTextures()
{ {
for (size_t i = 0; i < bound_textures.size(); ++i) for (size_t i = 0; i < bound_textures.size(); ++i)
{ {
if (bound_textures[i]) if (IsValidBindPoint(static_cast<u32>(i)) && bound_textures[i])
bound_textures[i]->texture->Bind(static_cast<u32>(i)); bound_textures[i]->texture->Bind(static_cast<u32>(i));
} }
} }
void TextureCacheBase::UnbindTextures()
{
bound_textures.fill(nullptr);
}
TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage) TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
{ {
// if this stage was not invalidated by changes to texture registers, keep the current texture
if (IsValidBindPoint(stage) && bound_textures[stage])
{
return ReturnEntry(stage, bound_textures[stage]);
}
const FourTexUnits& tex = bpmem.tex[stage >> 2]; const FourTexUnits& tex = bpmem.tex[stage >> 2];
const u32 id = stage & 3; const u32 id = stage & 3;
const u32 address = (tex.texImage3[id].image_base /* & 0x1FFFFF*/) << 5; const u32 address = (tex.texImage3[id].image_base /* & 0x1FFFFF*/) << 5;
@ -610,6 +626,14 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
while (iter != iter_range.second) while (iter != iter_range.second)
{ {
TCacheEntry* entry = iter->second; TCacheEntry* entry = iter->second;
// Skip entries that are only left in our texture cache for the tmem cache emulation
if (entry->tmem_only)
{
++iter;
continue;
}
// 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
if (entry->IsEfbCopy() && entry->native_width == nativeW && entry->native_height == nativeH && if (entry->IsEfbCopy() && entry->native_width == nativeW && entry->native_height == nativeH &&
entry->memory_stride == entry->BytesPerRow()) entry->memory_stride == entry->BytesPerRow())
@ -1466,6 +1490,18 @@ TextureCacheBase::InvalidateTexture(TexAddrCache::iterator iter)
entry->textures_by_hash_iter = textures_by_hash.end(); entry->textures_by_hash_iter = textures_by_hash.end();
} }
for (size_t i = 0; i < bound_textures.size(); ++i)
{
// If the entry is currently bound and not invalidated, keep it, but mark it as invalidated.
// This way it can still be used via tmem cache emulation, but nothing else.
// Spyro: A Hero's Tail is known for using such overwritten textures.
if (bound_textures[i] == entry && IsValidBindPoint(static_cast<u32>(i)))
{
bound_textures[i]->tmem_only = true;
return ++iter;
}
}
auto config = entry->texture->GetConfig(); auto config = entry->texture->GetConfig();
texture_pool.emplace(config, TexPoolEntry(std::move(entry->texture))); texture_pool.emplace(config, TexPoolEntry(std::move(entry->texture)));

View File

@ -5,6 +5,7 @@
#pragma once #pragma once
#include <array> #include <array>
#include <bitset>
#include <map> #include <map>
#include <memory> #include <memory>
#include <tuple> #include <tuple>
@ -39,6 +40,7 @@ public:
bool is_efb_copy; bool is_efb_copy;
bool is_custom_tex; bool is_custom_tex;
bool may_have_overlapping_textures = true; bool may_have_overlapping_textures = true;
bool tmem_only = false; // indicates that this texture only exists in the tmem cache
unsigned int native_width, unsigned int native_width,
native_height; // Texture dimensions from the GameCube's point of view native_height; // Texture dimensions from the GameCube's point of view
@ -125,8 +127,9 @@ public:
virtual void DeleteShaders() = 0; virtual void DeleteShaders() = 0;
TCacheEntry* Load(const u32 stage); TCacheEntry* Load(const u32 stage);
void UnbindTextures(); static void InvalidateAllBindPoints() { valid_bind_points.reset(); }
virtual void BindTextures(); static bool IsValidBindPoint(u32 i) { return valid_bind_points.test(i); }
void BindTextures();
void CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat, u32 dstStride, void CopyRenderTargetToTexture(u32 dstAddr, unsigned int dstFormat, u32 dstStride,
bool is_depth_copy, const EFBRectangle& srcRect, bool isIntensity, bool is_depth_copy, const EFBRectangle& srcRect, bool isIntensity,
bool scaleByHalf); bool scaleByHalf);
@ -158,6 +161,7 @@ protected:
size_t temp_size = 0; size_t temp_size = 0;
std::array<TCacheEntry*, 8> bound_textures{}; std::array<TCacheEntry*, 8> bound_textures{};
static std::bitset<8> valid_bind_points;
private: private:
// Minimal version of TCacheEntry just for TexPool // Minimal version of TCacheEntry just for TexPool

View File

@ -243,7 +243,6 @@ void VertexManagerBase::Flush()
if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages) if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages)
usedtextures[bpmem.tevindref.getTexMap(bpmem.tevind[i].bt)] = true; usedtextures[bpmem.tevindref.getTexMap(bpmem.tevind[i].bt)] = true;
g_texture_cache->UnbindTextures();
for (unsigned int i : usedtextures) for (unsigned int i : usedtextures)
{ {
const auto* tentry = g_texture_cache->Load(i); const auto* tentry = g_texture_cache->Load(i);