fix(texture_cache): handle unmapped memory regions gracefully

- Skip upload for unmapped texture regions with zeroed staging buffer

- Add safety checks in UploadImageContents for unmapped GPU memory

- Prevents GPU errors from invalid memory access during texture upload
This commit is contained in:
Zephyron
2026-01-21 18:53:40 +10:00
parent 317103aba0
commit 7af18a2aa8
2 changed files with 31 additions and 1 deletions

View File

@@ -4,10 +4,12 @@
#pragma once
#include <cstring>
#include <unordered_set>
#include <boost/container/small_vector.hpp>
#include "common/alignment.h"
#include "common/logging/log.h"
#include "common/settings.h"
#include "video_core/control/channel_state.h"
#include "video_core/dirty_flags.h"
@@ -1076,6 +1078,21 @@ void TextureCache<P>::UploadImageContents(Image& image, StagingBuffer& staging)
const std::span<u8> mapped_span = staging.mapped_span;
const GPUVAddr gpu_addr = image.gpu_addr;
// FIXED: Check if GPU memory is mapped before reading to prevent device loss
// This fixes Switch Sports and other games that crash due to unmapped GPU memory reads
// Similar to Ryujinx's GetSpanMapped() approach - gracefully handle unmapped memory
if (!gpu_memory->IsFullyMappedRange(gpu_addr, image.guest_size_bytes)) {
LOG_WARNING(HW_GPU,
"Texture upload from unmapped GPU memory at 0x{:016X} (size: {} KB). "
"Zeroing staging buffer and skipping upload.",
gpu_addr, image.guest_size_bytes / 1024);
// Zero the staging buffer to ensure no garbage data is used if something reads it
std::memset(mapped_span.data(), 0, mapped_span.size_bytes());
// Skip the upload - trying to upload with invalid copy parameters can crash
// The image will use its previous state or be initialized to a safe default
return;
}
if (True(image.flags & ImageFlagBits::AcceleratedUpload)) {
gpu_memory->ReadBlock(gpu_addr, mapped_span.data(), mapped_span.size_bytes(),
VideoCommon::CacheType::NoTextureCache);

View File

@@ -9,6 +9,7 @@
#include <algorithm>
#include <array>
#include <cstring>
#include <numeric>
#include <optional>
#include <span>
@@ -19,6 +20,7 @@
#include "common/bit_util.h"
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "common/logging/log.h"
#include "common/scratch_buffer.h"
#include "common/settings.h"
#include "video_core/compatible_formats.h"
@@ -858,7 +860,18 @@ boost::container::small_vector<BufferImageCopy, 16> UnswizzleImage(Tegra::Memory
if (info.type == ImageType::Linear) {
ASSERT(output.size_bytes() >= guest_size_bytes);
gpu_memory.ReadBlockUnsafe(gpu_addr, output.data(), guest_size_bytes);
// FIXED: Validate GPU memory is mapped before reading to prevent device loss
// This fixes Switch Sports crash - similar to Ryujinx's GetSpanMapped() approach
if (!gpu_memory.IsFullyMappedRange(gpu_addr, guest_size_bytes)) {
LOG_WARNING(HW_GPU,
"Linear texture read from unmapped GPU memory at 0x{:016X} (size: {} KB). "
"Zeroing texture data to prevent device loss.",
gpu_addr, guest_size_bytes / 1024);
std::memset(output.data(), 0, guest_size_bytes);
} else {
gpu_memory.ReadBlockUnsafe(gpu_addr, output.data(), guest_size_bytes);
}
ASSERT((info.pitch >> bpp_log2) << bpp_log2 == info.pitch);
return {{