Clear disk cache if it grows too large (dev_hdd1/cache) (#5411)

* Clear disk cache automatically

* Add disk cache to UI
This commit is contained in:
MSuih 2018-12-23 23:18:47 +02:00 committed by Ivan
parent b99a88afe2
commit eba364c64c
6 changed files with 153 additions and 1 deletions

View file

@ -494,8 +494,85 @@ bool Emulator::BootRsxCapture(const std::string& path)
return true;
}
void Emulator::LimitCacheSize()
{
const std::string cache_location = Emulator::GetHdd1Dir() + "/cache";
if (!fs::is_dir(cache_location))
{
LOG_WARNING(GENERAL, "Cache does not exist (%s)", cache_location);
return;
}
const u64 size = fs::get_dir_size(cache_location);
const u64 max_size = static_cast<u64>(g_cfg.vfs.cache_max_size) * 1024 * 1024;
if (max_size == 0) // Everything must go, so no need to do checks
{
fs::remove_all(cache_location, false);
LOG_SUCCESS(GENERAL, "Cleared disk cache");
return;
}
if (size <= max_size)
{
LOG_TRACE(GENERAL, "Cache size below limit: %llu/%llu", size, max_size);
return;
}
LOG_SUCCESS(GENERAL, "Cleaning disk cache...");
std::vector<fs::dir_entry> file_list{};
fs::dir cache_dir{};
if (!cache_dir.open(cache_location))
{
LOG_ERROR(GENERAL, "Could not open cache directory");
return;
}
// retrieve items to delete
for (const auto &item : cache_dir)
{
if (item.name != "." && item.name != "..")
file_list.push_back(item);
}
cache_dir.close();
// sort oldest first
std::sort(file_list.begin(), file_list.end(), [](auto left, auto right)
{
return left.mtime < right.mtime;
});
// keep removing until cache is empty or enough bytes have been cleared
// cache is cleared down to 80% of limit to increase interval between clears
const u64 to_remove = static_cast<u64>(size - max_size * 0.8);
u64 removed = 0;
for (const auto &item : file_list)
{
const std::string &name = cache_location + "/" + item.name;
const u64 item_size = fs::is_dir(name) ? fs::get_dir_size(name) : item.size;
if (fs::is_dir(name))
{
fs::remove_all(name, true);
}
else
{
fs::remove_file(name);
}
removed += item_size;
if (removed >= to_remove)
break;
}
LOG_SUCCESS(GENERAL, "Cleaned disk cache, removed %.2f MB", size / 1024.0 / 1024.0);
}
bool Emulator::BootGame(const std::string& path, bool direct, bool add_only)
{
if (g_cfg.vfs.limit_cache_size)
LimitCacheSize();
static const char* boot_list[] =
{
"/PS3_GAME/USRDIR/EBOOT.BIN",
@ -571,6 +648,11 @@ std::string Emulator::GetHddDir()
return fmt::replace_all(g_cfg.vfs.dev_hdd0, "$(EmulatorDir)", GetEmuDir());
}
std::string Emulator::GetHdd1Dir()
{
return fmt::replace_all(g_cfg.vfs.dev_hdd1, "$(EmulatorDir)", GetEmuDir());
}
std::string Emulator::GetSfoDirFromGamePath(const std::string& game_path, const std::string& user)
{
if (fs::is_file(game_path + "/PS3_DISC.SFB"))

View file

@ -318,6 +318,9 @@ public:
private:
static std::string GetEmuDir();
static std::string GetHdd1Dir();
void LimitCacheSize();
public:
static std::string GetHddDir();
static std::string GetSfoDirFromGamePath(const std::string& game_path, const std::string& user);
@ -405,6 +408,9 @@ struct cfg_root : cfg::node
cfg::_bool host_root{this, "Enable /host_root/"};
cfg::_bool init_dirs{this, "Initialize Directories", true};
cfg::_bool limit_cache_size{this, "Limit disk cache size", false};
cfg::_int<0, 10240> cache_max_size{this, "Disk cache maximum size (MB)", 5120};
} vfs{this};
struct node_video : cfg::node

View file

@ -140,6 +140,7 @@
"system": {
"sysLangBox": "Some games may fail to boot if the system language is not available in the game itself.\nOther games will switch language automatically to what is selected here.\nIt is recommended leaving this on a language supported by the game.",
"enterButtonAssignment": "The button used for enter/accept/confirm in system dialogs.\nChange this to use the circle button instead, which is the default configuration on japanese systems and in many japanese games.\nIn these cases having the cross button assigned can often lead to confusion.",
"enableHostRoot": "Required for some Homebrew.\nIf unsure, don't use this option."
"enableHostRoot": "Required for some Homebrew.\nIf unsure, don't use this option.",
"limitCacheSize": "Automatically removes older files from disk cache on boot if it grows larger than the specified value.\nGames can use the cache folder to temporarily store data outside of system memory. It is not used for long term storage."
}
}

View file

@ -128,6 +128,8 @@ public:
Language,
EnterButtonAssignment,
EnableHostRoot,
LimitCacheSize,
MaximumCacheSize,
// Virtual File System
emulatorLocation,
@ -330,6 +332,8 @@ private:
{ Language, { "System", "Language"}},
{ EnterButtonAssignment, { "System", "Enter button assignment"}},
{ EnableHostRoot, { "VFS", "Enable /host_root/"}},
{ LimitCacheSize, { "VFS", "Limit disk cache size"}},
{ MaximumCacheSize, { "VFS", "Disk cache maximum size (MB)"}},
// Virtual File System
{ emulatorLocation, { "VFS", "$(EmulatorDir)"}},

View file

@ -784,6 +784,16 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> guiSettings, std:
xemu_settings->EnhanceCheckBox(ui->enableHostRoot, emu_settings::EnableHostRoot);
SubscribeTooltip(ui->enableHostRoot, json_sys["enableHostRoot"].toString());
xemu_settings->EnhanceCheckBox(ui->enableCacheClearing, emu_settings::LimitCacheSize);
SubscribeTooltip(ui->enableCacheClearing, json_sys["limitCacheSize"].toString());
connect(ui->enableCacheClearing, &QCheckBox::stateChanged, ui->maximumCacheSize, &QSlider::setEnabled);
// Sliders
EnhanceSlider(emu_settings::MaximumCacheSize, ui->maximumCacheSize, ui->maximumCacheSizeLabel, tr("Maximum size: %0 MB"));
SubscribeTooltip(ui->maximumCacheSize, json_sys["limitCacheSize"].toString());
ui->maximumCacheSize->setEnabled(ui->enableCacheClearing->isChecked());
// Radio Buttons
SubscribeTooltip(ui->gb_enterButtonAssignment, json_sys["enterButtonAssignment"].toString());

View file

@ -1149,6 +1149,55 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_18" stretch="1,1,1">
<item>
<widget class="QGroupBox" name="gb_DiskCacheClearing">
<property name="title">
<string>Disk cache</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_73">
<item>
<widget class="QCheckBox" name="enableCacheClearing">
<property name="text">
<string>Clear cache automatically</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="maximumCacheSizeLabel">
<property name="text">
<string>Cache size: 3072 MB</string>
</property>
</widget>
</item>
<item>
<widget class="QSlider" name="maximumCacheSize">
<property name="pageStep">
<number>512</number>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="tickPosition">
<enum>QSlider::TicksBelow</enum>
</property>
<property name="tickInterval">
<number>1024</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_14" native="true"/>
</item>
<item>
<widget class="QWidget" name="widget_15" native="true"/>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_8">
<property name="orientation">