Merge pull request #90 from gizmo98/patch-2
[libretro-ppsspp.git] / UI / DevScreens.cpp
blob61bfce261867ef40e79b7e9189fad41e744cf47f
1 // Copyright (c) 2013- PPSSPP Project.
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.
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.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official git repository and contact information can be found at
16 // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
18 #include <algorithm>
20 #include "base/compat.h"
21 #include "gfx_es2/gl_state.h"
22 #include "gfx_es2/gpu_features.h"
23 #include "i18n/i18n.h"
24 #include "ui/ui_context.h"
25 #include "ui/view.h"
26 #include "ui/viewgroup.h"
27 #include "ui/ui.h"
28 #include "profiler/profiler.h"
30 #include "Common/LogManager.h"
31 #include "Common/CPUDetect.h"
33 #include "Core/MemMap.h"
34 #include "Core/Config.h"
35 #include "Core/System.h"
36 #include "Core/CoreParameter.h"
37 #include "Core/MIPS/MIPSTables.h"
38 #include "Core/MIPS/JitCommon/NativeJit.h"
39 #include "Core/MIPS/JitCommon/JitCommon.h"
40 #include "GPU/GPUInterface.h"
41 #include "GPU/GPUState.h"
42 #include "UI/MiscScreens.h"
43 #include "UI/DevScreens.h"
44 #include "UI/GameSettingsScreen.h"
46 #ifdef _WIN32
47 // Want to avoid including the full header here as it includes d3dx.h
48 int GetD3DXVersion();
49 #endif
51 static const char *logLevelList[] = {
52 "Notice",
53 "Error",
54 "Warn",
55 "Info",
56 "Debug",
57 "Verb."
60 void DevMenu::CreatePopupContents(UI::ViewGroup *parent) {
61 using namespace UI;
62 I18NCategory *dev = GetI18NCategory("Developer");
63 I18NCategory *sy = GetI18NCategory("System");
65 #if !defined(MOBILE_DEVICE)
66 parent->Add(new Choice(dev->T("Log View")))->OnClick.Handle(this, &DevMenu::OnLogView);
67 #endif
68 parent->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DevMenu::OnLogConfig);
69 parent->Add(new Choice(sy->T("Developer Tools")))->OnClick.Handle(this, &DevMenu::OnDeveloperTools);
70 parent->Add(new Choice(dev->T("Jit Compare")))->OnClick.Handle(this, &DevMenu::OnJitCompare);
71 parent->Add(new Choice(dev->T("Toggle Freeze")))->OnClick.Handle(this, &DevMenu::OnFreezeFrame);
72 parent->Add(new Choice(dev->T("Dump Frame GPU Commands")))->OnClick.Handle(this, &DevMenu::OnDumpFrame);
73 parent->Add(new Choice(dev->T("Toggle Audio Debug")))->OnClick.Handle(this, &DevMenu::OnToggleAudioDebug);
74 #ifdef USE_PROFILER
75 parent->Add(new CheckBox(&g_Config.bShowFrameProfiler, dev->T("Frame Profiler"), ""));
76 #endif
78 RingbufferLogListener *ring = LogManager::GetInstance()->GetRingbufferListener();
79 if (ring) {
80 ring->SetEnable(true);
84 UI::EventReturn DevMenu::OnToggleAudioDebug(UI::EventParams &e) {
85 g_Config.bShowAudioDebug = !g_Config.bShowAudioDebug;
86 return UI::EVENT_DONE;
90 UI::EventReturn DevMenu::OnLogView(UI::EventParams &e) {
91 UpdateUIState(UISTATE_PAUSEMENU);
92 screenManager()->push(new LogScreen());
93 return UI::EVENT_DONE;
96 UI::EventReturn DevMenu::OnLogConfig(UI::EventParams &e) {
97 UpdateUIState(UISTATE_PAUSEMENU);
98 screenManager()->push(new LogConfigScreen());
99 return UI::EVENT_DONE;
102 UI::EventReturn DevMenu::OnDeveloperTools(UI::EventParams &e) {
103 UpdateUIState(UISTATE_PAUSEMENU);
104 screenManager()->push(new DeveloperToolsScreen());
105 return UI::EVENT_DONE;
108 UI::EventReturn DevMenu::OnJitCompare(UI::EventParams &e) {
109 UpdateUIState(UISTATE_PAUSEMENU);
110 screenManager()->push(new JitCompareScreen());
111 return UI::EVENT_DONE;
114 UI::EventReturn DevMenu::OnFreezeFrame(UI::EventParams &e) {
115 if (PSP_CoreParameter().frozen) {
116 PSP_CoreParameter().frozen = false;
117 } else {
118 PSP_CoreParameter().freezeNext = true;
120 return UI::EVENT_DONE;
123 UI::EventReturn DevMenu::OnDumpFrame(UI::EventParams &e) {
124 gpu->DumpNextFrame();
125 return UI::EVENT_DONE;
128 void DevMenu::dialogFinished(const Screen *dialog, DialogResult result) {
129 UpdateUIState(UISTATE_INGAME);
130 // Close when a subscreen got closed.
131 // TODO: a bug in screenmanager causes this not to work here.
132 // screenManager()->finishDialog(this, DR_OK);
135 void LogScreen::UpdateLog() {
136 using namespace UI;
137 RingbufferLogListener *ring = LogManager::GetInstance()->GetRingbufferListener();
138 if (!ring)
139 return;
140 vert_->Clear();
141 for (int i = ring->GetCount() - 1; i >= 0; i--) {
142 TextView *v = vert_->Add(new TextView(ring->TextAt(i), FLAG_DYNAMIC_ASCII, false));
143 uint32_t color = 0xFFFFFF;
144 switch (ring->LevelAt(i)) {
145 case LogTypes::LDEBUG: color = 0xE0E0E0; break;
146 case LogTypes::LWARNING: color = 0x50FFFF; break;
147 case LogTypes::LERROR: color = 0x5050FF; break;
148 case LogTypes::LNOTICE: color = 0x30FF30; break;
149 case LogTypes::LINFO: color = 0xFFFFFF; break;
150 case LogTypes::LVERBOSE: color = 0xC0C0C0; break;
152 v->SetTextColor(0xFF000000 | color);
154 toBottom_ = true;
157 void LogScreen::update(InputState &input) {
158 UIDialogScreenWithBackground::update(input);
159 if (toBottom_) {
160 toBottom_ = false;
161 scroll_->ScrollToBottom();
165 void LogScreen::CreateViews() {
166 using namespace UI;
167 I18NCategory *di = GetI18NCategory("Dialog");
169 LinearLayout *outer = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
170 root_ = outer;
172 scroll_ = outer->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0)));
173 LinearLayout *bottom = outer->Add(new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
174 bottom->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
175 cmdLine_ = bottom->Add(new TextEdit("", "Command Line", new LinearLayoutParams(1.0)));
176 cmdLine_->OnEnter.Handle(this, &LogScreen::OnSubmit);
177 bottom->Add(new Button(di->T("Submit")))->OnClick.Handle(this, &LogScreen::OnSubmit);
179 vert_ = scroll_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
180 vert_->SetSpacing(0);
182 UpdateLog();
185 UI::EventReturn LogScreen::OnSubmit(UI::EventParams &e) {
186 std::string cmd = cmdLine_->GetText();
188 // TODO: Can add all sorts of fun stuff here that we can't be bothered writing proper UI for, like various memdumps etc.
190 NOTICE_LOG(HLE, "Submitted: %s", cmd.c_str());
192 UpdateLog();
193 cmdLine_->SetText("");
194 cmdLine_->SetFocus();
195 return UI::EVENT_DONE;
198 void LogConfigScreen::CreateViews() {
199 using namespace UI;
201 I18NCategory *di = GetI18NCategory("Dialog");
202 I18NCategory *dev = GetI18NCategory("Developer");
204 root_ = new ScrollView(ORIENT_VERTICAL);
206 LinearLayout *vert = root_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
207 vert->SetSpacing(0);
209 LinearLayout *topbar = new LinearLayout(ORIENT_HORIZONTAL);
210 topbar->Add(new Choice(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
211 topbar->Add(new Choice(di->T("Toggle All")))->OnClick.Handle(this, &LogConfigScreen::OnToggleAll);
212 topbar->Add(new Choice(dev->T("Log Level")))->OnClick.Handle(this, &LogConfigScreen::OnLogLevel);
214 vert->Add(topbar);
216 vert->Add(new ItemHeader(dev->T("Logging Channels")));
218 LogManager *logMan = LogManager::GetInstance();
220 int cellSize = 400;
222 UI::GridLayoutSettings gridsettings(cellSize, 64, 5);
223 gridsettings.fillCells = true;
224 GridLayout *grid = vert->Add(new GridLayout(gridsettings, new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
226 for (int i = 0; i < LogManager::GetNumChannels(); i++) {
227 LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i;
228 LogChannel *chan = logMan->GetLogChannel(type);
229 LinearLayout *row = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(cellSize - 50, WRAP_CONTENT));
230 row->SetSpacing(0);
231 row->Add(new CheckBox(&chan->enable_, "", "", new LinearLayoutParams(50, WRAP_CONTENT)));
232 row->Add(new PopupMultiChoice(&chan->level_, chan->GetFullName(), logLevelList, 1, 6, 0, screenManager(), new LinearLayoutParams(1.0)));
233 grid->Add(row);
237 UI::EventReturn LogConfigScreen::OnToggleAll(UI::EventParams &e) {
238 LogManager *logMan = LogManager::GetInstance();
240 for (int i = 0; i < LogManager::GetNumChannels(); i++) {
241 LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i;
242 LogChannel *chan = logMan->GetLogChannel(type);
243 chan->enable_ = !chan->enable_;
246 return UI::EVENT_DONE;
249 UI::EventReturn LogConfigScreen::OnLogLevelChange(UI::EventParams &e) {
250 RecreateViews();
251 return UI::EVENT_DONE;
254 UI::EventReturn LogConfigScreen::OnLogLevel(UI::EventParams &e) {
255 I18NCategory *dev = GetI18NCategory("Developer");
257 auto logLevelScreen = new LogLevelScreen(dev->T("Log Level"));
258 logLevelScreen->OnChoice.Handle(this, &LogConfigScreen::OnLogLevelChange);
259 screenManager()->push(logLevelScreen);
260 return UI::EVENT_DONE;
263 LogLevelScreen::LogLevelScreen(const std::string &title) : ListPopupScreen(title) {
264 int NUMLOGLEVEL = 6;
265 std::vector<std::string> list;
266 for(int i = 0; i < NUMLOGLEVEL; ++i) {
267 list.push_back(logLevelList[i]);
269 adaptor_ = UI::StringVectorListAdaptor(list, -1);
272 void LogLevelScreen::OnCompleted(DialogResult result) {
273 if (result != DR_OK)
274 return;
275 int selected = listView_->GetSelected();
276 LogManager *logMan = LogManager::GetInstance();
278 for (int i = 0; i < LogManager::GetNumChannels(); ++i) {
279 LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i;
280 LogChannel *chan = logMan->GetLogChannel(type);
281 if(chan->enable_ )
282 chan->level_ = selected + 1;
286 const char *GetCompilerABI() {
287 #ifdef HAVE_ARMV7
288 return "armeabi-v7a";
289 #elif defined(ARM)
290 return "armeabi";
291 #elif defined(ARM64)
292 return "arm64";
293 #elif defined(_M_IX86)
294 return "x86";
295 #elif defined(_M_X64)
296 return "x86-64";
297 #else
298 return "other";
299 #endif
302 void SystemInfoScreen::CreateViews() {
303 // NOTE: Do not translate this section. It will change a lot and will be impossible to keep up.
304 I18NCategory *di = GetI18NCategory("Dialog");
306 using namespace UI;
307 root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
309 ViewGroup *leftColumn = new AnchorLayout(new LinearLayoutParams(1.0f));
310 root_->Add(leftColumn);
312 AddStandardBack(root_);
314 TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, 225, new AnchorLayoutParams(10, 0, 10, 0, false));
316 root_->Add(tabHolder);
317 ViewGroup *deviceSpecsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
318 LinearLayout *deviceSpecs = new LinearLayout(ORIENT_VERTICAL);
319 deviceSpecs->SetSpacing(0);
320 deviceSpecsScroll->Add(deviceSpecs);
321 tabHolder->AddTab("Device Info", deviceSpecsScroll);
323 deviceSpecs->Add(new ItemHeader("System Information"));
324 deviceSpecs->Add(new InfoItem("Name", System_GetProperty(SYSPROP_NAME)));
325 deviceSpecs->Add(new InfoItem("Lang/Region", System_GetProperty(SYSPROP_LANGREGION)));
326 deviceSpecs->Add(new InfoItem("ABI", GetCompilerABI()));
327 deviceSpecs->Add(new ItemHeader("CPU Information"));
328 deviceSpecs->Add(new InfoItem("Name", cpu_info.brand_string));
329 #if defined(ARM) || defined(ARM64) || defined(MIPS)
330 deviceSpecs->Add(new InfoItem("Cores", StringFromInt(cpu_info.num_cores)));
331 #else
332 int totalThreads = cpu_info.num_cores * cpu_info.logical_cpu_count;
333 std::string cores = StringFromFormat("%d (%d per core, %d cores)", totalThreads, cpu_info.logical_cpu_count, cpu_info.num_cores);
334 deviceSpecs->Add(new InfoItem("Threads", cores));
335 #endif
336 deviceSpecs->Add(new ItemHeader("GPU Information"));
338 Thin3DContext *thin3d = screenManager()->getThin3DContext();
340 deviceSpecs->Add(new InfoItem("3D API", thin3d->GetInfoString(T3DInfo::APINAME)));
341 deviceSpecs->Add(new InfoItem("Vendor", std::string(thin3d->GetInfoString(T3DInfo::VENDORSTRING)) + " (" + thin3d->GetInfoString(T3DInfo::VENDOR) + ")"));
342 deviceSpecs->Add(new InfoItem("Model", thin3d->GetInfoString(T3DInfo::RENDERER)));
343 #ifdef _WIN32
344 deviceSpecs->Add(new InfoItem("Driver Version", System_GetProperty(SYSPROP_GPUDRIVER_VERSION)));
345 if (g_Config.iGPUBackend == GPU_BACKEND_DIRECT3D9) {
346 deviceSpecs->Add(new InfoItem("D3DX Version", StringFromFormat("%d", GetD3DXVersion())));
348 #endif
350 #ifdef ANDROID
351 deviceSpecs->Add(new ItemHeader("Audio Information"));
352 deviceSpecs->Add(new InfoItem("Sample rate", StringFromFormat("%d Hz", System_GetPropertyInt(SYSPROP_AUDIO_SAMPLE_RATE))));
353 deviceSpecs->Add(new InfoItem("Frames per buffer", StringFromFormat("%d", System_GetPropertyInt(SYSPROP_AUDIO_FRAMES_PER_BUFFER))));
354 deviceSpecs->Add(new InfoItem("Optimal sample rate", StringFromFormat("%d Hz", System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_SAMPLE_RATE))));
355 deviceSpecs->Add(new InfoItem("Optimal frames per buffer", StringFromFormat("%d", System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_FRAMES_PER_BUFFER))));
357 deviceSpecs->Add(new ItemHeader("Display Information"));
358 deviceSpecs->Add(new InfoItem("Native Resolution", StringFromFormat("%dx%d",
359 System_GetPropertyInt(SYSPROP_DISPLAY_XRES),
360 System_GetPropertyInt(SYSPROP_DISPLAY_YRES))));
361 deviceSpecs->Add(new InfoItem("Refresh rate", StringFromFormat("%0.3f Hz", (float)System_GetPropertyInt(SYSPROP_DISPLAY_REFRESH_RATE) / 1000.0f)));
362 #endif
365 deviceSpecs->Add(new ItemHeader("Version Information"));
366 std::string apiVersion;
367 if (g_Config.iGPUBackend == GPU_BACKEND_OPENGL) {
368 #ifdef USING_GLES2
369 apiVersion = StringFromFormat("v%d.%d.%d ES", gl_extensions.ver[0], gl_extensions.ver[1], gl_extensions.ver[2]);
370 #else
371 apiVersion = StringFromFormat("v%d.%d.%d", gl_extensions.ver[0], gl_extensions.ver[1], gl_extensions.ver[2]);
372 #endif
373 } else {
374 apiVersion = thin3d->GetInfoString(T3DInfo::APIVERSION);
375 if (apiVersion.size() > 30)
376 apiVersion.resize(30);
378 deviceSpecs->Add(new InfoItem("API Version", apiVersion));
379 deviceSpecs->Add(new InfoItem("Shading Language", thin3d->GetInfoString(T3DInfo::SHADELANGVERSION)));
381 #ifdef ANDROID
382 std::string moga = System_GetProperty(SYSPROP_MOGA_VERSION);
383 if (moga.empty()) {
384 moga = "(none detected)";
386 deviceSpecs->Add(new InfoItem("Moga", moga));
387 #endif
389 #ifdef ANDROID
390 char temp[256];
391 sprintf(temp, "%dx%d", System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES));
392 deviceSpecs->Add(new InfoItem("Display resolution", temp));
393 #endif
395 ViewGroup *cpuExtensionsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
396 LinearLayout *cpuExtensions = new LinearLayout(ORIENT_VERTICAL);
397 cpuExtensions->SetSpacing(0);
398 cpuExtensionsScroll->Add(cpuExtensions);
400 tabHolder->AddTab("CPU Extensions", cpuExtensionsScroll);
402 cpuExtensions->Add(new ItemHeader("CPU Extensions"));
403 std::vector<std::string> exts;
404 SplitString(cpu_info.Summarize(), ',', exts);
405 for (size_t i = 2; i < exts.size(); i++) {
406 cpuExtensions->Add(new TextView(exts[i]))->SetFocusable(true);
409 ViewGroup *oglExtensionsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
410 LinearLayout *oglExtensions = new LinearLayout(ORIENT_VERTICAL);
411 oglExtensions->SetSpacing(0);
412 oglExtensionsScroll->Add(oglExtensions);
414 tabHolder->AddTab("OGL Extensions", oglExtensionsScroll);
416 #ifndef USING_GLES2
417 oglExtensions->Add(new ItemHeader("OpenGL Extensions"));
418 #else
419 if (gl_extensions.GLES3)
420 oglExtensions->Add(new ItemHeader("OpenGL ES 3.0 Extensions"));
421 else
422 oglExtensions->Add(new ItemHeader("OpenGL ES 2.0 Extensions"));
423 #endif
425 exts.clear();
426 SplitString(g_all_gl_extensions, ' ', exts);
427 std::sort(exts.begin(), exts.end());
428 for (size_t i = 0; i < exts.size(); i++) {
429 oglExtensions->Add(new TextView(exts[i]))->SetFocusable(true);
432 exts.clear();
433 SplitString(g_all_egl_extensions, ' ', exts);
434 std::sort(exts.begin(), exts.end());
436 // If there aren't any EGL extensions, no need to show the tab.
437 if (exts.size() > 0) {
438 ViewGroup *eglExtensionsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
439 LinearLayout *eglExtensions = new LinearLayout(ORIENT_VERTICAL);
440 eglExtensions->SetSpacing(0);
441 eglExtensionsScroll->Add(eglExtensions);
443 tabHolder->AddTab("EGL Extensions", eglExtensionsScroll);
445 eglExtensions->Add(new ItemHeader("EGL Extensions"));
447 for (size_t i = 0; i < exts.size(); i++) {
448 eglExtensions->Add(new TextView(exts[i]))->SetFocusable(true);
453 void AddressPromptScreen::CreatePopupContents(UI::ViewGroup *parent) {
454 using namespace UI;
456 I18NCategory *dev = GetI18NCategory("Developer");
458 addrView_ = new TextView(dev->T("Enter address"), ALIGN_HCENTER, false);
459 parent->Add(addrView_);
461 ViewGroup *grid = new GridLayout(GridLayoutSettings(60, 40));
462 parent->Add(grid);
464 for (int i = 0; i < 16; ++i) {
465 char temp[16];
466 snprintf(temp, 16, " %X ", i);
467 buttons_[i] = new Button(temp);
468 grid->Add(buttons_[i])->OnClick.Handle(this, &AddressPromptScreen::OnDigitButton);
471 parent->Add(new Button(dev->T("Backspace")))->OnClick.Handle(this, &AddressPromptScreen::OnBackspace);
474 void AddressPromptScreen::OnCompleted(DialogResult result) {
475 if (result == DR_OK) {
476 UI::EventParams e;
477 e.v = root_;
478 e.a = addr_;
479 OnChoice.Trigger(e);
483 UI::EventReturn AddressPromptScreen::OnDigitButton(UI::EventParams &e) {
484 for (int i = 0; i < 16; ++i) {
485 if (buttons_[i] == e.v) {
486 AddDigit(i);
489 return UI::EVENT_DONE;
492 UI::EventReturn AddressPromptScreen::OnBackspace(UI::EventParams &e) {
493 BackspaceDigit();
494 return UI::EVENT_DONE;
497 void AddressPromptScreen::AddDigit(int n) {
498 if ((addr_ & 0xF0000000) == 0) {
499 addr_ = addr_ * 16 + n;
501 UpdatePreviewDigits();
504 void AddressPromptScreen::BackspaceDigit() {
505 addr_ /= 16;
506 UpdatePreviewDigits();
509 void AddressPromptScreen::UpdatePreviewDigits() {
510 I18NCategory *dev = GetI18NCategory("Developer");
512 if (addr_ != 0) {
513 char temp[32];
514 snprintf(temp, 32, "%8X", addr_);
515 addrView_->SetText(temp);
516 } else {
517 addrView_->SetText(dev->T("Enter address"));
521 bool AddressPromptScreen::key(const KeyInput &key) {
522 if (key.flags & KEY_DOWN) {
523 if (key.keyCode >= NKCODE_0 && key.keyCode <= NKCODE_9) {
524 AddDigit(key.keyCode - NKCODE_0);
525 } else if (key.keyCode >= NKCODE_A && key.keyCode <= NKCODE_F) {
526 AddDigit(10 + key.keyCode - NKCODE_A);
527 // NKCODE_DEL is backspace.
528 } else if (key.keyCode == NKCODE_DEL) {
529 BackspaceDigit();
530 } else if (key.keyCode == NKCODE_ENTER) {
531 OnCompleted(DR_OK);
532 screenManager()->finishDialog(this, DR_OK);
533 } else {
534 return UIDialogScreen::key(key);
536 } else {
537 return UIDialogScreen::key(key);
539 return true;
542 // Three panes: Block chooser, MIPS view, ARM/x86 view
543 void JitCompareScreen::CreateViews() {
544 I18NCategory *di = GetI18NCategory("Dialog");
545 I18NCategory *dev = GetI18NCategory("Developer");
547 using namespace UI;
549 root_ = new LinearLayout(ORIENT_HORIZONTAL);
551 ScrollView *leftColumnScroll = root_->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));
552 LinearLayout *leftColumn = leftColumnScroll->Add(new LinearLayout(ORIENT_VERTICAL));
554 ScrollView *midColumnScroll = root_->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(2.0f)));
555 LinearLayout *midColumn = midColumnScroll->Add(new LinearLayout(ORIENT_VERTICAL));
556 leftDisasm_ = midColumn->Add(new LinearLayout(ORIENT_VERTICAL));
557 leftDisasm_->SetSpacing(0.0f);
559 ScrollView *rightColumnScroll = root_->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(2.0f)));
560 LinearLayout *rightColumn = rightColumnScroll->Add(new LinearLayout(ORIENT_VERTICAL));
561 rightDisasm_ = rightColumn->Add(new LinearLayout(ORIENT_VERTICAL));
562 rightDisasm_->SetSpacing(0.0f);
564 leftColumn->Add(new Choice(dev->T("Current")))->OnClick.Handle(this, &JitCompareScreen::OnCurrentBlock);
565 leftColumn->Add(new Choice(dev->T("By Address")))->OnClick.Handle(this, &JitCompareScreen::OnSelectBlock);
566 leftColumn->Add(new Choice(dev->T("Prev")))->OnClick.Handle(this, &JitCompareScreen::OnPrevBlock);
567 leftColumn->Add(new Choice(dev->T("Next")))->OnClick.Handle(this, &JitCompareScreen::OnNextBlock);
568 leftColumn->Add(new Choice(dev->T("Random")))->OnClick.Handle(this, &JitCompareScreen::OnRandomBlock);
569 leftColumn->Add(new Choice(dev->T("FPU")))->OnClick.Handle(this, &JitCompareScreen::OnRandomFPUBlock);
570 leftColumn->Add(new Choice(dev->T("VFPU")))->OnClick.Handle(this, &JitCompareScreen::OnRandomVFPUBlock);
571 leftColumn->Add(new Choice(dev->T("Stats")))->OnClick.Handle(this, &JitCompareScreen::OnShowStats);
572 leftColumn->Add(new Choice(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
573 blockName_ = leftColumn->Add(new TextView(dev->T("No block")));
574 blockAddr_ = leftColumn->Add(new TextEdit("", "", new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
575 blockAddr_->OnTextChange.Handle(this, &JitCompareScreen::OnAddressChange);
576 blockStats_ = leftColumn->Add(new TextView(""));
578 EventParams ignore = {0};
579 OnCurrentBlock(ignore);
582 void JitCompareScreen::UpdateDisasm() {
583 leftDisasm_->Clear();
584 rightDisasm_->Clear();
586 using namespace UI;
588 I18NCategory *dev = GetI18NCategory("Developer");
590 JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
592 char temp[256];
593 snprintf(temp, sizeof(temp), "%i/%i", currentBlock_, blockCache->GetNumBlocks());
594 blockName_->SetText(temp);
596 if (currentBlock_ < 0 || currentBlock_ >= blockCache->GetNumBlocks()) {
597 leftDisasm_->Add(new TextView(dev->T("No block")));
598 rightDisasm_->Add(new TextView(dev->T("No block")));
599 blockStats_->SetText("");
600 return;
603 JitBlock *block = blockCache->GetBlock(currentBlock_);
605 snprintf(temp, sizeof(temp), "%08x", block->originalAddress);
606 blockAddr_->SetText(temp);
608 // Alright. First generate the MIPS disassembly.
610 // TODO: Need a way to communicate branch continuing.
611 for (u32 addr = block->originalAddress; addr <= block->originalAddress + block->originalSize * 4; addr += 4) {
612 char temp[256];
613 MIPSDisAsm(Memory::Read_Instruction(addr), addr, temp, true);
614 std::string mipsDis = temp;
615 leftDisasm_->Add(new TextView(mipsDis))->SetFocusable(true);
618 #if defined(ARM)
619 std::vector<std::string> targetDis = DisassembleArm2(block->normalEntry, block->codeSize);
620 #elif defined(ARM64)
621 std::vector<std::string> targetDis = DisassembleArm64(block->normalEntry, block->codeSize);
622 #elif defined(_M_IX86) || defined(_M_X64)
623 std::vector<std::string> targetDis = DisassembleX86(block->normalEntry, block->codeSize);
624 #endif
625 #if defined(ARM) || defined(ARM64) || defined(_M_IX86) || defined(_M_X64)
626 for (size_t i = 0; i < targetDis.size(); i++) {
627 rightDisasm_->Add(new TextView(targetDis[i]))->SetFocusable(true);
629 #endif
631 int numMips = leftDisasm_->GetNumSubviews();
632 int numHost = rightDisasm_->GetNumSubviews();
634 snprintf(temp, sizeof(temp), "%d to %d : %d%%", numMips, numHost, 100 * numHost / numMips);
635 blockStats_->SetText(temp);
638 UI::EventReturn JitCompareScreen::OnAddressChange(UI::EventParams &e) {
639 if (!MIPSComp::jit) {
640 return UI::EVENT_DONE;
642 JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
643 u32 addr;
644 if (blockAddr_->GetText().size() > 8)
645 return UI::EVENT_DONE;
646 if (1 == sscanf(blockAddr_->GetText().c_str(), "%08x", &addr)) {
647 if (Memory::IsValidAddress(addr)) {
648 currentBlock_ = blockCache->GetBlockNumberFromStartAddress(addr);
649 UpdateDisasm();
652 return UI::EVENT_DONE;
655 UI::EventReturn JitCompareScreen::OnShowStats(UI::EventParams &e) {
656 JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
657 BlockCacheStats bcStats;
658 blockCache->ComputeStats(bcStats);
659 NOTICE_LOG(JIT, "Num blocks: %i", bcStats.numBlocks);
660 NOTICE_LOG(JIT, "Average Bloat: %0.2f%%", 100 * bcStats.avgBloat);
661 NOTICE_LOG(JIT, "Min Bloat: %0.2f%% (%08x)", 100 * bcStats.minBloat, bcStats.minBloatBlock);
662 NOTICE_LOG(JIT, "Max Bloat: %0.2f%% (%08x)", 100 * bcStats.maxBloat, bcStats.maxBloatBlock);
664 int ctr = 0, sz = (int)bcStats.bloatMap.size();
665 for (auto iter : bcStats.bloatMap) {
666 if (ctr < 10 || ctr > sz - 10) {
667 NOTICE_LOG(JIT, "%08x: %f", iter.second, iter.first);
668 } else if (ctr == 10) {
669 NOTICE_LOG(JIT, "...");
671 ctr++;
674 return UI::EVENT_DONE;
678 UI::EventReturn JitCompareScreen::OnSelectBlock(UI::EventParams &e) {
679 I18NCategory *dev = GetI18NCategory("Developer");
681 auto addressPrompt = new AddressPromptScreen(dev->T("Block address"));
682 addressPrompt->OnChoice.Handle(this, &JitCompareScreen::OnBlockAddress);
683 screenManager()->push(addressPrompt);
684 return UI::EVENT_DONE;
687 UI::EventReturn JitCompareScreen::OnPrevBlock(UI::EventParams &e) {
688 currentBlock_--;
689 UpdateDisasm();
690 return UI::EVENT_DONE;
693 UI::EventReturn JitCompareScreen::OnNextBlock(UI::EventParams &e) {
694 currentBlock_++;
695 UpdateDisasm();
696 return UI::EVENT_DONE;
699 UI::EventReturn JitCompareScreen::OnBlockAddress(UI::EventParams &e) {
700 if (!MIPSComp::jit) {
701 return UI::EVENT_DONE;
704 JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
705 if (Memory::IsValidAddress(e.a)) {
706 currentBlock_ = blockCache->GetBlockNumberFromStartAddress(e.a);
707 } else {
708 currentBlock_ = -1;
710 UpdateDisasm();
711 return UI::EVENT_DONE;
714 UI::EventReturn JitCompareScreen::OnRandomBlock(UI::EventParams &e) {
715 if (!MIPSComp::jit) {
716 return UI::EVENT_DONE;
719 JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
720 int numBlocks = blockCache->GetNumBlocks();
721 if (numBlocks > 0) {
722 currentBlock_ = rand() % numBlocks;
724 UpdateDisasm();
725 return UI::EVENT_DONE;
728 UI::EventReturn JitCompareScreen::OnRandomVFPUBlock(UI::EventParams &e) {
729 OnRandomBlock(IS_VFPU);
730 return UI::EVENT_DONE;
733 UI::EventReturn JitCompareScreen::OnRandomFPUBlock(UI::EventParams &e) {
734 OnRandomBlock(IS_FPU);
735 return UI::EVENT_DONE;
738 void JitCompareScreen::OnRandomBlock(int flag) {
739 if (!MIPSComp::jit) {
740 return;
742 JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
743 int numBlocks = blockCache->GetNumBlocks();
744 if (numBlocks > 0) {
745 bool anyWanted = false;
746 int tries = 0;
747 while (!anyWanted && tries < 10000) {
748 currentBlock_ = rand() % numBlocks;
749 const JitBlock *b = blockCache->GetBlock(currentBlock_);
750 for (u32 addr = b->originalAddress; addr <= b->originalAddress + b->originalSize; addr += 4) {
751 MIPSOpcode opcode = Memory::Read_Instruction(addr);
752 if (MIPSGetInfo(opcode) & flag) {
753 char temp[256];
754 MIPSDisAsm(opcode, addr, temp);
755 // INFO_LOG(HLE, "Stopping VFPU instruction: %s", temp);
756 anyWanted = true;
757 break;
760 tries++;
763 UpdateDisasm();
767 UI::EventReturn JitCompareScreen::OnCurrentBlock(UI::EventParams &e) {
768 if (!MIPSComp::jit) {
769 return UI::EVENT_DONE;
771 JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
772 std::vector<int> blockNum;
773 blockCache->GetBlockNumbersFromAddress(currentMIPS->pc, &blockNum);
774 if (blockNum.size() > 0) {
775 currentBlock_ = blockNum[0];
776 } else {
777 currentBlock_ = -1;
779 UpdateDisasm();
780 return UI::EVENT_DONE;
783 static const uint32_t nice_colors[] = {
784 0xFF8040,
785 0x80FF40,
786 0x8040FF,
787 0xFFFF40,
789 0x40FFFF,
790 0xFF70FF,
791 0xc0c0c0,
792 0xb040c0,
794 0x184099,
795 0xCC3333,
796 0xFF99CC,
797 0x3399CC,
799 0x990000,
800 0x003366,
801 0xF8F8F8,
802 0x33FFFF,
805 enum ProfileCatStatus {
806 PROFILE_CAT_VISIBLE = 0,
807 PROFILE_CAT_IGNORE = 1,
808 PROFILE_CAT_NOLEGEND = 2,
811 void DrawProfile(UIContext &ui) {
812 #ifdef USE_PROFILER
813 int numCategories = Profiler_GetNumCategories();
814 int historyLength = Profiler_GetHistoryLength();
816 ui.SetFontStyle(ui.theme->uiFont);
818 static float lastMaxVal = 1.0f / 60.0f;
819 float legendMinVal = lastMaxVal * (1.0f / 120.0f);
821 std::vector<float> history;
822 std::vector<ProfileCatStatus> catStatus;
823 history.resize(historyLength);
824 catStatus.resize(numCategories);
826 float rowH = 30.0f;
827 float legendHeight = 0.0f;
828 float legendWidth = 80.0f;
829 for (int i = 0; i < numCategories; i++) {
830 const char *name = Profiler_GetCategoryName(i);
831 if (!strcmp(name, "timing")) {
832 catStatus[i] = PROFILE_CAT_IGNORE;
833 continue;
836 Profiler_GetHistory(i, &history[0], historyLength);
837 catStatus[i] = PROFILE_CAT_NOLEGEND;
838 for (int j = 0; j < historyLength; ++j) {
839 if (history[j] > legendMinVal) {
840 catStatus[i] = PROFILE_CAT_VISIBLE;
841 break;
845 // So they don't move horizontally, we always measure.
846 float w = 0.0f, h = 0.0f;
847 ui.MeasureText(ui.GetFontStyle(), name, &w, &h);
848 if (w > legendWidth) {
849 legendWidth = w;
851 legendHeight += rowH;
853 legendWidth += 20.0f;
855 float legendStartY = legendHeight > ui.GetBounds().centerY() ? ui.GetBounds().y2() - legendHeight : ui.GetBounds().centerY();
856 float legendStartX = ui.GetBounds().x2() - std::min(legendWidth, 200.0f);
858 const uint32_t opacity = 140 << 24;
860 int legendNum = 0;
861 for (int i = 0; i < numCategories; i++) {
862 const char *name = Profiler_GetCategoryName(i);
863 uint32_t color = nice_colors[i % ARRAY_SIZE(nice_colors)];
865 if (catStatus[i] == PROFILE_CAT_VISIBLE) {
866 float y = legendStartY + legendNum++ * rowH;
867 ui.FillRect(UI::Drawable(opacity | color), Bounds(legendStartX, y, rowH - 2, rowH - 2));
868 ui.DrawTextShadow(name, legendStartX + rowH + 2, y, 0xFFFFFFFF, ALIGN_VBASELINE);
872 float graphWidth = ui.GetBounds().x2() - legendWidth - 20.0f;
873 float graphHeight = ui.GetBounds().h * 0.8f;
875 float dx = graphWidth / historyLength;
878 ui.Flush();
880 ui.BeginNoTex();
883 bool area = true;
884 float minVal = 0.0f;
885 float maxVal = lastMaxVal; // TODO - adjust to frame length
886 if (maxVal < 0.001f)
887 maxVal = 0.001f;
888 if (maxVal > 1.0f / 15.0f)
889 maxVal = 1.0f / 15.0f;
891 float scale = (graphHeight) / (maxVal - minVal);
893 float y_60th = ui.GetBounds().y2() - 10 - (1.0f / 60.0f) * scale;
894 float y_1ms = ui.GetBounds().y2() - 10 - (1.0f / 1000.0f) * scale;
896 ui.FillRect(UI::Drawable(0x80FFFF00), Bounds(0, y_60th, graphWidth, 2));
897 ui.FillRect(UI::Drawable(0x80FFFF00), Bounds(0, y_1ms, graphWidth, 2));
898 ui.DrawTextShadow("1/60s", 5, y_60th, 0x80FFFF00);
899 ui.DrawTextShadow("1ms", 5, y_1ms, 0x80FFFF00);
901 std::vector<float> total;
902 total.resize(historyLength);
904 maxVal = 0.0f;
905 float maxTotal = 0.0f;
906 for (int i = 0; i < numCategories; i++) {
907 if (catStatus[i] == PROFILE_CAT_IGNORE) {
908 continue;
910 Profiler_GetHistory(i, &history[0], historyLength);
912 float x = 10;
913 uint32_t col = nice_colors[i % ARRAY_SIZE(nice_colors)];
914 if (area)
915 col = opacity | (col & 0xFFFFFF);
916 UI::Drawable color(col);
917 UI::Drawable outline((opacity >> 1) | 0xFFFFFF);
919 if (area) {
920 for (int n = 0; n < historyLength; n++) {
921 float val = history[n];
922 float valY1 = ui.GetBounds().y2() - 10 - (val + total[n]) * scale;
923 float valY2 = ui.GetBounds().y2() - 10 - total[n] * scale;
924 ui.FillRect(outline, Bounds(x, valY2, dx, 1.0f));
925 ui.FillRect(color, Bounds(x, valY1, dx, valY2 - valY1));
926 x += dx;
927 total[n] += val;
929 } else {
930 for (int n = 0; n < historyLength; n++) {
931 float val = history[n];
932 if (val > maxVal)
933 maxVal = val;
934 float valY = ui.GetBounds().y2() - 10 - history[n] * scale;
935 ui.FillRect(color, Bounds(x, valY, dx, 5));
936 x += dx;
941 for (int n = 0; n < historyLength; n++) {
942 if (total[n] > maxTotal)
943 maxTotal = total[n];
946 if (area) {
947 maxVal = maxTotal;
950 lastMaxVal = lastMaxVal * 0.95f + maxVal * 0.05f;
951 #endif