Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/UI/DeveloperToolsScreen.cpp
3185 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 "Common/UI/View.h"
21
#include "Common/UI/ViewGroup.h"
22
#include "Common/System/OSD.h"
23
#include "Common/GPU/OpenGL/GLFeatures.h"
24
#include "Common/File/FileUtil.h"
25
#include "Common/StringUtils.h"
26
#include "GPU/Common/TextureReplacer.h"
27
#include "GPU/Common/PostShader.h"
28
#include "Core/MIPS/MIPSTracer.h"
29
#include "Core/ELF/ParamSFO.h"
30
#include "Core/Config.h"
31
#include "Core/HLE/HLE.h"
32
#include "Core/Core.h"
33
#include "Core/System.h"
34
#include "Core/WebServer.h"
35
#include "UI/GPUDriverTestScreen.h"
36
#include "UI/DeveloperToolsScreen.h"
37
#include "UI/DevScreens.h"
38
#include "UI/DriverManagerScreen.h"
39
#include "UI/DisplayLayoutScreen.h"
40
#include "UI/GameSettingsScreen.h"
41
#include "UI/OnScreenDisplay.h"
42
43
#if PPSSPP_PLATFORM(ANDROID)
44
45
static bool CheckKgslPresent() {
46
constexpr auto KgslPath{ "/dev/kgsl-3d0" };
47
48
return access(KgslPath, F_OK) == 0;
49
}
50
51
static bool SupportsCustomDriver() {
52
return android_get_device_api_level() >= 28 && CheckKgslPresent();
53
}
54
55
#else
56
57
static bool SupportsCustomDriver() {
58
#ifdef _DEBUG
59
return false; // change to true to debug driver installation on other platforms
60
#else
61
return false;
62
#endif
63
}
64
65
#endif
66
67
static std::string PostShaderTranslateName(std::string_view value) {
68
const ShaderInfo *info = GetPostShaderInfo(value);
69
if (info) {
70
auto ps = GetI18NCategory(I18NCat::POSTSHADERS);
71
return std::string(ps->T(value, info->name));
72
} else {
73
return std::string(value);
74
}
75
}
76
77
void DeveloperToolsScreen::CreateTextureReplacementTab(UI::LinearLayout *list) {
78
using namespace UI;
79
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
80
auto di = GetI18NCategory(I18NCat::DIALOG);
81
82
list->Add(new ItemHeader(dev->T("Texture Replacement")));
83
list->Add(new CheckBox(&g_Config.bSaveNewTextures, dev->T("Save new textures")));
84
list->Add(new CheckBox(&g_Config.bReplaceTextures, dev->T("Replace textures")));
85
86
Choice *createTextureIni = list->Add(new Choice(dev->T("Create/Open textures.ini file for current game")));
87
createTextureIni->OnClick.Handle(this, &DeveloperToolsScreen::OnOpenTexturesIniFile);
88
createTextureIni->SetEnabledFunc([&] {
89
if (!PSP_IsInited())
90
return false;
91
92
// Disable the choice to Open/Create if the textures.ini file already exists, and we can't open it due to platform support limitations.
93
if (!System_GetPropertyBool(SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR)) {
94
if (hasTexturesIni_ == HasIni::MAYBE)
95
hasTexturesIni_ = TextureReplacer::IniExists(g_paramSFO.GetDiscID()) ? HasIni::YES : HasIni::NO;
96
return hasTexturesIni_ != HasIni::YES;
97
}
98
return true;
99
});
100
101
if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) {
102
// Best string we have
103
list->Add(new Choice(di->T("Show in folder")))->OnClick.Add([=](UI::EventParams &) {
104
Path path;
105
if (PSP_IsInited()) {
106
std::string gameID = g_paramSFO.GetDiscID();
107
path = GetSysDirectory(DIRECTORY_TEXTURES) / gameID;
108
} else {
109
// Just show the root textures directory.
110
path = GetSysDirectory(DIRECTORY_TEXTURES);
111
}
112
System_ShowFileInFolder(path);
113
return UI::EVENT_DONE;
114
});
115
}
116
117
static const char *texLoadSpeeds[] = { "Slow (smooth)", "Medium", "Fast", "Instant (may stutter)" };
118
PopupMultiChoice *texLoadSpeed = list->Add(new PopupMultiChoice(&g_Config.iReplacementTextureLoadSpeed, dev->T("Replacement texture load speed"), texLoadSpeeds, 0, ARRAY_SIZE(texLoadSpeeds), I18NCat::DEVELOPER, screenManager()));
119
texLoadSpeed->SetChoiceIcon(3, ImageID("I_WARNING"));
120
}
121
122
void DeveloperToolsScreen::CreateGeneralTab(UI::LinearLayout *list) {
123
using namespace UI;
124
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
125
auto sy = GetI18NCategory(I18NCat::SYSTEM);
126
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
127
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
128
129
list->Add(new ItemHeader(sy->T("CPU Core")));
130
131
bool canUseJit = System_GetPropertyBool(SYSPROP_CAN_JIT);
132
// iOS can now use JIT on all modes, apparently.
133
// The bool may come in handy for future non-jit platforms though (UWP XB1?)
134
// In iOS App Store builds, we disable the JIT.
135
136
static const char *cpuCores[] = { "Interpreter", "Dynarec/JIT (recommended)", "IR Interpreter", "JIT using IR" };
137
PopupMultiChoice *core = list->Add(new PopupMultiChoice(&g_Config.iCpuCore, sy->T("CPU Core"), cpuCores, 0, ARRAY_SIZE(cpuCores), I18NCat::SYSTEM, screenManager()));
138
core->OnChoice.Handle(this, &DeveloperToolsScreen::OnJitAffectingSetting);
139
core->OnChoice.Add([](UI::EventParams &) {
140
g_Config.NotifyUpdatedCpuCore();
141
return UI::EVENT_DONE;
142
});
143
if (!canUseJit) {
144
core->HideChoice(1);
145
core->HideChoice(3);
146
}
147
// TODO: Enable "JIT using IR" on more architectures.
148
#if !PPSSPP_ARCH(X86) && !PPSSPP_ARCH(AMD64) && !PPSSPP_ARCH(ARM64)
149
core->HideChoice(3);
150
#endif
151
152
list->Add(new Choice(dev->T("JIT debug tools")))->OnClick.Handle(this, &DeveloperToolsScreen::OnJitDebugTools);
153
list->Add(new CheckBox(&g_Config.bShowDeveloperMenu, dev->T("Show Developer Menu")));
154
155
AddOverlayList(list, screenManager());
156
157
list->Add(new ItemHeader(sy->T("General")));
158
159
list->Add(new CheckBox(&g_Config.bEnableLogging, dev->T("Enable Logging")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLoggingChanged);
160
list->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DeveloperToolsScreen::OnLogConfig);
161
list->Add(new CheckBox(&g_Config.bEnableFileLogging, dev->T("Log to file")))->SetEnabledPtr(&g_Config.bEnableLogging);
162
list->Add(new CheckBox(&g_Config.bLogFrameDrops, dev->T("Log Dropped Frame Statistics")));
163
if (GetGPUBackend() == GPUBackend::VULKAN) {
164
list->Add(new CheckBox(&g_Config.bGpuLogProfiler, dev->T("GPU log profiler")));
165
}
166
167
allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);
168
canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);
169
CheckBox *allowDebugger = new CheckBox(&allowDebugger_, dev->T("Allow remote debugger"));
170
list->Add(allowDebugger)->OnClick.Handle(this, &DeveloperToolsScreen::OnRemoteDebugger);
171
allowDebugger->SetEnabledPtr(&canAllowDebugger_);
172
173
list->Add(new Choice(dev->T("GPI/GPO switches/LEDs")))->OnClick.Add([=](UI::EventParams &e) {
174
screenManager()->push(new GPIGPOScreen(dev->T("GPI/GPO switches/LEDs")));
175
return UI::EVENT_DONE;
176
});
177
178
#if PPSSPP_PLATFORM(ANDROID)
179
static const char *framerateModes[] = { "Default", "Request 60Hz", "Force 60Hz" };
180
PopupMultiChoice *framerateMode = list->Add(new PopupMultiChoice(&g_Config.iDisplayFramerateMode, gr->T("Framerate mode"), framerateModes, 0, ARRAY_SIZE(framerateModes), I18NCat::GRAPHICS, screenManager()));
181
framerateMode->SetEnabledFunc([]() { return System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 30; });
182
framerateMode->OnChoice.Add([](UI::EventParams &e) {
183
System_Notify(SystemNotification::FORCE_RECREATE_ACTIVITY);
184
return UI::EVENT_DONE;
185
});
186
#endif
187
188
#if PPSSPP_PLATFORM(IOS)
189
list->Add(new NoticeView(NoticeLevel::WARN, ms->T("Moving the memstick directory is NOT recommended on iOS"), ""));
190
list->Add(new Choice(sy->T("Set Memory Stick folder")))->OnClick.Add(
191
[=](UI::EventParams &) {
192
SetMemStickDirDarwin(GetRequesterToken());
193
return UI::EVENT_DONE;
194
});
195
#endif
196
197
// Makes it easy to get savestates out of an iOS device. The file listing shown in MacOS doesn't allow
198
// you to descend into directories.
199
#if PPSSPP_PLATFORM(IOS)
200
list->Add(new Choice(dev->T("Copy savestates to memstick root")))->OnClick.Handle(this, &DeveloperToolsScreen::OnCopyStatesToRoot);
201
#endif
202
}
203
204
void DeveloperToolsScreen::CreateTestsTab(UI::LinearLayout *list) {
205
using namespace UI;
206
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
207
208
list->Add(new Choice(dev->T("Touchscreen Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnTouchscreenTest);
209
// list->Add(new Choice(dev->T("Memstick Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnMemstickTest);
210
Choice *frameDumpTests = list->Add(new Choice(dev->T("Framedump tests")));
211
frameDumpTests->OnClick.Add([this](UI::EventParams &e) {
212
screenManager()->push(new FrameDumpTestScreen());
213
return UI::EVENT_DONE;
214
});
215
frameDumpTests->SetEnabled(!PSP_IsInited());
216
// For now, we only implement GPU driver tests for Vulkan and OpenGL. This is simply
217
// because the D3D drivers are generally solid enough to not need this type of investigation.
218
if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL) {
219
list->Add(new Choice(dev->T("GPU Driver Test")))->OnClick.Handle(this, &DeveloperToolsScreen::OnGPUDriverTest);
220
}
221
222
// Not useful enough to be made visible.
223
/*
224
auto memmapTest = list->Add(new Choice(dev->T("Memory map test")));
225
memmapTest->OnClick.Add([this](UI::EventParams &e) {
226
MemoryMapTest();
227
return UI::EVENT_DONE;
228
});
229
memmapTest->SetEnabled(PSP_IsInited());
230
*/
231
}
232
233
void DeveloperToolsScreen::CreateDumpFileTab(UI::LinearLayout *list) {
234
using namespace UI;
235
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
236
237
list->Add(new ItemHeader(dev->T("Dump files")));
238
list->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::EBOOT, dev->T("Dump Decrypted Eboot", "Dump Decrypted EBOOT.BIN (If Encrypted) When Booting Game")));
239
list->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::PRX, dev->T("PRX")));
240
list->Add(new BitCheckBox(&g_Config.iDumpFileTypes, (int)DumpFileType::Atrac3, dev->T("Atrac3/3+")));
241
}
242
243
void DeveloperToolsScreen::CreateHLETab(UI::LinearLayout *list) {
244
using namespace UI;
245
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
246
247
list->Add(new CheckBox(&g_Config.bUseOldAtrac, dev->T("Use the old sceAtrac implementation")));
248
249
list->Add(new ItemHeader(dev->T("Disable HLE")));
250
251
for (int i = 0; i < (int)DisableHLEFlags::Count; i++) {
252
DisableHLEFlags flag = (DisableHLEFlags)(1 << i);
253
254
// Show a checkbox, unless the setting has graduated to always disabled.
255
if (!(flag & AlwaysDisableHLEFlags())) {
256
const HLEModuleMeta *meta = GetHLEModuleMetaByFlag(flag);
257
if (meta) {
258
BitCheckBox *checkBox = list->Add(new BitCheckBox(&g_Config.iDisableHLE, (int)flag, meta->modname));
259
checkBox->SetEnabled(!PSP_IsInited());
260
}
261
}
262
}
263
264
list->Add(new ItemHeader(dev->T("Force-enable HLE")));
265
266
for (int i = 0; i < (int)DisableHLEFlags::Count; i++) {
267
DisableHLEFlags flag = (DisableHLEFlags)(1 << i);
268
269
// Show a checkbox, only if the setting has graduated to always disabled (and thus it makes sense to force-enable it).
270
if (flag & AlwaysDisableHLEFlags()) {
271
const HLEModuleMeta *meta = GetHLEModuleMetaByFlag(flag);
272
if (meta) {
273
BitCheckBox *checkBox = list->Add(new BitCheckBox(&g_Config.iForceEnableHLE, (int)flag, meta->modname));
274
checkBox->SetEnabled(!PSP_IsInited());
275
}
276
}
277
}
278
}
279
280
void DeveloperToolsScreen::CreateMIPSTracerTab(UI::LinearLayout *list) {
281
using namespace UI;
282
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
283
list->Add(new ItemHeader(dev->T("MIPSTracer")));
284
285
MIPSTracerEnabled_ = mipsTracer.tracing_enabled;
286
CheckBox *MIPSTracerEnabled = new CheckBox(&MIPSTracerEnabled_, dev->T("MIPSTracer enabled"));
287
list->Add(MIPSTracerEnabled)->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerEnabled);
288
MIPSTracerEnabled->SetEnabledFunc([]() {
289
bool temp = g_Config.iCpuCore == static_cast<int>(CPUCore::IR_INTERPRETER) && PSP_IsInited();
290
return temp && Core_IsStepping() && coreState != CORE_POWERDOWN;
291
});
292
293
Choice *TraceDumpPath = list->Add(new Choice(dev->T("Select the file path for the trace")));
294
TraceDumpPath->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerPathChanged);
295
TraceDumpPath->SetEnabledFunc([]() {
296
if (!PSP_IsInited())
297
return false;
298
return true;
299
});
300
301
MIPSTracerPath_ = mipsTracer.get_logging_path();
302
MIPSTracerPath = list->Add(new InfoItem(dev->T("Current log file"), MIPSTracerPath_));
303
304
PopupSliderChoice* storage_capacity = list->Add(
305
new PopupSliderChoice(
306
&mipsTracer.in_storage_capacity, 0x4'0000, 0x40'0000, 0x10'0000, dev->T("Storage capacity"), 0x10000, screenManager()
307
)
308
);
309
storage_capacity->SetFormat("0x%x asm opcodes");
310
storage_capacity->OnChange.Add([&](UI::EventParams &) {
311
INFO_LOG(Log::JIT, "User changed the tracer's storage capacity to 0x%x", mipsTracer.in_storage_capacity);
312
return UI::EVENT_CONTINUE;
313
});
314
315
PopupSliderChoice* trace_max_size = list->Add(
316
new PopupSliderChoice(
317
&mipsTracer.in_max_trace_size, 0x1'0000, 0x40'0000, 0x10'0000, dev->T("Max allowed trace size"), 0x10000, screenManager()
318
)
319
);
320
trace_max_size->SetFormat("%d basic blocks");
321
trace_max_size->OnChange.Add([&](UI::EventParams &) {
322
INFO_LOG(Log::JIT, "User changed the tracer's max trace size to %d", mipsTracer.in_max_trace_size);
323
return UI::EVENT_CONTINUE;
324
});
325
326
Button *FlushTrace = list->Add(new Button(dev->T("Flush the trace")));
327
FlushTrace->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerFlushTrace);
328
329
Button *InvalidateJitCache = list->Add(new Button(dev->T("Clear the JIT cache")));
330
InvalidateJitCache->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerClearJitCache);
331
332
Button *ClearMIPSTracer = list->Add(new Button(dev->T("Clear the MIPSTracer")));
333
ClearMIPSTracer->OnClick.Handle(this, &DeveloperToolsScreen::OnMIPSTracerClearTracer);
334
}
335
336
void DeveloperToolsScreen::CreateAudioTab(UI::LinearLayout *list) {
337
using namespace UI;
338
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
339
list->Add(new CheckBox(&g_Config.bForceFfmpegForAudioDec, dev->T("Use FFMPEG for all compressed audio")));
340
}
341
342
void DeveloperToolsScreen::CreateNetworkTab(UI::LinearLayout *list) {
343
using namespace UI;
344
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
345
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
346
list->Add(new ItemHeader(ms->T("Networking")));
347
list->Add(new CheckBox(&g_Config.bDontDownloadInfraJson, dev->T("Don't download infra-dns.json")));
348
}
349
350
void DeveloperToolsScreen::CreateGraphicsTab(UI::LinearLayout *list) {
351
using namespace UI;
352
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
353
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
354
auto ps = GetI18NCategory(I18NCat::POSTSHADERS);
355
auto sy = GetI18NCategory(I18NCat::SYSTEM);
356
auto si = GetI18NCategory(I18NCat::SYSINFO);
357
358
Draw::DrawContext *draw = screenManager()->getDrawContext();
359
360
list->Add(new ItemHeader(sy->T("General")));
361
list->Add(new CheckBox(&g_Config.bVendorBugChecksEnabled, dev->T("Enable driver bug workarounds")));
362
list->Add(new CheckBox(&g_Config.bShaderCache, dev->T("Enable shader cache")));
363
364
static const char *ffModes[] = { "Render all frames", "", "Frame Skipping" };
365
PopupMultiChoice *ffMode = list->Add(new PopupMultiChoice(&g_Config.iFastForwardMode, dev->T("Fast-forward mode"), ffModes, 0, ARRAY_SIZE(ffModes), I18NCat::GRAPHICS, screenManager()));
366
ffMode->SetEnabledFunc([]() { return !g_Config.bVSync; });
367
ffMode->HideChoice(1); // not used
368
369
auto displayRefreshRate = list->Add(new PopupSliderChoice(&g_Config.iDisplayRefreshRate, 60, 1000, 60, dev->T("Display refresh rate"), 1, screenManager()));
370
displayRefreshRate->SetFormat(si->T("%d Hz"));
371
372
list->Add(new ItemHeader(dev->T("Vulkan")));
373
list->Add(new CheckBox(&g_Config.bVulkanDisableImplicitLayers, dev->T("Prevent loading overlays")));
374
375
if (g_Config.iGPUBackend == (int)GPUBackend::VULKAN) {
376
list->Add(new CheckBox(&g_Config.bRenderMultiThreading, dev->T("Multi-threaded rendering"), ""))->OnClick.Add([](UI::EventParams &e) {
377
// TODO: Not translating yet. Will combine with other translations of settings that need restart.
378
g_OSD.Show(OSDType::MESSAGE_WARNING, "Restart required");
379
return UI::EVENT_DONE;
380
});
381
}
382
383
if (GetGPUBackend() == GPUBackend::VULKAN && SupportsCustomDriver()) {
384
auto driverChoice = list->Add(new Choice(gr->T("AdrenoTools driver manager")));
385
driverChoice->OnClick.Add([=](UI::EventParams &e) {
386
screenManager()->push(new DriverManagerScreen(gamePath_));
387
return UI::EVENT_DONE;
388
});
389
}
390
391
static const char *depthRasterModes[] = { "Auto (default)", "Low", "Off", "Always on" };
392
393
PopupMultiChoice *depthRasterMode = list->Add(new PopupMultiChoice(&g_Config.iDepthRasterMode, gr->T("Lens flare occlusion"), depthRasterModes, 0, ARRAY_SIZE(depthRasterModes), I18NCat::GRAPHICS, screenManager()));
394
depthRasterMode->SetDisabledPtr(&g_Config.bSoftwareRendering);
395
depthRasterMode->SetChoiceIcon(3, ImageID("I_WARNING")); // It's a performance trap.
396
397
list->Add(new ItemHeader(dev->T("Ubershaders")));
398
if (draw->GetShaderLanguageDesc().bitwiseOps && !draw->GetBugs().Has(Draw::Bugs::UNIFORM_INDEXING_BROKEN)) {
399
// If the above if fails, the checkbox is redundant since it'll be force disabled anyway.
400
list->Add(new CheckBox(&g_Config.bUberShaderVertex, dev->T("Vertex")));
401
}
402
#if !PPSSPP_PLATFORM(UWP)
403
if (g_Config.iGPUBackend != (int)GPUBackend::OPENGL || gl_extensions.GLES3) {
404
#else
405
{
406
#endif
407
list->Add(new CheckBox(&g_Config.bUberShaderFragment, dev->T("Fragment")));
408
}
409
410
// Experimental, allow some VR features without OpenXR
411
if (GetGPUBackend() == GPUBackend::OPENGL) {
412
auto vr = GetI18NCategory(I18NCat::VR);
413
list->Add(new ItemHeader(vr->T("Virtual reality")));
414
list->Add(new CheckBox(&g_Config.bForceVR, vr->T("VR camera")));
415
}
416
417
// Experimental, will move to main graphics settings later.
418
bool multiViewSupported = draw->GetDeviceCaps().multiViewSupported;
419
420
auto enableStereo = [=]() -> bool {
421
return g_Config.bStereoRendering && multiViewSupported;
422
};
423
424
if (multiViewSupported) {
425
list->Add(new ItemHeader(gr->T("Stereo rendering")));
426
list->Add(new CheckBox(&g_Config.bStereoRendering, gr->T("Stereo rendering")));
427
std::vector<std::string> stereoShaderNames;
428
429
ChoiceWithValueDisplay *stereoShaderChoice = list->Add(new ChoiceWithValueDisplay(&g_Config.sStereoToMonoShader, gr->T("Stereo display shader"), &PostShaderTranslateName));
430
stereoShaderChoice->SetEnabledFunc(enableStereo);
431
stereoShaderChoice->OnClick.Add([=](EventParams &e) {
432
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
433
auto procScreen = new PostProcScreen(gr->T("Stereo display shader"), 0, true);
434
if (e.v)
435
procScreen->SetPopupOrigin(e.v);
436
screenManager()->push(procScreen);
437
return UI::EVENT_DONE;
438
});
439
const ShaderInfo *shaderInfo = GetPostShaderInfo(g_Config.sStereoToMonoShader);
440
if (shaderInfo) {
441
for (size_t i = 0; i < ARRAY_SIZE(shaderInfo->settings); ++i) {
442
auto &setting = shaderInfo->settings[i];
443
if (!setting.name.empty()) {
444
std::string key = StringFromFormat("%sSettingCurrentValue%d", shaderInfo->section.c_str(), i + 1);
445
bool keyExisted = g_Config.mPostShaderSetting.find(key) != g_Config.mPostShaderSetting.end();
446
auto &value = g_Config.mPostShaderSetting[key];
447
if (!keyExisted)
448
value = setting.value;
449
450
PopupSliderChoiceFloat *settingValue = list->Add(new PopupSliderChoiceFloat(&value, setting.minValue, setting.maxValue, setting.value, ps->T(setting.name), setting.step, screenManager()));
451
settingValue->SetEnabledFunc([=] {
452
return !g_Config.bSkipBufferEffects && enableStereo();
453
});
454
}
455
}
456
}
457
}
458
}
459
460
void DeveloperToolsScreen::CreateTabs() {
461
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
462
auto sy = GetI18NCategory(I18NCat::SYSTEM);
463
auto ms = GetI18NCategory(I18NCat::MAINSETTINGS);
464
465
AddTab("General", sy->T("General"), [this](UI::LinearLayout *parent) {
466
CreateGeneralTab(parent);
467
});
468
AddTab("TextureReplacement", dev->T("Texture Replacement"), [this](UI::LinearLayout *parent) {
469
CreateTextureReplacementTab(parent);
470
});
471
AddTab("Graphics", ms->T("Graphics"), [this](UI::LinearLayout *parent) {
472
CreateGraphicsTab(parent);
473
});
474
AddTab("Networking", ms->T("Networking"), [this](UI::LinearLayout *parent) {
475
CreateNetworkTab(parent);
476
});
477
AddTab("Audio", ms->T("Audio"), [this](UI::LinearLayout *parent) {
478
CreateAudioTab(parent);
479
});
480
AddTab("Tests", dev->T("Tests"), [this](UI::LinearLayout *parent) {
481
CreateTestsTab(parent);
482
});
483
AddTab("DumpFiles", sy->T("Dump files"), [this](UI::LinearLayout *parent) {
484
CreateDumpFileTab(parent);
485
});
486
// Need a better title string.
487
AddTab("HLE", dev->T("Disable HLE"), [this](UI::LinearLayout *parent) {
488
CreateHLETab(parent);
489
});
490
#if !PPSSPP_PLATFORM(ANDROID) && !PPSSPP_PLATFORM(IOS) && !PPSSPP_PLATFORM(SWITCH)
491
AddTab("MIPSTracer", dev->T("MIPSTracer"), [this](UI::LinearLayout *parent) {
492
CreateMIPSTracerTab(parent);
493
});
494
#endif
495
// Reconsider whenever recreating views.
496
hasTexturesIni_ = HasIni::MAYBE;
497
}
498
499
void DeveloperToolsScreen::onFinish(DialogResult result) {
500
UIScreen::onFinish(result);
501
g_Config.Save("DeveloperToolsScreen::onFinish");
502
System_PostUIMessage(UIMessage::GPU_CONFIG_CHANGED);
503
}
504
505
UI::EventReturn DeveloperToolsScreen::OnLoggingChanged(UI::EventParams &e) {
506
System_Notify(SystemNotification::TOGGLE_DEBUG_CONSOLE);
507
return UI::EVENT_DONE;
508
}
509
510
UI::EventReturn DeveloperToolsScreen::OnOpenTexturesIniFile(UI::EventParams &e) {
511
std::string gameID = g_paramSFO.GetDiscID();
512
Path generatedFilename;
513
514
if (TextureReplacer::GenerateIni(gameID, generatedFilename)) {
515
if (System_GetPropertyBool(SYSPROP_SUPPORTS_OPEN_FILE_IN_EDITOR)) {
516
File::OpenFileInEditor(generatedFilename);
517
} else {
518
// Can't do much here, let's send a "toast" so the user sees that something happened.
519
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
520
System_Toast((generatedFilename.ToVisualString() + ": " + dev->T_cstr("Texture ini file created")).c_str());
521
}
522
523
hasTexturesIni_ = HasIni::YES;
524
}
525
return UI::EVENT_DONE;
526
}
527
528
UI::EventReturn DeveloperToolsScreen::OnLogConfig(UI::EventParams &e) {
529
screenManager()->push(new LogConfigScreen());
530
return UI::EVENT_DONE;
531
}
532
533
UI::EventReturn DeveloperToolsScreen::OnJitDebugTools(UI::EventParams &e) {
534
screenManager()->push(new JitDebugScreen());
535
return UI::EVENT_DONE;
536
}
537
538
UI::EventReturn DeveloperToolsScreen::OnGPUDriverTest(UI::EventParams &e) {
539
screenManager()->push(new GPUDriverTestScreen());
540
return UI::EVENT_DONE;
541
}
542
543
UI::EventReturn DeveloperToolsScreen::OnTouchscreenTest(UI::EventParams &e) {
544
screenManager()->push(new TouchTestScreen(gamePath_));
545
return UI::EVENT_DONE;
546
}
547
548
UI::EventReturn DeveloperToolsScreen::OnJitAffectingSetting(UI::EventParams &e) {
549
System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT);
550
return UI::EVENT_DONE;
551
}
552
553
UI::EventReturn DeveloperToolsScreen::OnCopyStatesToRoot(UI::EventParams &e) {
554
Path savestate_dir = GetSysDirectory(DIRECTORY_SAVESTATE);
555
Path root_dir = GetSysDirectory(DIRECTORY_MEMSTICK_ROOT);
556
557
std::vector<File::FileInfo> files;
558
GetFilesInDir(savestate_dir, &files, nullptr, 0);
559
560
for (const File::FileInfo &file : files) {
561
Path src = file.fullName;
562
Path dst = root_dir / file.name;
563
INFO_LOG(Log::System, "Copying file '%s' to '%s'", src.c_str(), dst.c_str());
564
File::Copy(src, dst);
565
}
566
567
return UI::EVENT_DONE;
568
}
569
570
UI::EventReturn DeveloperToolsScreen::OnRemoteDebugger(UI::EventParams &e) {
571
if (allowDebugger_) {
572
StartWebServer(WebServerFlags::DEBUGGER);
573
} else {
574
StopWebServer(WebServerFlags::DEBUGGER);
575
}
576
// Persist the setting. Maybe should separate?
577
g_Config.bRemoteDebuggerOnStartup = allowDebugger_;
578
return UI::EVENT_DONE;
579
}
580
581
UI::EventReturn DeveloperToolsScreen::OnMIPSTracerEnabled(UI::EventParams &e) {
582
if (MIPSTracerEnabled_) {
583
u32 capacity = mipsTracer.in_storage_capacity;
584
u32 trace_size = mipsTracer.in_max_trace_size;
585
586
mipsTracer.initialize(capacity, trace_size);
587
mipsTracer.start_tracing();
588
} else {
589
mipsTracer.stop_tracing();
590
}
591
return UI::EVENT_DONE;
592
}
593
594
UI::EventReturn DeveloperToolsScreen::OnMIPSTracerPathChanged(UI::EventParams &e) {
595
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
596
System_BrowseForFileSave(
597
GetRequesterToken(),
598
dev->T("Select the log file"),
599
"trace.txt",
600
BrowseFileType::ANY,
601
[this](const std::string &value, int) {
602
mipsTracer.set_logging_path(value);
603
MIPSTracerPath_ = value;
604
MIPSTracerPath->SetRightText(MIPSTracerPath_);
605
}
606
);
607
608
return UI::EVENT_DONE;
609
}
610
611
UI::EventReturn DeveloperToolsScreen::OnMIPSTracerFlushTrace(UI::EventParams &e) {
612
mipsTracer.flush_to_file();
613
// The error logs are emitted inside the tracer
614
615
return UI::EVENT_DONE;
616
}
617
618
UI::EventReturn DeveloperToolsScreen::OnMIPSTracerClearJitCache(UI::EventParams &e) {
619
INFO_LOG(Log::JIT, "Clearing the jit cache...");
620
System_PostUIMessage(UIMessage::REQUEST_CLEAR_JIT);
621
return UI::EVENT_DONE;
622
}
623
624
UI::EventReturn DeveloperToolsScreen::OnMIPSTracerClearTracer(UI::EventParams &e) {
625
INFO_LOG(Log::JIT, "Clearing the MIPSTracer...");
626
mipsTracer.clear();
627
return UI::EVENT_DONE;
628
}
629
630
void DeveloperToolsScreen::update() {
631
UIDialogScreenWithBackground::update();
632
allowDebugger_ = !WebServerStopped(WebServerFlags::DEBUGGER);
633
canAllowDebugger_ = !WebServerStopping(WebServerFlags::DEBUGGER);
634
}
635
636
void DeveloperToolsScreen::MemoryMapTest() {
637
int sum = 0;
638
for (uint64_t addr = 0; addr < 0x100000000ULL; addr += 0x1000) {
639
const u32 addr32 = (u32)addr;
640
if (Memory::IsValidAddress(addr32)) {
641
sum += Memory::ReadUnchecked_U32(addr32);
642
}
643
}
644
// Just to force the compiler to do things properly.
645
INFO_LOG(Log::JIT, "Total sum: %08x", sum);
646
}
647
648
static bool RunMemstickTest(std::string *error) {
649
Path testRoot = GetSysDirectory(PSPDirectories::DIRECTORY_CACHE) / "test";
650
651
*error = "N/A";
652
653
File::CreateDir(testRoot);
654
if (!File::Exists(testRoot)) {
655
return false;
656
}
657
658
Path testFilePath = testRoot / "temp.txt";
659
File::CreateEmptyFile(testFilePath);
660
661
// Attempt to delete the test root. This should fail since it still contains files.
662
File::DeleteDir(testRoot);
663
if (!File::Exists(testRoot)) {
664
*error = "testroot was deleted with a file in it!";
665
return false;
666
}
667
668
File::Delete(testFilePath);
669
if (File::Exists(testFilePath)) {
670
*error = "testfile wasn't deleted";
671
return false;
672
}
673
674
File::DeleteDir(testRoot);
675
if (File::Exists(testRoot)) {
676
*error = "testroot wasn't deleted, even when empty";
677
return false;
678
}
679
680
*error = "passed";
681
return true;
682
}
683
684
UI::EventReturn DeveloperToolsScreen::OnMemstickTest(UI::EventParams &e) {
685
std::string error;
686
if (RunMemstickTest(&error)) {
687
g_OSD.Show(OSDType::MESSAGE_SUCCESS, "Memstick test passed", error, 6.0f);
688
} else {
689
g_OSD.Show(OSDType::MESSAGE_ERROR, "Memstick test failed", error, 6.0f);
690
}
691
692
return UI::EVENT_DONE;
693
}
694
695