~ppsspp/ppsspp/ppsspp_1.3.0

« back to all changes in this revision

Viewing changes to UI/DevScreens.cpp

  • Committer: Sérgio Benjamim
  • Date: 2017-01-02 00:12:05 UTC
  • Revision ID: sergio_br2@yahoo.com.br-20170102001205-cxbta9za203nmjwm
1.3.0 source (from ppsspp_1.3.0-r160.p5.l1762.a165.t83~56~ubuntu16.04.1.tar.xz).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) 2013- PPSSPP Project.
 
2
 
 
3
// This program is free software: you can redistribute it and/or modify
 
4
// it under the terms of the GNU General Public License as published by
 
5
// the Free Software Foundation, version 2.0 or later versions.
 
6
 
 
7
// This program is distributed in the hope that it will be useful,
 
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
10
// GNU General Public License 2.0 for more details.
 
11
 
 
12
// A copy of the GPL 2.0 should have been included with the program.
 
13
// If not, see http://www.gnu.org/licenses/
 
14
 
 
15
// Official git repository and contact information can be found at
 
16
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
 
17
 
 
18
#include <algorithm>
 
19
 
 
20
#include "base/compat.h"
 
21
#include "gfx_es2/gpu_features.h"
 
22
#include "i18n/i18n.h"
 
23
#include "ui/ui_context.h"
 
24
#include "ui/view.h"
 
25
#include "ui/viewgroup.h"
 
26
#include "ui/ui.h"
 
27
#include "profiler/profiler.h"
 
28
 
 
29
#include "Common/LogManager.h"
 
30
#include "Common/CPUDetect.h"
 
31
 
 
32
#include "Core/MemMap.h"
 
33
#include "Core/Config.h"
 
34
#include "Core/System.h"
 
35
#include "Core/CoreParameter.h"
 
36
#include "Core/MIPS/MIPSTables.h"
 
37
#include "Core/MIPS/JitCommon/JitBlockCache.h"
 
38
#include "Core/MIPS/JitCommon/JitCommon.h"
 
39
#include "GPU/GPUInterface.h"
 
40
#include "GPU/GPUState.h"
 
41
#include "UI/MiscScreens.h"
 
42
#include "UI/DevScreens.h"
 
43
#include "UI/GameSettingsScreen.h"
 
44
 
 
45
#ifdef _WIN32
 
46
// Want to avoid including the full header here as it includes d3dx.h
 
47
int GetD3DXVersion();
 
48
#endif
 
49
 
 
50
static const char *logLevelList[] = {
 
51
        "Notice",
 
52
        "Error",
 
53
        "Warn",
 
54
        "Info",
 
55
        "Debug",
 
56
        "Verb."
 
57
};
 
58
 
 
59
void DevMenu::CreatePopupContents(UI::ViewGroup *parent) {
 
60
        using namespace UI;
 
61
        I18NCategory *dev = GetI18NCategory("Developer");
 
62
        I18NCategory *sy = GetI18NCategory("System");
 
63
 
 
64
        ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f));
 
65
        LinearLayout *items = new LinearLayout(ORIENT_VERTICAL);
 
66
 
 
67
#if !defined(MOBILE_DEVICE)
 
68
        items->Add(new Choice(dev->T("Log View")))->OnClick.Handle(this, &DevMenu::OnLogView);
 
69
#endif
 
70
        items->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DevMenu::OnLogConfig);
 
71
        items->Add(new Choice(sy->T("Developer Tools")))->OnClick.Handle(this, &DevMenu::OnDeveloperTools);
 
72
        items->Add(new Choice(dev->T("Jit Compare")))->OnClick.Handle(this, &DevMenu::OnJitCompare);
 
73
        items->Add(new Choice(dev->T("Shader Viewer")))->OnClick.Handle(this, &DevMenu::OnShaderView);
 
74
        items->Add(new Choice(dev->T("Toggle Freeze")))->OnClick.Handle(this, &DevMenu::OnFreezeFrame);
 
75
        items->Add(new Choice(dev->T("Dump Frame GPU Commands")))->OnClick.Handle(this, &DevMenu::OnDumpFrame);
 
76
        items->Add(new Choice(dev->T("Toggle Audio Debug")))->OnClick.Handle(this, &DevMenu::OnToggleAudioDebug);
 
77
#ifdef USE_PROFILER
 
78
        items->Add(new CheckBox(&g_Config.bShowFrameProfiler, dev->T("Frame Profiler"), ""));
 
79
#endif
 
80
 
 
81
        scroll->Add(items);
 
82
        parent->Add(scroll);
 
83
 
 
84
        RingbufferLogListener *ring = LogManager::GetInstance()->GetRingbufferListener();
 
85
        if (ring) {
 
86
                ring->SetEnable(true);
 
87
        }
 
88
}
 
89
 
 
90
UI::EventReturn DevMenu::OnToggleAudioDebug(UI::EventParams &e) {
 
91
        g_Config.bShowAudioDebug = !g_Config.bShowAudioDebug;
 
92
        return UI::EVENT_DONE;
 
93
}
 
94
 
 
95
 
 
96
UI::EventReturn DevMenu::OnLogView(UI::EventParams &e) {
 
97
        UpdateUIState(UISTATE_PAUSEMENU);
 
98
        screenManager()->push(new LogScreen());
 
99
        return UI::EVENT_DONE;
 
100
}
 
101
 
 
102
UI::EventReturn DevMenu::OnLogConfig(UI::EventParams &e) {
 
103
        UpdateUIState(UISTATE_PAUSEMENU);
 
104
        screenManager()->push(new LogConfigScreen());
 
105
        return UI::EVENT_DONE;
 
106
}
 
107
 
 
108
UI::EventReturn DevMenu::OnDeveloperTools(UI::EventParams &e) {
 
109
        UpdateUIState(UISTATE_PAUSEMENU);
 
110
        screenManager()->push(new DeveloperToolsScreen());
 
111
        return UI::EVENT_DONE;
 
112
}
 
113
 
 
114
UI::EventReturn DevMenu::OnJitCompare(UI::EventParams &e) {
 
115
        UpdateUIState(UISTATE_PAUSEMENU);
 
116
        screenManager()->push(new JitCompareScreen());
 
117
        return UI::EVENT_DONE;
 
118
}
 
119
 
 
120
UI::EventReturn DevMenu::OnShaderView(UI::EventParams &e) {
 
121
        UpdateUIState(UISTATE_PAUSEMENU);
 
122
        screenManager()->push(new ShaderListScreen());
 
123
        return UI::EVENT_DONE;
 
124
}
 
125
 
 
126
 
 
127
 
 
128
UI::EventReturn DevMenu::OnFreezeFrame(UI::EventParams &e) {
 
129
        if (PSP_CoreParameter().frozen) {
 
130
                PSP_CoreParameter().frozen = false;
 
131
        } else {
 
132
                PSP_CoreParameter().freezeNext = true;
 
133
        }
 
134
        return UI::EVENT_DONE;
 
135
}
 
136
 
 
137
UI::EventReturn DevMenu::OnDumpFrame(UI::EventParams &e) {
 
138
        gpu->DumpNextFrame();
 
139
        return UI::EVENT_DONE;
 
140
}
 
141
 
 
142
void DevMenu::dialogFinished(const Screen *dialog, DialogResult result) {
 
143
        UpdateUIState(UISTATE_INGAME);
 
144
        // Close when a subscreen got closed.
 
145
        // TODO: a bug in screenmanager causes this not to work here.
 
146
        // screenManager()->finishDialog(this, DR_OK);
 
147
}
 
148
 
 
149
void LogScreen::UpdateLog() {
 
150
        using namespace UI;
 
151
        RingbufferLogListener *ring = LogManager::GetInstance()->GetRingbufferListener();
 
152
        if (!ring)
 
153
                return;
 
154
        vert_->Clear();
 
155
        for (int i = ring->GetCount() - 1; i >= 0; i--) {
 
156
                TextView *v = vert_->Add(new TextView(ring->TextAt(i), FLAG_DYNAMIC_ASCII, false));
 
157
                uint32_t color = 0xFFFFFF;
 
158
                switch (ring->LevelAt(i)) {
 
159
                case LogTypes::LDEBUG: color = 0xE0E0E0; break;
 
160
                case LogTypes::LWARNING: color = 0x50FFFF; break;
 
161
                case LogTypes::LERROR: color = 0x5050FF; break;
 
162
                case LogTypes::LNOTICE: color = 0x30FF30; break;
 
163
                case LogTypes::LINFO: color = 0xFFFFFF; break;
 
164
                case LogTypes::LVERBOSE: color = 0xC0C0C0; break;
 
165
                }
 
166
                v->SetTextColor(0xFF000000 | color);
 
167
        }
 
168
        toBottom_ = true;
 
169
}
 
170
 
 
171
void LogScreen::update(InputState &input) {
 
172
        UIDialogScreenWithBackground::update(input);
 
173
        if (toBottom_) {
 
174
                toBottom_ = false;
 
175
                scroll_->ScrollToBottom();
 
176
        }
 
177
}
 
178
 
 
179
void LogScreen::CreateViews() {
 
180
        using namespace UI;
 
181
        I18NCategory *di = GetI18NCategory("Dialog");
 
182
 
 
183
        LinearLayout *outer = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
 
184
        root_ = outer;
 
185
 
 
186
        scroll_ = outer->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0)));
 
187
        LinearLayout *bottom = outer->Add(new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
 
188
        bottom->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
 
189
        cmdLine_ = bottom->Add(new TextEdit("", "Command Line", new LinearLayoutParams(1.0)));
 
190
        cmdLine_->OnEnter.Handle(this, &LogScreen::OnSubmit);
 
191
        bottom->Add(new Button(di->T("Submit")))->OnClick.Handle(this, &LogScreen::OnSubmit);
 
192
 
 
193
        vert_ = scroll_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
 
194
        vert_->SetSpacing(0);
 
195
 
 
196
        UpdateLog();
 
197
}
 
198
 
 
199
UI::EventReturn LogScreen::OnSubmit(UI::EventParams &e) {
 
200
        std::string cmd = cmdLine_->GetText();
 
201
 
 
202
        // TODO: Can add all sorts of fun stuff here that we can't be bothered writing proper UI for, like various memdumps etc.
 
203
 
 
204
        NOTICE_LOG(HLE, "Submitted: %s", cmd.c_str());
 
205
 
 
206
        UpdateLog();
 
207
        cmdLine_->SetText("");
 
208
        cmdLine_->SetFocus();
 
209
        return UI::EVENT_DONE;
 
210
}
 
211
 
 
212
void LogConfigScreen::CreateViews() {
 
213
        using namespace UI;
 
214
 
 
215
        I18NCategory *di = GetI18NCategory("Dialog");
 
216
        I18NCategory *dev = GetI18NCategory("Developer");
 
217
 
 
218
        root_ = new ScrollView(ORIENT_VERTICAL);
 
219
 
 
220
        LinearLayout *vert = root_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));
 
221
        vert->SetSpacing(0);
 
222
 
 
223
        LinearLayout *topbar = new LinearLayout(ORIENT_HORIZONTAL);
 
224
        topbar->Add(new Choice(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
 
225
        topbar->Add(new Choice(di->T("Toggle All")))->OnClick.Handle(this, &LogConfigScreen::OnToggleAll);
 
226
        topbar->Add(new Choice(dev->T("Log Level")))->OnClick.Handle(this, &LogConfigScreen::OnLogLevel);
 
227
 
 
228
        vert->Add(topbar);
 
229
 
 
230
        vert->Add(new ItemHeader(dev->T("Logging Channels")));
 
231
 
 
232
        LogManager *logMan = LogManager::GetInstance();
 
233
 
 
234
        int cellSize = 400;
 
235
 
 
236
        UI::GridLayoutSettings gridsettings(cellSize, 64, 5);
 
237
        gridsettings.fillCells = true;
 
238
        GridLayout *grid = vert->Add(new GridLayout(gridsettings, new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
 
239
 
 
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
                LinearLayout *row = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(cellSize - 50, WRAP_CONTENT));
 
244
                row->SetSpacing(0);
 
245
                row->Add(new CheckBox(&chan->enable_, "", "", new LinearLayoutParams(50, WRAP_CONTENT)));
 
246
                row->Add(new PopupMultiChoice(&chan->level_, chan->GetFullName(), logLevelList, 1, 6, 0, screenManager(), new LinearLayoutParams(1.0)));
 
247
                grid->Add(row);
 
248
        }
 
249
}
 
250
 
 
251
UI::EventReturn LogConfigScreen::OnToggleAll(UI::EventParams &e) {
 
252
        LogManager *logMan = LogManager::GetInstance();
 
253
        
 
254
        for (int i = 0; i < LogManager::GetNumChannels(); i++) {
 
255
                LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i;
 
256
                LogChannel *chan = logMan->GetLogChannel(type);
 
257
                chan->enable_ = !chan->enable_;
 
258
        }
 
259
 
 
260
        return UI::EVENT_DONE;
 
261
}
 
262
 
 
263
UI::EventReturn LogConfigScreen::OnLogLevelChange(UI::EventParams &e) {
 
264
        RecreateViews();
 
265
        return UI::EVENT_DONE;
 
266
}
 
267
 
 
268
UI::EventReturn LogConfigScreen::OnLogLevel(UI::EventParams &e) {
 
269
        I18NCategory *dev = GetI18NCategory("Developer");
 
270
 
 
271
        auto logLevelScreen = new LogLevelScreen(dev->T("Log Level"));
 
272
        logLevelScreen->OnChoice.Handle(this, &LogConfigScreen::OnLogLevelChange);
 
273
        screenManager()->push(logLevelScreen);
 
274
        return UI::EVENT_DONE;
 
275
}
 
276
 
 
277
LogLevelScreen::LogLevelScreen(const std::string &title) : ListPopupScreen(title) {
 
278
        int NUMLOGLEVEL = 6;    
 
279
        std::vector<std::string> list;
 
280
        for(int i = 0; i < NUMLOGLEVEL; ++i) {
 
281
                list.push_back(logLevelList[i]);
 
282
        }
 
283
        adaptor_ = UI::StringVectorListAdaptor(list, -1);
 
284
}
 
285
 
 
286
void LogLevelScreen::OnCompleted(DialogResult result) {
 
287
        if (result != DR_OK)
 
288
                return;
 
289
        int selected = listView_->GetSelected();
 
290
        LogManager *logMan = LogManager::GetInstance();
 
291
        
 
292
        for (int i = 0; i < LogManager::GetNumChannels(); ++i) {
 
293
                LogTypes::LOG_TYPE type = (LogTypes::LOG_TYPE)i;
 
294
                LogChannel *chan = logMan->GetLogChannel(type);
 
295
                if(chan->enable_ )
 
296
                        chan->level_ = selected + 1;
 
297
        }
 
298
}
 
299
 
 
300
const char *GetCompilerABI() {
 
301
#ifdef HAVE_ARMV7
 
302
        return "armeabi-v7a";
 
303
#elif defined(ARM)
 
304
        return "armeabi";
 
305
#elif defined(ARM64)
 
306
        return "arm64";
 
307
#elif defined(_M_IX86)
 
308
        return "x86";
 
309
#elif defined(_M_X64)
 
310
        return "x86-64";
 
311
#else
 
312
        return "other";
 
313
#endif
 
314
}
 
315
 
 
316
void SystemInfoScreen::CreateViews() {
 
317
        // NOTE: Do not translate this section. It will change a lot and will be impossible to keep up.
 
318
        I18NCategory *di = GetI18NCategory("Dialog");
 
319
 
 
320
        using namespace UI;
 
321
        root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));
 
322
 
 
323
        ViewGroup *leftColumn = new AnchorLayout(new LinearLayoutParams(1.0f));
 
324
        root_->Add(leftColumn);
 
325
 
 
326
        AddStandardBack(root_);
 
327
 
 
328
        TabHolder *tabHolder = new TabHolder(ORIENT_VERTICAL, 225, new AnchorLayoutParams(10, 0, 10, 0, false));
 
329
        tabHolder->SetTag("DevSystemInfo");
 
330
 
 
331
        root_->Add(tabHolder);
 
332
        ViewGroup *deviceSpecsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
 
333
        deviceSpecsScroll->SetTag("DevSystemInfoDeviceSpecs");
 
334
        LinearLayout *deviceSpecs = new LinearLayout(ORIENT_VERTICAL);
 
335
        deviceSpecs->SetSpacing(0);
 
336
        deviceSpecsScroll->Add(deviceSpecs);
 
337
        tabHolder->AddTab("Device Info", deviceSpecsScroll);
 
338
 
 
339
        deviceSpecs->Add(new ItemHeader("System Information"));
 
340
        deviceSpecs->Add(new InfoItem("Name", System_GetProperty(SYSPROP_NAME)));
 
341
        deviceSpecs->Add(new InfoItem("Lang/Region", System_GetProperty(SYSPROP_LANGREGION)));
 
342
        deviceSpecs->Add(new InfoItem("ABI", GetCompilerABI()));
 
343
        deviceSpecs->Add(new ItemHeader("CPU Information"));
 
344
        deviceSpecs->Add(new InfoItem("Name", cpu_info.brand_string));
 
345
#if defined(ARM) || defined(ARM64) || defined(MIPS)
 
346
        deviceSpecs->Add(new InfoItem("Cores", StringFromInt(cpu_info.num_cores)));
 
347
#else
 
348
        int totalThreads = cpu_info.num_cores * cpu_info.logical_cpu_count;
 
349
        std::string cores = StringFromFormat("%d (%d per core, %d cores)", totalThreads, cpu_info.logical_cpu_count, cpu_info.num_cores);
 
350
        deviceSpecs->Add(new InfoItem("Threads", cores));
 
351
#endif
 
352
        deviceSpecs->Add(new ItemHeader("GPU Information"));
 
353
 
 
354
        Thin3DContext *thin3d = screenManager()->getThin3DContext();
 
355
 
 
356
        deviceSpecs->Add(new InfoItem("3D API", thin3d->GetInfoString(T3DInfo::APINAME)));
 
357
        deviceSpecs->Add(new InfoItem("Vendor", std::string(thin3d->GetInfoString(T3DInfo::VENDORSTRING)) + " (" + thin3d->GetInfoString(T3DInfo::VENDOR) + ")"));
 
358
        deviceSpecs->Add(new InfoItem("Model", thin3d->GetInfoString(T3DInfo::RENDERER)));
 
359
#ifdef _WIN32
 
360
        deviceSpecs->Add(new InfoItem("Driver Version", System_GetProperty(SYSPROP_GPUDRIVER_VERSION)));
 
361
        if (GetGPUBackend() == GPUBackend::DIRECT3D9) {
 
362
                deviceSpecs->Add(new InfoItem("D3DX Version", StringFromFormat("%d", GetD3DXVersion())));
 
363
        }
 
364
#endif
 
365
 
 
366
#ifdef ANDROID
 
367
        deviceSpecs->Add(new ItemHeader("Audio Information"));
 
368
        deviceSpecs->Add(new InfoItem("Sample rate", StringFromFormat("%d Hz", System_GetPropertyInt(SYSPROP_AUDIO_SAMPLE_RATE))));
 
369
        deviceSpecs->Add(new InfoItem("Frames per buffer", StringFromFormat("%d", System_GetPropertyInt(SYSPROP_AUDIO_FRAMES_PER_BUFFER))));
 
370
        deviceSpecs->Add(new InfoItem("Optimal sample rate", StringFromFormat("%d Hz", System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_SAMPLE_RATE))));
 
371
        deviceSpecs->Add(new InfoItem("Optimal frames per buffer", StringFromFormat("%d", System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_FRAMES_PER_BUFFER))));
 
372
 
 
373
        deviceSpecs->Add(new ItemHeader("Display Information"));
 
374
        deviceSpecs->Add(new InfoItem("Native Resolution", StringFromFormat("%dx%d",
 
375
                System_GetPropertyInt(SYSPROP_DISPLAY_XRES),
 
376
                System_GetPropertyInt(SYSPROP_DISPLAY_YRES))));
 
377
        deviceSpecs->Add(new InfoItem("Refresh rate", StringFromFormat("%0.3f Hz", (float)System_GetPropertyInt(SYSPROP_DISPLAY_REFRESH_RATE) / 1000.0f)));
 
378
#endif
 
379
 
 
380
 
 
381
        deviceSpecs->Add(new ItemHeader("Version Information"));
 
382
        std::string apiVersion;
 
383
        if (GetGPUBackend() == GPUBackend::OPENGL) {
 
384
                if (gl_extensions.IsGLES) {
 
385
                        apiVersion = StringFromFormat("v%d.%d.%d ES", gl_extensions.ver[0], gl_extensions.ver[1], gl_extensions.ver[2]);
 
386
                } else {
 
387
                        apiVersion = StringFromFormat("v%d.%d.%d", gl_extensions.ver[0], gl_extensions.ver[1], gl_extensions.ver[2]);
 
388
                }
 
389
        } else {
 
390
                apiVersion = thin3d->GetInfoString(T3DInfo::APIVERSION);
 
391
                if (apiVersion.size() > 30)
 
392
                        apiVersion.resize(30);
 
393
        }
 
394
        deviceSpecs->Add(new InfoItem("API Version", apiVersion));
 
395
        deviceSpecs->Add(new InfoItem("Shading Language", thin3d->GetInfoString(T3DInfo::SHADELANGVERSION)));
 
396
 
 
397
#ifdef ANDROID
 
398
        std::string moga = System_GetProperty(SYSPROP_MOGA_VERSION);
 
399
        if (moga.empty()) {
 
400
                moga = "(none detected)";
 
401
        }
 
402
        deviceSpecs->Add(new InfoItem("Moga", moga));
 
403
#endif
 
404
 
 
405
#ifdef ANDROID
 
406
        char temp[256];
 
407
        sprintf(temp, "%dx%d", System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES));
 
408
        deviceSpecs->Add(new InfoItem("Display resolution", temp));
 
409
#endif
 
410
 
 
411
        ViewGroup *cpuExtensionsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
 
412
        cpuExtensionsScroll->SetTag("DevSystemInfoCPUExt");
 
413
        LinearLayout *cpuExtensions = new LinearLayout(ORIENT_VERTICAL);
 
414
        cpuExtensions->SetSpacing(0);
 
415
        cpuExtensionsScroll->Add(cpuExtensions);
 
416
 
 
417
        tabHolder->AddTab("CPU Extensions", cpuExtensionsScroll);
 
418
 
 
419
        cpuExtensions->Add(new ItemHeader("CPU Extensions"));
 
420
        std::vector<std::string> exts;
 
421
        SplitString(cpu_info.Summarize(), ',', exts);
 
422
        for (size_t i = 2; i < exts.size(); i++) {
 
423
                cpuExtensions->Add(new TextView(exts[i]))->SetFocusable(true);
 
424
        }
 
425
 
 
426
        ViewGroup *oglExtensionsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
 
427
        oglExtensionsScroll->SetTag("DevSystemInfoOGLExt");
 
428
        LinearLayout *oglExtensions = new LinearLayout(ORIENT_VERTICAL);
 
429
        oglExtensions->SetSpacing(0);
 
430
        oglExtensionsScroll->Add(oglExtensions);
 
431
 
 
432
        if (g_Config.iGPUBackend == GPU_BACKEND_OPENGL) {
 
433
                tabHolder->AddTab("OGL Extensions", oglExtensionsScroll);
 
434
 
 
435
                if (!gl_extensions.IsGLES) {
 
436
                        oglExtensions->Add(new ItemHeader("OpenGL Extensions"));
 
437
                } else if (gl_extensions.GLES3) {
 
438
                        oglExtensions->Add(new ItemHeader("OpenGL ES 3.0 Extensions"));
 
439
                } else {
 
440
                        oglExtensions->Add(new ItemHeader("OpenGL ES 2.0 Extensions"));
 
441
                }
 
442
                exts.clear();
 
443
                SplitString(g_all_gl_extensions, ' ', exts);
 
444
                std::sort(exts.begin(), exts.end());
 
445
                for (size_t i = 0; i < exts.size(); i++) {
 
446
                        oglExtensions->Add(new TextView(exts[i]))->SetFocusable(true);
 
447
                }
 
448
 
 
449
                exts.clear();
 
450
                SplitString(g_all_egl_extensions, ' ', exts);
 
451
                std::sort(exts.begin(), exts.end());
 
452
 
 
453
                // If there aren't any EGL extensions, no need to show the tab.
 
454
                if (exts.size() > 0) {
 
455
                        ViewGroup *eglExtensionsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));
 
456
                        eglExtensionsScroll->SetTag("DevSystemInfoEGLExt");
 
457
                        LinearLayout *eglExtensions = new LinearLayout(ORIENT_VERTICAL);
 
458
                        eglExtensions->SetSpacing(0);
 
459
                        eglExtensionsScroll->Add(eglExtensions);
 
460
 
 
461
                        tabHolder->AddTab("EGL Extensions", eglExtensionsScroll);
 
462
 
 
463
                        eglExtensions->Add(new ItemHeader("EGL Extensions"));
 
464
 
 
465
                        for (size_t i = 0; i < exts.size(); i++) {
 
466
                                eglExtensions->Add(new TextView(exts[i]))->SetFocusable(true);
 
467
                        }
 
468
                }
 
469
        } else if (g_Config.iGPUBackend == GPU_BACKEND_VULKAN) {
 
470
                tabHolder->AddTab("Vulkan Features", oglExtensionsScroll);
 
471
 
 
472
                oglExtensions->Add(new ItemHeader("Vulkan Features"));
 
473
                std::vector<std::string> features = thin3d->GetFeatureList();
 
474
                for (auto &feature : features) {
 
475
                        oglExtensions->Add(new TextView(feature))->SetFocusable(true);
 
476
                }
 
477
        }
 
478
}
 
479
 
 
480
void AddressPromptScreen::CreatePopupContents(UI::ViewGroup *parent) {
 
481
        using namespace UI;
 
482
 
 
483
        I18NCategory *dev = GetI18NCategory("Developer");
 
484
 
 
485
        addrView_ = new TextView(dev->T("Enter address"), ALIGN_HCENTER, false);
 
486
        parent->Add(addrView_);
 
487
 
 
488
        ViewGroup *grid = new GridLayout(GridLayoutSettings(60, 40));
 
489
        parent->Add(grid);
 
490
 
 
491
        for (int i = 0; i < 16; ++i) {
 
492
                char temp[16];
 
493
                snprintf(temp, 16, " %X ", i);
 
494
                buttons_[i] = new Button(temp);
 
495
                grid->Add(buttons_[i])->OnClick.Handle(this, &AddressPromptScreen::OnDigitButton);
 
496
        }
 
497
 
 
498
        parent->Add(new Button(dev->T("Backspace")))->OnClick.Handle(this, &AddressPromptScreen::OnBackspace);
 
499
}
 
500
 
 
501
void AddressPromptScreen::OnCompleted(DialogResult result) {
 
502
        if (result == DR_OK) {
 
503
                UI::EventParams e;
 
504
                e.v = root_;
 
505
                e.a = addr_;
 
506
                OnChoice.Trigger(e);
 
507
        }
 
508
}
 
509
 
 
510
UI::EventReturn AddressPromptScreen::OnDigitButton(UI::EventParams &e) {
 
511
        for (int i = 0; i < 16; ++i) {
 
512
                if (buttons_[i] == e.v) {
 
513
                        AddDigit(i);
 
514
                }
 
515
        }
 
516
        return UI::EVENT_DONE;
 
517
}
 
518
 
 
519
UI::EventReturn AddressPromptScreen::OnBackspace(UI::EventParams &e) {
 
520
        BackspaceDigit();
 
521
        return UI::EVENT_DONE;
 
522
}
 
523
 
 
524
void AddressPromptScreen::AddDigit(int n) {
 
525
        if ((addr_ & 0xF0000000) == 0) {
 
526
                addr_ = addr_ * 16 + n;
 
527
        }
 
528
        UpdatePreviewDigits();
 
529
}
 
530
 
 
531
void AddressPromptScreen::BackspaceDigit() {
 
532
        addr_ /= 16;
 
533
        UpdatePreviewDigits();
 
534
}
 
535
 
 
536
void AddressPromptScreen::UpdatePreviewDigits() {
 
537
        I18NCategory *dev = GetI18NCategory("Developer");
 
538
 
 
539
        if (addr_ != 0) {
 
540
                char temp[32];
 
541
                snprintf(temp, 32, "%8X", addr_);
 
542
                addrView_->SetText(temp);
 
543
        } else {
 
544
                addrView_->SetText(dev->T("Enter address"));
 
545
        }
 
546
}
 
547
 
 
548
bool AddressPromptScreen::key(const KeyInput &key) {
 
549
        if (key.flags & KEY_DOWN) {
 
550
                if (key.keyCode >= NKCODE_0 && key.keyCode <= NKCODE_9) {
 
551
                        AddDigit(key.keyCode - NKCODE_0);
 
552
                } else if (key.keyCode >= NKCODE_A && key.keyCode <= NKCODE_F) {
 
553
                        AddDigit(10 + key.keyCode - NKCODE_A);
 
554
                // NKCODE_DEL is backspace.
 
555
                } else if (key.keyCode == NKCODE_DEL) {
 
556
                        BackspaceDigit();
 
557
                } else if (key.keyCode == NKCODE_ENTER) {
 
558
                        OnCompleted(DR_OK);
 
559
                        screenManager()->finishDialog(this, DR_OK);
 
560
                } else {
 
561
                        return UIDialogScreen::key(key);
 
562
                }
 
563
        } else {
 
564
                return UIDialogScreen::key(key);
 
565
        }
 
566
        return true;
 
567
}
 
568
 
 
569
// Three panes: Block chooser, MIPS view, ARM/x86 view
 
570
void JitCompareScreen::CreateViews() {
 
571
        I18NCategory *di = GetI18NCategory("Dialog");
 
572
        I18NCategory *dev = GetI18NCategory("Developer");
 
573
 
 
574
        using namespace UI;
 
575
        
 
576
        root_ = new LinearLayout(ORIENT_HORIZONTAL);
 
577
 
 
578
        ScrollView *leftColumnScroll = root_->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0f)));
 
579
        LinearLayout *leftColumn = leftColumnScroll->Add(new LinearLayout(ORIENT_VERTICAL));
 
580
 
 
581
        ScrollView *midColumnScroll = root_->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(2.0f)));
 
582
        LinearLayout *midColumn = midColumnScroll->Add(new LinearLayout(ORIENT_VERTICAL));
 
583
        midColumn->SetTag("JitCompareLeftDisasm");
 
584
        leftDisasm_ = midColumn->Add(new LinearLayout(ORIENT_VERTICAL));
 
585
        leftDisasm_->SetSpacing(0.0f);
 
586
 
 
587
        ScrollView *rightColumnScroll = root_->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(2.0f)));
 
588
        rightColumnScroll->SetTag("JitCompareRightDisasm");
 
589
        LinearLayout *rightColumn = rightColumnScroll->Add(new LinearLayout(ORIENT_VERTICAL));
 
590
        rightDisasm_ = rightColumn->Add(new LinearLayout(ORIENT_VERTICAL));
 
591
        rightDisasm_->SetSpacing(0.0f);
 
592
 
 
593
        leftColumn->Add(new Choice(dev->T("Current")))->OnClick.Handle(this, &JitCompareScreen::OnCurrentBlock);
 
594
        leftColumn->Add(new Choice(dev->T("By Address")))->OnClick.Handle(this, &JitCompareScreen::OnSelectBlock);
 
595
        leftColumn->Add(new Choice(dev->T("Prev")))->OnClick.Handle(this, &JitCompareScreen::OnPrevBlock);
 
596
        leftColumn->Add(new Choice(dev->T("Next")))->OnClick.Handle(this, &JitCompareScreen::OnNextBlock);
 
597
        leftColumn->Add(new Choice(dev->T("Random")))->OnClick.Handle(this, &JitCompareScreen::OnRandomBlock);
 
598
        leftColumn->Add(new Choice(dev->T("FPU")))->OnClick.Handle(this, &JitCompareScreen::OnRandomFPUBlock);
 
599
        leftColumn->Add(new Choice(dev->T("VFPU")))->OnClick.Handle(this, &JitCompareScreen::OnRandomVFPUBlock);
 
600
        leftColumn->Add(new Choice(dev->T("Stats")))->OnClick.Handle(this, &JitCompareScreen::OnShowStats);
 
601
        leftColumn->Add(new Choice(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
 
602
        blockName_ = leftColumn->Add(new TextView(dev->T("No block")));
 
603
        blockAddr_ = leftColumn->Add(new TextEdit("", "", new LayoutParams(FILL_PARENT, WRAP_CONTENT)));
 
604
        blockAddr_->OnTextChange.Handle(this, &JitCompareScreen::OnAddressChange);
 
605
        blockStats_ = leftColumn->Add(new TextView(""));
 
606
 
 
607
        EventParams ignore = {0};
 
608
        OnCurrentBlock(ignore);
 
609
}
 
610
 
 
611
void JitCompareScreen::UpdateDisasm() {
 
612
        leftDisasm_->Clear();
 
613
        rightDisasm_->Clear();
 
614
 
 
615
        using namespace UI;
 
616
 
 
617
        I18NCategory *dev = GetI18NCategory("Developer");
 
618
 
 
619
        JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
 
620
 
 
621
        char temp[256];
 
622
        snprintf(temp, sizeof(temp), "%i/%i", currentBlock_, blockCache->GetNumBlocks());
 
623
        blockName_->SetText(temp);
 
624
 
 
625
        if (currentBlock_ < 0 || currentBlock_ >= blockCache->GetNumBlocks()) {
 
626
                leftDisasm_->Add(new TextView(dev->T("No block")));
 
627
                rightDisasm_->Add(new TextView(dev->T("No block")));
 
628
                blockStats_->SetText("");
 
629
                return;
 
630
        }
 
631
 
 
632
        JitBlock *block = blockCache->GetBlock(currentBlock_);
 
633
 
 
634
        snprintf(temp, sizeof(temp), "%08x", block->originalAddress);
 
635
        blockAddr_->SetText(temp);
 
636
 
 
637
        // Alright. First generate the MIPS disassembly.
 
638
        
 
639
        // TODO: Need a way to communicate branch continuing.
 
640
        for (u32 addr = block->originalAddress; addr <= block->originalAddress + block->originalSize * 4; addr += 4) {
 
641
                char temp[256];
 
642
                MIPSDisAsm(Memory::Read_Instruction(addr), addr, temp, true);
 
643
                std::string mipsDis = temp;
 
644
                leftDisasm_->Add(new TextView(mipsDis))->SetFocusable(true);
 
645
        }
 
646
 
 
647
#if defined(ARM)
 
648
        std::vector<std::string> targetDis = DisassembleArm2(block->normalEntry, block->codeSize);
 
649
#elif defined(ARM64)
 
650
        std::vector<std::string> targetDis = DisassembleArm64(block->normalEntry, block->codeSize);
 
651
#elif defined(_M_IX86) || defined(_M_X64)
 
652
        std::vector<std::string> targetDis = DisassembleX86(block->normalEntry, block->codeSize);
 
653
#endif
 
654
#if defined(ARM) || defined(ARM64) || defined(_M_IX86) || defined(_M_X64)
 
655
        for (size_t i = 0; i < targetDis.size(); i++) {
 
656
                rightDisasm_->Add(new TextView(targetDis[i]))->SetFocusable(true);
 
657
        }
 
658
#endif
 
659
 
 
660
        int numMips = leftDisasm_->GetNumSubviews();
 
661
        int numHost = rightDisasm_->GetNumSubviews();
 
662
 
 
663
        snprintf(temp, sizeof(temp), "%d to %d : %d%%", numMips, numHost, 100 * numHost / numMips);
 
664
        blockStats_->SetText(temp);
 
665
}
 
666
 
 
667
UI::EventReturn JitCompareScreen::OnAddressChange(UI::EventParams &e) {
 
668
        if (!MIPSComp::jit) {
 
669
                return UI::EVENT_DONE;
 
670
        }
 
671
        JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
 
672
        if (!blockCache)
 
673
                return UI::EVENT_DONE;
 
674
        u32 addr;
 
675
        if (blockAddr_->GetText().size() > 8)
 
676
                return UI::EVENT_DONE;
 
677
        if (1 == sscanf(blockAddr_->GetText().c_str(), "%08x", &addr)) {
 
678
                if (Memory::IsValidAddress(addr)) {
 
679
                        currentBlock_ = blockCache->GetBlockNumberFromStartAddress(addr);
 
680
                        UpdateDisasm();
 
681
                }
 
682
        }
 
683
        return UI::EVENT_DONE;
 
684
}
 
685
 
 
686
UI::EventReturn JitCompareScreen::OnShowStats(UI::EventParams &e) {
 
687
        JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
 
688
        BlockCacheStats bcStats;
 
689
        blockCache->ComputeStats(bcStats);
 
690
        NOTICE_LOG(JIT, "Num blocks: %i", bcStats.numBlocks);
 
691
        NOTICE_LOG(JIT, "Average Bloat: %0.2f%%", 100 * bcStats.avgBloat);
 
692
        NOTICE_LOG(JIT, "Min Bloat: %0.2f%%  (%08x)", 100 * bcStats.minBloat, bcStats.minBloatBlock);
 
693
        NOTICE_LOG(JIT, "Max Bloat: %0.2f%%  (%08x)", 100 * bcStats.maxBloat, bcStats.maxBloatBlock);
 
694
 
 
695
        int ctr = 0, sz = (int)bcStats.bloatMap.size();
 
696
        for (auto iter : bcStats.bloatMap) {
 
697
                if (ctr < 10 || ctr > sz - 10) {
 
698
                        NOTICE_LOG(JIT, "%08x: %f", iter.second, iter.first);
 
699
                } else if (ctr == 10) {
 
700
                        NOTICE_LOG(JIT, "...");
 
701
                }
 
702
                ctr++;
 
703
        }
 
704
 
 
705
        return UI::EVENT_DONE;
 
706
}
 
707
 
 
708
 
 
709
UI::EventReturn JitCompareScreen::OnSelectBlock(UI::EventParams &e) {
 
710
        I18NCategory *dev = GetI18NCategory("Developer");
 
711
 
 
712
        auto addressPrompt = new AddressPromptScreen(dev->T("Block address"));
 
713
        addressPrompt->OnChoice.Handle(this, &JitCompareScreen::OnBlockAddress);
 
714
        screenManager()->push(addressPrompt);
 
715
        return UI::EVENT_DONE;
 
716
}
 
717
 
 
718
UI::EventReturn JitCompareScreen::OnPrevBlock(UI::EventParams &e) {
 
719
        currentBlock_--;
 
720
        UpdateDisasm();
 
721
        return UI::EVENT_DONE;
 
722
}
 
723
 
 
724
UI::EventReturn JitCompareScreen::OnNextBlock(UI::EventParams &e) {
 
725
        currentBlock_++;
 
726
        UpdateDisasm();
 
727
        return UI::EVENT_DONE;
 
728
}
 
729
 
 
730
UI::EventReturn JitCompareScreen::OnBlockAddress(UI::EventParams &e) {
 
731
        if (!MIPSComp::jit) {
 
732
                return UI::EVENT_DONE;
 
733
        }
 
734
 
 
735
        JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
 
736
        if (!blockCache)
 
737
                return UI::EVENT_DONE;
 
738
 
 
739
        if (Memory::IsValidAddress(e.a)) {
 
740
                currentBlock_ = blockCache->GetBlockNumberFromStartAddress(e.a);
 
741
        } else {
 
742
                currentBlock_ = -1;
 
743
        }
 
744
        UpdateDisasm();
 
745
        return UI::EVENT_DONE;
 
746
}
 
747
 
 
748
UI::EventReturn JitCompareScreen::OnRandomBlock(UI::EventParams &e) {
 
749
        if (!MIPSComp::jit) {
 
750
                return UI::EVENT_DONE;
 
751
        }
 
752
 
 
753
        JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
 
754
        if (!blockCache)
 
755
                return UI::EVENT_DONE;
 
756
 
 
757
        int numBlocks = blockCache->GetNumBlocks();
 
758
        if (numBlocks > 0) {
 
759
                currentBlock_ = rand() % numBlocks;
 
760
        }
 
761
        UpdateDisasm();
 
762
        return UI::EVENT_DONE;
 
763
}
 
764
 
 
765
UI::EventReturn JitCompareScreen::OnRandomVFPUBlock(UI::EventParams &e) {
 
766
        OnRandomBlock(IS_VFPU);
 
767
        return UI::EVENT_DONE;
 
768
}
 
769
 
 
770
UI::EventReturn JitCompareScreen::OnRandomFPUBlock(UI::EventParams &e) {
 
771
        OnRandomBlock(IS_FPU);
 
772
        return UI::EVENT_DONE;
 
773
}
 
774
 
 
775
void JitCompareScreen::OnRandomBlock(int flag) {
 
776
        if (!MIPSComp::jit) {
 
777
                return;
 
778
        }
 
779
        JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
 
780
        if (!blockCache)
 
781
                return;
 
782
 
 
783
        int numBlocks = blockCache->GetNumBlocks();
 
784
        if (numBlocks > 0) {
 
785
                bool anyWanted = false;
 
786
                int tries = 0;
 
787
                while (!anyWanted && tries < 10000) {
 
788
                        currentBlock_ = rand() % numBlocks;
 
789
                        const JitBlock *b = blockCache->GetBlock(currentBlock_);
 
790
                        for (u32 addr = b->originalAddress; addr <= b->originalAddress + b->originalSize; addr += 4) {
 
791
                                MIPSOpcode opcode = Memory::Read_Instruction(addr);
 
792
                                if (MIPSGetInfo(opcode) & flag) {
 
793
                                        char temp[256];
 
794
                                        MIPSDisAsm(opcode, addr, temp);
 
795
                                        // INFO_LOG(HLE, "Stopping VFPU instruction: %s", temp);
 
796
                                        anyWanted = true;
 
797
                                        break;
 
798
                                }
 
799
                        }
 
800
                        tries++;
 
801
                }
 
802
        }
 
803
        UpdateDisasm();
 
804
}
 
805
 
 
806
UI::EventReturn JitCompareScreen::OnCurrentBlock(UI::EventParams &e) {
 
807
        if (!MIPSComp::jit) {
 
808
                return UI::EVENT_DONE;
 
809
        }
 
810
        JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache();
 
811
        if (!blockCache)
 
812
                return UI::EVENT_DONE;
 
813
        std::vector<int> blockNum;
 
814
        blockCache->GetBlockNumbersFromAddress(currentMIPS->pc, &blockNum);
 
815
        if (blockNum.size() > 0) {
 
816
                currentBlock_ = blockNum[0];
 
817
        } else {
 
818
                currentBlock_ = -1;
 
819
        }
 
820
        UpdateDisasm();
 
821
        return UI::EVENT_DONE;
 
822
}
 
823
 
 
824
void ShaderListScreen::ListShaders(DebugShaderType shaderType, UI::LinearLayout *view) {
 
825
        using namespace UI;
 
826
        std::vector<std::string> shaderIds_ = gpu->DebugGetShaderIDs(shaderType);
 
827
        for (auto id : shaderIds_) {
 
828
                Choice *choice = view->Add(new Choice(gpu->DebugGetShaderString(id, shaderType, SHADER_STRING_SHORT_DESC)));
 
829
                choice->SetTag(id);
 
830
                choice->OnClick.Handle(this, &ShaderListScreen::OnShaderClick);
 
831
        }
 
832
}
 
833
 
 
834
struct { DebugShaderType type; const char *name; } shaderTypes[] = {
 
835
        { SHADER_TYPE_VERTEX, "Vertex" },
 
836
        { SHADER_TYPE_FRAGMENT, "Fragment" },
 
837
        // { SHADER_TYPE_GEOMETRY, "Geometry" },
 
838
        { SHADER_TYPE_VERTEXLOADER, "VertexLoader" },
 
839
};
 
840
 
 
841
void ShaderListScreen::CreateViews() {
 
842
        using namespace UI;
 
843
 
 
844
        I18NCategory *di = GetI18NCategory("Dialog");
 
845
 
 
846
        LinearLayout *layout = new LinearLayout(ORIENT_VERTICAL);
 
847
        root_ = layout;
 
848
 
 
849
        tabs_ = new TabHolder(ORIENT_HORIZONTAL, 40, new LinearLayoutParams(1.0));
 
850
        tabs_->SetTag("DevShaderList");
 
851
        layout->Add(tabs_);
 
852
        layout->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
 
853
 
 
854
        for (size_t i = 0; i < ARRAY_SIZE(shaderTypes); i++) {
 
855
                ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
 
856
                LinearLayout *shaderList = new LinearLayout(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT));
 
857
                ListShaders(shaderTypes[i].type, shaderList);
 
858
                scroll->Add(shaderList);
 
859
                tabs_->AddTab(shaderTypes[i].name, scroll);
 
860
        }
 
861
}
 
862
 
 
863
UI::EventReturn ShaderListScreen::OnShaderClick(UI::EventParams &e) {
 
864
        using namespace UI;
 
865
        std::string id = e.v->Tag();
 
866
        DebugShaderType type = shaderTypes[tabs_->GetCurrentTab()].type;
 
867
        screenManager()->push(new ShaderViewScreen(id, type));
 
868
        return EVENT_DONE;
 
869
}
 
870
 
 
871
void ShaderViewScreen::CreateViews() {
 
872
        using namespace UI;
 
873
 
 
874
        I18NCategory *di = GetI18NCategory("Dialog");
 
875
 
 
876
        LinearLayout *layout = new LinearLayout(ORIENT_VERTICAL);
 
877
        root_ = layout;
 
878
 
 
879
        layout->Add(new TextView(gpu->DebugGetShaderString(id_, type_, SHADER_STRING_SHORT_DESC)));
 
880
 
 
881
        ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0));
 
882
        scroll->SetTag("DevShaderView");
 
883
        layout->Add(scroll);
 
884
 
 
885
        LinearLayout *lineLayout = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));
 
886
        lineLayout->SetSpacing(0.0);
 
887
        scroll->Add(lineLayout);
 
888
 
 
889
        std::vector<std::string> lines;
 
890
        SplitString(gpu->DebugGetShaderString(id_, type_, SHADER_STRING_SOURCE_CODE), '\n', lines);
 
891
 
 
892
        for (auto line : lines) {
 
893
                lineLayout->Add(new TextView(line, FLAG_DYNAMIC_ASCII, true));
 
894
        }
 
895
 
 
896
        layout->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);
 
897
}