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/.
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"
26 #include "ui/viewgroup.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"
47 // Want to avoid including the full header here as it includes d3dx.h
51 static const char *logLevelList
[] = {
60 void DevMenu::CreatePopupContents(UI::ViewGroup
*parent
) {
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
);
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
);
75 parent
->Add(new CheckBox(&g_Config
.bShowFrameProfiler
, dev
->T("Frame Profiler"), ""));
78 RingbufferLogListener
*ring
= LogManager::GetInstance()->GetRingbufferListener();
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;
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() {
137 RingbufferLogListener
*ring
= LogManager::GetInstance()->GetRingbufferListener();
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
);
157 void LogScreen::update(InputState
&input
) {
158 UIDialogScreenWithBackground::update(input
);
161 scroll_
->ScrollToBottom();
165 void LogScreen::CreateViews() {
167 I18NCategory
*di
= GetI18NCategory("Dialog");
169 LinearLayout
*outer
= new LinearLayout(ORIENT_VERTICAL
, new LinearLayoutParams(FILL_PARENT
, WRAP_CONTENT
));
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);
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());
193 cmdLine_
->SetText("");
194 cmdLine_
->SetFocus();
195 return UI::EVENT_DONE
;
198 void LogConfigScreen::CreateViews() {
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
)));
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
);
216 vert
->Add(new ItemHeader(dev
->T("Logging Channels")));
218 LogManager
*logMan
= LogManager::GetInstance();
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
));
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)));
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
) {
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
) {
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
) {
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
);
282 chan
->level_
= selected
+ 1;
286 const char *GetCompilerABI() {
288 return "armeabi-v7a";
293 #elif defined(_M_IX86)
295 #elif defined(_M_X64)
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");
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
)));
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
));
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
)));
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())));
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
)));
365 deviceSpecs
->Add(new ItemHeader("Version Information"));
366 std::string apiVersion
;
367 if (g_Config
.iGPUBackend
== GPU_BACKEND_OPENGL
) {
369 apiVersion
= StringFromFormat("v%d.%d.%d ES", gl_extensions
.ver
[0], gl_extensions
.ver
[1], gl_extensions
.ver
[2]);
371 apiVersion
= StringFromFormat("v%d.%d.%d", gl_extensions
.ver
[0], gl_extensions
.ver
[1], gl_extensions
.ver
[2]);
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
)));
382 std::string moga
= System_GetProperty(SYSPROP_MOGA_VERSION
);
384 moga
= "(none detected)";
386 deviceSpecs
->Add(new InfoItem("Moga", moga
));
391 sprintf(temp
, "%dx%d", System_GetPropertyInt(SYSPROP_DISPLAY_XRES
), System_GetPropertyInt(SYSPROP_DISPLAY_YRES
));
392 deviceSpecs
->Add(new InfoItem("Display resolution", temp
));
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
);
417 oglExtensions
->Add(new ItemHeader("OpenGL Extensions"));
419 if (gl_extensions
.GLES3
)
420 oglExtensions
->Add(new ItemHeader("OpenGL ES 3.0 Extensions"));
422 oglExtensions
->Add(new ItemHeader("OpenGL ES 2.0 Extensions"));
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);
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
) {
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));
464 for (int i
= 0; i
< 16; ++i
) {
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
) {
483 UI::EventReturn
AddressPromptScreen::OnDigitButton(UI::EventParams
&e
) {
484 for (int i
= 0; i
< 16; ++i
) {
485 if (buttons_
[i
] == e
.v
) {
489 return UI::EVENT_DONE
;
492 UI::EventReturn
AddressPromptScreen::OnBackspace(UI::EventParams
&e
) {
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() {
506 UpdatePreviewDigits();
509 void AddressPromptScreen::UpdatePreviewDigits() {
510 I18NCategory
*dev
= GetI18NCategory("Developer");
514 snprintf(temp
, 32, "%8X", addr_
);
515 addrView_
->SetText(temp
);
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
) {
530 } else if (key
.keyCode
== NKCODE_ENTER
) {
532 screenManager()->finishDialog(this, DR_OK
);
534 return UIDialogScreen::key(key
);
537 return UIDialogScreen::key(key
);
542 // Three panes: Block chooser, MIPS view, ARM/x86 view
543 void JitCompareScreen::CreateViews() {
544 I18NCategory
*di
= GetI18NCategory("Dialog");
545 I18NCategory
*dev
= GetI18NCategory("Developer");
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();
588 I18NCategory
*dev
= GetI18NCategory("Developer");
590 JitBlockCache
*blockCache
= MIPSComp::jit
->GetBlockCache();
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("");
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) {
613 MIPSDisAsm(Memory::Read_Instruction(addr
), addr
, temp
, true);
614 std::string mipsDis
= temp
;
615 leftDisasm_
->Add(new TextView(mipsDis
))->SetFocusable(true);
619 std::vector
<std::string
> targetDis
= DisassembleArm2(block
->normalEntry
, block
->codeSize
);
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
);
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);
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();
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
);
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
, "...");
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
) {
690 return UI::EVENT_DONE
;
693 UI::EventReturn
JitCompareScreen::OnNextBlock(UI::EventParams
&e
) {
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
);
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();
722 currentBlock_
= rand() % numBlocks
;
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
) {
742 JitBlockCache
*blockCache
= MIPSComp::jit
->GetBlockCache();
743 int numBlocks
= blockCache
->GetNumBlocks();
745 bool anyWanted
= false;
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
) {
754 MIPSDisAsm(opcode
, addr
, temp
);
755 // INFO_LOG(HLE, "Stopping VFPU instruction: %s", temp);
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];
780 return UI::EVENT_DONE
;
783 static const uint32_t nice_colors
[] = {
805 enum ProfileCatStatus
{
806 PROFILE_CAT_VISIBLE
= 0,
807 PROFILE_CAT_IGNORE
= 1,
808 PROFILE_CAT_NOLEGEND
= 2,
811 void DrawProfile(UIContext
&ui
) {
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
);
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
;
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
;
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
) {
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;
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
;
885 float maxVal
= lastMaxVal
; // TODO - adjust to frame length
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
);
905 float maxTotal
= 0.0f
;
906 for (int i
= 0; i
< numCategories
; i
++) {
907 if (catStatus
[i
] == PROFILE_CAT_IGNORE
) {
910 Profiler_GetHistory(i
, &history
[0], historyLength
);
913 uint32_t col
= nice_colors
[i
% ARRAY_SIZE(nice_colors
)];
915 col
= opacity
| (col
& 0xFFFFFF);
916 UI::Drawable
color(col
);
917 UI::Drawable
outline((opacity
>> 1) | 0xFFFFFF);
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
));
930 for (int n
= 0; n
< historyLength
; n
++) {
931 float val
= history
[n
];
934 float valY
= ui
.GetBounds().y2() - 10 - history
[n
] * scale
;
935 ui
.FillRect(color
, Bounds(x
, valY
, dx
, 5));
941 for (int n
= 0; n
< historyLength
; n
++) {
942 if (total
[n
] > maxTotal
)
950 lastMaxVal
= lastMaxVal
* 0.95f
+ maxVal
* 0.05f
;