CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/UI/DevScreens.cpp
Views: 1401
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
// Hack around name collisions between UI and xlib
19
// Only affects this file.
20
#undef VK_USE_PLATFORM_XLIB_KHR
21
#undef VK_USE_PLATFORM_XCB_KHR
22
#undef VK_USE_PLATFORM_DIRECTFB_EXT
23
#undef VK_USE_PLATFORM_XLIB_XRANDR_EXT
24
25
#include <algorithm>
26
#include <cstring>
27
28
#include "ppsspp_config.h"
29
30
#include "Common/Common.h"
31
#include "Common/System/Display.h"
32
#include "Common/System/NativeApp.h"
33
#include "Common/System/System.h"
34
#include "Common/System/OSD.h"
35
#include "Common/GPU/OpenGL/GLFeatures.h"
36
37
#include "Common/File/AndroidStorage.h"
38
#include "Common/Data/Text/I18n.h"
39
#include "Common/Data/Encoding/Utf8.h"
40
#include "Common/Net/HTTPClient.h"
41
#include "Common/UI/Context.h"
42
#include "Common/UI/View.h"
43
#include "Common/UI/ViewGroup.h"
44
#include "Common/UI/UI.h"
45
#include "Common/UI/IconCache.h"
46
#include "Common/Data/Text/Parsers.h"
47
#include "Common/Profiler/Profiler.h"
48
49
#include "Common/Log/LogManager.h"
50
#include "Common/CPUDetect.h"
51
#include "Common/StringUtils.h"
52
53
#include "Core/MemMap.h"
54
#include "Core/Config.h"
55
#include "Core/ConfigValues.h"
56
#include "Core/System.h"
57
#include "Core/Reporting.h"
58
#include "Core/CoreParameter.h"
59
#include "Core/HLE/sceKernel.h" // GPI/GPO
60
#include "Core/MIPS/MIPSTables.h"
61
#include "Core/MIPS/JitCommon/JitBlockCache.h"
62
#include "Core/MIPS/JitCommon/JitCommon.h"
63
#include "Core/MIPS/JitCommon/JitState.h"
64
#include "GPU/Debugger/Record.h"
65
#include "GPU/GPUInterface.h"
66
#include "GPU/GPUState.h"
67
#include "UI/MiscScreens.h"
68
#include "UI/DevScreens.h"
69
#include "UI/MainScreen.h"
70
#include "UI/ControlMappingScreen.h"
71
#include "UI/GameSettingsScreen.h"
72
#include "UI/JitCompareScreen.h"
73
74
#ifdef _WIN32
75
// Want to avoid including the full header here as it includes d3dx.h
76
int GetD3DCompilerVersion();
77
#endif
78
79
#include "android/jni/app-android.h"
80
81
static const char *logLevelList[] = {
82
"Notice",
83
"Error",
84
"Warn",
85
"Info",
86
"Debug",
87
"Verb."
88
};
89
90
static const char *g_debugOverlayList[] = {
91
"Off",
92
"Debug stats",
93
"Draw Frametimes Graph",
94
"Frame timing",
95
#ifdef USE_PROFILER
96
"Frame profile",
97
#endif
98
"Control Debug",
99
"Audio Debug",
100
"GPU Profile",
101
"GPU Allocator Viewer",
102
"Framebuffer List",
103
};
104
105
void AddOverlayList(UI::ViewGroup *items, ScreenManager *screenManager) {
106
using namespace UI;
107
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
108
int numOverlays = ARRAY_SIZE(g_debugOverlayList);
109
if (!(g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL)) {
110
numOverlays -= 2; // skip the last 2.
111
}
112
items->Add(new PopupMultiChoice((int *)&g_Config.iDebugOverlay, dev->T("Debug overlay"), g_debugOverlayList, 0, numOverlays, I18NCat::DEVELOPER, screenManager));
113
}
114
115
void DevMenuScreen::CreatePopupContents(UI::ViewGroup *parent) {
116
using namespace UI;
117
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
118
auto sy = GetI18NCategory(I18NCat::SYSTEM);
119
120
ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f));
121
LinearLayout *items = new LinearLayout(ORIENT_VERTICAL);
122
123
#if !defined(MOBILE_DEVICE)
124
items->Add(new Choice(dev->T("Log View")))->OnClick.Handle(this, &DevMenuScreen::OnLogView);
125
#endif
126
items->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DevMenuScreen::OnLogConfig);
127
items->Add(new Choice(sy->T("Developer Tools")))->OnClick.Handle(this, &DevMenuScreen::OnDeveloperTools);
128
129
// Debug overlay
130
AddOverlayList(items, screenManager());
131
132
items->Add(new Choice(dev->T("Jit Compare")))->OnClick.Handle(this, &DevMenuScreen::OnJitCompare);
133
items->Add(new Choice(dev->T("Shader Viewer")))->OnClick.Handle(this, &DevMenuScreen::OnShaderView);
134
135
items->Add(new Choice(dev->T("Toggle Freeze")))->OnClick.Add([](UI::EventParams &e) {
136
if (PSP_CoreParameter().frozen) {
137
PSP_CoreParameter().frozen = false;
138
} else {
139
PSP_CoreParameter().freezeNext = true;
140
}
141
return UI::EVENT_DONE;
142
});
143
144
items->Add(new Choice(dev->T("Reset limited logging")))->OnClick.Handle(this, &DevMenuScreen::OnResetLimitedLogging);
145
146
items->Add(new Choice(dev->T("GPI/GPO switches/LEDs")))->OnClick.Add([=](UI::EventParams &e) {
147
screenManager()->push(new GPIGPOScreen(dev->T("GPI/GPO switches/LEDs")));
148
return UI::EVENT_DONE;
149
});
150
151
items->Add(new Choice(dev->T("Create frame dump")))->OnClick.Add([](UI::EventParams &e) {
152
GPURecord::RecordNextFrame([](const Path &dumpPath) {
153
NOTICE_LOG(Log::System, "Frame dump created at '%s'", dumpPath.c_str());
154
if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) {
155
System_ShowFileInFolder(dumpPath);
156
} else {
157
g_OSD.Show(OSDType::MESSAGE_SUCCESS, dumpPath.ToVisualString(), 7.0f);
158
}
159
});
160
return UI::EVENT_DONE;
161
});
162
163
// This one is not very useful these days, and only really on desktop. Hide it on other platforms.
164
if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_DESKTOP) {
165
items->Add(new Choice(dev->T("Dump next frame to log")))->OnClick.Add([](UI::EventParams &e) {
166
gpu->DumpNextFrame();
167
return UI::EVENT_DONE;
168
});
169
}
170
171
scroll->Add(items);
172
parent->Add(scroll);
173
174
RingbufferLogListener *ring = LogManager::GetInstance()->GetRingbufferListener();
175
if (ring) {
176
ring->SetEnabled(true);
177
}
178
}
179
180
UI::EventReturn DevMenuScreen::OnResetLimitedLogging(UI::EventParams &e) {
181
Reporting::ResetCounts();
182
return UI::EVENT_DONE;
183
}
184
185
UI::EventReturn DevMenuScreen::OnLogView(UI::EventParams &e) {
186
UpdateUIState(UISTATE_PAUSEMENU);
187
screenManager()->push(new LogScreen());
188
return UI::EVENT_DONE;
189
}
190
191
UI::EventReturn DevMenuScreen::OnLogConfig(UI::EventParams &e) {
192
UpdateUIState(UISTATE_PAUSEMENU);
193
screenManager()->push(new LogConfigScreen());
194
return UI::EVENT_DONE;
195
}
196
197
UI::EventReturn DevMenuScreen::OnDeveloperTools(UI::EventParams &e) {
198
UpdateUIState(UISTATE_PAUSEMENU);
199
screenManager()->push(new DeveloperToolsScreen(gamePath_));
200
return UI::EVENT_DONE;
201
}
202
203
UI::EventReturn DevMenuScreen::OnJitCompare(UI::EventParams &e) {
204
UpdateUIState(UISTATE_PAUSEMENU);
205
screenManager()->push(new JitCompareScreen());
206
return UI::EVENT_DONE;
207
}
208
209
UI::EventReturn DevMenuScreen::OnShaderView(UI::EventParams &e) {
210
UpdateUIState(UISTATE_PAUSEMENU);
211
if (gpu) // Avoid crashing if chosen while the game is being loaded.
212
screenManager()->push(new ShaderListScreen());
213
return UI::EVENT_DONE;
214
}
215
216
void DevMenuScreen::dialogFinished(const Screen *dialog, DialogResult result) {
217
UpdateUIState(UISTATE_INGAME);
218
// Close when a subscreen got closed.
219
// TODO: a bug in screenmanager causes this not to work here.
220
// TriggerFinish(DR_OK);
221
}
222
223
void GPIGPOScreen::CreatePopupContents(UI::ViewGroup *parent) {
224
using namespace UI;
225
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
226
parent->Add(new CheckBox(&g_Config.bShowGPOLEDs, dev->T("Show GPO LEDs")));
227
for (int i = 0; i < 8; i++) {
228
std::string name = ApplySafeSubstitutions(dev->T("GPI switch %1"), i);
229
parent->Add(new BitCheckBox(&g_GPIBits, 1 << i, name));
230
}
231
}
232
233
void LogScreen::UpdateLog() {
234
using namespace UI;
235
RingbufferLogListener *ring = LogManager::GetInstance()->GetRingbufferListener();
236
if (!ring)
237
return;
238
vert_->Clear();
239
for (int i = ring->GetCount() - 1; i >= 0; i--) {
240
TextView *v = vert_->Add(new TextView(ring->TextAt(i), FLAG_DYNAMIC_ASCII, false));
241
uint32_t color = 0xFFFFFF;
242
switch (ring->LevelAt(i)) {
243
case LogLevel::LDEBUG: color = 0xE0E0E0; break;
244
case LogLevel::LWARNING: color = 0x50FFFF; break;
245
case LogLevel::LERROR: color = 0x5050FF; break;
246
case LogLevel::LNOTICE: color = 0x30FF30; break;
247
case LogLevel::LINFO: color = 0xFFFFFF; break;
248
case LogLevel::LVERBOSE: color = 0xC0C0C0; break;
249
}
250
v->SetTextColor(0xFF000000 | color);
251
}
252
toBottom_ = true;
253
}
254
255
void LogScreen::update() {
256
UIDialogScreenWithBackground::update();
257
if (toBottom_) {
258
toBottom_ = false;
259
scroll_->ScrollToBottom();
260
}
261
}
262
263
void LogScreen::CreateViews() {
264
using namespace UI;
265
auto di = GetI18NCategory(I18NCat::DIALOG);
266
267
LinearLayout *outer = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
268
root_ = outer;
269
270
scroll_ = outer->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0)));
271
LinearLayout *bottom = outer->Add(new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
272
bottom->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
273
cmdLine_ = bottom->Add(new TextEdit("", "Command", "Command Line", new LinearLayoutParams(1.0)));
274
cmdLine_->OnEnter.Handle(this, &LogScreen::OnSubmit);
275
bottom->Add(new Button(di->T("Submit")))->OnClick.Handle(this, &LogScreen::OnSubmit);
276
277
vert_ = scroll_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
278
vert_->SetSpacing(0);
279
280
UpdateLog();
281
}
282
283
UI::EventReturn LogScreen::OnSubmit(UI::EventParams &e) {
284
std::string cmd = cmdLine_->GetText();
285
286
// TODO: Can add all sorts of fun stuff here that we can't be bothered writing proper UI for, like various memdumps etc.
287
288
NOTICE_LOG(Log::System, "Submitted: %s", cmd.c_str());
289
290
UpdateLog();
291
cmdLine_->SetText("");
292
cmdLine_->SetFocus();
293
return UI::EVENT_DONE;
294
}
295
296
void LogConfigScreen::CreateViews() {
297
using namespace UI;
298
299
auto di = GetI18NCategory(I18NCat::DIALOG);
300
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
301
302
root_ = new ScrollView(ORIENT_VERTICAL);
303
304
LinearLayout *vert = root_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
305
vert->SetSpacing(0);
306
307
LinearLayout *topbar = new LinearLayout(ORIENT_HORIZONTAL);
308
topbar->Add(new Choice(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
309
topbar->Add(new Choice(di->T("Toggle All")))->OnClick.Handle(this, &LogConfigScreen::OnToggleAll);
310
topbar->Add(new Choice(di->T("Enable All")))->OnClick.Handle(this, &LogConfigScreen::OnEnableAll);
311
topbar->Add(new Choice(di->T("Disable All")))->OnClick.Handle(this, &LogConfigScreen::OnDisableAll);
312
topbar->Add(new Choice(dev->T("Log Level")))->OnClick.Handle(this, &LogConfigScreen::OnLogLevel);
313
314
vert->Add(topbar);
315
316
vert->Add(new ItemHeader(dev->T("Logging Channels")));
317
318
LogManager *logMan = LogManager::GetInstance();
319
320
int cellSize = 400;
321
322
UI::GridLayoutSettings gridsettings(cellSize, 64, 5);
323
gridsettings.fillCells = true;
324
GridLayout *grid = vert->Add(new GridLayoutList(gridsettings, new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
325
326
for (int i = 0; i < LogManager::GetNumChannels(); i++) {
327
Log type = (Log)i;
328
LogChannel *chan = logMan->GetLogChannel(type);
329
LinearLayout *row = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(cellSize - 50, WRAP_CONTENT));
330
row->SetSpacing(0);
331
row->Add(new CheckBox(&chan->enabled, "", "", new LinearLayoutParams(50, WRAP_CONTENT)));
332
row->Add(new PopupMultiChoice((int *)&chan->level, chan->m_shortName, logLevelList, 1, 6, I18NCat::NONE, screenManager(), new LinearLayoutParams(1.0)));
333
grid->Add(row);
334
}
335
}
336
337
UI::EventReturn LogConfigScreen::OnToggleAll(UI::EventParams &e) {
338
LogManager *logMan = LogManager::GetInstance();
339
for (int i = 0; i < LogManager::GetNumChannels(); i++) {
340
LogChannel *chan = logMan->GetLogChannel((Log)i);
341
chan->enabled = !chan->enabled;
342
}
343
return UI::EVENT_DONE;
344
}
345
346
UI::EventReturn LogConfigScreen::OnEnableAll(UI::EventParams &e) {
347
LogManager *logMan = LogManager::GetInstance();
348
for (int i = 0; i < LogManager::GetNumChannels(); i++) {
349
LogChannel *chan = logMan->GetLogChannel((Log)i);
350
chan->enabled = true;
351
}
352
return UI::EVENT_DONE;
353
}
354
355
UI::EventReturn LogConfigScreen::OnDisableAll(UI::EventParams &e) {
356
LogManager *logMan = LogManager::GetInstance();
357
for (int i = 0; i < LogManager::GetNumChannels(); i++) {
358
LogChannel *chan = logMan->GetLogChannel((Log)i);
359
chan->enabled = false;
360
}
361
return UI::EVENT_DONE;
362
}
363
364
UI::EventReturn LogConfigScreen::OnLogLevelChange(UI::EventParams &e) {
365
RecreateViews();
366
return UI::EVENT_DONE;
367
}
368
369
UI::EventReturn LogConfigScreen::OnLogLevel(UI::EventParams &e) {
370
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
371
372
auto logLevelScreen = new LogLevelScreen(dev->T("Log Level"));
373
logLevelScreen->OnChoice.Handle(this, &LogConfigScreen::OnLogLevelChange);
374
if (e.v)
375
logLevelScreen->SetPopupOrigin(e.v);
376
screenManager()->push(logLevelScreen);
377
return UI::EVENT_DONE;
378
}
379
380
LogLevelScreen::LogLevelScreen(std::string_view title) : ListPopupScreen(title) {
381
int NUMLOGLEVEL = 6;
382
std::vector<std::string> list;
383
for (int i = 0; i < NUMLOGLEVEL; ++i) {
384
list.push_back(logLevelList[i]);
385
}
386
adaptor_ = UI::StringVectorListAdaptor(list, -1);
387
}
388
389
void LogLevelScreen::OnCompleted(DialogResult result) {
390
if (result != DR_OK)
391
return;
392
int selected = listView_->GetSelected();
393
LogManager *logMan = LogManager::GetInstance();
394
395
for (int i = 0; i < LogManager::GetNumChannels(); ++i) {
396
Log type = (Log)i;
397
LogChannel *chan = logMan->GetLogChannel(type);
398
if (chan->enabled)
399
chan->level = (LogLevel)(selected + 1);
400
}
401
}
402
403
struct JitDisableFlag {
404
MIPSComp::JitDisable flag;
405
const char *name;
406
};
407
408
// Please do not try to translate these :)
409
static const JitDisableFlag jitDisableFlags[] = {
410
{ MIPSComp::JitDisable::ALU, "ALU" },
411
{ MIPSComp::JitDisable::ALU_IMM, "ALU_IMM" },
412
{ MIPSComp::JitDisable::ALU_BIT, "ALU_BIT" },
413
{ MIPSComp::JitDisable::MULDIV, "MULDIV" },
414
{ MIPSComp::JitDisable::FPU, "FPU" },
415
{ MIPSComp::JitDisable::FPU_COMP, "FPU_COMP" },
416
{ MIPSComp::JitDisable::FPU_XFER, "FPU_XFER" },
417
{ MIPSComp::JitDisable::VFPU_VEC, "VFPU_VEC" },
418
{ MIPSComp::JitDisable::VFPU_MTX_VTFM, "VFPU_MTX_VTFM" },
419
{ MIPSComp::JitDisable::VFPU_MTX_VMSCL, "VFPU_MTX_VMSCL" },
420
{ MIPSComp::JitDisable::VFPU_MTX_VMMUL, "VFPU_MTX_VMMUL" },
421
{ MIPSComp::JitDisable::VFPU_MTX_VMMOV, "VFPU_MTX_VMMOV" },
422
{ MIPSComp::JitDisable::VFPU_COMP, "VFPU_COMP" },
423
{ MIPSComp::JitDisable::VFPU_XFER, "VFPU_XFER" },
424
{ MIPSComp::JitDisable::LSU, "LSU" },
425
{ MIPSComp::JitDisable::LSU_UNALIGNED, "LSU_UNALIGNED" },
426
{ MIPSComp::JitDisable::LSU_FPU, "LSU_FPU" },
427
{ MIPSComp::JitDisable::LSU_VFPU, "LSU_VFPU" },
428
{ MIPSComp::JitDisable::SIMD, "SIMD" },
429
{ MIPSComp::JitDisable::BLOCKLINK, "Block Linking" },
430
{ MIPSComp::JitDisable::POINTERIFY, "Pointerify" },
431
{ MIPSComp::JitDisable::STATIC_ALLOC, "Static regalloc" },
432
{ MIPSComp::JitDisable::CACHE_POINTERS, "Cached pointers" },
433
{ MIPSComp::JitDisable::REGALLOC_GPR, "GPR Regalloc across instructions" },
434
{ MIPSComp::JitDisable::REGALLOC_FPR, "FPR Regalloc across instructions" },
435
};
436
437
void JitDebugScreen::CreateViews() {
438
using namespace UI;
439
440
auto di = GetI18NCategory(I18NCat::DIALOG);
441
auto dev = GetI18NCategory(I18NCat::DEVELOPER);
442
443
root_ = new ScrollView(ORIENT_VERTICAL);
444
445
LinearLayout *vert = root_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
446
vert->SetSpacing(0);
447
448
LinearLayout *topbar = new LinearLayout(ORIENT_HORIZONTAL);
449
topbar->Add(new Choice(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
450
topbar->Add(new Choice(di->T("Disable All")))->OnClick.Handle(this, &JitDebugScreen::OnDisableAll);
451
topbar->Add(new Choice(di->T("Enable All")))->OnClick.Handle(this, &JitDebugScreen::OnEnableAll);
452
453
vert->Add(topbar);
454
vert->Add(new ItemHeader(dev->T("Disabled JIT functionality")));
455
456
for (auto flag : jitDisableFlags) {
457
// Do not add translation of these.
458
vert->Add(new BitCheckBox(&g_Config.uJitDisableFlags, (uint32_t)flag.flag, flag.name));
459
}
460
}
461
462
UI::EventReturn JitDebugScreen::OnEnableAll(UI::EventParams &e) {
463
g_Config.uJitDisableFlags &= ~(uint32_t)MIPSComp::JitDisable::ALL_FLAGS;
464
return UI::EVENT_DONE;
465
}
466
467
UI::EventReturn JitDebugScreen::OnDisableAll(UI::EventParams &e) {
468
g_Config.uJitDisableFlags |= (uint32_t)MIPSComp::JitDisable::ALL_FLAGS;
469
return UI::EVENT_DONE;
470
}
471
472
void SystemInfoScreen::update() {
473
TabbedUIDialogScreenWithGameBackground::update();
474
g_OSD.NudgeSidebar();
475
}
476
477
void SystemInfoScreen::CreateTabs() {
478
using namespace Draw;
479
using namespace UI;
480
481
auto di = GetI18NCategory(I18NCat::DIALOG);
482
auto si = GetI18NCategory(I18NCat::SYSINFO);
483
auto sy = GetI18NCategory(I18NCat::SYSTEM);
484
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
485
486
LinearLayout *deviceSpecs = AddTab("Device Info", si->T("Device Info"));
487
488
CollapsibleSection *systemInfo = deviceSpecs->Add(new CollapsibleSection(si->T("System Information")));
489
systemInfo->Add(new InfoItem(si->T("System Name", "Name"), System_GetProperty(SYSPROP_NAME)));
490
#if PPSSPP_PLATFORM(ANDROID)
491
systemInfo->Add(new InfoItem(si->T("System Version"), StringFromInt(System_GetPropertyInt(SYSPROP_SYSTEMVERSION))));
492
#elif PPSSPP_PLATFORM(WINDOWS)
493
std::string sysVersion = System_GetProperty(SYSPROP_SYSTEMBUILD);
494
if (!sysVersion.empty()) {
495
systemInfo->Add(new InfoItem(si->T("OS Build"), sysVersion));
496
}
497
#endif
498
systemInfo->Add(new InfoItem(si->T("Lang/Region"), System_GetProperty(SYSPROP_LANGREGION)));
499
std::string board = System_GetProperty(SYSPROP_BOARDNAME);
500
if (!board.empty())
501
systemInfo->Add(new InfoItem(si->T("Board"), board));
502
systemInfo->Add(new InfoItem(si->T("ABI"), GetCompilerABI()));
503
if (System_GetPropertyBool(SYSPROP_DEBUGGER_PRESENT)) {
504
systemInfo->Add(new InfoItem(si->T("Debugger Present"), di->T("Yes")));
505
}
506
507
CollapsibleSection *cpuInfo = deviceSpecs->Add(new CollapsibleSection(si->T("CPU Information")));
508
509
// Don't bother showing the CPU name if we don't have one.
510
if (strcmp(cpu_info.brand_string, "Unknown") != 0) {
511
cpuInfo->Add(new InfoItem(si->T("CPU Name", "Name"), cpu_info.brand_string));
512
}
513
514
int totalThreads = cpu_info.num_cores * cpu_info.logical_cpu_count;
515
std::string cores = StringFromFormat(si->T_cstr("%d (%d per core, %d cores)"), totalThreads, cpu_info.logical_cpu_count, cpu_info.num_cores);
516
cpuInfo->Add(new InfoItem(si->T("Threads"), cores));
517
#if PPSSPP_PLATFORM(IOS)
518
cpuInfo->Add(new InfoItem(si->T("JIT available"), System_GetPropertyBool(SYSPROP_CAN_JIT) ? di->T("Yes") : di->T("No")));
519
#endif
520
521
CollapsibleSection *gpuInfo = deviceSpecs->Add(new CollapsibleSection(si->T("GPU Information")));
522
523
DrawContext *draw = screenManager()->getDrawContext();
524
525
const std::string apiNameKey = draw->GetInfoString(InfoField::APINAME);
526
std::string_view apiName = gr->T(apiNameKey);
527
gpuInfo->Add(new InfoItem(si->T("3D API"), apiName));
528
529
// TODO: Not really vendor, on most APIs it's a device name (GL calls it vendor though).
530
std::string vendorString;
531
if (draw->GetDeviceCaps().deviceID != 0) {
532
vendorString = StringFromFormat("%s (%08x)", draw->GetInfoString(InfoField::VENDORSTRING).c_str(), draw->GetDeviceCaps().deviceID);
533
} else {
534
vendorString = draw->GetInfoString(InfoField::VENDORSTRING);
535
}
536
gpuInfo->Add(new InfoItem(si->T("Vendor"), vendorString));
537
std::string vendor = draw->GetInfoString(InfoField::VENDOR);
538
if (vendor.size())
539
gpuInfo->Add(new InfoItem(si->T("Vendor (detected)"), vendor));
540
gpuInfo->Add(new InfoItem(si->T("Driver Version"), draw->GetInfoString(InfoField::DRIVER)));
541
#ifdef _WIN32
542
if (GetGPUBackend() != GPUBackend::VULKAN) {
543
gpuInfo->Add(new InfoItem(si->T("Driver Version"), System_GetProperty(SYSPROP_GPUDRIVER_VERSION)));
544
}
545
#if !PPSSPP_PLATFORM(UWP)
546
if (GetGPUBackend() == GPUBackend::DIRECT3D9) {
547
gpuInfo->Add(new InfoItem(si->T("D3DCompiler Version"), StringFromFormat("%d", GetD3DCompilerVersion())));
548
}
549
#endif
550
#endif
551
if (GetGPUBackend() == GPUBackend::OPENGL) {
552
gpuInfo->Add(new InfoItem(si->T("Core Context"), gl_extensions.IsCoreContext ? di->T("Active") : di->T("Inactive")));
553
int highp_int_min = gl_extensions.range[1][5][0];
554
int highp_int_max = gl_extensions.range[1][5][1];
555
int highp_float_min = gl_extensions.range[1][2][0];
556
int highp_float_max = gl_extensions.range[1][2][1];
557
if (highp_int_max != 0) {
558
char temp[128];
559
snprintf(temp, sizeof(temp), "%d-%d", highp_int_min, highp_int_max);
560
gpuInfo->Add(new InfoItem(si->T("High precision int range"), temp));
561
}
562
if (highp_float_max != 0) {
563
char temp[128];
564
snprintf(temp, sizeof(temp), "%d-%d", highp_int_min, highp_int_max);
565
gpuInfo->Add(new InfoItem(si->T("High precision float range"), temp));
566
}
567
}
568
gpuInfo->Add(new InfoItem(si->T("Depth buffer format"), DataFormatToString(draw->GetDeviceCaps().preferredDepthBufferFormat)));
569
570
std::string texCompressionFormats;
571
// Simple non-detailed summary of supported tex compression formats.
572
if (draw->GetDataFormatSupport(Draw::DataFormat::ETC2_R8G8B8_UNORM_BLOCK)) texCompressionFormats += "ETC2 ";
573
if (draw->GetDataFormatSupport(Draw::DataFormat::ASTC_4x4_UNORM_BLOCK)) texCompressionFormats += "ASTC ";
574
if (draw->GetDataFormatSupport(Draw::DataFormat::BC1_RGBA_UNORM_BLOCK)) texCompressionFormats += "BC1-3 ";
575
if (draw->GetDataFormatSupport(Draw::DataFormat::BC4_UNORM_BLOCK)) texCompressionFormats += "BC4-5 ";
576
if (draw->GetDataFormatSupport(Draw::DataFormat::BC7_UNORM_BLOCK)) texCompressionFormats += "BC7 ";
577
gpuInfo->Add(new InfoItem(si->T("Compressed texture formats"), texCompressionFormats));
578
579
CollapsibleSection *osInformation = deviceSpecs->Add(new CollapsibleSection(si->T("OS Information")));
580
osInformation->Add(new InfoItem(si->T("Memory Page Size"), StringFromFormat(si->T_cstr("%d bytes"), GetMemoryProtectPageSize())));
581
osInformation->Add(new InfoItem(si->T("RW/RX exclusive"), PlatformIsWXExclusive() ? di->T("Active") : di->T("Inactive")));
582
#if PPSSPP_PLATFORM(ANDROID)
583
osInformation->Add(new InfoItem(si->T("Sustained perf mode"), System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE) ? di->T("Supported") : di->T("Unsupported")));
584
#endif
585
586
std::string_view build = si->T("Release");
587
#ifdef _DEBUG
588
build = si->T("Debug");
589
#endif
590
osInformation->Add(new InfoItem(si->T("PPSSPP build"), build));
591
592
CollapsibleSection *audioInformation = deviceSpecs->Add(new CollapsibleSection(si->T("Audio Information")));
593
audioInformation->Add(new InfoItem(si->T("Sample rate"), StringFromFormat(si->T_cstr("%d Hz"), System_GetPropertyInt(SYSPROP_AUDIO_SAMPLE_RATE))));
594
int framesPerBuffer = System_GetPropertyInt(SYSPROP_AUDIO_FRAMES_PER_BUFFER);
595
if (framesPerBuffer > 0) {
596
audioInformation->Add(new InfoItem(si->T("Frames per buffer"), StringFromFormat("%d", framesPerBuffer)));
597
}
598
#if PPSSPP_PLATFORM(ANDROID)
599
audioInformation->Add(new InfoItem(si->T("Optimal sample rate"), StringFromFormat(si->T_cstr("%d Hz"), System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_SAMPLE_RATE))));
600
audioInformation->Add(new InfoItem(si->T("Optimal frames per buffer"), StringFromFormat("%d", System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_FRAMES_PER_BUFFER))));
601
#endif
602
603
CollapsibleSection *displayInfo = deviceSpecs->Add(new CollapsibleSection(si->T("Display Information")));
604
#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(UWP)
605
displayInfo->Add(new InfoItem(si->T("Native resolution"), StringFromFormat("%dx%d",
606
System_GetPropertyInt(SYSPROP_DISPLAY_XRES),
607
System_GetPropertyInt(SYSPROP_DISPLAY_YRES))));
608
#endif
609
displayInfo->Add(new InfoItem(si->T("UI resolution"), StringFromFormat("%dx%d (%s: %0.2f)",
610
g_display.dp_xres,
611
g_display.dp_yres,
612
si->T_cstr("DPI"),
613
g_display.dpi)));
614
displayInfo->Add(new InfoItem(si->T("Pixel resolution"), StringFromFormat("%dx%d",
615
g_display.pixel_xres,
616
g_display.pixel_yres)));
617
618
const float insets[4] = {
619
System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT),
620
System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP),
621
System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_RIGHT),
622
System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_BOTTOM),
623
};
624
if (insets[0] != 0.0f || insets[1] != 0.0f || insets[2] != 0.0f || insets[3] != 0.0f) {
625
displayInfo->Add(new InfoItem(si->T("Screen notch insets"), StringFromFormat("%0.1f %0.1f %0.1f %0.1f", insets[0], insets[1], insets[2], insets[3])));
626
}
627
628
// Don't show on Windows, since it's always treated as 60 there.
629
displayInfo->Add(new InfoItem(si->T("Refresh rate"), StringFromFormat(si->T_cstr("%0.2f Hz"), (float)System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE))));
630
std::string presentModes;
631
if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::FIFO) presentModes += "FIFO, ";
632
if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::IMMEDIATE) presentModes += "IMMEDIATE, ";
633
if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::MAILBOX) presentModes += "MAILBOX, ";
634
if (!presentModes.empty()) {
635
presentModes.pop_back();
636
presentModes.pop_back();
637
}
638
displayInfo->Add(new InfoItem(si->T("Present modes"), presentModes));
639
640
CollapsibleSection *versionInfo = deviceSpecs->Add(new CollapsibleSection(si->T("Version Information")));
641
std::string apiVersion;
642
if (GetGPUBackend() == GPUBackend::OPENGL) {
643
if (gl_extensions.IsGLES) {
644
apiVersion = StringFromFormat("v%d.%d.%d ES", gl_extensions.ver[0], gl_extensions.ver[1], gl_extensions.ver[2]);
645
} else {
646
apiVersion = StringFromFormat("v%d.%d.%d", gl_extensions.ver[0], gl_extensions.ver[1], gl_extensions.ver[2]);
647
}
648
versionInfo->Add(new InfoItem(si->T("API Version"), apiVersion));
649
} else {
650
apiVersion = draw->GetInfoString(InfoField::APIVERSION);
651
if (apiVersion.size() > 30)
652
apiVersion.resize(30);
653
versionInfo->Add(new InfoItem(si->T("API Version"), apiVersion));
654
655
if (GetGPUBackend() == GPUBackend::VULKAN) {
656
std::string deviceApiVersion = draw->GetInfoString(InfoField::DEVICE_API_VERSION);
657
versionInfo->Add(new InfoItem(si->T("Device API Version"), deviceApiVersion));
658
}
659
}
660
versionInfo->Add(new InfoItem(si->T("Shading Language"), draw->GetInfoString(InfoField::SHADELANGVERSION)));
661
662
#if PPSSPP_PLATFORM(ANDROID)
663
std::string moga = System_GetProperty(SYSPROP_MOGA_VERSION);
664
if (moga.empty()) {
665
moga = si->T("(none detected)");
666
}
667
versionInfo->Add(new InfoItem("Moga", moga));
668
#endif
669
670
if (gstate_c.GetUseFlags()) {
671
// We're in-game, and can determine these.
672
// TODO: Call a static version of GPUCommon::CheckGPUFeatures() and derive them here directly.
673
674
CollapsibleSection *gpuFlags = deviceSpecs->Add(new CollapsibleSection(si->T("GPU Flags")));
675
676
for (int i = 0; i < 32; i++) {
677
if (gstate_c.Use((1 << i))) {
678
gpuFlags->Add(new TextView(GpuUseFlagToString(i), new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
679
}
680
}
681
}
682
683
LinearLayout *storage = AddTab("Storage", si->T("Storage"));
684
685
storage->Add(new ItemHeader(si->T("Directories")));
686
// Intentionally non-translated
687
storage->Add(new InfoItem("MemStickDirectory", g_Config.memStickDirectory.ToVisualString()));
688
storage->Add(new InfoItem("InternalDataDirectory", g_Config.internalDataDirectory.ToVisualString()));
689
storage->Add(new InfoItem("AppCacheDir", g_Config.appCacheDirectory.ToVisualString()));
690
storage->Add(new InfoItem("DefaultCurrentDir", g_Config.defaultCurrentDirectory.ToVisualString()));
691
692
#if PPSSPP_PLATFORM(ANDROID)
693
storage->Add(new InfoItem("ExtFilesDir", g_extFilesDir));
694
bool scoped = System_GetPropertyBool(SYSPROP_ANDROID_SCOPED_STORAGE);
695
storage->Add(new InfoItem("Scoped Storage", scoped ? di->T("Yes") : di->T("No")));
696
if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 30) {
697
// This flag is only relevant on Android API 30+.
698
storage->Add(new InfoItem("IsStoragePreservedLegacy", Android_IsExternalStoragePreservedLegacy() ? di->T("Yes") : di->T("No")));
699
}
700
#endif
701
702
LinearLayout *buildConfig = AddTab("DevSystemInfoBuildConfig", si->T("Build Config"));
703
704
buildConfig->Add(new ItemHeader(si->T("Build Configuration")));
705
#ifdef JENKINS
706
buildConfig->Add(new InfoItem(si->T("Built by"), "Jenkins"));
707
#endif
708
#ifdef ANDROID_LEGACY
709
buildConfig->Add(new InfoItem("ANDROID_LEGACY", ""));
710
#endif
711
#ifdef _DEBUG
712
buildConfig->Add(new InfoItem("_DEBUG", ""));
713
#else
714
buildConfig->Add(new InfoItem("NDEBUG", ""));
715
#endif
716
#ifdef USE_ASAN
717
buildConfig->Add(new InfoItem("USE_ASAN", ""));
718
#endif
719
#ifdef USING_GLES2
720
buildConfig->Add(new InfoItem("USING_GLES2", ""));
721
#endif
722
#ifdef MOBILE_DEVICE
723
buildConfig->Add(new InfoItem("MOBILE_DEVICE", ""));
724
#endif
725
#if PPSSPP_ARCH(ARMV7S)
726
buildConfig->Add(new InfoItem("ARMV7S", ""));
727
#endif
728
#if PPSSPP_ARCH(ARM_NEON)
729
buildConfig->Add(new InfoItem("ARM_NEON", ""));
730
#endif
731
#ifdef _M_SSE
732
buildConfig->Add(new InfoItem("_M_SSE", StringFromFormat("0x%x", _M_SSE)));
733
#endif
734
if (System_GetPropertyBool(SYSPROP_APP_GOLD)) {
735
buildConfig->Add(new InfoItem("GOLD", ""));
736
}
737
738
LinearLayout *cpuExtensions = AddTab("DevSystemInfoCPUExt", si->T("CPU Extensions"));
739
cpuExtensions->Add(new ItemHeader(si->T("CPU Extensions")));
740
std::vector<std::string> exts = cpu_info.Features();
741
for (std::string &ext : exts) {
742
cpuExtensions->Add(new TextView(ext, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
743
}
744
745
LinearLayout *driverBugs = AddTab("DevSystemInfoDriverBugs", si->T("Driver bugs"));
746
747
bool anyDriverBugs = false;
748
for (int i = 0; i < (int)draw->GetBugs().MaxBugIndex(); i++) {
749
if (draw->GetBugs().Has(i)) {
750
anyDriverBugs = true;
751
driverBugs->Add(new TextView(draw->GetBugs().GetBugName(i), new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
752
}
753
}
754
755
if (!anyDriverBugs) {
756
driverBugs->Add(new TextView(si->T("No GPU driver bugs detected"), new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
757
}
758
759
if (GetGPUBackend() == GPUBackend::OPENGL) {
760
LinearLayout *gpuExtensions = AddTab("DevSystemInfoOGLExt", si->T("OGL Extensions"));
761
762
if (!gl_extensions.IsGLES) {
763
gpuExtensions->Add(new ItemHeader(si->T("OpenGL Extensions")));
764
} else if (gl_extensions.GLES3) {
765
gpuExtensions->Add(new ItemHeader(si->T("OpenGL ES 3.0 Extensions")));
766
} else {
767
gpuExtensions->Add(new ItemHeader(si->T("OpenGL ES 2.0 Extensions")));
768
}
769
exts.clear();
770
SplitString(g_all_gl_extensions, ' ', exts);
771
std::sort(exts.begin(), exts.end());
772
for (auto &extension : exts) {
773
gpuExtensions->Add(new TextView(extension, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
774
}
775
776
exts.clear();
777
SplitString(g_all_egl_extensions, ' ', exts);
778
std::sort(exts.begin(), exts.end());
779
780
// If there aren't any EGL extensions, no need to show the tab.
781
if (exts.size() > 0) {
782
LinearLayout *eglExtensions = AddTab("EglExt", si->T("EGL Extensions"));
783
eglExtensions->SetSpacing(0);
784
eglExtensions->Add(new ItemHeader(si->T("EGL Extensions")));
785
for (auto &extension : exts) {
786
eglExtensions->Add(new TextView(extension, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
787
}
788
}
789
} else if (GetGPUBackend() == GPUBackend::VULKAN) {
790
LinearLayout *gpuExtensions = AddTab("DevSystemInfoOGLExt", si->T("Vulkan Features"));
791
792
CollapsibleSection *vulkanFeatures = gpuExtensions->Add(new CollapsibleSection(si->T("Vulkan Features")));
793
std::vector<std::string> features = draw->GetFeatureList();
794
for (auto &feature : features) {
795
vulkanFeatures->Add(new TextView(feature, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
796
}
797
798
CollapsibleSection *presentModes = gpuExtensions->Add(new CollapsibleSection(si->T("Present modes")));
799
for (auto mode : draw->GetPresentModeList(di->T("Current"))) {
800
presentModes->Add(new TextView(mode, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
801
}
802
803
CollapsibleSection *colorFormats = gpuExtensions->Add(new CollapsibleSection(si->T("Display Color Formats")));
804
for (auto &format : draw->GetSurfaceFormatList()) {
805
colorFormats->Add(new TextView(format, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
806
}
807
808
CollapsibleSection *enabledExtensions = gpuExtensions->Add(new CollapsibleSection(std::string(si->T("Vulkan Extensions")) + " (" + std::string(di->T("Enabled")) + ")"));
809
std::vector<std::string> extensions = draw->GetExtensionList(true, true);
810
std::sort(extensions.begin(), extensions.end());
811
for (auto &extension : extensions) {
812
enabledExtensions->Add(new TextView(extension, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
813
}
814
// Also get instance extensions
815
enabledExtensions->Add(new ItemHeader(si->T("Instance")));
816
extensions = draw->GetExtensionList(false, true);
817
std::sort(extensions.begin(), extensions.end());
818
for (auto &extension : extensions) {
819
enabledExtensions->Add(new TextView(extension, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
820
}
821
822
CollapsibleSection *vulkanExtensions = gpuExtensions->Add(new CollapsibleSection(si->T("Vulkan Extensions")));
823
extensions = draw->GetExtensionList(true, false);
824
std::sort(extensions.begin(), extensions.end());
825
for (auto &extension : extensions) {
826
vulkanExtensions->Add(new TextView(extension, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
827
}
828
829
vulkanExtensions->Add(new ItemHeader(si->T("Instance")));
830
// Also get instance extensions
831
extensions = draw->GetExtensionList(false, false);
832
std::sort(extensions.begin(), extensions.end());
833
for (auto &extension : extensions) {
834
vulkanExtensions->Add(new TextView(extension, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);
835
}
836
}
837
838
#ifdef _DEBUG
839
LinearLayout *internals = AddTab("DevSystemInfoInternals", si->T("Internals"));
840
CreateInternalsTab(internals);
841
#endif
842
}
843
844
void SystemInfoScreen::CreateInternalsTab(UI::ViewGroup *internals) {
845
using namespace UI;
846
847
auto di = GetI18NCategory(I18NCat::DIALOG);
848
auto si = GetI18NCategory(I18NCat::SYSINFO);
849
auto sy = GetI18NCategory(I18NCat::SYSTEM);
850
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);
851
852
internals->Add(new ItemHeader(si->T("Icon cache")));
853
IconCacheStats iconStats = g_iconCache.GetStats();
854
internals->Add(new InfoItem(si->T("Image data count"), StringFromFormat("%d", iconStats.cachedCount)));
855
internals->Add(new InfoItem(si->T("Texture count"), StringFromFormat("%d", iconStats.textureCount)));
856
internals->Add(new InfoItem(si->T("Data size"), NiceSizeFormat(iconStats.dataSize)));
857
internals->Add(new Choice(di->T("Clear")))->OnClick.Add([&](UI::EventParams &) {
858
g_iconCache.ClearData();
859
RecreateViews();
860
return UI::EVENT_DONE;
861
});
862
863
internals->Add(new ItemHeader(si->T("Notification tests")));
864
internals->Add(new Choice(si->T("Error")))->OnClick.Add([&](UI::EventParams &) {
865
std::string str = "Error " + CodepointToUTF8(0x1F41B) + CodepointToUTF8(0x1F41C) + CodepointToUTF8(0x1F914);
866
g_OSD.Show(OSDType::MESSAGE_ERROR, str);
867
return UI::EVENT_DONE;
868
});
869
internals->Add(new Choice(si->T("Warning")))->OnClick.Add([&](UI::EventParams &) {
870
g_OSD.Show(OSDType::MESSAGE_WARNING, "Warning", "Some\nAdditional\nDetail");
871
return UI::EVENT_DONE;
872
});
873
internals->Add(new Choice(si->T("Info")))->OnClick.Add([&](UI::EventParams &) {
874
g_OSD.Show(OSDType::MESSAGE_INFO, "Info");
875
return UI::EVENT_DONE;
876
});
877
// This one is clickable
878
internals->Add(new Choice(si->T("Success")))->OnClick.Add([&](UI::EventParams &) {
879
g_OSD.Show(OSDType::MESSAGE_SUCCESS, "Success", 0.0f, "clickable");
880
g_OSD.SetClickCallback("clickable", [](bool clicked, void *) {
881
if (clicked) {
882
System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://www.google.com/");
883
}
884
}, nullptr);
885
return UI::EVENT_DONE;
886
});
887
internals->Add(new Choice(sy->T("RetroAchievements")))->OnClick.Add([&](UI::EventParams &) {
888
g_OSD.Show(OSDType::MESSAGE_WARNING, "RetroAchievements warning", "", "I_RETROACHIEVEMENTS_LOGO");
889
return UI::EVENT_DONE;
890
});
891
internals->Add(new ItemHeader(si->T("Progress tests")));
892
internals->Add(new Choice(si->T("30%")))->OnClick.Add([&](UI::EventParams &) {
893
g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 30, 0.0f);
894
return UI::EVENT_DONE;
895
});
896
internals->Add(new Choice(si->T("100%")))->OnClick.Add([&](UI::EventParams &) {
897
g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 100, 1.0f);
898
return UI::EVENT_DONE;
899
});
900
internals->Add(new Choice(si->T("N/A%")))->OnClick.Add([&](UI::EventParams &) {
901
g_OSD.SetProgressBar("testprogress", "Test Progress", 0, 0, 0, 0.0f);
902
return UI::EVENT_DONE;
903
});
904
internals->Add(new Choice(si->T("Success")))->OnClick.Add([&](UI::EventParams &) {
905
g_OSD.RemoveProgressBar("testprogress", true, 0.5f);
906
return UI::EVENT_DONE;
907
});
908
internals->Add(new Choice(si->T("Failure")))->OnClick.Add([&](UI::EventParams &) {
909
g_OSD.RemoveProgressBar("testprogress", false, 0.5f);
910
return UI::EVENT_DONE;
911
});
912
internals->Add(new ItemHeader(si->T("Achievement tests")));
913
internals->Add(new Choice(si->T("Leaderboard tracker: Show")))->OnClick.Add([=](UI::EventParams &) {
914
g_OSD.ShowLeaderboardTracker(1, "My leaderboard tracker", true);
915
return UI::EVENT_DONE;
916
});
917
internals->Add(new Choice(si->T("Leaderboard tracker: Update")))->OnClick.Add([=](UI::EventParams &) {
918
g_OSD.ShowLeaderboardTracker(1, "Updated tracker", true);
919
return UI::EVENT_DONE;
920
});
921
internals->Add(new Choice(si->T("Leaderboard tracker: Hide")))->OnClick.Add([=](UI::EventParams &) {
922
g_OSD.ShowLeaderboardTracker(1, nullptr, false);
923
return UI::EVENT_DONE;
924
});
925
926
static const char *positions[] = { "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right", "None" };
927
928
internals->Add(new ItemHeader(ac->T("Notifications")));
929
internals->Add(new PopupMultiChoice(&g_Config.iAchievementsLeaderboardTrackerPos, ac->T("Leaderboard tracker"), positions, 0, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bAchievementsEnable);
930
931
#if PPSSPP_PLATFORM(ANDROID)
932
internals->Add(new Choice(si->T("Exception")))->OnClick.Add([&](UI::EventParams &) {
933
System_Notify(SystemNotification::TEST_JAVA_EXCEPTION);
934
return UI::EVENT_DONE;
935
});
936
#endif
937
}
938
939
int ShaderListScreen::ListShaders(DebugShaderType shaderType, UI::LinearLayout *view) {
940
using namespace UI;
941
std::vector<std::string> shaderIds_ = gpu->DebugGetShaderIDs(shaderType);
942
int count = 0;
943
for (const auto &id : shaderIds_) {
944
Choice *choice = view->Add(new Choice(gpu->DebugGetShaderString(id, shaderType, SHADER_STRING_SHORT_DESC)));
945
choice->SetTag(id);
946
choice->SetDrawTextFlags(FLAG_DYNAMIC_ASCII);
947
choice->OnClick.Handle(this, &ShaderListScreen::OnShaderClick);
948
count++;
949
}
950
return count;
951
}
952
953
struct { DebugShaderType type; const char *name; } shaderTypes[] = {
954
{ SHADER_TYPE_VERTEX, "Vertex" },
955
{ SHADER_TYPE_FRAGMENT, "Fragment" },
956
{ SHADER_TYPE_GEOMETRY, "Geometry" },
957
{ SHADER_TYPE_VERTEXLOADER, "VertexLoader" },
958
{ SHADER_TYPE_PIPELINE, "Pipeline" },
959
{ SHADER_TYPE_TEXTURE, "Texture" },
960
{ SHADER_TYPE_SAMPLER, "Sampler" },
961
};
962
963
void ShaderListScreen::CreateViews() {
964
using namespace UI;
965
966
auto di = GetI18NCategory(I18NCat::DIALOG);
967
968
LinearLayout *layout = new LinearLayout(ORIENT_VERTICAL);
969
root_ = layout;
970
971
tabs_ = new TabHolder(ORIENT_HORIZONTAL, 40, new LinearLayoutParams(1.0));
972
tabs_->SetTag("DevShaderList");
973
layout->Add(tabs_);
974
layout->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
975
for (size_t i = 0; i < ARRAY_SIZE(shaderTypes); i++) {
976
ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
977
LinearLayout *shaderList = new LinearLayoutList(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT));
978
int count = ListShaders(shaderTypes[i].type, shaderList);
979
scroll->Add(shaderList);
980
tabs_->AddTab(StringFromFormat("%s (%d)", shaderTypes[i].name, count), scroll);
981
}
982
}
983
984
UI::EventReturn ShaderListScreen::OnShaderClick(UI::EventParams &e) {
985
using namespace UI;
986
std::string id = e.v->Tag();
987
DebugShaderType type = shaderTypes[tabs_->GetCurrentTab()].type;
988
screenManager()->push(new ShaderViewScreen(id, type));
989
return EVENT_DONE;
990
}
991
992
void ShaderViewScreen::CreateViews() {
993
using namespace UI;
994
995
auto di = GetI18NCategory(I18NCat::DIALOG);
996
997
LinearLayout *layout = new LinearLayout(ORIENT_VERTICAL);
998
root_ = layout;
999
1000
layout->Add(new TextView(gpu->DebugGetShaderString(id_, type_, SHADER_STRING_SHORT_DESC), FLAG_DYNAMIC_ASCII | FLAG_WRAP_TEXT, false));
1001
1002
ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
1003
scroll->SetTag("DevShaderView");
1004
layout->Add(scroll);
1005
1006
LinearLayout *lineLayout = new LinearLayoutList(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
1007
lineLayout->SetSpacing(0.0);
1008
scroll->Add(lineLayout);
1009
1010
std::vector<std::string> lines;
1011
SplitString(gpu->DebugGetShaderString(id_, type_, SHADER_STRING_SOURCE_CODE), '\n', lines);
1012
1013
for (const auto &line : lines) {
1014
lineLayout->Add(new TextView(line, FLAG_DYNAMIC_ASCII | FLAG_WRAP_TEXT, true));
1015
}
1016
1017
layout->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
1018
}
1019
1020
bool ShaderViewScreen::key(const KeyInput &ki) {
1021
if (ki.flags & KEY_CHAR) {
1022
if (ki.unicodeChar == 'C' || ki.unicodeChar == 'c') {
1023
System_CopyStringToClipboard(gpu->DebugGetShaderString(id_, type_, SHADER_STRING_SHORT_DESC));
1024
}
1025
}
1026
return UIDialogScreenWithBackground::key(ki);
1027
}
1028
1029
1030
const std::string framedumpsBaseUrl = "http://framedump.ppsspp.org/repro/";
1031
1032
FrameDumpTestScreen::FrameDumpTestScreen() {
1033
1034
}
1035
1036
FrameDumpTestScreen::~FrameDumpTestScreen() {
1037
g_DownloadManager.CancelAll();
1038
}
1039
1040
void FrameDumpTestScreen::CreateViews() {
1041
using namespace UI;
1042
1043
root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
1044
auto di = GetI18NCategory(I18NCat::DIALOG);
1045
1046
TabHolder *tabHolder;
1047
tabHolder = new TabHolder(ORIENT_VERTICAL, 200, new AnchorLayoutParams(10, 0, 10, 0, false));
1048
root_->Add(tabHolder);
1049
AddStandardBack(root_);
1050
tabHolder->SetTag("DumpTypes");
1051
root_->SetDefaultFocusView(tabHolder);
1052
1053
ViewGroup *dumpsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
1054
dumpsScroll->SetTag("GameSettingsGraphics");
1055
LinearLayout *dumps = new LinearLayoutList(ORIENT_VERTICAL);
1056
dumps->SetSpacing(0);
1057
dumpsScroll->Add(dumps);
1058
tabHolder->AddTab("Dumps", dumpsScroll);
1059
1060
dumps->Add(new ItemHeader("GE Frame Dumps"));
1061
1062
for (auto &file : files_) {
1063
std::string url = framedumpsBaseUrl + file;
1064
Choice *c = dumps->Add(new Choice(file));
1065
c->SetTag(url);
1066
c->OnClick.Handle<FrameDumpTestScreen>(this, &FrameDumpTestScreen::OnLoadDump);
1067
}
1068
}
1069
1070
UI::EventReturn FrameDumpTestScreen::OnLoadDump(UI::EventParams &params) {
1071
std::string url = params.v->Tag();
1072
INFO_LOG(Log::Common, "Trying to launch '%s'", url.c_str());
1073
// Our disc streaming functionality detects the URL and takes over and handles loading framedumps well,
1074
// except for some reason the game ID.
1075
// TODO: Fix that since it can be important for compat settings.
1076
LaunchFile(screenManager(), Path(url));
1077
return UI::EVENT_DONE;
1078
}
1079
1080
void FrameDumpTestScreen::update() {
1081
UIScreen::update();
1082
1083
if (!listing_) {
1084
const char *acceptMime = "text/html, */*; q=0.8";
1085
listing_ = g_DownloadManager.StartDownload(framedumpsBaseUrl, Path(), http::ProgressBarMode::DELAYED, acceptMime);
1086
}
1087
1088
if (listing_ && listing_->Done() && files_.empty()) {
1089
if (listing_->ResultCode() == 200) {
1090
std::string listingHtml;
1091
listing_->buffer().TakeAll(&listingHtml);
1092
1093
std::vector<std::string> lines;
1094
// We rely slightly on nginx listing format here. Not great.
1095
SplitString(listingHtml, '\n', lines);
1096
for (auto &line : lines) {
1097
std::string trimmed = StripSpaces(line);
1098
if (startsWith(trimmed, "<a href=\"")) {
1099
trimmed = trimmed.substr(strlen("<a href=\""));
1100
size_t offset = trimmed.find('\"');
1101
if (offset != std::string::npos) {
1102
trimmed = trimmed.substr(0, offset);
1103
if (endsWith(trimmed, ".ppdmp")) {
1104
INFO_LOG(Log::Common, "Found ppdmp: '%s'", trimmed.c_str());
1105
files_.push_back(trimmed);
1106
}
1107
}
1108
}
1109
}
1110
} else {
1111
// something went bad. Too lazy to make UI, so let's just finish this screen.
1112
TriggerFinish(DialogResult::DR_CANCEL);
1113
}
1114
RecreateViews();
1115
}
1116
}
1117
1118
void TouchTestScreen::touch(const TouchInput &touch) {
1119
UIDialogScreenWithGameBackground::touch(touch);
1120
if (touch.flags & TOUCH_DOWN) {
1121
bool found = false;
1122
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
1123
if (touches_[i].id == touch.id) {
1124
WARN_LOG(Log::System, "Double touch");
1125
touches_[i].x = touch.x;
1126
touches_[i].y = touch.y;
1127
found = true;
1128
}
1129
}
1130
if (!found) {
1131
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
1132
if (touches_[i].id == -1) {
1133
touches_[i].id = touch.id;
1134
touches_[i].x = touch.x;
1135
touches_[i].y = touch.y;
1136
break;
1137
}
1138
}
1139
}
1140
}
1141
if (touch.flags & TOUCH_MOVE) {
1142
bool found = false;
1143
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
1144
if (touches_[i].id == touch.id) {
1145
touches_[i].x = touch.x;
1146
touches_[i].y = touch.y;
1147
found = true;
1148
}
1149
}
1150
if (!found) {
1151
WARN_LOG(Log::System, "Move without touch down: %d", touch.id);
1152
}
1153
}
1154
if (touch.flags & TOUCH_UP) {
1155
bool found = false;
1156
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
1157
if (touches_[i].id == touch.id) {
1158
found = true;
1159
touches_[i].id = -1;
1160
break;
1161
}
1162
}
1163
if (!found) {
1164
WARN_LOG(Log::System, "Touch release without touch down");
1165
}
1166
}
1167
}
1168
1169
// TODO: Move this screen out into its own file.
1170
void TouchTestScreen::CreateViews() {
1171
using namespace UI;
1172
1173
auto di = GetI18NCategory(I18NCat::DIALOG);
1174
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
1175
root_ = new LinearLayout(ORIENT_VERTICAL);
1176
LinearLayout *theTwo = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f));
1177
1178
// TODO: This one should use DYNAMIC_ASCII. Though doesn't matter much.
1179
lastKeyEvents_ = theTwo->Add(new TextView("-", new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
1180
1181
root_->Add(theTwo);
1182
1183
#if !PPSSPP_PLATFORM(UWP)
1184
static const char *renderingBackend[] = { "OpenGL", "Direct3D 9", "Direct3D 11", "Vulkan" };
1185
PopupMultiChoice *renderingBackendChoice = root_->Add(new PopupMultiChoice(&g_Config.iGPUBackend, gr->T("Backend"), renderingBackend, (int)GPUBackend::OPENGL, ARRAY_SIZE(renderingBackend), I18NCat::GRAPHICS, screenManager()));
1186
renderingBackendChoice->OnChoice.Handle(this, &TouchTestScreen::OnRenderingBackend);
1187
1188
if (!g_Config.IsBackendEnabled(GPUBackend::OPENGL))
1189
renderingBackendChoice->HideChoice((int)GPUBackend::OPENGL);
1190
if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D9))
1191
renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D9);
1192
if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D11))
1193
renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D11);
1194
if (!g_Config.IsBackendEnabled(GPUBackend::VULKAN))
1195
renderingBackendChoice->HideChoice((int)GPUBackend::VULKAN);
1196
#endif
1197
1198
#if PPSSPP_PLATFORM(ANDROID)
1199
root_->Add(new Choice(gr->T("Recreate Activity")))->OnClick.Handle(this, &TouchTestScreen::OnRecreateActivity);
1200
#endif
1201
root_->Add(new CheckBox(&g_Config.bImmersiveMode, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &TouchTestScreen::OnImmersiveModeChange);
1202
root_->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
1203
}
1204
1205
void TouchTestScreen::UpdateLogView() {
1206
while (keyEventLog_.size() > 8) {
1207
keyEventLog_.erase(keyEventLog_.begin());
1208
}
1209
1210
std::string text;
1211
for (auto &iter : keyEventLog_) {
1212
text += iter + "\n";
1213
}
1214
1215
if (lastKeyEvents_) {
1216
lastKeyEvents_->SetText(text);
1217
}
1218
}
1219
1220
bool TouchTestScreen::key(const KeyInput &key) {
1221
UIScreen::key(key);
1222
char buf[512];
1223
snprintf(buf, sizeof(buf), "%s (%d) Device ID: %d [%s%s%s%s]", KeyMap::GetKeyName(key.keyCode).c_str(), key.keyCode, key.deviceId,
1224
(key.flags & KEY_IS_REPEAT) ? "REP" : "",
1225
(key.flags & KEY_UP) ? "UP" : "",
1226
(key.flags & KEY_DOWN) ? "DOWN" : "",
1227
(key.flags & KEY_CHAR) ? "CHAR" : "");
1228
keyEventLog_.push_back(buf);
1229
UpdateLogView();
1230
return true;
1231
}
1232
1233
void TouchTestScreen::axis(const AxisInput &axis) {
1234
char buf[512];
1235
snprintf(buf, sizeof(buf), "Axis: %s (%d) (value %1.3f) Device ID: %d",
1236
KeyMap::GetAxisName(axis.axisId).c_str(), axis.axisId, axis.value, axis.deviceId);
1237
1238
keyEventLog_.push_back(buf);
1239
if (keyEventLog_.size() > 8) {
1240
keyEventLog_.erase(keyEventLog_.begin());
1241
}
1242
UpdateLogView();
1243
}
1244
1245
void TouchTestScreen::DrawForeground(UIContext &dc) {
1246
Bounds bounds = dc.GetLayoutBounds();
1247
1248
double now = dc.FrameStartTime();
1249
double delta = now - lastFrameTime_;
1250
lastFrameTime_ = now;
1251
1252
dc.BeginNoTex();
1253
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
1254
if (touches_[i].id != -1) {
1255
dc.Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0);
1256
}
1257
}
1258
dc.Flush();
1259
1260
dc.Begin();
1261
1262
char buffer[4096];
1263
for (int i = 0; i < MAX_TOUCH_POINTS; i++) {
1264
if (touches_[i].id != -1) {
1265
dc.Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0);
1266
snprintf(buffer, sizeof(buffer), "%0.1fx%0.1f", touches_[i].x, touches_[i].y);
1267
dc.DrawText(buffer, touches_[i].x, touches_[i].y + (touches_[i].y > g_display.dp_yres - 100.0f ? -135.0f : 95.0f), 0xFFFFFFFF, ALIGN_HCENTER | FLAG_DYNAMIC_ASCII);
1268
}
1269
}
1270
1271
char extra_debug[2048]{};
1272
1273
#if PPSSPP_PLATFORM(ANDROID)
1274
truncate_cpy(extra_debug, Android_GetInputDeviceDebugString().c_str());
1275
#endif
1276
1277
snprintf(buffer, sizeof(buffer),
1278
#if PPSSPP_PLATFORM(ANDROID)
1279
"display_res: %dx%d\n"
1280
#endif
1281
"dp_res: %dx%d pixel_res: %dx%d\n"
1282
"g_dpi: %0.3f g_dpi_scale: %0.3fx%0.3f\n"
1283
"g_dpi_scale_real: %0.3fx%0.3f\n"
1284
"delta: %0.2f ms fps: %0.3f\n%s",
1285
#if PPSSPP_PLATFORM(ANDROID)
1286
(int)System_GetPropertyInt(SYSPROP_DISPLAY_XRES), (int)System_GetPropertyInt(SYSPROP_DISPLAY_YRES),
1287
#endif
1288
g_display.dp_xres, g_display.dp_yres, g_display.pixel_xres, g_display.pixel_yres,
1289
g_display.dpi, g_display.dpi_scale_x, g_display.dpi_scale_y,
1290
g_display.dpi_scale_real_x, g_display.dpi_scale_real_y,
1291
delta * 1000.0, 1.0 / delta,
1292
extra_debug);
1293
1294
// On Android, also add joystick debug data.
1295
dc.DrawTextShadow(buffer, bounds.centerX(), bounds.y + 20.0f, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);
1296
dc.Flush();
1297
}
1298
1299
void RecreateActivity() {
1300
const int SYSTEM_JELLYBEAN = 16;
1301
if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= SYSTEM_JELLYBEAN) {
1302
INFO_LOG(Log::System, "Sending recreate");
1303
System_Notify(SystemNotification::FORCE_RECREATE_ACTIVITY);
1304
INFO_LOG(Log::System, "Got back from recreate");
1305
} else {
1306
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
1307
System_Toast(gr->T_cstr("Must Restart", "You must restart PPSSPP for this change to take effect"));
1308
}
1309
}
1310
1311
UI::EventReturn TouchTestScreen::OnImmersiveModeChange(UI::EventParams &e) {
1312
System_Notify(SystemNotification::IMMERSIVE_MODE_CHANGE);
1313
if (g_Config.iAndroidHwScale != 0) {
1314
RecreateActivity();
1315
}
1316
return UI::EVENT_DONE;
1317
}
1318
1319
UI::EventReturn TouchTestScreen::OnRenderingBackend(UI::EventParams &e) {
1320
g_Config.Save("GameSettingsScreen::RenderingBackend");
1321
System_RestartApp("--touchscreentest");
1322
return UI::EVENT_DONE;
1323
}
1324
1325
UI::EventReturn TouchTestScreen::OnRecreateActivity(UI::EventParams &e) {
1326
RecreateActivity();
1327
return UI::EVENT_DONE;
1328
}
1329
1330