Skip to content

Commit

Permalink
Fix double-free problem in "low-memory" texture fallback (Vulkan)
Browse files Browse the repository at this point in the history
  • Loading branch information
hrydgard committed Dec 4, 2023
1 parent fb8ad0c commit 5373b8c
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 5 deletions.
21 changes: 19 additions & 2 deletions GPU/Common/TextureCacheCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -147,7 +148,7 @@ void TextureCacheCommon::StartFrame() {
Clear(true);
clearCacheNextFrame_ = false;
} else {
Decimate(false);
Decimate(nullptr, false);
}
}

Expand Down Expand Up @@ -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 {
Expand All @@ -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) {
Expand All @@ -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);
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion GPU/Common/TextureCacheCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
9 changes: 7 additions & 2 deletions GPU/Vulkan/TextureCacheVulkan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -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.

Expand Down

0 comments on commit 5373b8c

Please sign in to comment.