mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 14:20:42 -07:00
iidx, linux: cache lstat result for movie thumbnails (#474)
## Link to GitHub Issue, if one exists https://codeberg.org/nixac/spicetools/issues/4 ## Description of change Inspired by the Linux issue above in the fork, cache lstat calls for thumbnail. IIDX 32 and 33 make a call for this every frame which can cause significant overhead on Linux. This is being enabled for all versions and not just the Linux variant of the executables since it provides some marginal performance gain on Windows. ## Testing Tested IIDX 32/33, with the option on/off, and with AVS verbose logging on/off. Tested with ifs_layeredfs hook as well just to verify functionality.
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
#include "avshook.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <external/robin_hood.h>
|
||||
|
||||
#include "avs/core.h"
|
||||
#include "avs/ea3.h"
|
||||
@@ -20,8 +23,12 @@ static bool FAKE_FILE_OPEN = false;
|
||||
static std::map<std::string, std::string> ROM_FILE_MAP;
|
||||
static std::string *ROM_FILE_CONTENTS = nullptr;
|
||||
|
||||
static std::mutex lstat_cache_mutex;
|
||||
static robin_hood::unordered_map<std::string, std::pair<int, struct avs::core::avs_stat>> lstat_cache;
|
||||
|
||||
namespace hooks::avs::config {
|
||||
bool DISABLE_VFS_DRIVE_REDIRECTION = false;
|
||||
bool DISABLE_CACHE = false;
|
||||
bool LOG = false;
|
||||
};
|
||||
|
||||
@@ -79,6 +86,74 @@ static bool is_spam_file(const char *file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool is_ldj_cacheable_file(const char *file) {
|
||||
static const char *spam_prefixes[] = {
|
||||
// iidx33: profile background customization, spams on every frame during card entry
|
||||
"/data/graphic/entry_card",
|
||||
// iidx32+: movie thumbnails, spams on every frame during song select
|
||||
"/data/graphic/thumbnail/",
|
||||
"/data/graphic/movie_thumbnail/",
|
||||
};
|
||||
|
||||
for (auto &spam : spam_prefixes) {
|
||||
if (string_begins_with(file, spam)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int cached_avs_fs_lstat(const char *name, struct avs::core::avs_stat *st) {
|
||||
assert(name != nullptr);
|
||||
assert(st != nullptr);
|
||||
|
||||
const std::string filename(name);
|
||||
int value = 0;
|
||||
std::lock_guard<std::mutex> lock(lstat_cache_mutex);
|
||||
|
||||
// if this was already seen, return cached result
|
||||
if (lstat_cache.contains(filename)) {
|
||||
const auto &result = lstat_cache.at(filename);
|
||||
value = result.first;
|
||||
memcpy(st, &result.second, sizeof(*st));
|
||||
WRAP_DEBUG_FMT(
|
||||
"(cached) file={}, a={}, m={}, c={}, size={}, u={}",
|
||||
name,
|
||||
st->st_atime,
|
||||
st->st_mtime,
|
||||
st->st_ctime,
|
||||
st->filesize,
|
||||
st->unk1);
|
||||
return value;
|
||||
}
|
||||
|
||||
// if not, call the original avs function
|
||||
// this returns 1 on success with valid values in st
|
||||
// return 0 on failure, with filesize set to 0
|
||||
// maybe this could be used for future optimization
|
||||
value = avs::core::avs_fs_lstat(name, st);
|
||||
WRAP_DEBUG_FMT(
|
||||
"file={} a={}, m={}, c={}, size={}, u={}",
|
||||
name,
|
||||
st->st_atime,
|
||||
st->st_mtime,
|
||||
st->st_ctime,
|
||||
st->filesize,
|
||||
st->unk1);
|
||||
|
||||
// and cache it if there is room (prevent unconstrained growth)
|
||||
if (lstat_cache.size() <= 4096) {
|
||||
// c++ was a mistake
|
||||
lstat_cache.emplace(
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(filename),
|
||||
std::forward_as_tuple(value, *st));
|
||||
}
|
||||
|
||||
// done
|
||||
return value;
|
||||
}
|
||||
|
||||
static int avs_fs_fstat(avs::core::avs_file_t fd, struct avs::core::avs_stat *st) {
|
||||
if (is_fake_fd(fd) && ROM_FILE_CONTENTS) {
|
||||
if (st) {
|
||||
@@ -104,7 +179,12 @@ static int avs_fs_lstat(const char *name, struct avs::core::avs_stat *st) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto value = avs::core::avs_fs_lstat(name, st);
|
||||
if (!hooks::avs::config::DISABLE_CACHE && (st != nullptr) &&
|
||||
avs::game::is_model("LDJ") && is_ldj_cacheable_file(name)) {
|
||||
return cached_avs_fs_lstat(name, st);
|
||||
}
|
||||
|
||||
const auto value = avs::core::avs_fs_lstat(name, st);
|
||||
if (!is_spam_file(name)) {
|
||||
WRAP_DEBUG_FMT("name: {}", name);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
namespace hooks::avs {
|
||||
namespace config {
|
||||
extern bool DISABLE_VFS_DRIVE_REDIRECTION;
|
||||
extern bool DISABLE_CACHE;
|
||||
extern bool LOG;
|
||||
};
|
||||
|
||||
|
||||
@@ -752,6 +752,9 @@ int main_implementation(int argc, char *argv[]) {
|
||||
if (options[launcher::Options::DisableAvsVfsDriveMountRedirection].is_active()) {
|
||||
hooks::avs::config::DISABLE_VFS_DRIVE_REDIRECTION = true;
|
||||
}
|
||||
if (options[launcher::Options::DisableAvsCache].is_active()) {
|
||||
hooks::avs::config::DISABLE_CACHE = true;
|
||||
}
|
||||
if (options[launcher::Options::ScreenResizeConfigPath].is_active()) {
|
||||
cfg::SCREEN_RESIZE_CFG_PATH_OVERRIDE =
|
||||
options[launcher::Options::ScreenResizeConfigPath].value_text();
|
||||
|
||||
@@ -1766,6 +1766,14 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
|
||||
.type = OptionType::Bool,
|
||||
.category = "Development",
|
||||
},
|
||||
{
|
||||
// DisableAvsCache
|
||||
.title = "Disable AVS Cache",
|
||||
.name = "avscachedisable",
|
||||
.desc = "Disable optimization used for some games to cache data file info (only for IIDX32+ for now)",
|
||||
.type = OptionType::Bool,
|
||||
.category = "Development",
|
||||
},
|
||||
{
|
||||
.title = "Output PEB",
|
||||
.name = "pebprint",
|
||||
|
||||
@@ -196,6 +196,7 @@ namespace launcher {
|
||||
DisableSignalHandling,
|
||||
DisableDebugHooks,
|
||||
DisableAvsVfsDriveMountRedirection,
|
||||
DisableAvsCache,
|
||||
OutputPEB,
|
||||
DumpSystemInfo,
|
||||
QKSArgs,
|
||||
|
||||
Reference in New Issue
Block a user