mirror of
https://git.citron-emu.org/citron/emulator
synced 2026-01-03 08:43:47 +00:00
video_core: Add mip level clamping for CTGP-DX compatibility
Fixes crashes when games/mods request more than MAX_MIP_LEVELS (14). Implements defensive clamping at four critical points in the texture cache (CalculateLevelSizes, CalculateMipLevelOffsets, CalculateMipLevelSizes, CalculateLevelBytes) to gracefully handle excessive mip level requests. When >14 mip levels are requested: - Logs informative warning (debug builds) - Clamps to MAX_MIP_LEVELS - Continues execution safely This improves upon Eden's solution by adding actual bounds checking instead of just converting ASSERT to ASSERT_MSG. Fixes: CTGP-DX (Mario Kart 8 Deluxe mod) Co-authored-by: JPikachu <jpikachu.eden@gmail.com> Co-authored-by: JPikachu <jpikachu@eden-emu.dev> Co-authored-by: MaranBr <maranbr@outlook.com> Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-FileCopyrightText: Ryujinx Team and Contributors
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later AND MIT
|
||||
|
||||
// This files contains code from Ryujinx
|
||||
// A copy of the code can be obtained from https://github.com/Ryujinx/Ryujinx
|
||||
// This file contains code from Ryujinx
|
||||
// A copy of the code can be obtained from https://git.ryujinx.app/ryubing/ryujinx
|
||||
// The sections using code from Ryujinx are marked with a link to the original version
|
||||
|
||||
#include <algorithm>
|
||||
@@ -245,16 +246,23 @@ template <u32 GOB_EXTENT>
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr LevelArray CalculateLevelSizes(const LevelInfo& info, u32 num_levels) {
|
||||
ASSERT(num_levels <= MAX_MIP_LEVELS);
|
||||
ASSERT_MSG(num_levels <= MAX_MIP_LEVELS,
|
||||
"Requested {} mip levels exceeds maximum of {}. Clamping to prevent crash. "
|
||||
"This may occur with certain mods like CTGP-DX.",
|
||||
num_levels, MAX_MIP_LEVELS);
|
||||
// Clamp to MAX_MIP_LEVELS to prevent out-of-bounds access
|
||||
const u32 clamped_levels = std::min(num_levels, static_cast<u32>(MAX_MIP_LEVELS));
|
||||
LevelArray sizes{};
|
||||
for (u32 level = 0; level < num_levels; ++level) {
|
||||
for (u32 level = 0; level < clamped_levels; ++level) {
|
||||
sizes[level] = CalculateLevelSize(info, level);
|
||||
}
|
||||
return sizes;
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 CalculateLevelBytes(const LevelArray& sizes, u32 num_levels) {
|
||||
return std::reduce(sizes.begin(), sizes.begin() + num_levels, 0U);
|
||||
// Clamp to prevent out-of-bounds access (CTGP-DX compatibility)
|
||||
const u32 clamped_levels = std::min(num_levels, static_cast<u32>(MAX_MIP_LEVELS));
|
||||
return std::reduce(sizes.begin(), sizes.begin() + clamped_levels, 0U);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr LevelInfo MakeLevelInfo(PixelFormat format, Extent3D size, Extent3D block,
|
||||
@@ -637,11 +645,16 @@ LevelArray CalculateMipLevelOffsets(const ImageInfo& info) noexcept {
|
||||
if (info.type == ImageType::Linear) {
|
||||
return {};
|
||||
}
|
||||
ASSERT(info.resources.levels <= static_cast<s32>(MAX_MIP_LEVELS));
|
||||
ASSERT_MSG(info.resources.levels <= static_cast<s32>(MAX_MIP_LEVELS),
|
||||
"Image has {} mip levels, exceeds maximum of {}. Clamping to prevent crash. "
|
||||
"This may occur with certain mods like CTGP-DX.",
|
||||
info.resources.levels, MAX_MIP_LEVELS);
|
||||
// Clamp to MAX_MIP_LEVELS to prevent out-of-bounds access
|
||||
const s32 clamped_levels = std::min(info.resources.levels, static_cast<s32>(MAX_MIP_LEVELS));
|
||||
const LevelInfo level_info = MakeLevelInfo(info);
|
||||
LevelArray offsets{};
|
||||
u32 offset = 0;
|
||||
for (s32 level = 0; level < info.resources.levels; ++level) {
|
||||
for (s32 level = 0; level < clamped_levels; ++level) {
|
||||
offsets[level] = offset;
|
||||
offset += CalculateLevelSize(level_info, level);
|
||||
}
|
||||
@@ -650,9 +663,11 @@ LevelArray CalculateMipLevelOffsets(const ImageInfo& info) noexcept {
|
||||
|
||||
LevelArray CalculateMipLevelSizes(const ImageInfo& info) noexcept {
|
||||
const u32 num_levels = info.resources.levels;
|
||||
// Clamp to MAX_MIP_LEVELS to prevent out-of-bounds access (CTGP-DX compatibility)
|
||||
const u32 clamped_levels = std::min(num_levels, static_cast<u32>(MAX_MIP_LEVELS));
|
||||
const LevelInfo level_info = MakeLevelInfo(info);
|
||||
LevelArray sizes{};
|
||||
for (u32 level = 0; level < num_levels; ++level) {
|
||||
for (u32 level = 0; level < clamped_levels; ++level) {
|
||||
sizes[level] = CalculateLevelSize(level_info, level);
|
||||
}
|
||||
return sizes;
|
||||
|
||||
Reference in New Issue
Block a user