summaryrefslogtreecommitdiff
path: root/Source/Core/VideoCommon/TextureCacheBase.cpp
diff options
context:
space:
mode:
authorScott Mansell <phiren@gmail.com>2023-02-09 19:59:16 +1300
committerGitHub <noreply@github.com>2023-02-09 19:59:16 +1300
commitccf92a3e56764d6378c436a8040519a5bffc3372 (patch)
treee2b330b225044e1901e77a6ed147cf21b9d7ac0e /Source/Core/VideoCommon/TextureCacheBase.cpp
parent72b22ef0a54f841b1c52d49cf0e00d3b12c29ac7 (diff)
parent5c1b3ac61d7e660a9d1c206dbd0e615d637d3272 (diff)
Merge pull request #11522 from phire/KillRendererWithFire
Kill Renderer (with phire)
Diffstat (limited to 'Source/Core/VideoCommon/TextureCacheBase.cpp')
-rw-r--r--Source/Core/VideoCommon/TextureCacheBase.cpp585
1 files changed, 311 insertions, 274 deletions
diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp
index 14cf207034..8b5e56af37 100644
--- a/Source/Core/VideoCommon/TextureCacheBase.cpp
+++ b/Source/Core/VideoCommon/TextureCacheBase.cpp
@@ -34,15 +34,17 @@
#include "Core/System.h"
#include "VideoCommon/AbstractFramebuffer.h"
+#include "VideoCommon/AbstractGfx.h"
#include "VideoCommon/AbstractStagingTexture.h"
#include "VideoCommon/BPMemory.h"
#include "VideoCommon/FramebufferManager.h"
#include "VideoCommon/GraphicsModSystem/Runtime/FBInfo.h"
#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModActionData.h"
+#include "VideoCommon/GraphicsModSystem/Runtime/GraphicsModManager.h"
#include "VideoCommon/HiresTextures.h"
#include "VideoCommon/OpcodeDecoding.h"
#include "VideoCommon/PixelShaderManager.h"
-#include "VideoCommon/RenderBase.h"
+#include "VideoCommon/Present.h"
#include "VideoCommon/ShaderCache.h"
#include "VideoCommon/Statistics.h"
#include "VideoCommon/TMEM.h"
@@ -62,16 +64,18 @@ static int xfb_count = 0;
std::unique_ptr<TextureCacheBase> g_texture_cache;
-TextureCacheBase::TCacheEntry::TCacheEntry(std::unique_ptr<AbstractTexture> tex,
- std::unique_ptr<AbstractFramebuffer> fb)
+TCacheEntry::TCacheEntry(std::unique_ptr<AbstractTexture> tex,
+ std::unique_ptr<AbstractFramebuffer> fb)
: texture(std::move(tex)), framebuffer(std::move(fb))
{
}
-TextureCacheBase::TCacheEntry::~TCacheEntry()
+TCacheEntry::~TCacheEntry()
{
for (auto& reference : references)
reference->references.erase(this);
+ ASSERT_MSG(VIDEO, g_texture_cache, "Texture cache destroyed before TCacheEntry was destroyed");
+ g_texture_cache->ReleaseToPool(this);
}
void TextureCacheBase::CheckTempSize(size_t required_size)
@@ -99,13 +103,19 @@ TextureCacheBase::TextureCacheBase()
TMEM::InvalidateAll();
}
-TextureCacheBase::~TextureCacheBase()
+void TextureCacheBase::Shutdown()
{
// Clear pending EFB copies first, so we don't try to flush them.
m_pending_efb_copies.clear();
HiresTexture::Shutdown();
+
+ // For correctness, we need to invalidate textures before the gpu context starts shutting down.
Invalidate();
+}
+
+TextureCacheBase::~TextureCacheBase()
+{
Common::FreeAlignedMemory(temp);
temp = nullptr;
}
@@ -126,13 +136,10 @@ void TextureCacheBase::Invalidate()
FlushEFBCopies();
TMEM::InvalidateAll();
- bound_textures.fill(nullptr);
- for (auto& tex : textures_by_address)
- {
- delete tex.second;
- }
- textures_by_address.clear();
+ for (auto& bind : bound_textures)
+ bind.reset();
textures_by_hash.clear();
+ textures_by_address.clear();
texture_pool.clear();
}
@@ -183,11 +190,7 @@ void TextureCacheBase::Cleanup(int _frameCount)
TexAddrCache::iterator tcend = textures_by_address.end();
while (iter != tcend)
{
- if (iter->second->tmem_only)
- {
- iter = InvalidateTexture(iter);
- }
- else if (iter->second->frameCount == FRAMECOUNT_INVALID)
+ if (iter->second->frameCount == FRAMECOUNT_INVALID)
{
iter->second->frameCount = _frameCount;
++iter;
@@ -240,7 +243,7 @@ void TextureCacheBase::Cleanup(int _frameCount)
}
}
-bool TextureCacheBase::TCacheEntry::OverlapsMemoryRange(u32 range_address, u32 range_size) const
+bool TCacheEntry::OverlapsMemoryRange(u32 range_address, u32 range_size) const
{
if (addr + size_in_bytes <= range_address)
return false;
@@ -268,8 +271,8 @@ void TextureCacheBase::SetBackupConfig(const VideoConfig& config)
config.graphics_mod_config ? config.graphics_mod_config->GetChangeCount() : 0;
}
-TextureCacheBase::TCacheEntry*
-TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, const u8* palette, TLUTFormat tlutfmt)
+RcTcacheEntry TextureCacheBase::ApplyPaletteToEntry(RcTcacheEntry& entry, const u8* palette,
+ TLUTFormat tlutfmt)
{
DEBUG_ASSERT(g_ActiveConfig.backend_info.bSupportsPaletteConversion);
@@ -277,16 +280,16 @@ TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, const u8* palette, TLU
if (!pipeline)
{
ERROR_LOG_FMT(VIDEO, "Failed to get conversion pipeline for format {}", tlutfmt);
- return nullptr;
+ return {};
}
TextureConfig new_config = entry->texture->GetConfig();
new_config.levels = 1;
new_config.flags |= AbstractTextureFlag_RenderTarget;
- TCacheEntry* decoded_entry = AllocateCacheEntry(new_config);
+ RcTcacheEntry decoded_entry = AllocateCacheEntry(new_config);
if (!decoded_entry)
- return nullptr;
+ return decoded_entry;
decoded_entry->SetGeneralParameters(entry->addr, entry->size_in_bytes, entry->format,
entry->should_force_safe_hashing);
@@ -297,7 +300,7 @@ TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, const u8* palette, TLU
decoded_entry->SetNotCopy();
decoded_entry->may_have_overlapping_textures = entry->may_have_overlapping_textures;
- g_renderer->BeginUtilityDrawing();
+ g_gfx->BeginUtilityDrawing();
const u32 palette_size = entry->format == TextureFormat::I4 ? 32 : 512;
u32 texel_buffer_offset;
@@ -317,19 +320,19 @@ TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, const u8* palette, TLU
uniforms.texel_buffer_offset = texel_buffer_offset;
g_vertex_manager->UploadUtilityUniforms(&uniforms, sizeof(uniforms));
- g_renderer->SetAndDiscardFramebuffer(decoded_entry->framebuffer.get());
- g_renderer->SetViewportAndScissor(decoded_entry->texture->GetRect());
- g_renderer->SetPipeline(pipeline);
- g_renderer->SetTexture(1, entry->texture.get());
- g_renderer->SetSamplerState(1, RenderState::GetPointSamplerState());
- g_renderer->Draw(0, 3);
- g_renderer->EndUtilityDrawing();
+ g_gfx->SetAndDiscardFramebuffer(decoded_entry->framebuffer.get());
+ g_gfx->SetViewportAndScissor(decoded_entry->texture->GetRect());
+ g_gfx->SetPipeline(pipeline);
+ g_gfx->SetTexture(1, entry->texture.get());
+ g_gfx->SetSamplerState(1, RenderState::GetPointSamplerState());
+ g_gfx->Draw(0, 3);
+ g_gfx->EndUtilityDrawing();
decoded_entry->texture->FinishedRendering();
}
else
{
ERROR_LOG_FMT(VIDEO, "Texel buffer upload of {} bytes failed", palette_size);
- g_renderer->EndUtilityDrawing();
+ g_gfx->EndUtilityDrawing();
}
textures_by_address.emplace(decoded_entry->addr, decoded_entry);
@@ -337,8 +340,8 @@ TextureCacheBase::ApplyPaletteToEntry(TCacheEntry* entry, const u8* palette, TLU
return decoded_entry;
}
-TextureCacheBase::TCacheEntry* TextureCacheBase::ReinterpretEntry(const TCacheEntry* existing_entry,
- TextureFormat new_format)
+RcTcacheEntry TextureCacheBase::ReinterpretEntry(const RcTcacheEntry& existing_entry,
+ TextureFormat new_format)
{
const AbstractPipeline* pipeline =
g_shader_cache->GetTextureReinterpretPipeline(existing_entry->format.texfmt, new_format);
@@ -346,16 +349,16 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::ReinterpretEntry(const TCacheEn
{
ERROR_LOG_FMT(VIDEO, "Failed to obtain texture reinterpreting pipeline from format {} to {}",
existing_entry->format.texfmt, new_format);
- return nullptr;
+ return {};
}
TextureConfig new_config = existing_entry->texture->GetConfig();
new_config.levels = 1;
new_config.flags |= AbstractTextureFlag_RenderTarget;
- TCacheEntry* reinterpreted_entry = AllocateCacheEntry(new_config);
+ RcTcacheEntry reinterpreted_entry = AllocateCacheEntry(new_config);
if (!reinterpreted_entry)
- return nullptr;
+ return {};
reinterpreted_entry->SetGeneralParameters(existing_entry->addr, existing_entry->size_in_bytes,
new_format, existing_entry->should_force_safe_hashing);
@@ -368,14 +371,14 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::ReinterpretEntry(const TCacheEn
reinterpreted_entry->may_have_overlapping_textures =
existing_entry->may_have_overlapping_textures;
- g_renderer->BeginUtilityDrawing();
- g_renderer->SetAndDiscardFramebuffer(reinterpreted_entry->framebuffer.get());
- g_renderer->SetViewportAndScissor(reinterpreted_entry->texture->GetRect());
- g_renderer->SetPipeline(pipeline);
- g_renderer->SetTexture(0, existing_entry->texture.get());
- g_renderer->SetSamplerState(1, RenderState::GetPointSamplerState());
- g_renderer->Draw(0, 3);
- g_renderer->EndUtilityDrawing();
+ g_gfx->BeginUtilityDrawing();
+ g_gfx->SetAndDiscardFramebuffer(reinterpreted_entry->framebuffer.get());
+ g_gfx->SetViewportAndScissor(reinterpreted_entry->texture->GetRect());
+ g_gfx->SetPipeline(pipeline);
+ g_gfx->SetTexture(0, existing_entry->texture.get());
+ g_gfx->SetSamplerState(1, RenderState::GetPointSamplerState());
+ g_gfx->Draw(0, 3);
+ g_gfx->EndUtilityDrawing();
reinterpreted_entry->texture->FinishedRendering();
textures_by_address.emplace(reinterpreted_entry->addr, reinterpreted_entry);
@@ -383,8 +386,7 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::ReinterpretEntry(const TCacheEn
return reinterpreted_entry;
}
-void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntry* entry, u32 new_width,
- u32 new_height)
+void TextureCacheBase::ScaleTextureCacheEntryTo(RcTcacheEntry& entry, u32 new_width, u32 new_height)
{
if (entry->GetWidth() == new_width && entry->GetHeight() == new_height)
{
@@ -408,9 +410,8 @@ void TextureCacheBase::ScaleTextureCacheEntryTo(TextureCacheBase::TCacheEntry* e
}
// No need to convert the coordinates here since they'll be the same.
- g_renderer->ScaleTexture(new_texture->framebuffer.get(),
- new_texture->texture->GetConfig().GetRect(), entry->texture.get(),
- entry->texture->GetConfig().GetRect());
+ g_gfx->ScaleTexture(new_texture->framebuffer.get(), new_texture->texture->GetConfig().GetRect(),
+ entry->texture.get(), entry->texture->GetConfig().GetRect());
entry->texture.swap(new_texture->texture);
entry->framebuffer.swap(new_texture->framebuffer);
@@ -432,8 +433,7 @@ bool TextureCacheBase::CheckReadbackTexture(u32 width, u32 height, AbstractTextu
TextureConfig staging_config(std::max(width, 128u), std::max(height, 128u), 1, 1, 1, format, 0);
m_readback_texture.reset();
- m_readback_texture =
- g_renderer->CreateStagingTexture(StagingTextureType::Readback, staging_config);
+ m_readback_texture = g_gfx->CreateStagingTexture(StagingTextureType::Readback, staging_config);
return m_readback_texture != nullptr;
}
@@ -559,15 +559,19 @@ void TextureCacheBase::DoState(PointerWrap& p)
void TextureCacheBase::DoSaveState(PointerWrap& p)
{
+ // Flush all stale binds
+ FlushStaleBinds();
+
std::map<const TCacheEntry*, u32> entry_map;
std::vector<TCacheEntry*> entries_to_save;
- auto ShouldSaveEntry = [](const TCacheEntry* entry) {
+ auto ShouldSaveEntry = [](const RcTcacheEntry& entry) {
// We skip non-copies as they can be decoded from RAM when the state is loaded.
// Storing them would duplicate data in the save state file, adding to decompression time.
- return entry->IsCopy();
+ // We also need to store invalidated entires, as they can't be restored from RAM.
+ return entry->IsCopy() || entry->invalidated;
};
- auto AddCacheEntryToMap = [&entry_map, &entries_to_save](TCacheEntry* entry) -> u32 {
- auto iter = entry_map.find(entry);
+ auto AddCacheEntryToMap = [&entry_map, &entries_to_save](const RcTcacheEntry& entry) -> u32 {
+ auto iter = entry_map.find(entry.get());
if (iter != entry_map.end())
return iter->second;
@@ -575,8 +579,8 @@ void TextureCacheBase::DoSaveState(PointerWrap& p)
// same order they were collected. This is because of iterating both the address and hash maps.
// Therefore, the map is used for fast lookup, and the vector for ordering.
u32 id = static_cast<u32>(entry_map.size());
- entry_map.emplace(entry, id);
- entries_to_save.push_back(entry);
+ entry_map.emplace(entry.get(), id);
+ entries_to_save.push_back(entry.get());
return id;
};
auto GetCacheEntryId = [&entry_map](const TCacheEntry* entry) -> std::optional<u32> {
@@ -588,6 +592,7 @@ void TextureCacheBase::DoSaveState(PointerWrap& p)
// of address/hash to entry ID.
std::vector<std::pair<u32, u32>> textures_by_address_list;
std::vector<std::pair<u64, u32>> textures_by_hash_list;
+ std::vector<std::pair<u32, u32>> bound_textures_list;
if (Config::Get(Config::GFX_SAVE_TEXTURE_CACHE_TO_STATE))
{
for (const auto& it : textures_by_address)
@@ -606,6 +611,15 @@ void TextureCacheBase::DoSaveState(PointerWrap& p)
textures_by_hash_list.emplace_back(it.first, id);
}
}
+ for (u32 i = 0; i < bound_textures.size(); i++)
+ {
+ const auto& tentry = bound_textures[i];
+ if (bound_textures[i] && ShouldSaveEntry(tentry))
+ {
+ const u32 id = AddCacheEntryToMap(tentry);
+ bound_textures_list.emplace_back(i, id);
+ }
+ }
}
// Save the texture cache entries out in the order the were referenced.
@@ -641,29 +655,20 @@ void TextureCacheBase::DoSaveState(PointerWrap& p)
}
}
- size = static_cast<u32>(reference_pairs.size());
- p.Do(size);
- for (const auto& it : reference_pairs)
- {
- p.Do(it.first);
- p.Do(it.second);
- }
-
- size = static_cast<u32>(textures_by_address_list.size());
- p.Do(size);
- for (const auto& it : textures_by_address_list)
- {
- p.Do(it.first);
- p.Do(it.second);
- }
+ auto doList = [&p](auto list) {
+ u32 size = static_cast<u32>(list.size());
+ p.Do(size);
+ for (const auto& it : list)
+ {
+ p.Do(it.first);
+ p.Do(it.second);
+ }
+ };
- size = static_cast<u32>(textures_by_hash_list.size());
- p.Do(size);
- for (const auto& it : textures_by_hash_list)
- {
- p.Do(it.first);
- p.Do(it.second);
- }
+ doList(reference_pairs);
+ doList(textures_by_address_list);
+ doList(textures_by_hash_list);
+ doList(bound_textures_list);
// Free the readback texture to potentially save host-mapped GPU memory, depending on where
// the driver mapped the staging buffer.
@@ -673,10 +678,11 @@ void TextureCacheBase::DoSaveState(PointerWrap& p)
void TextureCacheBase::DoLoadState(PointerWrap& p)
{
// Helper for getting a cache entry from an ID.
- std::map<u32, TCacheEntry*> id_map;
- auto GetEntry = [&id_map](u32 id) {
+ std::map<u32, RcTcacheEntry> id_map;
+ RcTcacheEntry null_entry;
+ auto GetEntry = [&id_map, &null_entry](u32 id) -> RcTcacheEntry& {
auto iter = id_map.find(id);
- return iter == id_map.end() ? nullptr : iter->second;
+ return iter == id_map.end() ? null_entry : iter->second;
};
// Only clear out state when actually restoring/loading.
@@ -694,13 +700,12 @@ void TextureCacheBase::DoLoadState(PointerWrap& p)
// Even if the texture isn't valid, we still need to create the cache entry object
// to update the point in the state state. We'll just throw it away if it's invalid.
auto tex = DeserializeTexture(p);
- TCacheEntry* entry = new TCacheEntry(std::move(tex->texture), std::move(tex->framebuffer));
+ auto entry =
+ std::make_shared<TCacheEntry>(std::move(tex->texture), std::move(tex->framebuffer));
entry->textures_by_hash_iter = textures_by_hash.end();
entry->DoState(p);
if (entry->texture && commit_state)
id_map.emplace(i, entry);
- else
- delete entry;
}
p.DoMarker("TextureCacheEntries");
@@ -711,10 +716,10 @@ void TextureCacheBase::DoLoadState(PointerWrap& p)
u32 id1 = 0, id2 = 0;
p.Do(id1);
p.Do(id2);
- TCacheEntry* e1 = GetEntry(id1);
- TCacheEntry* e2 = GetEntry(id2);
+ auto e1 = GetEntry(id1);
+ auto e2 = GetEntry(id2);
if (e1 && e2)
- e1->CreateReference(e2);
+ e1->CreateReference(e2.get());
}
// Fill in address map.
@@ -726,7 +731,7 @@ void TextureCacheBase::DoLoadState(PointerWrap& p)
p.Do(addr);
p.Do(id);
- TCacheEntry* entry = GetEntry(id);
+ auto& entry = GetEntry(id);
if (entry)
textures_by_address.emplace(addr, entry);
}
@@ -740,13 +745,48 @@ void TextureCacheBase::DoLoadState(PointerWrap& p)
p.Do(hash);
p.Do(id);
- TCacheEntry* entry = GetEntry(id);
+ auto& entry = GetEntry(id);
if (entry)
entry->textures_by_hash_iter = textures_by_hash.emplace(hash, entry);
}
+
+ // Clear bound textures
+ for (u32 i = 0; i < bound_textures.size(); i++)
+ bound_textures[i].reset();
+
+ // Fill in bound textures
+ p.Do(size);
+ for (u32 i = 0; i < size; i++)
+ {
+ u32 index = 0;
+ u32 id = 0;
+ p.Do(index);
+ p.Do(id);
+
+ auto& entry = GetEntry(id);
+ if (entry)
+ bound_textures[index] = entry;
+ }
}
-void TextureCacheBase::TCacheEntry::DoState(PointerWrap& p)
+void TextureCacheBase::OnFrameEnd()
+{
+ if (m_force_reload_textures.TestAndClear())
+ {
+ ForceReload();
+ }
+ else
+ {
+ // Flush any outstanding EFB copies to RAM, in case the game is running at an uncapped frame
+ // rate and not waiting for vblank. Otherwise, we'd end up with a huge list of pending
+ // copies.
+ FlushEFBCopies();
+ }
+
+ Cleanup(g_presenter->FrameCount());
+}
+
+void TCacheEntry::DoState(PointerWrap& p)
{
p.Do(addr);
p.Do(size_in_bytes);
@@ -757,7 +797,7 @@ void TextureCacheBase::TCacheEntry::DoState(PointerWrap& p)
p.Do(is_efb_copy);
p.Do(is_custom_tex);
p.Do(may_have_overlapping_textures);
- p.Do(tmem_only);
+ p.Do(invalidated);
p.Do(has_arbitrary_mips);
p.Do(should_force_safe_hashing);
p.Do(is_xfb_copy);
@@ -770,9 +810,8 @@ void TextureCacheBase::TCacheEntry::DoState(PointerWrap& p)
p.Do(frameCount);
}
-TextureCacheBase::TCacheEntry*
-TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8* palette,
- TLUTFormat tlutfmt)
+RcTcacheEntry TextureCacheBase::DoPartialTextureUpdates(RcTcacheEntry& entry_to_update,
+ const u8* palette, TLUTFormat tlutfmt)
{
// If the flag may_have_overlapping_textures is cleared, there are no overlapping EFB copies,
// which aren't applied already. It is set for new textures, and for the affected range
@@ -788,6 +827,13 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8
if (entry_to_update->IsCopy())
return entry_to_update;
+ if (entry_to_update->IsLocked())
+ {
+ // TODO: Shouldn't be too hard, just need to clone the texture entry + texture contents.
+ PanicAlertFmt("TextureCache: PartialTextureUpdates of locked textures is not implemented");
+ return {};
+ }
+
u32 block_width = TexDecoder_GetBlockWidthInTexels(entry_to_update->format.texfmt);
u32 block_height = TexDecoder_GetBlockHeightInTexels(entry_to_update->format.texfmt);
u32 block_size = block_width * block_height *
@@ -798,9 +844,9 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8
auto iter = FindOverlappingTextures(entry_to_update->addr, entry_to_update->size_in_bytes);
while (iter.first != iter.second)
{
- TCacheEntry* entry = iter.first->second;
- if (entry != entry_to_update && entry->IsCopy() && !entry->tmem_only &&
- entry->references.count(entry_to_update) == 0 &&
+ auto& entry = iter.first->second;
+ if (entry != entry_to_update && entry->IsCopy() &&
+ entry->references.count(entry_to_update.get()) == 0 &&
entry->OverlapsMemoryRange(entry_to_update->addr, entry_to_update->size_in_bytes) &&
entry->memory_stride == numBlocksX * block_size)
{
@@ -815,20 +861,19 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8
continue;
}
- TCacheEntry* reinterpreted_entry =
- ReinterpretEntry(entry, entry_to_update->format.texfmt);
+ auto reinterpreted_entry = ReinterpretEntry(entry, entry_to_update->format.texfmt);
if (reinterpreted_entry)
entry = reinterpreted_entry;
}
if (isPaletteTexture)
{
- TCacheEntry* decoded_entry = ApplyPaletteToEntry(entry, palette, tlutfmt);
+ auto decoded_entry = ApplyPaletteToEntry(entry, palette, 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);
+ entry->CreateReference(entry_to_update.get());
// Mark the texture update as used, as if it was loaded directly
entry->frameCount = FRAMECOUNT_INVALID;
entry = decoded_entry;
@@ -875,18 +920,18 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8
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);
+ ScaleTextureCacheEntryTo(
+ entry_to_update, g_framebuffer_manager->EFBToScaledX(entry_to_update->native_width),
+ g_framebuffer_manager->EFBToScaledY(entry_to_update->native_height));
+ ScaleTextureCacheEntryTo(entry, g_framebuffer_manager->EFBToScaledX(entry->native_width),
+ g_framebuffer_manager->EFBToScaledY(entry->native_height));
+
+ src_x = g_framebuffer_manager->EFBToScaledX(src_x);
+ src_y = g_framebuffer_manager->EFBToScaledY(src_y);
+ dst_x = g_framebuffer_manager->EFBToScaledX(dst_x);
+ dst_y = g_framebuffer_manager->EFBToScaledY(dst_y);
+ copy_width = g_framebuffer_manager->EFBToScaledX(copy_width);
+ copy_height = g_framebuffer_manager->EFBToScaledY(copy_height);
}
// If the source rectangle is outside of what we actually have in VRAM, skip the copy.
@@ -929,7 +974,7 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8
else
{
// Link the two textures together, so we won't apply this partial update again
- entry->CreateReference(entry_to_update);
+ entry->CreateReference(entry_to_update.get());
// Mark the texture update as used, as if it was loaded directly
entry->frameCount = FRAMECOUNT_INVALID;
}
@@ -947,7 +992,7 @@ TextureCacheBase::DoPartialTextureUpdates(TCacheEntry* entry_to_update, const u8
return entry_to_update;
}
-void TextureCacheBase::DumpTexture(TCacheEntry* entry, std::string basename, unsigned int level,
+void TextureCacheBase::DumpTexture(RcTcacheEntry& entry, std::string basename, unsigned int level,
bool is_arbitrary)
{
std::string szDir = File::GetUserPath(D_DUMPTEXTURES_IDX) + SConfig::GetInstance().GetGameID();
@@ -1046,14 +1091,14 @@ static void SetSamplerState(u32 index, float custom_tex_scale, bool custom_tex,
// that have arbitrary contents, eg. are used for fog effects where the
// distance they kick in at is important to preserve at any resolution.
// Correct this with the upscaling factor of custom textures.
- s32 lod_offset = std::log2(g_renderer->GetEFBScale() / custom_tex_scale) * 256.f;
+ s32 lod_offset = std::log2(g_framebuffer_manager->GetEFBScale() / custom_tex_scale) * 256.f;
state.tm0.lod_bias = std::clamp<s32>(state.tm0.lod_bias + lod_offset, -32768, 32767);
// Anisotropic also pushes mips farther away so it cannot be used either
state.tm0.anisotropic_filtering = false;
}
- g_renderer->SetSamplerState(index, state);
+ g_gfx->SetSamplerState(index, state);
auto& system = Core::System::GetInstance();
auto& pixel_shader_manager = system.GetPixelShaderManager();
pixel_shader_manager.SetSamplerState(index, state.tm0.hex, state.tm1.hex);
@@ -1065,10 +1110,10 @@ void TextureCacheBase::BindTextures(BitSet32 used_textures)
auto& pixel_shader_manager = system.GetPixelShaderManager();
for (u32 i = 0; i < bound_textures.size(); i++)
{
- const TCacheEntry* tentry = bound_textures[i];
+ const RcTcacheEntry& tentry = bound_textures[i];
if (used_textures[i] && tentry)
{
- g_renderer->SetTexture(i, tentry->texture.get());
+ g_gfx->SetTexture(i, tentry->texture.get());
pixel_shader_manager.SetTexDims(i, tentry->native_width, tentry->native_height);
const float custom_tex_scale = tentry->GetWidth() / float(tentry->native_width);
@@ -1224,14 +1269,21 @@ private:
std::vector<Level> levels;
};
-TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const TextureInfo& texture_info)
+TCacheEntry* TextureCacheBase::Load(const TextureInfo& texture_info)
{
// if this stage was not invalidated by changes to texture registers, keep the current texture
if (TMEM::IsValid(texture_info.GetStage()) && bound_textures[texture_info.GetStage()])
{
- TCacheEntry* entry = bound_textures[texture_info.GetStage()];
+ TCacheEntry* entry = bound_textures[texture_info.GetStage()].get();
// If the TMEM configuration is such that this texture is more or less guaranteed to still
// be in TMEM, then we know we can reuse the old entry without even hashing the memory
+ //
+ // It's possible this texture has already been overwritten in emulated memory and therfore
+ // invalidated from our texture cache, but we want to use it anyway to approximate the
+ // result of the game using an overwritten texture cached in TMEM.
+ //
+ // Spyro: A Hero's Tail is known for (deliberately?) using such overwritten textures
+ // in it's bloom effect, which breaks without giving it the invalidated texture.
if (TMEM::IsCached(texture_info.GetStage()))
{
return entry;
@@ -1239,7 +1291,7 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const TextureInfo& texture
// Otherwise, hash the backing memory and check it's unchanged.
// FIXME: this doesn't correctly handle textures from tmem.
- if (!entry->tmem_only && entry->base_hash == entry->CalculateHash())
+ if (!entry->invalidated && entry->base_hash == entry->CalculateHash())
{
return entry;
}
@@ -1257,7 +1309,7 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const TextureInfo& texture
GraphicsModActionData::TextureLoad texture_load{entry->texture_info_name};
for (const auto action :
- g_renderer->GetGraphicsModManager().GetTextureLoadActions(entry->texture_info_name))
+ g_graphics_mod_manager->GetTextureLoadActions(entry->texture_info_name))
{
action->OnTextureLoad(&texture_load);
}
@@ -1269,12 +1321,11 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const TextureInfo& texture
TMEM::Bind(texture_info.GetStage(), entry->NumBlocksX(), entry->NumBlocksY(),
entry->GetNumLevels() > 1, entry->format == TextureFormat::RGBA8);
- return entry;
+ return entry.get();
}
-TextureCacheBase::TCacheEntry*
-TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize,
- const TextureInfo& texture_info)
+RcTcacheEntry TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize,
+ const TextureInfo& texture_info)
{
u32 expanded_width = texture_info.GetExpandedWidth();
u32 expanded_height = texture_info.GetExpandedHeight();
@@ -1291,7 +1342,7 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize,
// Reject invalid tlut format.
if (texture_info.GetPaletteSize() && !IsValidTLUTFormat(texture_info.GetTlutFormat()))
- return nullptr;
+ return {};
u32 bytes_per_block = (texture_info.GetBlockWidth() * texture_info.GetBlockHeight() *
TexDecoder_GetTexelSizeInNibbles(texture_info.GetTextureFormat())) /
@@ -1304,7 +1355,7 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize,
{
ERROR_LOG_FMT(VIDEO, "Trying to use an invalid texture address {:#010x}",
texture_info.GetRawAddress());
- return nullptr;
+ return {};
}
// If we are recording a FifoLog, keep track of what memory we read. FifoRecorder does
@@ -1368,14 +1419,7 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize,
while (iter != iter_range.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;
- }
+ RcTcacheEntry& entry = iter->second;
// TODO: Some games (Rogue Squadron 3, Twin Snakes) seem to load a previously made XFB
// copy as a regular texture. You can see this particularly well in RS3 whenever the
@@ -1455,8 +1499,11 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize,
{
entry = DoPartialTextureUpdates(iter->second, texture_info.GetTlutAddress(),
texture_info.GetTlutFormat());
- entry->texture->FinishedRendering();
- return entry;
+ if (entry)
+ {
+ entry->texture->FinishedRendering();
+ return entry;
+ }
}
}
@@ -1478,7 +1525,7 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize,
if (unreinterpreted_copy != textures_by_address.end())
{
- TCacheEntry* decoded_entry =
+ auto decoded_entry =
ReinterpretEntry(unreinterpreted_copy->second, texture_info.GetTextureFormat());
// It's possible to combine reinterpreted textures + palettes.
@@ -1492,7 +1539,7 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize,
if (unconverted_copy != textures_by_address.end())
{
- TCacheEntry* decoded_entry = ApplyPaletteToEntry(
+ auto decoded_entry = ApplyPaletteToEntry(
unconverted_copy->second, texture_info.GetTlutAddress(), texture_info.GetTlutFormat());
if (decoded_entry)
@@ -1515,7 +1562,7 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize,
TexHashCache::iterator hash_iter = hash_range.first;
while (hash_iter != hash_range.second)
{
- TCacheEntry* entry = hash_iter->second;
+ RcTcacheEntry& entry = hash_iter->second;
// All parameters, except the address, need to match here
if (entry->format == full_format && entry->native_levels >= texture_info.GetLevelCount() &&
entry->native_width == texture_info.GetRawWidth() &&
@@ -1523,8 +1570,11 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize,
{
entry = DoPartialTextureUpdates(hash_iter->second, texture_info.GetTlutAddress(),
texture_info.GetTlutFormat());
- entry->texture->FinishedRendering();
- return entry;
+ if (entry)
+ {
+ entry->texture->FinishedRendering();
+ return entry;
+ }
}
++hash_iter;
}
@@ -1577,9 +1627,9 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize,
// create the entry/texture
const TextureConfig config(width, height, texLevels, 1, 1,
hires_tex ? hires_tex->GetFormat() : AbstractTextureFormat::RGBA8, 0);
- TCacheEntry* entry = AllocateCacheEntry(config);
+ RcTcacheEntry entry = AllocateCacheEntry(config);
if (!entry)
- return nullptr;
+ return entry;
ArbitraryMipmapDetector arbitrary_mip_detector;
if (hires_tex)
@@ -1729,7 +1779,7 @@ TextureCacheBase::GetTexture(const int textureCacheSafetyColorSampleSize,
return entry;
}
-static void GetDisplayRectForXFBEntry(TextureCacheBase::TCacheEntry* entry, u32 width, u32 height,
+static void GetDisplayRectForXFBEntry(TCacheEntry* entry, u32 width, u32 height,
MathUtil::Rectangle<int>* display_rect)
{
// Scale the sub-rectangle to the full resolution of the texture.
@@ -1739,9 +1789,8 @@ static void GetDisplayRectForXFBEntry(TextureCacheBase::TCacheEntry* entry, u32
display_rect->bottom = static_cast<int>(height * entry->GetHeight() / entry->native_height);
}
-TextureCacheBase::TCacheEntry*
-TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height, u32 stride,
- MathUtil::Rectangle<int>* display_rect)
+RcTcacheEntry TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height, u32 stride,
+ MathUtil::Rectangle<int>* display_rect)
{
auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory();
@@ -1749,12 +1798,12 @@ TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height, u32 stride,
if (!src_data)
{
ERROR_LOG_FMT(VIDEO, "Trying to load XFB texture from invalid address {:#010x}", address);
- return nullptr;
+ return {};
}
- // Do we currently have a version of this XFB copy in VRAM?
- TCacheEntry* entry = GetXFBFromCache(address, width, height, stride);
- if (entry)
+ // Do we currently have a mutable version of this XFB copy in VRAM?
+ RcTcacheEntry entry = GetXFBFromCache(address, width, height, stride);
+ if (entry && !entry->IsLocked())
{
if (entry->is_xfb_container)
{
@@ -1762,7 +1811,7 @@ TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height, u32 stride,
entry->texture->FinishedRendering();
}
- GetDisplayRectForXFBEntry(entry, width, height, display_rect);
+ GetDisplayRectForXFBEntry(entry.get(), width, height, display_rect);
return entry;
}
@@ -1818,19 +1867,18 @@ TextureCacheBase::GetXFBTexture(u32 address, u32 width, u32 height, u32 stride,
}
}
- GetDisplayRectForXFBEntry(entry, width, height, display_rect);
+ GetDisplayRectForXFBEntry(entry.get(), width, height, display_rect);
return entry;
}
-TextureCacheBase::TCacheEntry* TextureCacheBase::GetXFBFromCache(u32 address, u32 width, u32 height,
- u32 stride)
+RcTcacheEntry TextureCacheBase::GetXFBFromCache(u32 address, u32 width, u32 height, u32 stride)
{
auto iter_range = textures_by_address.equal_range(address);
TexAddrCache::iterator iter = iter_range.first;
while (iter != iter_range.second)
{
- TCacheEntry* entry = iter->second;
+ auto& entry = iter->second;
// The only thing which has to match exactly is the stride. We can use a partial rectangle if
// the VI width/height differs from that of the XFB copy.
@@ -1854,10 +1902,10 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::GetXFBFromCache(u32 address, u3
++iter;
}
- return nullptr;
+ return {};
}
-void TextureCacheBase::StitchXFBCopy(TCacheEntry* stitched_entry)
+void TextureCacheBase::StitchXFBCopy(RcTcacheEntry& stitched_entry)
{
// 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
@@ -1876,8 +1924,8 @@ void TextureCacheBase::StitchXFBCopy(TCacheEntry* stitched_entry)
// our force progressive hack means that an XFB copy should always have a matching stride. If
// the hack is disabled, XFB2RAM should also be enabled. Should we wish to implement interlaced
// stitching in the future, this would require a shader which grabs every second line.
- TCacheEntry* entry = iter.first->second;
- if (entry != stitched_entry && entry->IsCopy() && !entry->tmem_only &&
+ auto& entry = iter.first->second;
+ if (entry != stitched_entry && entry->IsCopy() &&
entry->OverlapsMemoryRange(stitched_entry->addr, stitched_entry->size_in_bytes) &&
entry->memory_stride == stitched_entry->memory_stride)
{
@@ -1887,7 +1935,7 @@ void TextureCacheBase::StitchXFBCopy(TCacheEntry* stitched_entry)
if (entry->native_width != entry->GetWidth())
create_upscaled_copy = true;
- candidates.emplace_back(entry);
+ candidates.emplace_back(entry.get());
}
else
{
@@ -1909,8 +1957,9 @@ void TextureCacheBase::StitchXFBCopy(TCacheEntry* stitched_entry)
// copies to be stitched together.
if (create_upscaled_copy)
{
- ScaleTextureCacheEntryTo(stitched_entry, g_renderer->EFBToScaledX(stitched_entry->native_width),
- g_renderer->EFBToScaledY(stitched_entry->native_height));
+ ScaleTextureCacheEntryTo(stitched_entry,
+ g_framebuffer_manager->EFBToScaledX(stitched_entry->native_width),
+ g_framebuffer_manager->EFBToScaledY(stitched_entry->native_height));
}
for (TCacheEntry* entry : candidates)
@@ -1945,17 +1994,17 @@ void TextureCacheBase::StitchXFBCopy(TCacheEntry* stitched_entry)
// Scale to internal resolution.
if (entry->native_width != entry->GetWidth())
{
- src_x = g_renderer->EFBToScaledX(src_x);
- src_y = g_renderer->EFBToScaledY(src_y);
- src_width = g_renderer->EFBToScaledX(src_width);
- src_height = g_renderer->EFBToScaledY(src_height);
+ src_x = g_framebuffer_manager->EFBToScaledX(src_x);
+ src_y = g_framebuffer_manager->EFBToScaledY(src_y);
+ src_width = g_framebuffer_manager->EFBToScaledX(src_width);
+ src_height = g_framebuffer_manager->EFBToScaledY(src_height);
}
if (create_upscaled_copy)
{
- dst_x = g_renderer->EFBToScaledX(dst_x);
- dst_y = g_renderer->EFBToScaledY(dst_y);
- dst_width = g_renderer->EFBToScaledX(dst_width);
- dst_height = g_renderer->EFBToScaledY(dst_height);
+ dst_x = g_framebuffer_manager->EFBToScaledX(dst_x);
+ dst_y = g_framebuffer_manager->EFBToScaledY(dst_y);
+ dst_width = g_framebuffer_manager->EFBToScaledX(dst_width);
+ dst_height = g_framebuffer_manager->EFBToScaledY(dst_height);
}
// If the source rectangle is outside of what we actually have in VRAM, skip the copy.
@@ -1982,8 +2031,8 @@ void TextureCacheBase::StitchXFBCopy(TCacheEntry* stitched_entry)
// We may have to scale if one of the copies is not internal resolution.
if (srcrect.GetWidth() != dstrect.GetWidth() || srcrect.GetHeight() != dstrect.GetHeight())
{
- g_renderer->ScaleTexture(stitched_entry->framebuffer.get(), dstrect, entry->texture.get(),
- srcrect);
+ g_gfx->ScaleTexture(stitched_entry->framebuffer.get(), dstrect, entry->texture.get(),
+ srcrect);
}
else
{
@@ -1997,7 +2046,7 @@ void TextureCacheBase::StitchXFBCopy(TCacheEntry* stitched_entry)
}
// Link the two textures together, so we won't apply this partial update again
- entry->CreateReference(stitched_entry);
+ entry->CreateReference(stitched_entry.get());
// Mark the texture update as used, as if it was loaded directly
entry->frameCount = FRAMECOUNT_INVALID;
@@ -2135,8 +2184,8 @@ void TextureCacheBase::CopyRenderTargetToTexture(
// For the latter, we keep the EFB resolution for the virtual XFB blit.
u32 tex_w = width;
u32 tex_h = height;
- u32 scaled_tex_w = g_renderer->EFBToScaledX(width);
- u32 scaled_tex_h = g_renderer->EFBToScaledY(height);
+ u32 scaled_tex_w = g_framebuffer_manager->EFBToScaledX(width);
+ u32 scaled_tex_h = g_framebuffer_manager->EFBToScaledY(height);
if (scaleByHalf)
{
@@ -2180,7 +2229,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(
info.m_texture_format = baseFormat;
if (is_xfb_copy)
{
- for (const auto action : g_renderer->GetGraphicsModManager().GetXFBActions(info))
+ for (const auto action : g_graphics_mod_manager->GetXFBActions(info))
{
action->OnXFB();
}
@@ -2189,7 +2238,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(
{
bool skip = false;
GraphicsModActionData::EFB efb{tex_w, tex_h, &skip, &scaled_tex_w, &scaled_tex_h};
- for (const auto action : g_renderer->GetGraphicsModManager().GetEFBActions(info))
+ for (const auto action : g_graphics_mod_manager->GetEFBActions(info))
{
action->OnEFB(&efb);
}
@@ -2221,9 +2270,10 @@ void TextureCacheBase::CopyRenderTargetToTexture(
// TODO: This only produces perfect downsampling for 2x IR, other resolutions will need more
// complex down filtering to average all pixels and produce the correct result.
const bool linear_filter =
- !is_depth_copy && (scaleByHalf || g_renderer->GetEFBScale() != 1 || y_scale > 1.0f);
+ !is_depth_copy &&
+ (scaleByHalf || g_framebuffer_manager->GetEFBScale() != 1 || y_scale > 1.0f);
- TCacheEntry* entry = nullptr;
+ RcTcacheEntry entry;
if (copy_to_vram)
{
// create the texture
@@ -2314,7 +2364,6 @@ void TextureCacheBase::CopyRenderTargetToTexture(
entry->pending_efb_copy = std::move(staging_texture);
entry->pending_efb_copy_width = bytes_per_row / sizeof(u32);
entry->pending_efb_copy_height = num_blocks_y;
- entry->pending_efb_copy_invalidated = false;
m_pending_efb_copies.push_back(entry);
}
}
@@ -2339,7 +2388,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(
auto iter = FindOverlappingTextures(dstAddr, covered_range);
while (iter.first != iter.second)
{
- TCacheEntry* overlapping_entry = iter.first->second;
+ RcTcacheEntry& overlapping_entry = iter.first->second;
if (overlapping_entry->addr == dstAddr && overlapping_entry->is_xfb_copy)
{
@@ -2412,7 +2461,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(
{
const u64 hash = entry->CalculateHash();
entry->SetHashes(hash, hash);
- textures_by_address.emplace(dstAddr, entry);
+ textures_by_address.emplace(dstAddr, std::move(entry));
}
}
@@ -2421,11 +2470,20 @@ void TextureCacheBase::FlushEFBCopies()
if (m_pending_efb_copies.empty())
return;
- for (TCacheEntry* entry : m_pending_efb_copies)
- FlushEFBCopy(entry);
+ for (auto& entry : m_pending_efb_copies)
+ FlushEFBCopy(entry.get());
m_pending_efb_copies.clear();
}
+void TextureCacheBase::FlushStaleBinds()
+{
+ for (u32 i = 0; i < bound_textures.size(); i++)
+ {
+ if (!TMEM::IsCached(i))
+ bound_textures[i].reset();
+ }
+}
+
void TextureCacheBase::WriteEFBCopyToRAM(u8* dst_ptr, u32 width, u32 height, u32 stride,
std::unique_ptr<AbstractStagingTexture> staging_texture)
{
@@ -2443,14 +2501,10 @@ void TextureCacheBase::FlushEFBCopy(TCacheEntry* entry)
WriteEFBCopyToRAM(dst, entry->pending_efb_copy_width, entry->pending_efb_copy_height,
entry->memory_stride, std::move(entry->pending_efb_copy));
- // If the EFB copy was invalidated (e.g. the bloom case mentioned in InvalidateTexture), now is
- // the time to clean up the TCacheEntry. In which case, we don't need to compute the new hash of
- // the RAM copy. But we need to clean up the TCacheEntry, as InvalidateTexture doesn't free it.
- if (entry->pending_efb_copy_invalidated)
- {
- delete entry;
+ // If the EFB copy was invalidated (e.g. the bloom case mentioned in InvalidateTexture), we don't
+ // need to do anything more. The entry will be automatically deleted by smart pointers
+ if (entry->invalidated)
return;
- }
// Re-hash the texture now that the guest memory is populated.
// This should be safe because we'll catch any writes before the game can modify it.
@@ -2465,7 +2519,7 @@ void TextureCacheBase::FlushEFBCopy(TCacheEntry* entry)
auto range = FindOverlappingTextures(entry->addr, covered_range);
for (auto iter = range.first; iter != range.second; ++iter)
{
- TCacheEntry* overlapping_entry = iter->second;
+ auto& overlapping_entry = iter->second;
if (overlapping_entry->may_have_overlapping_textures && overlapping_entry->is_xfb_copy &&
overlapping_entry->OverlapsMemoryRange(entry->addr, covered_range))
{
@@ -2486,7 +2540,7 @@ std::unique_ptr<AbstractStagingTexture> TextureCacheBase::GetEFBCopyStagingTextu
return ptr;
}
- std::unique_ptr<AbstractStagingTexture> tex = g_renderer->CreateStagingTexture(
+ std::unique_ptr<AbstractStagingTexture> tex = g_gfx->CreateStagingTexture(
StagingTextureType::Readback, m_efb_encoding_texture->GetConfig());
if (!tex)
WARN_LOG_FMT(VIDEO, "Failed to create EFB copy staging texture");
@@ -2555,14 +2609,14 @@ void TextureCacheBase::UninitializeXFBMemory(u8* dst, u32 stride, u32 bytes_per_
}
}
-TextureCacheBase::TCacheEntry* TextureCacheBase::AllocateCacheEntry(const TextureConfig& config)
+RcTcacheEntry TextureCacheBase::AllocateCacheEntry(const TextureConfig& config)
{
std::optional<TexPoolEntry> alloc = AllocateTexture(config);
if (!alloc)
- return nullptr;
+ return {};
- TCacheEntry* cacheEntry =
- new TCacheEntry(std::move(alloc->texture), std::move(alloc->framebuffer));
+ auto cacheEntry =
+ std::make_shared<TCacheEntry>(std::move(alloc->texture), std::move(alloc->framebuffer));
cacheEntry->textures_by_hash_iter = textures_by_hash.end();
cacheEntry->id = last_entry_id++;
return cacheEntry;
@@ -2579,7 +2633,7 @@ TextureCacheBase::AllocateTexture(const TextureConfig& config)
return std::move(entry);
}
- std::unique_ptr<AbstractTexture> texture = g_renderer->CreateTexture(config);
+ std::unique_ptr<AbstractTexture> texture = g_gfx->CreateTexture(config);
if (!texture)
{
WARN_LOG_FMT(VIDEO, "Failed to allocate a {}x{}x{} texture", config.width, config.height,
@@ -2590,7 +2644,7 @@ TextureCacheBase::AllocateTexture(const TextureConfig& config)
std::unique_ptr<AbstractFramebuffer> framebuffer;
if (config.IsRenderTarget())
{
- framebuffer = g_renderer->CreateFramebuffer(texture.get(), nullptr);
+ framebuffer = g_gfx->CreateFramebuffer(texture.get(), nullptr);
if (!framebuffer)
{
WARN_LOG_FMT(VIDEO, "Failed to allocate a {}x{}x{} framebuffer", config.width, config.height,
@@ -2618,14 +2672,13 @@ TextureCacheBase::FindMatchingTextureFromPool(const TextureConfig& config)
return matching_iter != range.second ? matching_iter : texture_pool.end();
}
-TextureCacheBase::TexAddrCache::iterator
-TextureCacheBase::GetTexCacheIter(TextureCacheBase::TCacheEntry* entry)
+TextureCacheBase::TexAddrCache::iterator TextureCacheBase::GetTexCacheIter(TCacheEntry* entry)
{
auto iter_range = textures_by_address.equal_range(entry->addr);
TexAddrCache::iterator iter = iter_range.first;
while (iter != iter_range.second)
{
- if (iter->second == entry)
+ if (iter->second.get() == entry)
{
return iter;
}
@@ -2657,7 +2710,7 @@ TextureCacheBase::InvalidateTexture(TexAddrCache::iterator iter, bool discard_pe
if (iter == textures_by_address.end())
return textures_by_address.end();
- TCacheEntry* entry = iter->second;
+ RcTcacheEntry& entry = iter->second;
if (entry->textures_by_hash_iter != textures_by_hash.end())
{
@@ -2665,26 +2718,6 @@ TextureCacheBase::InvalidateTexture(TexAddrCache::iterator iter, bool discard_pe
entry->textures_by_hash_iter = textures_by_hash.end();
}
- for (size_t i = 0; i < bound_textures.size(); ++i)
- {
- if (bound_textures[i] == entry)
- {
- if (TMEM::IsCached(static_cast<u32>(i)))
- {
- // If the entry is currently bound and tmem has it recorded as cached, 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.
- bound_textures[i]->tmem_only = true;
- return ++iter;
- }
- else
- {
- // Otherwise, delete the reference to it from bound_textures
- bound_textures[i] = nullptr;
- }
- }
- }
-
// If this is a pending EFB copy, we don't want to flush it here.
// Why? Because let's say a game is rendering a bloom-type effect, using EFB copies to essentially
// downscale the framebuffer. Copy from EFB->Texture, draw texture to EFB, copy EFB->Texture,
@@ -2707,31 +2740,35 @@ TextureCacheBase::InvalidateTexture(TexAddrCache::iterator iter, bool discard_pe
}
else
{
- entry->pending_efb_copy_invalidated = true;
+ // The texture data has already been copied into the staging texture, so it's valid to
+ // optimistically release the texture data. Will slightly lower VRAM usage.
+ if (!entry->IsLocked())
+ ReleaseToPool(entry.get());
}
}
+ entry->invalidated = true;
+
+ return textures_by_address.erase(iter);
+}
+void TextureCacheBase::ReleaseToPool(TCacheEntry* entry)
+{
+ if (!entry->texture)
+ return;
auto config = entry->texture->GetConfig();
texture_pool.emplace(config,
TexPoolEntry(std::move(entry->texture), std::move(entry->framebuffer)));
-
- // Don't delete if there's a pending EFB copy, as we need the TCacheEntry alive.
- if (!entry->pending_efb_copy)
- delete entry;
-
- return textures_by_address.erase(iter);
}
bool TextureCacheBase::CreateUtilityTextures()
{
constexpr TextureConfig encoding_texture_config(
EFB_WIDTH * 4, 1024, 1, 1, 1, AbstractTextureFormat::BGRA8, AbstractTextureFlag_RenderTarget);
- m_efb_encoding_texture =
- g_renderer->CreateTexture(encoding_texture_config, "EFB encoding texture");
+ m_efb_encoding_texture = g_gfx->CreateTexture(encoding_texture_config, "EFB encoding texture");
if (!m_efb_encoding_texture)
return false;
- m_efb_encoding_framebuffer = g_renderer->CreateFramebuffer(m_efb_encoding_texture.get(), nullptr);
+ m_efb_encoding_framebuffer = g_gfx->CreateFramebuffer(m_efb_encoding_texture.get(), nullptr);
if (!m_efb_encoding_framebuffer)
return false;
@@ -2740,7 +2777,7 @@ bool TextureCacheBase::CreateUtilityTextures()
constexpr TextureConfig decoding_texture_config(
1024, 1024, 1, 1, 1, AbstractTextureFormat::RGBA8, AbstractTextureFlag_ComputeImage);
m_decoding_texture =
- g_renderer->CreateTexture(decoding_texture_config, "GPU texture decoding texture");
+ g_gfx->CreateTexture(decoding_texture_config, "GPU texture decoding texture");
if (!m_decoding_texture)
return false;
}
@@ -2748,7 +2785,7 @@ bool TextureCacheBase::CreateUtilityTextures()
return true;
}
-void TextureCacheBase::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_copy,
+void TextureCacheBase::CopyEFBToCacheEntry(RcTcacheEntry& entry, bool is_depth_copy,
const MathUtil::Rectangle<int>& src_rect,
bool scale_by_half, bool linear_filter,
EFBCopyFormat dst_format, bool is_intensity, float gamma,
@@ -2768,15 +2805,15 @@ void TextureCacheBase::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_cop
return;
}
- const auto scaled_src_rect = g_renderer->ConvertEFBRectangle(src_rect);
- const auto framebuffer_rect = g_renderer->ConvertFramebufferRectangle(
+ const auto scaled_src_rect = g_framebuffer_manager->ConvertEFBRectangle(src_rect);
+ const auto framebuffer_rect = g_gfx->ConvertFramebufferRectangle(
scaled_src_rect, g_framebuffer_manager->GetEFBFramebuffer());
AbstractTexture* src_texture =
is_depth_copy ? g_framebuffer_manager->ResolveEFBDepthTexture(framebuffer_rect) :
g_framebuffer_manager->ResolveEFBColorTexture(framebuffer_rect);
src_texture->FinishedRendering();
- g_renderer->BeginUtilityDrawing();
+ g_gfx->BeginUtilityDrawing();
// Fill uniform buffer.
struct Uniforms
@@ -2813,14 +2850,14 @@ void TextureCacheBase::CopyEFBToCacheEntry(TCacheEntry* entry, bool is_depth_cop
g_vertex_manager->UploadUtilityUniforms(&uniforms, sizeof(uniforms));
// Use the copy pipeline to render the VRAM copy.
- g_renderer->SetAndDiscardFramebuffer(entry->framebuffer.get());
- g_renderer->SetViewportAndScissor(entry->framebuffer->GetRect());
- g_renderer->SetPipeline(copy_pipeline);
- g_renderer->SetTexture(0, src_texture);
- g_renderer->SetSamplerState(0, linear_filter ? RenderState::GetLinearSamplerState() :
- RenderState::GetPointSamplerState());
- g_renderer->Draw(0, 3);
- g_renderer->EndUtilityDrawing();
+ g_gfx->SetAndDiscardFramebuffer(entry->framebuffer.get());
+ g_gfx->SetViewportAndScissor(entry->framebuffer->GetRect());
+ g_gfx->SetPipeline(copy_pipeline);
+ g_gfx->SetTexture(0, src_texture);
+ g_gfx->SetSamplerState(0, linear_filter ? RenderState::GetLinearSamplerState() :
+ RenderState::GetPointSamplerState());
+ g_gfx->Draw(0, 3);
+ g_gfx->EndUtilityDrawing();
entry->texture->FinishedRendering();
}
@@ -2842,15 +2879,15 @@ void TextureCacheBase::CopyEFB(AbstractStagingTexture* dst, const EFBCopyParams&
return;
}
- const auto scaled_src_rect = g_renderer->ConvertEFBRectangle(src_rect);
- const auto framebuffer_rect = g_renderer->ConvertFramebufferRectangle(
+ const auto scaled_src_rect = g_framebuffer_manager->ConvertEFBRectangle(src_rect);
+ const auto framebuffer_rect = g_gfx->ConvertFramebufferRectangle(
scaled_src_rect, g_framebuffer_manager->GetEFBFramebuffer());
AbstractTexture* src_texture =
params.depth ? g_framebuffer_manager->ResolveEFBDepthTexture(framebuffer_rect) :
g_framebuffer_manager->ResolveEFBColorTexture(framebuffer_rect);
src_texture->FinishedRendering();
- g_renderer->BeginUtilityDrawing();
+ g_gfx->BeginUtilityDrawing();
// Fill uniform buffer.
struct Uniforms
@@ -2890,21 +2927,21 @@ void TextureCacheBase::CopyEFB(AbstractStagingTexture* dst, const EFBCopyParams&
const auto encode_rect = MathUtil::Rectangle<int>(0, 0, render_width, render_height);
// Render to GPU texture, and then copy to CPU-accessible texture.
- g_renderer->SetAndDiscardFramebuffer(m_efb_encoding_framebuffer.get());
- g_renderer->SetViewportAndScissor(encode_rect);
- g_renderer->SetPipeline(copy_pipeline);
- g_renderer->SetTexture(0, src_texture);
- g_renderer->SetSamplerState(0, linear_filter ? RenderState::GetLinearSamplerState() :
- RenderState::GetPointSamplerState());
- g_renderer->Draw(0, 3);
+ g_gfx->SetAndDiscardFramebuffer(m_efb_encoding_framebuffer.get());
+ g_gfx->SetViewportAndScissor(encode_rect);
+ g_gfx->SetPipeline(copy_pipeline);
+ g_gfx->SetTexture(0, src_texture);
+ g_gfx->SetSamplerState(0, linear_filter ? RenderState::GetLinearSamplerState() :
+ RenderState::GetPointSamplerState());
+ g_gfx->Draw(0, 3);
dst->CopyFromTexture(m_efb_encoding_texture.get(), encode_rect, 0, 0, encode_rect);
- g_renderer->EndUtilityDrawing();
+ g_gfx->EndUtilityDrawing();
// Flush if there's sufficient draws between this copy and the last.
g_vertex_manager->OnEFBCopyToRAM();
}
-bool TextureCacheBase::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, const u8* data,
+bool TextureCacheBase::DecodeTextureOnGPU(RcTcacheEntry& entry, u32 dst_level, const u8* data,
u32 data_size, TextureFormat format, u32 width,
u32 height, u32 aligned_width, u32 aligned_height,
u32 row_stride, const u8* palette,
@@ -2951,12 +2988,12 @@ bool TextureCacheBase::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, con
aligned_height, src_offset, row_stride / bytes_per_buffer_elem,
palette_offset};
g_vertex_manager->UploadUtilityUniforms(&uniforms, sizeof(uniforms));
- g_renderer->SetComputeImageTexture(m_decoding_texture.get(), false, true);
+ g_gfx->SetComputeImageTexture(m_decoding_texture.get(), false, true);
auto dispatch_groups =
TextureConversionShaderTiled::GetDispatchCount(info, aligned_width, aligned_height);
- g_renderer->DispatchComputeShader(shader, info->group_size_x, info->group_size_y, 1,
- dispatch_groups.first, dispatch_groups.second, 1);
+ g_gfx->DispatchComputeShader(shader, info->group_size_x, info->group_size_y, 1,
+ dispatch_groups.first, dispatch_groups.second, 1);
// Copy from decoding texture -> final texture
// This is because we don't want to have to create compute view for every layer
@@ -2967,7 +3004,7 @@ bool TextureCacheBase::DecodeTextureOnGPU(TCacheEntry* entry, u32 dst_level, con
return true;
}
-u32 TextureCacheBase::TCacheEntry::BytesPerRow() const
+u32 TCacheEntry::BytesPerRow() const
{
// RGBA takes two cache lines per block; all others take one
const u32 bytes_per_block = format == TextureFormat::RGBA8 ? 64 : 32;
@@ -2975,7 +3012,7 @@ u32 TextureCacheBase::TCacheEntry::BytesPerRow() const
return NumBlocksX() * bytes_per_block;
}
-u32 TextureCacheBase::TCacheEntry::NumBlocksX() const
+u32 TCacheEntry::NumBlocksX() const
{
const u32 blockW = TexDecoder_GetBlockWidthInTexels(format.texfmt);
@@ -2985,7 +3022,7 @@ u32 TextureCacheBase::TCacheEntry::NumBlocksX() const
return actualWidth / blockW;
}
-u32 TextureCacheBase::TCacheEntry::NumBlocksY() const
+u32 TCacheEntry::NumBlocksY() const
{
u32 blockH = TexDecoder_GetBlockHeightInTexels(format.texfmt);
// Round up source height to multiple of block size
@@ -2994,7 +3031,7 @@ u32 TextureCacheBase::TCacheEntry::NumBlocksY() const
return actualHeight / blockH;
}
-void TextureCacheBase::TCacheEntry::SetXfbCopy(u32 stride)
+void TCacheEntry::SetXfbCopy(u32 stride)
{
is_efb_copy = false;
is_xfb_copy = true;
@@ -3006,7 +3043,7 @@ void TextureCacheBase::TCacheEntry::SetXfbCopy(u32 stride)
size_in_bytes = memory_stride * NumBlocksY();
}
-void TextureCacheBase::TCacheEntry::SetEfbCopy(u32 stride)
+void TCacheEntry::SetEfbCopy(u32 stride)
{
is_efb_copy = true;
is_xfb_copy = false;
@@ -3018,14 +3055,14 @@ void TextureCacheBase::TCacheEntry::SetEfbCopy(u32 stride)
size_in_bytes = memory_stride * NumBlocksY();
}
-void TextureCacheBase::TCacheEntry::SetNotCopy()
+void TCacheEntry::SetNotCopy()
{
is_efb_copy = false;
is_xfb_copy = false;
is_xfb_container = false;
}
-int TextureCacheBase::TCacheEntry::HashSampleSize() const
+int TCacheEntry::HashSampleSize() const
{
if (should_force_safe_hashing)
{
@@ -3035,7 +3072,7 @@ int TextureCacheBase::TCacheEntry::HashSampleSize() const
return g_ActiveConfig.iSafeTextureCache_ColorSamples;
}
-u64 TextureCacheBase::TCacheEntry::CalculateHash() const
+u64 TCacheEntry::CalculateHash() const
{
const u32 bytes_per_row = BytesPerRow();
const u32 hash_sample_size = HashSampleSize();