diff options
| author | Scott Mansell <phiren@gmail.com> | 2023-02-09 19:59:16 +1300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-02-09 19:59:16 +1300 |
| commit | ccf92a3e56764d6378c436a8040519a5bffc3372 (patch) | |
| tree | e2b330b225044e1901e77a6ed147cf21b9d7ac0e /Source/Core/VideoCommon/TextureCacheBase.cpp | |
| parent | 72b22ef0a54f841b1c52d49cf0e00d3b12c29ac7 (diff) | |
| parent | 5c1b3ac61d7e660a9d1c206dbd0e615d637d3272 (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.cpp | 585 |
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(); |
