mirror of
https://git.citron-emu.org/citron/emulator
synced 2026-01-25 20:23:28 +00:00
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:
@@ -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);
|
||||
|
||||
@@ -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 {{
|
||||
|
||||
Reference in New Issue
Block a user