video_core: Add fallback handling for failed storage buffer lookups

Implements a more robust error handling approach when storage buffer lookups
fail in the buffer cache. Instead of returning a null binding, the code now:

- Provides a fallback buffer with safe default values
- Implements warning rate limiting to prevent log spam
- Tracks warning counts per cbuf_index
- Logs detailed debug information periodically

This change helps prevent potential crashes when storage buffer lookups fail
while still maintaining visibility into the issue through strategic logging.

The fallback mechanism uses a safe static address and a reasonable buffer
size (16KB) to handle cases where the normal GPU to CPU address translation
fails.

Also updates copyright headers to include citron Emulator Project.

Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
Zephyron 2025-04-22 16:56:59 +10:00
parent ff9c61e7c7
commit 66bdd6ed27

View file

@ -1,4 +1,5 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
@ -6,6 +7,7 @@
#include <algorithm>
#include <memory>
#include <numeric>
#include <unordered_map>
#include "common/range_sets.inc"
#include "video_core/buffer_cache/buffer_cache_base.h"
@ -18,7 +20,7 @@ using Core::DEVICE_PAGESIZE;
template <class P>
BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, Runtime& runtime_)
: runtime{runtime_}, device_memory{device_memory_}, memory_tracker{device_memory} {
: runtime{runtime_}, device_memory{device_memory_}, memory_tracker{device_memory}, immediate_buffer_alloc{} {
// Ensure the first slot is used for the null buffer
void(slot_buffers.insert(runtime, NullBufferParams{}));
gpu_modified_ranges.Clear();
@ -1719,8 +1721,31 @@ Binding BufferCache<P>::StorageBufferBinding(GPUVAddr ssbo_addr, u32 cbuf_index,
const std::optional<DAddr> aligned_device_addr = gpu_memory->GpuToCpuAddress(aligned_gpu_addr);
if (!aligned_device_addr || size == 0) {
LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}", cbuf_index);
return NULL_BINDING;
// Use a static counter to track and limit warnings
static std::unordered_map<u32, u32> warning_counts;
// Increment the warning count for this cbuf_index
warning_counts[cbuf_index]++;
// Only log the first warning for each cbuf_index
if (warning_counts[cbuf_index] == 1) {
LOG_WARNING(HW_GPU, "Failed to find storage buffer for cbuf index {}. Using fallback.",
cbuf_index);
} else if (warning_counts[cbuf_index] % 1000 == 0) {
// Log occasional reminder warnings
LOG_DEBUG(HW_GPU, "Still using fallback for storage buffer cbuf index {} (count: {})",
cbuf_index, warning_counts[cbuf_index]);
}
// Create a dummy binding with non-zero values to avoid potential crashes
static DAddr safe_device_addr = 0x1000;
static const u32 safe_size = 16 * 1024; // 16KB should be adequate for most cases
return Binding{
.device_addr = safe_device_addr,
.size = safe_size,
.buffer_id = const_cast<BufferCache<P>*>(this)->FindBuffer(safe_device_addr, safe_size),
};
}
const std::optional<DAddr> device_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
ASSERT_MSG(device_addr, "Unaligned storage buffer address not found for cbuf index {}",