Skip to content

New setting: Texture replacement load speed #20286

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Common/Data/Format/IniFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ static bool ParseLineKey(std::string_view line, size_t &pos, std::string *keyOut
}

static bool ParseLineValue(std::string_view line, size_t &pos, std::string *valueOut) {
std::string value = "";
std::string value;

std::string_view strippedLine = StripSpaces(line.substr(pos));
if (strippedLine.size() >= 2 && strippedLine[0] == '"' && strippedLine[strippedLine.size() - 1] == '"') {
Expand Down Expand Up @@ -408,7 +408,7 @@ std::map<std::string, std::string> Section::ToMap() const {
std::map<std::string, std::string> outMap;
for (auto &line : lines_) {
if (!line.Key().empty()) {
outMap[std::string(line.Key())] = line.Value();
outMap.emplace(line.Key(), line.Value());
}
}
return outMap;
Expand Down
5 changes: 5 additions & 0 deletions Common/Data/Format/IniFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,11 @@ class Section {
return name_;
}

// For reading without copying. Note: You may have to ignore lines with empty keys.
const std::vector<ParsedIniLine> &Lines() const {
return lines_;
}

protected:
std::vector<ParsedIniLine> lines_;
std::string name_;
Expand Down
1 change: 1 addition & 0 deletions Core/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,7 @@ static const ConfigSetting graphicsSettings[] = {
ConfigSetting("ReplaceTextures", &g_Config.bReplaceTextures, true, CfgFlag::PER_GAME | CfgFlag::REPORT),
ConfigSetting("SaveNewTextures", &g_Config.bSaveNewTextures, false, CfgFlag::PER_GAME | CfgFlag::REPORT),
ConfigSetting("IgnoreTextureFilenames", &g_Config.bIgnoreTextureFilenames, false, CfgFlag::PER_GAME),
ConfigSetting("ReplacementTextureLoadSpeed", &g_Config.iReplacementTextureLoadSpeed, 0, CfgFlag::PER_GAME),

ConfigSetting("TexScalingLevel", &g_Config.iTexScalingLevel, 1, CfgFlag::PER_GAME | CfgFlag::REPORT),
ConfigSetting("TexScalingType", &g_Config.iTexScalingType, 0, CfgFlag::PER_GAME | CfgFlag::REPORT),
Expand Down
1 change: 1 addition & 0 deletions Core/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ struct Config {
int bHighQualityDepth;
bool bReplaceTextures;
bool bSaveNewTextures;
int iReplacementTextureLoadSpeed;
bool bIgnoreTextureFilenames;
int iTexScalingLevel; // 0 = auto, 1 = off, 2 = 2x, ..., 5 = 5x
int iTexScalingType; // 0 = xBRZ, 1 = Hybrid
Expand Down
7 changes: 7 additions & 0 deletions Core/ConfigValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,13 @@ enum TextureFiltering {
TEX_FILTER_AUTO_MAX_QUALITY = 4,
};

enum ReplacementTextureLoadSpeed {
SLOW = 0,
MEDIUM = 1,
FAST = 2,
INSTANT = 3,
};

enum BufferFilter {
SCALE_LINEAR = 1,
SCALE_NEAREST = 2,
Expand Down
12 changes: 9 additions & 3 deletions Core/HW/Display.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,15 @@ static void CalculateFPS() {

// TODO: Also average actualFps
void __DisplayGetFPS(float *out_vps, float *out_fps, float *out_actual_fps) {
*out_vps = (float)fps;
*out_fps = flips;
*out_actual_fps = actualFps;
if (out_vps) {
*out_vps = (float)fps;
}
if (out_fps) {
*out_fps = flips;
}
if (out_actual_fps) {
*out_actual_fps = actualFps;
}
}

void __DisplayGetVPS(float *out_vps) {
Expand Down
2 changes: 1 addition & 1 deletion GPU/Common/ReplacedTexture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ bool ReplacedTexture::Poll(double budget) {
lastUsed_ = now;

// Let's not even start a new texture if we're already behind.
if (budget < 0.0)
if (budget <= 0.0)
return false;

_assert_(!threadWaitable_);
Expand Down
37 changes: 31 additions & 6 deletions GPU/Common/TextureCacheCommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "Core/Config.h"
#include "Core/Debugger/MemBlockInfo.h"
#include "Core/System.h"
#include "Core/HW/Display.h"
#include "GPU/Common/FramebufferManagerCommon.h"
#include "GPU/Common/TextureCacheCommon.h"
#include "GPU/Common/TextureDecoder.h"
Expand Down Expand Up @@ -127,6 +128,31 @@ void TextureCacheCommon::StartFrame() {
timesInvalidatedAllThisFrame_ = 0;
replacementTimeThisFrame_ = 0.0;

float fps;
__DisplayGetFPS(nullptr, &fps, nullptr);
if (fps <= 5.0f) {
fps = 60.0f;
}

float baseValue = 0.5f;
switch (g_Config.iReplacementTextureLoadSpeed) {
case (int)ReplacementTextureLoadSpeed::SLOW:
baseValue = 0.5f;
break;
case (int)ReplacementTextureLoadSpeed::MEDIUM:
baseValue = 0.75f;
break;
case (int)ReplacementTextureLoadSpeed::FAST:
baseValue = 1.0f;
break;
case (int)ReplacementTextureLoadSpeed::INSTANT:
baseValue = 100000.0f; // no budget limit, effectively.
break;
}

// Allow spending half a frame on uploading textures.
replacementFrameBudgetSeconds_ = baseValue / fps;

if ((DebugOverlay)g_Config.iDebugOverlay == DebugOverlay::DEBUG_STATS) {
gpuStats.numReplacerTrackedTex = replacer_.GetNumTrackedTextures();
gpuStats.numCachedReplacedTextures = replacer_.GetNumCachedReplacedTextures();
Expand Down Expand Up @@ -530,7 +556,7 @@ TexCacheEntry *TextureCacheCommon::SetTexture() {
}
}

if (match && (entry->status & TexCacheEntry::STATUS_TO_REPLACE) && replacementTimeThisFrame_ < replacementFrameBudget_) {
if (match && (entry->status & TexCacheEntry::STATUS_TO_REPLACE) && replacementTimeThisFrame_ < replacementFrameBudgetSeconds_) {
int w0 = gstate.getTextureWidth(0);
int h0 = gstate.getTextureHeight(0);
int d0 = 1;
Expand Down Expand Up @@ -1572,10 +1598,9 @@ ReplacedTexture *TextureCacheCommon::FindReplacement(TexCacheEntry *entry, int *
}

void TextureCacheCommon::PollReplacement(TexCacheEntry *entry, int *w, int *h, int *d) {
// Allow some delay to reduce pop-in.
constexpr double MAX_BUDGET_PER_TEX = 0.25 / 60.0;

double budget = std::min(MAX_BUDGET_PER_TEX, replacementFrameBudget_ - replacementTimeThisFrame_);
double budget = replacementFrameBudgetSeconds_ - replacementTimeThisFrame_;
// Note: Don't avoid the Poll call if budget is 0, we do meaningful things there.
// Poll also handles negative budgets.

double replaceStart = time_now_d();
if (entry->replacedTexture->Poll(budget)) {
Expand Down Expand Up @@ -3157,7 +3182,7 @@ void TextureCacheCommon::DrawImGuiDebug(uint64_t &selectedTextureId) const {
ImGui::Text("Texels scaled this frame: %d", texelsScaledThisFrame_);
ImGui::Text("Low memory mode: %d", (int)lowMemoryMode_);
if (ImGui::CollapsingHeader("Texture Replacement", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::Text("Frame time/budget: %0.3f/%0.3f ms", replacementTimeThisFrame_ * 1000.0f, replacementFrameBudget_ * 1000.0f);
ImGui::Text("Frame time/budget: %0.3f/%0.3f ms", replacementTimeThisFrame_ * 1000.0f, replacementFrameBudgetSeconds_ * 1000.0f);
ImGui::Text("UNLOADED: %d PENDING: %d NOT_FOUND: %d ACTIVE: %d CANCEL_INIT: %d",
replacementStateCounts[(int)ReplacementState::UNLOADED],
replacementStateCounts[(int)ReplacementState::PENDING],
Expand Down
4 changes: 2 additions & 2 deletions GPU/Common/TextureCacheCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -496,8 +496,8 @@ class TextureCacheCommon {
int texelsScaledThisFrame_ = 0;
int timesInvalidatedAllThisFrame_ = 0;
double replacementTimeThisFrame_ = 0;
// TODO: Maybe vary by FPS...
double replacementFrameBudget_ = 0.5 / 60.0;
// Recomputed once per frame. Depends FPS and soon also config.
double replacementFrameBudgetSeconds_ = 0.5 / 60.0;

TexCache cache_;
u32 cacheSizeEstimate_ = 0;
Expand Down
25 changes: 13 additions & 12 deletions GPU/Common/TextureReplacer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,16 @@ void TextureReplacer::NotifyConfigChanged() {
delete vfs_;
vfs_ = nullptr;
Decimate(ReplacerDecimateMode::ALL);
}

if (replaceEnabled_) {
} else if (!wasReplaceEnabled && replaceEnabled_) {
std::string error;
replaceEnabled_ = LoadIni(&error);
if (!error.empty() && !replaceEnabled_) {
ERROR_LOG(Log::G3D, "ERROR: %s", error.c_str());
g_OSD.Show(OSDType::MESSAGE_ERROR, error, 5.0f);
}
} else if (saveEnabled_) {
// Even if just saving is enabled, it makes sense to load the ini to get the correct
// settings for saving. See issue #19086
// Even if just saving is enabled, it makes sense to reload the ini to get the correct
// settings for saving. See issue #19086. This can be expensive though.
std::string error;
bool result = LoadIni(&error);
if (!result) {
Expand Down Expand Up @@ -281,7 +279,7 @@ void TextureReplacer::ComputeAliasMap(const std::map<ReplacementCacheKey, std::m
}
}
if (alias == "|") {
alias = ""; // marker for no replacement
alias.clear(); // marker for no replacement
}
// Replace any '\' with '/', to be safe and consistent. Since these are from the ini file, we do this on all platforms.
for (auto &c : alias) {
Expand Down Expand Up @@ -345,16 +343,21 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, VFSBackend *dir, bool isOverri
std::string badFilenames;

if (ini.HasSection("hashes")) {
auto hashes = ini.GetOrCreateSection("hashes")->ToMap();
const Section *hashesSection = ini.GetOrCreateSection("hashes");
// Format: hashname = filename.png
bool checkFilenames = saveEnabled_ && !g_Config.bIgnoreTextureFilenames && !vfsIsZip_;

for (const auto &[k, v] : hashes) {
for (const auto &line : hashesSection->Lines()) {
if (line.Key().empty())
continue;
ReplacementCacheKey key(0, 0);
// sscanf might fail to pluck the level if omitted from the line, but that's ok, we default level to 0.
// sscanf doesn't write to non-matched outputs.
int level = 0;
if (sscanf(k.c_str(), "%16llx%8x_%d", &key.cachekey, &key.hash, &level) >= 1) {
char k[128];
truncate_cpy(k, line.Key());
std::string_view v = line.Value();
if (sscanf(k, "%16llx%8x_%d", &key.cachekey, &key.hash, &level) >= 1) {
// We allow empty filenames, to mark textures that we don't want to keep saving.
filenameMap[key][level] = v;
if (checkFilenames) {
Expand All @@ -376,10 +379,8 @@ bool TextureReplacer::LoadIniValues(IniFile &ini, VFSBackend *dir, bool isOverri
}
}
}
} else if (k.empty()) {
INFO_LOG(Log::TexReplacement, "Ignoring [hashes] line with empty key: '= %s'", v.c_str());
} else {
ERROR_LOG(Log::TexReplacement, "Unsupported syntax under [hashes], ignoring: %s = ", k.c_str());
ERROR_LOG(Log::TexReplacement, "Unsupported syntax under [hashes], ignoring: %s = ", k);
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions Tools/langtool/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions UI/DeveloperToolsScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ static std::string PostShaderTranslateName(std::string_view value) {
void DeveloperToolsScreen::CreateTextureReplacementTab(UI::LinearLayout *list) {
using namespace UI;
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
auto di = GetI18NCategory(I18NCat::DIALOG);

list->Add(new ItemHeader(dev->T("Texture Replacement")));
list->Add(new CheckBox(&g_Config.bSaveNewTextures, dev->T("Save new textures")));
Expand All @@ -95,6 +96,26 @@ void DeveloperToolsScreen::CreateTextureReplacementTab(UI::LinearLayout *list) {
}
return true;
});

if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) {
// Best string we have
list->Add(new Choice(di->T("Show in folder")))->OnClick.Add([=](UI::EventParams &) {
Path path;
if (PSP_IsInited()) {
std::string gameID = g_paramSFO.GetDiscID();
path = GetSysDirectory(DIRECTORY_TEXTURES) / gameID;
} else {
// Just show the root textures directory.
path = GetSysDirectory(DIRECTORY_TEXTURES);
}
System_ShowFileInFolder(path);
return UI::EVENT_DONE;
});
}

static const char *texLoadSpeeds[] = { "Slow (smooth)", "Medium", "Fast", "Instant (may stutter)" };
PopupMultiChoice *texLoadSpeed = list->Add(new PopupMultiChoice(&g_Config.iReplacementTextureLoadSpeed, dev->T("Replacement texture load speed"), texLoadSpeeds, 0, ARRAY_SIZE(texLoadSpeeds), I18NCat::DEVELOPER, screenManager()));
texLoadSpeed->SetChoiceIcon(3, ImageID("I_WARNING"));
}

void DeveloperToolsScreen::CreateGeneralTab(UI::LinearLayout *list) {
Expand Down
3 changes: 1 addition & 2 deletions UI/EmuScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1768,8 +1768,6 @@ void EmuScreen::runImDebugger() {
if (g_Config.bShowImDebugger) {
Draw::DrawContext *draw = screenManager()->getDrawContext();
if (!imguiInited_) {
imguiInited_ = true;

// TODO: Do this only on demand.
IMGUI_CHECKVERSION();
ctx_ = ImGui::CreateContext();
Expand All @@ -1783,6 +1781,7 @@ void EmuScreen::runImDebugger() {
// This call works even if fontData is nullptr, in which case the font just won't get loaded.
// This takes ownership of the font array.
ImGui_ImplThin3d_Init(draw, fontData, size);
imguiInited_ = true;
}

if (PSP_IsInited()) {
Expand Down
2 changes: 1 addition & 1 deletion UI/InstallZipScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ void InstallZipScreen::CreateViews() {
compareColumns->Add(rightCompare);
existingSaveView_ = rightCompare->Add(new SavedataView(*screenManager()->getUIContext(), ginfo.get(), IdentifiedFileType::PSP_SAVEDATA_DIRECTORY, false, new LinearLayoutParams(columnWidth, WRAP_CONTENT)));
if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) {
rightCompare->Add(new Button(ga->T("Show In Folder")))->OnClick.Add([=](UI::EventParams &) {
rightCompare->Add(new Button(di->T("Show in folder")))->OnClick.Add([=](UI::EventParams &) {
System_ShowFileInFolder(savedataToOverwrite_);
return UI::EVENT_DONE;
});
Expand Down
5 changes: 5 additions & 0 deletions assets/lang/ar_AE.ini
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,7 @@ Dump next frame to log = Dump next frame to log
Enable driver bug workarounds = Enable driver bug workarounds
Enable Logging = ‎تفعيل سجل التصحيح
Enter address = ‎أدخل العنوان
Fast = Fast
Fast-forward mode = Fast-forward mode
FPU = FPU
Fragment = Fragment
Expand All @@ -330,12 +331,14 @@ GPU Allocator Viewer = GPU Allocator Viewer
GPU Driver Test = GPU driver test
GPU log profiler = GPU log profiler
GPU Profile = GPU profile
Instant (may stutter) = Instant (may stutter)
Jit Compare = Jit compare
JIT debug tools = JIT debug tools
Log Dropped Frame Statistics = ‎سجل التفاصيل للفريمات المتدنية
Log Level = ‎مستوي السجل
Log View = ‎أظهر السجل
Logging Channels = ‎قنوات التسجيل
Medium = Medium
Multi-threaded rendering = Multi-threaded rendering
Next = ‎التالي
No block = بدون منع
Expand All @@ -344,6 +347,7 @@ Prev = ‎السابق
Prevent loading overlays = Prevent loading overlays
Random = ‎عشوائي
Replace textures = ‎إستبدال الرسوم
Replacement texture load speed = Replacement texture load speed
Reset = إعادة تشغيل
Reset limited logging = إعادة تشغيل التصحيح المؤقت
RestoreDefaultSettings = Restore these settings back to their defaults?\nYou can't undo this.\nPlease restart PPSSPP after restoring settings.
Expand All @@ -355,6 +359,7 @@ Shader Viewer = ‎مستعرض الرسوميات
Show Developer Menu = ‎أظهر قائمة المطور
Show GPO LEDs = Show GPO LEDs
Show on-screen messages = إظهار الرسائل على الشاشة
Slow (smooth) = Slow (smooth)
Stats = ‎الحالات
System Information = ‎معلومات النظام
Texture ini file created = Texture ini file created
Expand Down
Loading
Loading