mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-07 14:26:36 +00:00
Some changes to GC
This commit is contained in:
@@ -22,6 +22,7 @@ add_library(video_core STATIC
|
|||||||
buffer_cache/usage_tracker.h
|
buffer_cache/usage_tracker.h
|
||||||
buffer_cache/virtual_range_cache.h
|
buffer_cache/virtual_range_cache.h
|
||||||
buffer_cache/word_manager.h
|
buffer_cache/word_manager.h
|
||||||
|
cache_reclaim.h
|
||||||
cache_types.h
|
cache_types.h
|
||||||
capture.h
|
capture.h
|
||||||
cdma_pusher.cpp
|
cdma_pusher.cpp
|
||||||
|
|||||||
@@ -30,34 +30,52 @@ BufferCache<P>::BufferCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, R
|
|||||||
#ifdef YUZU_LEGACY
|
#ifdef YUZU_LEGACY
|
||||||
immediately_free = (Settings::values.vram_usage_mode.GetValue() == Settings::VramUsageMode::Aggressive);
|
immediately_free = (Settings::values.vram_usage_mode.GetValue() == Settings::VramUsageMode::Aggressive);
|
||||||
#endif
|
#endif
|
||||||
if (!runtime.CanReportMemoryUsage()) {
|
device_local_memory = runtime.GetDeviceLocalMemory();
|
||||||
minimum_memory = DEFAULT_EXPECTED_MEMORY;
|
const auto thresholds = VideoCommon::MakeReclaimThresholds(
|
||||||
critical_memory = DEFAULT_CRITICAL_MEMORY;
|
device_local_memory, static_cast<u64>(TARGET_THRESHOLD),
|
||||||
return;
|
static_cast<u64>(DEFAULT_EXPECTED_MEMORY), static_cast<u64>(DEFAULT_CRITICAL_MEMORY));
|
||||||
}
|
minimum_memory = thresholds.minimum;
|
||||||
|
expected_memory = thresholds.expected;
|
||||||
const s64 device_local_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
|
critical_memory = thresholds.critical;
|
||||||
const s64 min_spacing_expected = device_local_memory - 1_GiB;
|
|
||||||
const s64 min_spacing_critical = device_local_memory - 512_MiB;
|
|
||||||
const s64 mem_threshold = (std::min)(device_local_memory, TARGET_THRESHOLD);
|
|
||||||
const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
|
|
||||||
const s64 min_vacancy_critical = (2 * mem_threshold) / 10;
|
|
||||||
minimum_memory = static_cast<u64>(
|
|
||||||
(std::max)((std::min)(device_local_memory - min_vacancy_expected, min_spacing_expected),
|
|
||||||
DEFAULT_EXPECTED_MEMORY));
|
|
||||||
critical_memory = static_cast<u64>(
|
|
||||||
(std::max)((std::min)(device_local_memory - min_vacancy_critical, min_spacing_critical),
|
|
||||||
DEFAULT_CRITICAL_MEMORY));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
BufferCache<P>::~BufferCache() = default;
|
BufferCache<P>::~BufferCache() = default;
|
||||||
|
|
||||||
|
template <class P>
|
||||||
|
void BufferCache<P>::ReclaimInline() {
|
||||||
|
if (total_used_memory < minimum_memory) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int num_iterations = 8;
|
||||||
|
const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
|
||||||
|
if (num_iterations == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Buffer& buffer = slot_buffers[buffer_id];
|
||||||
|
if (memory_tracker.IsRegionGpuModified(buffer.CpuAddr(), buffer.SizeBytes())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
--num_iterations;
|
||||||
|
DeleteBuffer(buffer_id);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
lru_cache.ForEachItemBelow(frame_tick - INLINE_TICKS_TO_DESTROY, clean_up);
|
||||||
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void BufferCache<P>::RunGarbageCollector() {
|
void BufferCache<P>::RunGarbageCollector() {
|
||||||
const bool aggressive_gc = total_used_memory >= critical_memory;
|
const bool aggressive_gc = heap_pressure || total_used_memory >= critical_memory;
|
||||||
const u64 ticks_to_destroy = aggressive_gc ? 60 : 120;
|
const bool priority_gc = aggressive_gc || total_used_memory >= expected_memory;
|
||||||
int num_iterations = aggressive_gc ? 64 : 32;
|
u64 ticks_to_destroy = 120;
|
||||||
|
int num_iterations = 32;
|
||||||
|
if (aggressive_gc) {
|
||||||
|
ticks_to_destroy = 30;
|
||||||
|
num_iterations = 64;
|
||||||
|
} else if (priority_gc) {
|
||||||
|
ticks_to_destroy = 60;
|
||||||
|
num_iterations = 48;
|
||||||
|
}
|
||||||
const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
|
const auto clean_up = [this, &num_iterations](BufferId buffer_id) {
|
||||||
if (num_iterations == 0) {
|
if (num_iterations == 0) {
|
||||||
return true;
|
return true;
|
||||||
@@ -96,11 +114,12 @@ void BufferCache<P>::TickFrame() {
|
|||||||
const bool skip_preferred = hits * 256 < shots * 251;
|
const bool skip_preferred = hits * 256 < shots * 251;
|
||||||
channel_state->uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
|
channel_state->uniform_buffer_skip_cache_size = skip_preferred ? DEFAULT_SKIP_CACHE_SIZE : 0;
|
||||||
|
|
||||||
// If we can obtain the memory info, use it instead of the estimate.
|
heap_pressure = false;
|
||||||
if (runtime.CanReportMemoryUsage()) {
|
if (device_local_memory != 0 && runtime.CanReportMemoryUsage()) {
|
||||||
total_used_memory = runtime.GetDeviceMemoryUsage();
|
heap_pressure = runtime.GetDeviceMemoryUsage() + HEAP_PRESSURE_HEADROOM >=
|
||||||
|
device_local_memory;
|
||||||
}
|
}
|
||||||
if (total_used_memory >= minimum_memory) {
|
if (total_used_memory >= minimum_memory || heap_pressure) {
|
||||||
RunGarbageCollector();
|
RunGarbageCollector();
|
||||||
}
|
}
|
||||||
++frame_tick;
|
++frame_tick;
|
||||||
@@ -1674,6 +1693,7 @@ void BufferCache<P>::JoinOverlap(BufferId new_buffer_id, BufferId overlap_id,
|
|||||||
template <class P>
|
template <class P>
|
||||||
BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size,
|
BufferId BufferCache<P>::CreateBuffer(DAddr device_addr, u32 wanted_size,
|
||||||
bool sparse_compatible) {
|
bool sparse_compatible) {
|
||||||
|
ReclaimInline();
|
||||||
DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE);
|
DAddr device_addr_end = Common::AlignUp(device_addr + wanted_size, CACHING_PAGESIZE);
|
||||||
device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE);
|
device_addr = Common::AlignDown(device_addr, CACHING_PAGESIZE);
|
||||||
wanted_size = static_cast<u32>(device_addr_end - device_addr);
|
wanted_size = static_cast<u32>(device_addr_end - device_addr);
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "video_core/buffer_cache/buffer_base.h"
|
#include "video_core/buffer_cache/buffer_base.h"
|
||||||
#include "video_core/buffer_cache/virtual_range_cache.h"
|
#include "video_core/buffer_cache/virtual_range_cache.h"
|
||||||
#include "video_core/control/channel_state_cache.h"
|
#include "video_core/control/channel_state_cache.h"
|
||||||
|
#include "video_core/cache_reclaim.h"
|
||||||
#include "video_core/delayed_destruction_ring.h"
|
#include "video_core/delayed_destruction_ring.h"
|
||||||
#include "video_core/dirty_flags.h"
|
#include "video_core/dirty_flags.h"
|
||||||
#include "video_core/engines/maxwell_3d.h"
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
@@ -199,6 +200,8 @@ class BufferCache : public VideoCommon::ChannelSetupCaches<BufferCacheChannelInf
|
|||||||
|
|
||||||
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
|
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 512_MiB;
|
||||||
static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB;
|
static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB;
|
||||||
|
static constexpr u64 HEAP_PRESSURE_HEADROOM = 512_MiB;
|
||||||
|
static constexpr u64 INLINE_TICKS_TO_DESTROY = 240;
|
||||||
|
|
||||||
// Debug Flags.
|
// Debug Flags.
|
||||||
|
|
||||||
@@ -381,6 +384,8 @@ private:
|
|||||||
|
|
||||||
void RunGarbageCollector();
|
void RunGarbageCollector();
|
||||||
|
|
||||||
|
void ReclaimInline();
|
||||||
|
|
||||||
void BindHostIndexBuffer();
|
void BindHostIndexBuffer();
|
||||||
|
|
||||||
void BindHostVertexBuffers();
|
void BindHostVertexBuffers();
|
||||||
@@ -540,8 +545,11 @@ private:
|
|||||||
std::vector<MultiRangeSegment> compute_segments;
|
std::vector<MultiRangeSegment> compute_segments;
|
||||||
u64 frame_tick = 0;
|
u64 frame_tick = 0;
|
||||||
u64 total_used_memory = 0;
|
u64 total_used_memory = 0;
|
||||||
|
u64 device_local_memory = 0;
|
||||||
u64 minimum_memory = 0;
|
u64 minimum_memory = 0;
|
||||||
|
u64 expected_memory = 0;
|
||||||
u64 critical_memory = 0;
|
u64 critical_memory = 0;
|
||||||
|
bool heap_pressure = false;
|
||||||
BufferId inline_buffer_id;
|
BufferId inline_buffer_id;
|
||||||
#ifdef YUZU_LEGACY
|
#ifdef YUZU_LEGACY
|
||||||
bool immediately_free = false;
|
bool immediately_free = false;
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace VideoCommon {
|
||||||
|
|
||||||
|
struct ReclaimThresholds {
|
||||||
|
u64 minimum{};
|
||||||
|
u64 expected{};
|
||||||
|
u64 critical{};
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr ReclaimThresholds MakeReclaimThresholds(u64 device_local_memory,
|
||||||
|
u64 target_threshold,
|
||||||
|
u64 default_expected,
|
||||||
|
u64 default_critical) {
|
||||||
|
u64 critical = default_critical;
|
||||||
|
if (device_local_memory != 0) {
|
||||||
|
const u64 budget = (std::min)(device_local_memory, target_threshold);
|
||||||
|
critical = (std::min)(critical, budget / 2);
|
||||||
|
}
|
||||||
|
const u64 expected = (std::min)(default_expected, (critical * 3) / 4);
|
||||||
|
return ReclaimThresholds{
|
||||||
|
.minimum = (expected * 3) / 4,
|
||||||
|
.expected = expected,
|
||||||
|
.critical = critical,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace VideoCommon
|
||||||
@@ -58,24 +58,14 @@ TextureCache<P>::TextureCache(Runtime& runtime_, Tegra::MaxwellDeviceMemoryManag
|
|||||||
void(slot_samplers.insert(runtime, sampler_descriptor));
|
void(slot_samplers.insert(runtime, sampler_descriptor));
|
||||||
|
|
||||||
if constexpr (HAS_DEVICE_MEMORY_INFO) {
|
if constexpr (HAS_DEVICE_MEMORY_INFO) {
|
||||||
const s64 device_local_memory = static_cast<s64>(runtime.GetDeviceLocalMemory());
|
device_local_memory = runtime.GetDeviceLocalMemory();
|
||||||
const s64 min_spacing_expected = device_local_memory - 1_GiB;
|
|
||||||
const s64 min_spacing_critical = device_local_memory - 512_MiB;
|
|
||||||
const s64 mem_threshold = (std::min)(device_local_memory, TARGET_THRESHOLD);
|
|
||||||
const s64 min_vacancy_expected = (6 * mem_threshold) / 10;
|
|
||||||
const s64 min_vacancy_critical = (2 * mem_threshold) / 10;
|
|
||||||
expected_memory = static_cast<u64>(
|
|
||||||
(std::max)((std::min)(device_local_memory - min_vacancy_expected, min_spacing_expected),
|
|
||||||
DEFAULT_EXPECTED_MEMORY));
|
|
||||||
critical_memory = static_cast<u64>(
|
|
||||||
(std::max)((std::min)(device_local_memory - min_vacancy_critical, min_spacing_critical),
|
|
||||||
DEFAULT_CRITICAL_MEMORY));
|
|
||||||
minimum_memory = static_cast<u64>((device_local_memory - mem_threshold) / 2);
|
|
||||||
} else {
|
|
||||||
expected_memory = DEFAULT_EXPECTED_MEMORY + 512_MiB;
|
|
||||||
critical_memory = DEFAULT_CRITICAL_MEMORY + 1_GiB;
|
|
||||||
minimum_memory = 0;
|
|
||||||
}
|
}
|
||||||
|
const auto thresholds = VideoCommon::MakeReclaimThresholds(
|
||||||
|
device_local_memory, static_cast<u64>(TARGET_THRESHOLD),
|
||||||
|
static_cast<u64>(DEFAULT_EXPECTED_MEMORY), static_cast<u64>(DEFAULT_CRITICAL_MEMORY));
|
||||||
|
minimum_memory = thresholds.minimum;
|
||||||
|
expected_memory = thresholds.expected;
|
||||||
|
critical_memory = thresholds.critical;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
@@ -85,8 +75,9 @@ void TextureCache<P>::RunGarbageCollector() {
|
|||||||
u64 ticks_to_destroy = 0;
|
u64 ticks_to_destroy = 0;
|
||||||
size_t num_iterations = 0;
|
size_t num_iterations = 0;
|
||||||
const auto Configure = [&](bool allow_aggressive) {
|
const auto Configure = [&](bool allow_aggressive) {
|
||||||
high_priority_mode = total_used_memory >= expected_memory;
|
high_priority_mode = heap_pressure || total_used_memory >= expected_memory;
|
||||||
aggressive_mode = allow_aggressive && total_used_memory >= critical_memory;
|
aggressive_mode =
|
||||||
|
allow_aggressive && (heap_pressure || total_used_memory >= critical_memory);
|
||||||
ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
|
ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
|
||||||
num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
|
num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
|
||||||
};
|
};
|
||||||
@@ -100,7 +91,7 @@ void TextureCache<P>::RunGarbageCollector() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const bool must_download = IsDownloadable(image) && False(image.flags & ImageFlagBits::BadOverlap);
|
const bool must_download = IsDownloadable(image) && False(image.flags & ImageFlagBits::BadOverlap);
|
||||||
if ((!aggressive_mode && True(image.flags & ImageFlagBits::CostlyLoad)) || (!high_priority_mode && must_download)) {
|
if (!aggressive_mode && (must_download || True(image.flags & ImageFlagBits::CostlyLoad))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (must_download) {
|
if (must_download) {
|
||||||
@@ -126,7 +117,7 @@ void TextureCache<P>::RunGarbageCollector() {
|
|||||||
};
|
};
|
||||||
Configure(false);
|
Configure(false);
|
||||||
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
|
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
|
||||||
if (total_used_memory >= critical_memory) {
|
if (heap_pressure || total_used_memory >= critical_memory) {
|
||||||
Configure(true);
|
Configure(true);
|
||||||
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
|
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
|
||||||
}
|
}
|
||||||
@@ -134,11 +125,12 @@ void TextureCache<P>::RunGarbageCollector() {
|
|||||||
|
|
||||||
template <class P>
|
template <class P>
|
||||||
void TextureCache<P>::TickFrame() {
|
void TextureCache<P>::TickFrame() {
|
||||||
// If we can obtain the memory info, use it instead of the estimate.
|
heap_pressure = false;
|
||||||
if (runtime.CanReportMemoryUsage()) {
|
if (device_local_memory != 0 && runtime.CanReportMemoryUsage()) {
|
||||||
total_used_memory = runtime.GetDeviceMemoryUsage();
|
heap_pressure = runtime.GetDeviceMemoryUsage() + HEAP_PRESSURE_HEADROOM >=
|
||||||
|
device_local_memory;
|
||||||
}
|
}
|
||||||
if (total_used_memory > minimum_memory) {
|
if (total_used_memory > minimum_memory || heap_pressure) {
|
||||||
RunGarbageCollector();
|
RunGarbageCollector();
|
||||||
}
|
}
|
||||||
sentenced_images.Tick();
|
sentenced_images.Tick();
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
#include "common/thread_worker.h"
|
#include "common/thread_worker.h"
|
||||||
#include "video_core/compatible_formats.h"
|
#include "video_core/compatible_formats.h"
|
||||||
#include "video_core/control/channel_state_cache.h"
|
#include "video_core/control/channel_state_cache.h"
|
||||||
|
#include "video_core/cache_reclaim.h"
|
||||||
#include "video_core/delayed_destruction_ring.h"
|
#include "video_core/delayed_destruction_ring.h"
|
||||||
#include "video_core/engines/fermi_2d.h"
|
#include "video_core/engines/fermi_2d.h"
|
||||||
#include "video_core/surface.h"
|
#include "video_core/surface.h"
|
||||||
@@ -121,6 +122,7 @@ class TextureCache : public VideoCommon::ChannelSetupCaches<TextureCacheChannelI
|
|||||||
|
|
||||||
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB;
|
static constexpr s64 DEFAULT_EXPECTED_MEMORY = 1_GiB + 125_MiB;
|
||||||
static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB;
|
static constexpr s64 DEFAULT_CRITICAL_MEMORY = 1_GiB + 625_MiB;
|
||||||
|
static constexpr u64 HEAP_PRESSURE_HEADROOM = 512_MiB;
|
||||||
static constexpr size_t GC_EMERGENCY_COUNTS = 2;
|
static constexpr size_t GC_EMERGENCY_COUNTS = 2;
|
||||||
|
|
||||||
using Runtime = typename P::Runtime;
|
using Runtime = typename P::Runtime;
|
||||||
@@ -440,9 +442,11 @@ private:
|
|||||||
bool has_deleted_images = false;
|
bool has_deleted_images = false;
|
||||||
bool is_rescaling = false;
|
bool is_rescaling = false;
|
||||||
u64 total_used_memory = 0;
|
u64 total_used_memory = 0;
|
||||||
u64 minimum_memory;
|
u64 device_local_memory = 0;
|
||||||
u64 expected_memory;
|
u64 minimum_memory = 0;
|
||||||
u64 critical_memory;
|
u64 expected_memory = 0;
|
||||||
|
u64 critical_memory = 0;
|
||||||
|
bool heap_pressure = false;
|
||||||
|
|
||||||
struct BufferDownload {
|
struct BufferDownload {
|
||||||
GPUVAddr address;
|
GPUVAddr address;
|
||||||
|
|||||||
Reference in New Issue
Block a user