GS/HW: Prevent creating texture with invalid mipmap levels

Fixes crash during loading in Jak with Metal renderer, and UB with
Vulkan.
This commit is contained in:
Stenzek 2024-01-21 15:08:28 +10:00 committed by Connor McLaughlin
parent b4b2f3a0b6
commit ed57355948
3 changed files with 12 additions and 8 deletions

View File

@ -74,11 +74,6 @@ const char* shaderName(PresentShader value)
}
}
static int MipmapLevelsForSize(int width, int height)
{
return std::min(static_cast<int>(std::log2(std::max(width, height))) + 1, MAXIMUM_TEXTURE_MIPMAP_LEVELS);
}
#ifdef PCSX2_DEVBUILD
enum class TextureLabel
@ -285,6 +280,11 @@ std::optional<std::string> GSDevice::ReadShaderSource(const char* filename)
return FileSystem::ReadFileToString(Path::Combine(EmuFolders::Resources, filename).c_str());
}
int GSDevice::GetMipmapLevelsForSize(int width, int height)
{
return std::min(static_cast<int>(std::log2(std::max(width, height))) + 1, MAXIMUM_TEXTURE_MIPMAP_LEVELS);
}
bool GSDevice::Create()
{
m_vsync_mode = Host::GetEffectiveVSyncMode();
@ -546,7 +546,8 @@ GSTexture* GSDevice::CreateDepthStencil(int w, int h, GSTexture::Format format,
GSTexture* GSDevice::CreateTexture(int w, int h, int mipmap_levels, GSTexture::Format format, bool prefer_reuse /* = false */)
{
const int levels = mipmap_levels < 0 ? MipmapLevelsForSize(w, h) : mipmap_levels;
pxAssert(mipmap_levels != 0 && (mipmap_levels < 0 || mipmap_levels <= GetMipmapLevelsForSize(w, h)));
const int levels = mipmap_levels < 0 ? GetMipmapLevelsForSize(w, h) : mipmap_levels;
return FetchSurface(GSTexture::Type::Texture, w, h, levels, format, false, m_features.prefer_new_textures && !prefer_reuse);
}

View File

@ -815,6 +815,9 @@ public:
/// Reads the specified shader source file.
static std::optional<std::string> ReadShaderSource(const char* filename);
/// Returns the maximum number of mipmap levels for a given texture size.
static int GetMipmapLevelsForSize(int width, int height);
__fi u64 GetPoolMemoryUsage() const { return m_pool_memory_usage; }
__fi FeatureSupport Features() const { return m_features; }

View File

@ -4166,7 +4166,7 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
{
// lod won't contain the full range when using basic mipmapping, only that
// which is hashed, so we just allocate the full thing.
tlevels = (GSConfig.HWMipmap != HWMipmapLevel::Full) ? -1 : (lod->y - lod->x + 1);
tlevels = (GSConfig.HWMipmap != HWMipmapLevel::Full) ? -1 : std::min(lod->y - lod->x + 1, GSDevice::GetMipmapLevelsForSize(tw, th));
src->m_lod = *lod;
}
@ -5069,7 +5069,7 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
// expand/upload texture
const int tw = region.HasX() ? region.GetWidth() : (1 << TEX0.TW);
const int th = region.HasY() ? region.GetHeight() : (1 << TEX0.TH);
const int tlevels = lod ? ((GSConfig.HWMipmap != HWMipmapLevel::Full) ? -1 : (lod->y - lod->x + 1)) : 1;
const int tlevels = lod ? ((GSConfig.HWMipmap != HWMipmapLevel::Full) ? -1 : std::min(lod->y - lod->x + 1, GSDevice::GetMipmapLevelsForSize(tw, th))) : 1;
GSTexture* tex = g_gs_device->CreateTexture(tw, th, tlevels, paltex ? GSTexture::Format::UNorm8 : GSTexture::Format::Color);
if (!tex)
{