diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index efa2aac69056..c3ad81ddd78d 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -29,6 +29,7 @@ #include "Common/TimeUtil.h" #include "Common/Math/math_util.h" #include "Common/GPU/thin3d.h" +#include "Core/HDRemaster.h" #include "Core/Config.h" #include "Core/Debugger/MemBlockInfo.h" #include "Core/System.h" @@ -147,7 +148,7 @@ void TextureCacheCommon::StartFrame() { Clear(true); clearCacheNextFrame_ = false; } else { - Decimate(false); + Decimate(nullptr, false); } } @@ -776,7 +777,7 @@ bool TextureCacheCommon::GetBestFramebufferCandidate(const TextureDefinition &en } // Removes old textures. -void TextureCacheCommon::Decimate(bool forcePressure) { +void TextureCacheCommon::Decimate(TexCacheEntry *exceptThisOne, bool forcePressure) { if (--decimationCounter_ <= 0) { decimationCounter_ = TEXCACHE_DECIMATION_INTERVAL; } else { @@ -789,6 +790,10 @@ void TextureCacheCommon::Decimate(bool forcePressure) { ForgetLastTexture(); int killAgeBase = lowMemoryMode_ ? TEXTURE_KILL_AGE_LOWMEM : TEXTURE_KILL_AGE; for (TexCache::iterator iter = cache_.begin(); iter != cache_.end(); ) { + if (iter->second.get() == exceptThisOne) { + ++iter; + continue; + } bool hasClut = (iter->second->status & TexCacheEntry::STATUS_CLUT_VARIANTS) != 0; int killAge = hasClut ? TEXTURE_KILL_AGE_CLUT : killAgeBase; if (iter->second->lastFrame + killAge < gpuStats.numFlips) { @@ -806,6 +811,10 @@ void TextureCacheCommon::Decimate(bool forcePressure) { const u32 had = secondCacheSizeEstimate_; for (TexCache::iterator iter = secondCache_.begin(); iter != secondCache_.end(); ) { + if (iter->second.get() == exceptThisOne) { + ++iter; + continue; + } // In low memory mode, we kill them all since secondary cache is disabled. if (lowMemoryMode_ || iter->second->lastFrame + TEXTURE_SECOND_KILL_AGE < gpuStats.numFlips) { ReleaseTexture(iter->second.get(), true); @@ -2803,6 +2812,14 @@ bool TextureCacheCommon::PrepareBuildTexture(BuildTexturePlan &plan, TexCacheEnt plan.w = gstate.getTextureWidth(0); plan.h = gstate.getTextureHeight(0); + if (!g_DoubleTextureCoordinates) { + // Refuse to load invalid-ly sized textures, which can happen through display list corruption. + if (plan.w > 512 || plan.h > 512) { + ERROR_LOG(G3D, "Bad texture dimensions: %dx%d", plan.w, plan.h); + return false; + } + } + bool isPPGETexture = entry->addr >= PSP_GetKernelMemoryBase() && entry->addr < PSP_GetKernelMemoryEnd(); // Don't scale the PPGe texture. diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 9523da30f3cb..372043870e91 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -387,7 +387,7 @@ class TextureCacheCommon { virtual void Unbind() = 0; virtual void ReleaseTexture(TexCacheEntry *entry, bool delete_them) = 0; void DeleteTexture(TexCache::iterator it); - void Decimate(bool forcePressure = false); + void Decimate(TexCacheEntry *exceptThisOne, bool forcePressure); // forcePressure defaults to false. void ApplyTextureFramebuffer(VirtualFramebuffer *framebuffer, GETextureFormat texFormat, RasterChannel channel); void ApplyTextureDepal(TexCacheEntry *entry); diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 0bfb758eee06..5d9f7b66072f 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -921,9 +921,6 @@ void GPUCommon::Execute_Call(u32 op, u32 diff) { } void GPUCommon::DoExecuteCall(u32 target) { - // Saint Seiya needs correct support for relative calls. - const u32 retval = currentList->pc + 4; - // Bone matrix optimization - many games will CALL a bone matrix (!). // We don't optimize during recording - so the matrix data gets recorded. if (!debugRecording_ && Memory::IsValidRange(target, 13 * 4) && (Memory::ReadUnchecked_U32(target) >> 24) == GE_CMD_BONEMATRIXDATA) { @@ -944,7 +941,7 @@ void GPUCommon::DoExecuteCall(u32 target) { // TODO: UpdateState(GPUSTATE_ERROR) ? } else { auto &stackEntry = currentList->stack[currentList->stackptr++]; - stackEntry.pc = retval; + stackEntry.pc = currentList->pc + 4; stackEntry.offsetAddr = gstate_c.offsetAddr; // The base address is NOT saved/restored for a regular call. UpdatePC(currentList->pc, target - 4); diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index 84898ff3faa5..70999904279a 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -429,7 +429,11 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { plan.hardwareScaling = g_Config.bTexHardwareScaling && uploadCS_ != VK_NULL_HANDLE; plan.slowScaler = !plan.hardwareScaling || vulkan->DevicePerfClass() == PerfClass::SLOW; if (!PrepareBuildTexture(plan, entry)) { - // We're screwed? + // We're screwed (invalid size or something, corrupt display list), let's just zap it. + if (entry->vkTex) { + delete entry->vkTex; + entry->vkTex = nullptr; + } return; } @@ -511,7 +515,8 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode"); lowMemoryMode_ = true; decimationCounter_ = 0; - Decimate(); + Decimate(entry, true); + // TODO: We should stall the GPU here and wipe things out of memory. // As is, it will almost definitely fail the second time, but next frame it may recover.