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:
bicarus-dev
2025-12-26 20:17:20 -08:00
committed by GitHub
parent 24d3062c66
commit 96e7e4949f
5 changed files with 94 additions and 1 deletions
+81 -1
View File
@@ -1,7 +1,10 @@
#include "avshook.h" #include "avshook.h"
#include <cassert>
#include <mutex>
#include <map> #include <map>
#include <optional> #include <optional>
#include <external/robin_hood.h>
#include "avs/core.h" #include "avs/core.h"
#include "avs/ea3.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::map<std::string, std::string> ROM_FILE_MAP;
static std::string *ROM_FILE_CONTENTS = nullptr; 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 { namespace hooks::avs::config {
bool DISABLE_VFS_DRIVE_REDIRECTION = false; bool DISABLE_VFS_DRIVE_REDIRECTION = false;
bool DISABLE_CACHE = false;
bool LOG = false; bool LOG = false;
}; };
@@ -79,6 +86,74 @@ static bool is_spam_file(const char *file) {
return false; 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) { 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 (is_fake_fd(fd) && ROM_FILE_CONTENTS) {
if (st) { if (st) {
@@ -104,7 +179,12 @@ static int avs_fs_lstat(const char *name, struct avs::core::avs_stat *st) {
return 1; 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)) { if (!is_spam_file(name)) {
WRAP_DEBUG_FMT("name: {}", name); WRAP_DEBUG_FMT("name: {}", name);
} }
+1
View File
@@ -9,6 +9,7 @@
namespace hooks::avs { namespace hooks::avs {
namespace config { namespace config {
extern bool DISABLE_VFS_DRIVE_REDIRECTION; extern bool DISABLE_VFS_DRIVE_REDIRECTION;
extern bool DISABLE_CACHE;
extern bool LOG; extern bool LOG;
}; };
+3
View File
@@ -752,6 +752,9 @@ int main_implementation(int argc, char *argv[]) {
if (options[launcher::Options::DisableAvsVfsDriveMountRedirection].is_active()) { if (options[launcher::Options::DisableAvsVfsDriveMountRedirection].is_active()) {
hooks::avs::config::DISABLE_VFS_DRIVE_REDIRECTION = true; 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()) { if (options[launcher::Options::ScreenResizeConfigPath].is_active()) {
cfg::SCREEN_RESIZE_CFG_PATH_OVERRIDE = cfg::SCREEN_RESIZE_CFG_PATH_OVERRIDE =
options[launcher::Options::ScreenResizeConfigPath].value_text(); options[launcher::Options::ScreenResizeConfigPath].value_text();
+8
View File
@@ -1766,6 +1766,14 @@ static const std::vector<OptionDefinition> OPTION_DEFINITIONS = {
.type = OptionType::Bool, .type = OptionType::Bool,
.category = "Development", .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", .title = "Output PEB",
.name = "pebprint", .name = "pebprint",
+1
View File
@@ -196,6 +196,7 @@ namespace launcher {
DisableSignalHandling, DisableSignalHandling,
DisableDebugHooks, DisableDebugHooks,
DisableAvsVfsDriveMountRedirection, DisableAvsVfsDriveMountRedirection,
DisableAvsCache,
OutputPEB, OutputPEB,
DumpSystemInfo, DumpSystemInfo,
QKSArgs, QKSArgs,