Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/UI/DeveloperToolsScreen.cpp
4776 views
1
// Copyright (c) 2013- PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official git repository and contact information can be found at
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
17
18
#include <string>
19
20
#include "android/jni/app-android.h"
21
#include "Common/UI/View.h"
22
#include "Common/UI/ViewGroup.h"
23
#include "Common/System/OSD.h"
24
#include "Common/GPU/OpenGL/GLFeatures.h"
25
#include "Common/Data/Text/Parsers.h"
26
#include "Common/Data/Encoding/Utf8.h"
27
#include "Common/File/FileUtil.h"
28
#include "Common/Render/Text/draw_text.h"
29
#include "Common/StringUtils.h"
30
#include "GPU/Common/TextureReplacer.h"
31
#include "GPU/Common/PostShader.h"
32
#include "Core/MIPS/MIPSTracer.h"
33
#include "Core/ELF/ParamSFO.h"
34
#include "Core/Config.h"
35
#include "Core/HLE/HLE.h"
36
#include "Core/Core.h"
37
#include "Core/System.h"
38
#include "Core/WebServer.h"
39
#include "UI/GPUDriverTestScreen.h"
40
#include "UI/DeveloperToolsScreen.h"
41
#include "UI/DevScreens.h"
42
#include "UI/DriverManagerScreen.h"
43
#include "UI/DisplayLayoutScreen.h"
44
#include "UI/GameSettingsScreen.h"
45
#include "UI/OnScreenDisplay.h"
46
#include "UI/IconCache.h"
47
#include "UI/MiscViews.h"
48
49
#if PPSSPP_PLATFORM(ANDROID)
50
51
static bool CheckKgslPresent() {
52
constexpr auto KgslPath{ "/dev/kgsl-3d0" };
53
54
return access(KgslPath, F_OK) == 0;
55
}
56
57
static bool SupportsCustomDriver() {
58
return android_get_device_api_level() >= 28 && CheckKgslPresent();
59
}
60
61
#else
62
63
static bool SupportsCustomDriver() {
64
#ifdef _DEBUG
65
return false; // change to true to debug driver installation on other platforms
66
#else
67
return false;
68
#endif
69
}
70
71
#endif
72
73
static std::string PostShaderTranslateName(std::string_view value) {
74
const ShaderInfo *info = GetPostShaderInfo(value);
75
if (info) {
76
auto ps = GetI18NCategory(I18NCat::POSTSHADERS);
77
return std::string(ps->T(value, info->name));
78
} else {
79
return std::string(value);
80
}
81
}
82
83
void DeveloperToolsScreen::CreateTextureReplacementTab(UI::LinearLayout *list) {
84
using namespace UI;
85
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
86
auto di = GetI18NCategory(I18NCat::DIALOG);
87
88
list->Add(new ItemHeader(dev->T("Texture Replacement")));
89
list->Add(new CheckBox(&g_Config.bSaveNewTextures, dev->T("Save new textures")));
90
list->Add(new CheckBox(&g_Config.bReplaceTextures, dev->T("Replace textures")));
91
92
Choice *createTextureIni = list->Add(new Choice(dev->T("Create/Open textures.ini file for current game")));
93
createTextureIni->OnClick.Handle(this, &DeveloperToolsScreen::OnOpenTexturesIniFile);
94
createTextureIni->SetEnabledFunc([&] {
95
if (!PSP_IsInited())
96
return false;
97
98
// Disable the choice to Open/Create if the textures.ini file already exists, and we can't open it due to platform support limitations.
99
if (!System_GetPropertyBool(SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR)) {
100
if (hasTexturesIni_ == HasIni::MAYBE)
101
hasTexturesIni_ = TextureReplacer::IniExists(g_paramSFO.GetDiscID()) ? HasIni::YES : HasIni::NO;
102
return hasTexturesIni_ != HasIni::YES;
103
}
104
return true;
105
});
106
107
if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) {
108
// Best string we have
109
list->Add(new Choice(di->T("Show in folder")))->OnClick.Add([=](UI::EventParams &) {
110
Path path;
111
if (PSP_IsInited()) {
112
std::string gameID = g_paramSFO.GetDiscID();
113
path = GetSysDirectory(DIRECTORY_TEXTURES) / gameID;
114
} else {
115
// Just show the root textures directory.
116
path = GetSysDirectory(DIRECTORY_TEXTURES);
117
}
118
System_ShowFileInFolder(path);
119
});
120
}
121
122
static const char *texLoadSpeeds[] = { "Slow (smooth)", "Medium", "Fast", "Instant (may stutter)" };
123
PopupMultiChoice *texLoadSpeed = list->Add(new PopupMultiChoice(&g_Config.iReplacementTextureLoadSpeed, dev->T("Replacement texture load speed"), texLoadSpeeds, 0, ARRAY_SIZE(texLoadSpeeds), I18NCat::DEVELOPER, screenManager()));
124
texLoadSpeed->SetChoiceIcon(3, ImageID("I_WARNING"));
125
}
126
127
void DeveloperToolsScreen::CreateGeneralTab(UI::LinearLayout *list) {
128
using namespace UI;
129
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
130
auto sy = GetI18NCategory(I18NCat::SYSTEM);
131
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
132
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
133
134
list->Add(new ItemHeader(sy->T("CPU Core")));
135
136
bool canUseJit = System_GetPropertyBool(SYSPROP_CAN_JIT);
137
// iOS can now use JIT on all modes, apparently.
138
// The bool may come in handy for future non-jit platforms though (UWP XB1?)
139
// In iOS App Store builds, we disable the JIT.
140
141
static const char *cpuCores[] = { "Interpreter", "Dynarec/JIT (recommended)", "IR Interpreter", "JIT using IR" };
142
PopupMultiChoice *core = list->Add(new PopupMultiChoice(&g_Config.iCpuCore, sy->T("CPU Core"), cpuCores, 0, ARRAY_SIZE(cpuCores), I18NCat::SYSTEM, screenManager()));
143
core->OnChoice.Add([=](UI::EventParams &e) {
144
OnJitAffectingSetting(e);
145
g_Config.NotifyUpdatedCpuCore();
146
});
147
if (!canUseJit) {
148
core->HideChoice(1);
149
core->HideChoice(3);
150
}
151
// TODO: Enable "JIT using IR" on more architectures.
152
#if !PPSSPP_ARCH(X86) && !PPSSPP_ARCH(AMD64) && !PPSSPP_ARCH(ARM64)
153
core->HideChoice(3);
154
#endif
155
156
list->Add(new Choice(dev->T("JIT debug tools")))->OnClick.Handle(this, &DeveloperToolsScreen::OnJitDebugTools);
157
list->Add(new CheckBox(&g_Config.bShowDeveloperMenu, dev->T("Show Developer Menu")));
158
159
AddOverlayList(list, screenManager());
160
161
list->Add(new ItemHeader(sy->T("General")));
162
163
list->Add(new CheckBox(&g_Config.bEnableLogging, dev->T("Enable Logging")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLoggingChanged);
164
list->Add(new Choice(dev->T("Logging Channels")))->OnClick.Add([this](UI::EventParams &e) {
165
screenManager()->push(new LogConfigScreen());
166
});
167
list->Add(new CheckBox(&g_Config.bEnableFileLogging, dev->T("Log to file")))->SetEnabledPtr(&g_Config.bEnableLogging);
168
list->Add(new CheckBox(&g_Config.bLogFrameDrops, dev->T("Log Dropped Frame Statistics")));
169
if (GetGPUBackend() == GPUBackend::VULKAN) {
170
list->Add(new CheckBox(&g_Config.bGpuLogProfiler, dev->T("GPU log profiler")));
171
}
172
173
allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);
174
canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);
175
CheckBox *allowDebugger = new CheckBox(&allowDebugger_, dev->T("Allow remote debugger"));
176
list->Add(allowDebugger)->OnClick.Handle(this, &DeveloperToolsScreen::OnRemoteDebugger);
177
allowDebugger->SetEnabledPtr(&canAllowDebugger_);
178
179
CheckBox *localDebugger = list->Add(new CheckBox(&g_Config.bRemoteDebuggerLocal, dev->T("Use locally hosted remote debugger")));
180
localDebugger->SetEnabledPtr(&allowDebugger_);
181
182
list->Add(new Choice(dev->T("GPI/GPO switches/LEDs")))->OnClick.Add([=](UI::EventParams &e) {
183
screenManager()->push(new GPIGPOScreen(dev->T("GPI/GPO switches/LEDs")));
184
});
185
186
#if PPSSPP_PLATFORM(ANDROID)
187
static const char *framerateModes[] = { "Default", "Request 60 Hz", "Force 60Hz" };
188
PopupMultiChoice *framerateMode = list->Add(new PopupMultiChoice(&g_Config.iDisplayFramerateMode, gr->T("Framerate mode"), framerateModes, 0, ARRAY_SIZE(framerateModes), I18NCat::GRAPHICS, screenManager()));
189
framerateMode->SetEnabledFunc([]() { return System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 30; });
190
framerateMode->OnChoice.Add([](UI::EventParams &e) {
191
System_Notify(SystemNotification::FORCE_RECREATE_ACTIVITY);
192
});
193
#endif
194
195
#if PPSSPP_PLATFORM(IOS)
196
list->Add(new NoticeView(NoticeLevel::WARN, ms->T("Moving the memstick directory is NOT recommended on iOS"), ""));
197
list->Add(new Choice(sy->T("Set Memory Stick folder")))->OnClick.Add(
198
[=](UI::EventParams &) {
199
SetMemStickDirDarwin(GetRequesterToken());
200
});
201
#endif
202
203
// Makes it easy to get savestates out of an iOS device. The file listing shown in MacOS doesn't allow
204
// you to descend into directories.
205
#if PPSSPP_PLATFORM(IOS)
206
list->Add(new Choice(dev->T("Copy savestates to memstick root")))->OnClick.Handle(this, &DeveloperToolsScreen::OnCopyStatesToRoot);
207
#endif
208
}
209
210
void DeveloperToolsScreen::CreateTestsTab(UI::LinearLayout *list) {
211
using namespace UI;
212
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
213
214
list->Add(new Choice(dev->T("Touchscreen Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnTouchscreenTest);
215
// list->Add(new Choice(dev->T("Memstick Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnMemstickTest);
216
Choice *frameDumpTests = list->Add(new Choice(dev->T("Framedump tests")));
217
frameDumpTests->OnClick.Add([this](UI::EventParams &e) {
218
screenManager()->push(new FrameDumpTestScreen());
219
});
220
frameDumpTests->SetEnabled(!PSP_IsInited());
221
// For now, we only implement GPU driver tests for Vulkan and OpenGL. This is simply
222
// because the D3D drivers are generally solid enough to not need this type of investigation.
223
if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL) {
224
list->Add(new Choice(dev->T("GPU Driver Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnGPUDriverTest);
225
}
226
227
// Not useful enough to be made visible.
228
/*
229
auto memmapTest = list->Add(new Choice(dev->T("Memory map test")));
230
memmapTest->OnClick.Add([this](UI::EventParams &e) {
231
MemoryMapTest();
232
});
233
memmapTest->SetEnabled(PSP_IsInited());
234
*/
235
}
236
237
void DeveloperToolsScreen::CreateDumpFileTab(UI::LinearLayout *list) {
238
using namespace UI;
239
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
240
241
list->Add(new ItemHeader(dev->T("Dump files")));
242
list->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::EBOOT, dev->T("Dump Decrypted Eboot", "Dump Decrypted EBOOT.BIN (If Encrypted) When Booting Game")));
243
list->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::PRX, dev->T("PRX")));
244
list->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::Atrac3, dev->T("Atrac3/3+")));
245
}
246
247
void DeveloperToolsScreen::CreateHLETab(UI::LinearLayout *list) {
248
using namespace UI;
249
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
250
251
list->Add(new CheckBox(&g_Config.bUseOldAtrac, dev->T("Use the old sceAtrac implementation")));
252
253
list->Add(new ItemHeader(dev->T("Disable HLE")));
254
255
for (int i = 0; i < (int)DisableHLEFlags::Count; i++) {
256
DisableHLEFlags flag = (DisableHLEFlags)(1 << i);
257
258
// Show a checkbox, unless the setting has graduated to always disabled.
259
if (!(flag & AlwaysDisableHLEFlags())) {
260
const HLEModuleMeta *meta = GetHLEModuleMetaByFlag(flag);
261
if (meta) {
262
BitCheckBox *checkBox = list->Add(new BitCheckBox(&g_Config.iDisableHLE, (int)flag, meta->modname));
263
checkBox->SetEnabled(!PSP_IsInited());
264
}
265
}
266
}
267
268
list->Add(new ItemHeader(dev->T("Force-enable HLE")));
269
270
for (int i = 0; i < (int)DisableHLEFlags::Count; i++) {
271
DisableHLEFlags flag = (DisableHLEFlags)(1 << i);
272
273
// Show a checkbox, only if the setting has graduated to always disabled (and thus it makes sense to force-enable it).
274
if (flag & AlwaysDisableHLEFlags()) {
275
const HLEModuleMeta *meta = GetHLEModuleMetaByFlag(flag);
276
if (meta) {
277
BitCheckBox *checkBox = list->Add(new BitCheckBox(&g_Config.iForceEnableHLE, (int)flag, meta->modname));
278
checkBox->SetEnabled(!PSP_IsInited());
279
}
280
}
281
}
282
}
283
284
void DeveloperToolsScreen::CreateMIPSTracerTab(UI::LinearLayout *list) {
285
using namespace UI;
286
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
287
list->Add(new ItemHeader(dev->T("MIPSTracer")));
288
289
MIPSTracerEnabled_ = mipsTracer.tracing_enabled;
290
CheckBox *MIPSTracerEnabled = new CheckBox(&MIPSTracerEnabled_, dev->T("MIPSTracer enabled"));
291
list->Add(MIPSTracerEnabled)->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerEnabled);
292
MIPSTracerEnabled->SetEnabledFunc([]() {
293
bool temp = g_Config.iCpuCore == static_cast<int>(CPUCore::IR_INTERPRETER) && PSP_IsInited();
294
return temp && Core_IsStepping() && coreState != CORE_POWERDOWN;
295
});
296
297
Choice *TraceDumpPath = list->Add(new Choice(dev->T("Select the file path for the trace")));
298
TraceDumpPath->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerPathChanged);
299
TraceDumpPath->SetEnabledFunc([]() {
300
if (!PSP_IsInited())
301
return false;
302
return true;
303
});
304
305
MIPSTracerPath_ = mipsTracer.get_logging_path();
306
MIPSTracerPath = list->Add(new InfoItem(dev->T("Current log file"), MIPSTracerPath_));
307
308
PopupSliderChoice* storage_capacity = list->Add(
309
new PopupSliderChoice(
310
&mipsTracer.in_storage_capacity, 0x4'0000, 0x40'0000, 0x10'0000, dev->T("Storage capacity"), 0x10000, screenManager()
311
)
312
);
313
storage_capacity->SetFormat("0x%x asm opcodes");
314
storage_capacity->OnChange.Add([&](UI::EventParams &) {
315
INFO_LOG(Log::JIT, "User changed the tracer's storage capacity to 0x%x", mipsTracer.in_storage_capacity);
316
});
317
318
PopupSliderChoice* trace_max_size = list->Add(
319
new PopupSliderChoice(
320
&mipsTracer.in_max_trace_size, 0x1'0000, 0x40'0000, 0x10'0000, dev->T("Max allowed trace size"), 0x10000, screenManager()
321
)
322
);
323
trace_max_size->SetFormat("%d basic blocks");
324
trace_max_size->OnChange.Add([&](UI::EventParams &) {
325
INFO_LOG(Log::JIT, "User changed the tracer's max trace size to %d", mipsTracer.in_max_trace_size);
326
});
327
328
list->Add(new ItemHeader(dev->T("MIPSTracer actions")));
329
Choice *FlushTrace = list->Add(new Choice(dev->T("Flush the trace")));
330
FlushTrace->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerFlushTrace);
331
332
Choice *InvalidateJitCache = list->Add(new Choice(dev->T("Clear the JIT cache")));
333
InvalidateJitCache->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerClearJitCache);
334
335
Choice *ClearMIPSTracer = list->Add(new Choice(dev->T("Clear the MIPSTracer")));
336
ClearMIPSTracer->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerClearTracer);
337
}
338
339
void DeveloperToolsScreen::CreateAudioTab(UI::LinearLayout *list) {
340
using namespace UI;
341
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
342
list->Add(new CheckBox(&g_Config.bForceFfmpegForAudioDec, dev->T("Use FFMPEG for all compressed audio")));
343
}
344
345
void DeveloperToolsScreen::CreateUITab(UI::LinearLayout *list) {
346
using namespace UI;
347
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
348
UIContext *uiContext = screenManager()->getUIContext();
349
list->Add(new Choice(dev->T("Reload UI atlas")))->OnClick.Add([uiContext](UI::EventParams &) {
350
uiContext->InvalidateAtlas();
351
});
352
353
auto di = GetI18NCategory(I18NCat::DIALOG);
354
auto si = GetI18NCategory(I18NCat::SYSINFO);
355
auto sy = GetI18NCategory(I18NCat::SYSTEM);
356
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
357
358
// TODO: Move most of these strings out of the SysInfo category.
359
360
list->Add(new ItemHeader(si->T("Icon cache")));
361
IconCacheStats iconStats = g_iconCache.GetStats();
362
list->Add(new InfoItem(si->T("Image data count"), StringFromFormat("%d", iconStats.cachedCount)));
363
list->Add(new InfoItem(si->T("Texture count"), StringFromFormat("%d", iconStats.textureCount)));
364
list->Add(new InfoItem(si->T("Data size"), NiceSizeFormat(iconStats.dataSize)));
365
list->Add(new Choice(di->T("Clear")))->OnClick.Add([&](UI::EventParams &) {
366
g_iconCache.ClearData();
367
RecreateViews();
368
});
369
370
list->Add(new ItemHeader(si->T("Font cache")));
371
const TextDrawer *text = screenManager()->getUIContext()->Text();
372
if (text) {
373
list->Add(new InfoItem(si->T("Texture count"), StringFromFormat("%d", text->GetStringCacheSize())));
374
list->Add(new InfoItem(si->T("Data size"), NiceSizeFormat(text->GetCacheDataSize())));
375
}
376
377
list->Add(new ItemHeader(si->T("Slider test")));
378
list->Add(new Slider(&testSliderValue_, 0, 100, 1, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
379
380
static const char *positions[] = {"Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right", "None"};
381
382
list->Add(new ItemHeader(si->T("Notification tests")));
383
list->Add(new Choice(si->T("Error")))->OnClick.Add([&](UI::EventParams &) {
384
std::string str = "Error " + CodepointToUTF8(0x1F41B) + CodepointToUTF8(0x1F41C) + CodepointToUTF8(0x1F914);
385
g_OSD.Show(OSDType::MESSAGE_ERROR, str);
386
});
387
list->Add(new Choice(si->T("Warning")))->OnClick.Add([&](UI::EventParams &) {
388
g_OSD.Show(OSDType::MESSAGE_WARNING, "Warning, a pretty long warning heading", "Some\nAdditional\nDetail, some of which is very, very long and wide and will need line wrapping on most screens.");
389
});
390
list->Add(new Choice(si->T("Info")))->OnClick.Add([&](UI::EventParams &) {
391
g_OSD.Show(OSDType::MESSAGE_INFO, "Info, info info info info info info info info info info");
392
});
393
// This one is clickable
394
list->Add(new Choice(si->T("Success")))->OnClick.Add([&](UI::EventParams &) {
395
g_OSD.Show(OSDType::MESSAGE_SUCCESS, "Success", 0.0f, "clickable");
396
g_OSD.SetClickCallback("clickable", [](bool clicked, void *) {
397
if (clicked) {
398
System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://www.google.com/");
399
}
400
}, nullptr);
401
});
402
list->Add(new Choice(sy->T("RetroAchievements")))->OnClick.Add([&](UI::EventParams &) {
403
g_OSD.Show(OSDType::MESSAGE_WARNING, "RetroAchievements warning", "", "I_RETROACHIEVEMENTS_LOGO");
404
});
405
list->Add(new ItemHeader(si->T("Progress tests")));
406
list->Add(new Choice(si->T("30%")))->OnClick.Add([&](UI::EventParams &) {
407
g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 30, 0.0f);
408
});
409
list->Add(new Choice(si->T("100%")))->OnClick.Add([&](UI::EventParams &) {
410
g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 100, 1.0f);
411
});
412
list->Add(new Choice(si->T("N/A%")))->OnClick.Add([&](UI::EventParams &) {
413
g_OSD.SetProgressBar("testprogress", "Test Progress", 0, 0, 0, 0.0f);
414
});
415
list->Add(new Choice(si->T("Success")))->OnClick.Add([&](UI::EventParams &) {
416
g_OSD.RemoveProgressBar("testprogress", true, 0.5f);
417
});
418
list->Add(new Choice(si->T("Failure")))->OnClick.Add([&](UI::EventParams &) {
419
g_OSD.RemoveProgressBar("testprogress", false, 0.5f);
420
});
421
list->Add(new ItemHeader(si->T("Achievement tests")));
422
list->Add(new Choice(si->T("Leaderboard tracker: Show")))->OnClick.Add([=](UI::EventParams &) {
423
g_OSD.ShowLeaderboardTracker(1, "My leaderboard tracker", true);
424
});
425
list->Add(new Choice(si->T("Leaderboard tracker: Update")))->OnClick.Add([=](UI::EventParams &) {
426
g_OSD.ShowLeaderboardTracker(1, "Updated tracker", true);
427
});
428
list->Add(new Choice(si->T("Leaderboard tracker: Hide")))->OnClick.Add([=](UI::EventParams &) {
429
g_OSD.ShowLeaderboardTracker(1, "", false);
430
});
431
432
list->Add(new ItemHeader(ac->T("Notifications")));
433
list->Add(new PopupMultiChoice(&g_Config.iNotificationPos, "General notifications", positions, 0, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager()));
434
list->Add(new PopupMultiChoice(&g_Config.iAchievementsLeaderboardTrackerPos, ac->T("Leaderboard tracker"), positions, 0, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager()));
435
list->Add(new CheckBox(&pretendIngame_, ac->T("Pretend to be in-game (for testing)")));
436
437
#ifdef _DEBUG
438
// Untranslated string because this is debug mode only, only for PPSSPP developers.
439
list->Add(new ItemHeader(ac->T("Assert")));
440
list->Add(new Choice("Assert"))->OnClick.Add([=](UI::EventParams &) {
441
_dbg_assert_msg_(false, "Test assert message");
442
});
443
#endif
444
#if PPSSPP_PLATFORM(ANDROID)
445
list->Add(new Choice(si->T("Exception")))->OnClick.Add([&](UI::EventParams &) {
446
System_Notify(SystemNotification::TEST_JAVA_EXCEPTION);
447
});
448
#endif
449
}
450
451
void DeveloperToolsScreen::CreateNetworkTab(UI::LinearLayout *list) {
452
using namespace UI;
453
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
454
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
455
auto ri = GetI18NCategory(I18NCat::REMOTEISO);
456
list->Add(new ItemHeader(ms->T("Networking")));
457
list->Add(new CheckBox(&g_Config.bDontDownloadInfraJson, dev->T("Don't download infra-dns.json")));
458
459
// This is shared between RemoteISO and the remote debugger.
460
PopupSliderChoice *portChoice = new PopupSliderChoice(&g_Config.iRemoteISOPort, 0, 65535, 0, ri->T("Local Server Port", "Local Server Port"), 100, screenManager());
461
list->Add(portChoice);
462
}
463
464
void DeveloperToolsScreen::CreateGraphicsTab(UI::LinearLayout *list) {
465
using namespace UI;
466
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
467
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
468
auto ps = GetI18NCategory(I18NCat::POSTSHADERS);
469
auto sy = GetI18NCategory(I18NCat::SYSTEM);
470
auto si = GetI18NCategory(I18NCat::SYSINFO);
471
472
Draw::DrawContext *draw = screenManager()->getDrawContext();
473
474
list->Add(new ItemHeader(sy->T("General")));
475
list->Add(new CheckBox(&g_Config.bVendorBugChecksEnabled, dev->T("Enable driver bug workarounds")));
476
list->Add(new CheckBox(&g_Config.bShaderCache, dev->T("Enable shader cache")));
477
478
auto displayRefreshRate = list->Add(new PopupSliderChoice(&g_Config.iDisplayRefreshRate, 60, 1000, 60, dev->T("Display refresh rate"), 1, screenManager()));
479
displayRefreshRate->SetFormat(si->T("%d Hz"));
480
481
list->Add(new ItemHeader(dev->T("Vulkan")));
482
list->Add(new CheckBox(&g_Config.bVulkanDisableImplicitLayers, dev->T("Prevent loading overlays")));
483
484
if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) {
485
list->Add(new CheckBox(&g_Config.bRenderMultiThreading, dev->T("Multi-threaded rendering"), ""))->OnClick.Add([](UI::EventParams &e) {
486
// TODO: Not translating yet. Will combine with other translations of settings that need restart.
487
g_OSD.Show(OSDType::MESSAGE_WARNING, "Restart required");
488
});
489
}
490
491
if (GetGPUBackend() == GPUBackend::VULKAN && SupportsCustomDriver()) {
492
auto driverChoice = list->Add(new Choice(gr->T("AdrenoTools driver manager")));
493
driverChoice->OnClick.Add([=](UI::EventParams &e) {
494
screenManager()->push(new DriverManagerScreen(gamePath_));
495
});
496
}
497
498
static const char *depthRasterModes[] = { "Auto (default)", "Low", "Off", "Always on" };
499
500
PopupMultiChoice *depthRasterMode = list->Add(new PopupMultiChoice(&g_Config.iDepthRasterMode, gr->T("Lens flare occlusion"), depthRasterModes, 0, ARRAY_SIZE(depthRasterModes), I18NCat::GRAPHICS, screenManager()));
501
depthRasterMode->SetDisabledPtr(&g_Config.bSoftwareRendering);
502
depthRasterMode->SetChoiceIcon(3, ImageID("I_WARNING")); // It's a performance trap.
503
504
list->Add(new ItemHeader(dev->T("Ubershaders")));
505
if (draw->GetShaderLanguageDesc().bitwiseOps && !draw->GetBugs().Has(Draw::Bugs::UNIFORM_INDEXING_BROKEN)) {
506
// If the above if fails, the checkbox is redundant since it'll be force disabled anyway.
507
list->Add(new CheckBox(&g_Config.bUberShaderVertex, dev->T("Vertex")));
508
}
509
#if !PPSSPP_PLATFORM(UWP)
510
if (g_Config.iGPUBackend != (int)GPUBackend::OPENGL || gl_extensions.GLES3) {
511
#else
512
{
513
#endif
514
list->Add(new CheckBox(&g_Config.bUberShaderFragment, dev->T("Fragment")));
515
}
516
517
// Experimental, allow some VR features without OpenXR
518
if (GetGPUBackend() == GPUBackend::OPENGL) {
519
auto vr = GetI18NCategory(I18NCat::VR);
520
list->Add(new ItemHeader(vr->T("Virtual reality")));
521
list->Add(new CheckBox(&g_Config.bForceVR, vr->T("VR camera")));
522
}
523
524
// Experimental, will move to main graphics settings later.
525
bool multiViewSupported = draw->GetDeviceCaps().multiViewSupported;
526
527
auto enableStereo = [=]() -> bool {
528
return g_Config.bStereoRendering && multiViewSupported;
529
};
530
531
if (multiViewSupported) {
532
list->Add(new ItemHeader(gr->T("Stereo rendering")));
533
list->Add(new CheckBox(&g_Config.bStereoRendering, gr->T("Stereo rendering")));
534
std::vector<std::string> stereoShaderNames;
535
536
ChoiceWithValueDisplay *stereoShaderChoice = list->Add(new ChoiceWithValueDisplay(&g_Config.sStereoToMonoShader, gr->T("Stereo display shader"), &PostShaderTranslateName));
537
stereoShaderChoice->SetEnabledFunc(enableStereo);
538
stereoShaderChoice->OnClick.Add([=](EventParams &e) {
539
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
540
auto procScreen = new PostProcScreen(gr->T("Stereo display shader"), 0, true);
541
if (e.v)
542
procScreen->SetPopupOrigin(e.v);
543
screenManager()->push(procScreen);
544
});
545
const ShaderInfo *shaderInfo = GetPostShaderInfo(g_Config.sStereoToMonoShader);
546
if (shaderInfo) {
547
for (size_t i = 0; i < ARRAY_SIZE(shaderInfo->settings); ++i) {
548
auto &setting = shaderInfo->settings[i];
549
if (!setting.name.empty()) {
550
std::string key = StringFromFormat("%sSettingCurrentValue%d", shaderInfo->section.c_str(), i + 1);
551
bool keyExisted = g_Config.mPostShaderSetting.find(key) != g_Config.mPostShaderSetting.end();
552
auto &value = g_Config.mPostShaderSetting[key];
553
if (!keyExisted)
554
value = setting.value;
555
556
PopupSliderChoiceFloat *settingValue = list->Add(new PopupSliderChoiceFloat(&value, setting.minValue, setting.maxValue, setting.value, ps->T(setting.name), setting.step, screenManager()));
557
settingValue->SetEnabledFunc([=] {
558
return !g_Config.bSkipBufferEffects && enableStereo();
559
});
560
}
561
}
562
}
563
}
564
}
565
566
void DeveloperToolsScreen::CreateCrashHistoryTab(UI::LinearLayout *list) {
567
using namespace UI;
568
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
569
auto di = GetI18NCategory(I18NCat::DIALOG);
570
std::vector<std::string> reports = Android_GetNativeCrashHistory(20);
571
list->Add(new ItemHeader(dev->T("Crash history")));
572
if (reports.empty()) {
573
list->Add(new TextView(di->T("None")));
574
return;
575
}
576
for (size_t i = 0; i < reports.size(); i++) {
577
std::string name = StringFromFormat("Crash %d", (int)i);
578
CollapsibleSection *section = list->Add(new CollapsibleSection(name));
579
const std::string report = reports[i];
580
if (report.size() > 150) {
581
section->Add(new Choice(di->T("Copy to clipboard"), ImageID("I_FILE_COPY")))->OnClick.Add([report](UI::EventParams&) {
582
System_CopyStringToClipboard(report);
583
});
584
}
585
section->Add(new TextView(report, FLAG_WRAP_TEXT | FLAG_DYNAMIC_ASCII, true));
586
}
587
}
588
589
void DeveloperToolsScreen::CreateTabs() {
590
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
591
auto sy = GetI18NCategory(I18NCat::SYSTEM);
592
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
593
594
AddTab("General", sy->T("General"), [this](UI::LinearLayout *parent) {
595
CreateGeneralTab(parent);
596
});
597
AddTab("TextureReplacement", dev->T("Texture Replacement"), [this](UI::LinearLayout *parent) {
598
CreateTextureReplacementTab(parent);
599
});
600
AddTab("Graphics", ms->T("Graphics"), [this](UI::LinearLayout *parent) {
601
CreateGraphicsTab(parent);
602
});
603
AddTab("Networking", ms->T("Networking"), [this](UI::LinearLayout *parent) {
604
CreateNetworkTab(parent);
605
});
606
AddTab("Audio", ms->T("Audio"), [this](UI::LinearLayout *parent) {
607
CreateAudioTab(parent);
608
});
609
AddTab("Tests", dev->T("Tests"), [this](UI::LinearLayout *parent) {
610
CreateTestsTab(parent);
611
});
612
AddTab("UI", dev->T("UI"), [this](UI::LinearLayout *parent) {
613
CreateUITab(parent);
614
});
615
AddTab("DumpFiles", dev->T("Dump files"), [this](UI::LinearLayout *parent) {
616
CreateDumpFileTab(parent);
617
});
618
// Need a better title string.
619
AddTab("HLE", dev->T("Disable HLE"), [this](UI::LinearLayout *parent) {
620
CreateHLETab(parent);
621
});
622
#if !PPSSPP_PLATFORM(ANDROID) && !PPSSPP_PLATFORM(IOS) && !PPSSPP_PLATFORM(SWITCH)
623
AddTab("MIPSTracer", dev->T("MIPSTracer"), [this](UI::LinearLayout *parent) {
624
CreateMIPSTracerTab(parent);
625
});
626
#endif
627
//#if PPSSPP_PLATFORM(ANDROID)
628
if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 30) {
629
AddTab("Crash history", dev->T("Crash history"), [this](UI::LinearLayout *parent) {
630
CreateCrashHistoryTab(parent);
631
});
632
}
633
//#endif
634
635
// Reconsider whenever recreating views.
636
hasTexturesIni_ = HasIni::MAYBE;
637
}
638
639
void DeveloperToolsScreen::onFinish(DialogResult result) {
640
UIScreen::onFinish(result);
641
g_Config.Save("DeveloperToolsScreen::onFinish");
642
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
643
}
644
645
void DeveloperToolsScreen::OnLoggingChanged(UI::EventParams &e) {
646
System_Notify(SystemNotification::TOGGLE_DEBUG_CONSOLE);
647
}
648
649
void DeveloperToolsScreen::OnOpenTexturesIniFile(UI::EventParams &e) {
650
std::string gameID = g_paramSFO.GetDiscID();
651
Path generatedFilename;
652
653
if (TextureReplacer::GenerateIni(gameID, generatedFilename)) {
654
if (System_GetPropertyBool(SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR)) {
655
File::OpenFileInEditor(generatedFilename);
656
} else {
657
// Can't do much here, let's send a "toast" so the user sees that something happened.
658
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
659
System_Toast((generatedFilename.ToVisualString() + ": " + dev->T_cstr("Texture ini file created")).c_str());
660
}
661
662
hasTexturesIni_ = HasIni::YES;
663
}
664
}
665
666
void DeveloperToolsScreen::OnJitDebugTools(UI::EventParams &e) {
667
screenManager()->push(new JitDebugScreen());
668
}
669
670
void DeveloperToolsScreen::OnGPUDriverTest(UI::EventParams &e) {
671
screenManager()->push(new GPUDriverTestScreen());
672
}
673
674
void DeveloperToolsScreen::OnTouchscreenTest(UI::EventParams &e) {
675
screenManager()->push(new TouchTestScreen(gamePath_));
676
}
677
678
void DeveloperToolsScreen::OnJitAffectingSetting(UI::EventParams &e) {
679
System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT);
680
}
681
682
void DeveloperToolsScreen::OnCopyStatesToRoot(UI::EventParams &e) {
683
Path savestate_dir = GetSysDirectory(DIRECTORY_SAVESTATE);
684
Path root_dir = GetSysDirectory(DIRECTORY_MEMSTICK_ROOT);
685
686
std::vector<File::FileInfo> files;
687
GetFilesInDir(savestate_dir, &files, nullptr, 0);
688
689
for (const File::FileInfo &file : files) {
690
Path src = file.fullName;
691
Path dst = root_dir / file.name;
692
INFO_LOG(Log::System, "Copying file '%s' to '%s'", src.c_str(), dst.c_str());
693
File::Copy(src, dst);
694
}
695
}
696
697
void DeveloperToolsScreen::OnRemoteDebugger(UI::EventParams &e) {
698
if (allowDebugger_) {
699
StartWebServer(WebServerFlags::DEBUGGER);
700
} else {
701
StopWebServer(WebServerFlags::DEBUGGER);
702
}
703
// Persist the setting. Maybe should separate?
704
g_Config.bRemoteDebuggerOnStartup = allowDebugger_;
705
}
706
707
void DeveloperToolsScreen::OnMIPSTracerEnabled(UI::EventParams &e) {
708
if (MIPSTracerEnabled_) {
709
u32 capacity = mipsTracer.in_storage_capacity;
710
u32 trace_size = mipsTracer.in_max_trace_size;
711
712
mipsTracer.initialize(capacity, trace_size);
713
mipsTracer.start_tracing();
714
} else {
715
mipsTracer.stop_tracing();
716
}
717
}
718
719
void DeveloperToolsScreen::OnMIPSTracerPathChanged(UI::EventParams &e) {
720
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
721
System_BrowseForFileSave(
722
GetRequesterToken(),
723
dev->T("Select the log file"),
724
"trace.txt",
725
BrowseFileType::ANY,
726
[this](const std::string &value, int) {
727
mipsTracer.set_logging_path(value);
728
MIPSTracerPath_ = value;
729
MIPSTracerPath->SetRightText(MIPSTracerPath_);
730
}
731
);
732
}
733
734
void DeveloperToolsScreen::OnMIPSTracerFlushTrace(UI::EventParams &e) {
735
mipsTracer.flush_to_file();
736
// The error logs are emitted inside the tracer
737
}
738
739
void DeveloperToolsScreen::OnMIPSTracerClearJitCache(UI::EventParams &e) {
740
INFO_LOG(Log::JIT, "Clearing the jit cache...");
741
System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT);
742
}
743
744
void DeveloperToolsScreen::OnMIPSTracerClearTracer(UI::EventParams &e) {
745
INFO_LOG(Log::JIT, "Clearing the MIPSTracer...");
746
mipsTracer.clear();
747
}
748
749
void DeveloperToolsScreen::update() {
750
UIBaseDialogScreen::update();
751
allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);
752
canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);
753
754
// For the UI tab's notification tests.
755
if (pretendIngame_) {
756
g_OSD.NudgeIngameNotifications();
757
}
758
}
759
760
void DeveloperToolsScreen::MemoryMapTest() {
761
int sum = 0;
762
for (uint64_t addr = 0; addr < 0x100000000ULL; addr += 0x1000) {
763
const u32 addr32 = (u32)addr;
764
if (Memory::IsValidAddress(addr32)) {
765
sum += Memory::ReadUnchecked_U32(addr32);
766
}
767
}
768
// Just to force the compiler to do things properly.
769
INFO_LOG(Log::JIT, "Total sum: %08x", sum);
770
}
771
772
static bool RunMemstickTest(std::string *error) {
773
Path testRoot = GetSysDirectory(PSPDirectories::DIRECTORY_CACHE) / "test";
774
775
*error = "N/A";
776
777
File::CreateDir(testRoot);
778
if (!File::Exists(testRoot)) {
779
return false;
780
}
781
782
Path testFilePath = testRoot / "temp.txt";
783
File::CreateEmptyFile(testFilePath);
784
785
// Attempt to delete the test root. This should fail since it still contains files.
786
File::DeleteDir(testRoot);
787
if (!File::Exists(testRoot)) {
788
*error = "testroot was deleted with a file in it!";
789
return false;
790
}
791
792
File::Delete(testFilePath);
793
if (File::Exists(testFilePath)) {
794
*error = "testfile wasn't deleted";
795
return false;
796
}
797
798
File::DeleteDir(testRoot);
799
if (File::Exists(testRoot)) {
800
*error = "testroot wasn't deleted, even when empty";
801
return false;
802
}
803
804
*error = "passed";
805
return true;
806
}
807
808
void DeveloperToolsScreen::OnMemstickTest(UI::EventParams &e) {
809
std::string error;
810
if (RunMemstickTest(&error)) {
811
g_OSD.Show(OSDType::MESSAGE_SUCCESS, "Memstick test passed", error, 6.0f);
812
} else {
813
g_OSD.Show(OSDType::MESSAGE_ERROR, "Memstick test failed", error, 6.0f);
814
}
815
}
816
817