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