CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/UI/DevScreens.cpp
Views: 1401
// Copyright (c) 2013- PPSSPP Project.12// This program is free software: you can redistribute it and/or modify3// it under the terms of the GNU General Public License as published by4// the Free Software Foundation, version 2.0 or later versions.56// This program is distributed in the hope that it will be useful,7// but WITHOUT ANY WARRANTY; without even the implied warranty of8// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9// GNU General Public License 2.0 for more details.1011// A copy of the GPL 2.0 should have been included with the program.12// If not, see http://www.gnu.org/licenses/1314// Official git repository and contact information can be found at15// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.1617// Hack around name collisions between UI and xlib18// Only affects this file.19#undef VK_USE_PLATFORM_XLIB_KHR20#undef VK_USE_PLATFORM_XCB_KHR21#undef VK_USE_PLATFORM_DIRECTFB_EXT22#undef VK_USE_PLATFORM_XLIB_XRANDR_EXT2324#include <algorithm>25#include <cstring>2627#include "ppsspp_config.h"2829#include "Common/Common.h"30#include "Common/System/Display.h"31#include "Common/System/NativeApp.h"32#include "Common/System/System.h"33#include "Common/System/OSD.h"34#include "Common/GPU/OpenGL/GLFeatures.h"3536#include "Common/File/AndroidStorage.h"37#include "Common/Data/Text/I18n.h"38#include "Common/Data/Encoding/Utf8.h"39#include "Common/Net/HTTPClient.h"40#include "Common/UI/Context.h"41#include "Common/UI/View.h"42#include "Common/UI/ViewGroup.h"43#include "Common/UI/UI.h"44#include "Common/UI/IconCache.h"45#include "Common/Data/Text/Parsers.h"46#include "Common/Profiler/Profiler.h"4748#include "Common/Log/LogManager.h"49#include "Common/CPUDetect.h"50#include "Common/StringUtils.h"5152#include "Core/MemMap.h"53#include "Core/Config.h"54#include "Core/ConfigValues.h"55#include "Core/System.h"56#include "Core/Reporting.h"57#include "Core/CoreParameter.h"58#include "Core/HLE/sceKernel.h" // GPI/GPO59#include "Core/MIPS/MIPSTables.h"60#include "Core/MIPS/JitCommon/JitBlockCache.h"61#include "Core/MIPS/JitCommon/JitCommon.h"62#include "Core/MIPS/JitCommon/JitState.h"63#include "GPU/Debugger/Record.h"64#include "GPU/GPUInterface.h"65#include "GPU/GPUState.h"66#include "UI/MiscScreens.h"67#include "UI/DevScreens.h"68#include "UI/MainScreen.h"69#include "UI/ControlMappingScreen.h"70#include "UI/GameSettingsScreen.h"71#include "UI/JitCompareScreen.h"7273#ifdef _WIN3274// Want to avoid including the full header here as it includes d3dx.h75int GetD3DCompilerVersion();76#endif7778#include "android/jni/app-android.h"7980static const char *logLevelList[] = {81"Notice",82"Error",83"Warn",84"Info",85"Debug",86"Verb."87};8889static const char *g_debugOverlayList[] = {90"Off",91"Debug stats",92"Draw Frametimes Graph",93"Frame timing",94#ifdef USE_PROFILER95"Frame profile",96#endif97"Control Debug",98"Audio Debug",99"GPU Profile",100"GPU Allocator Viewer",101"Framebuffer List",102};103104void AddOverlayList(UI::ViewGroup *items, ScreenManager *screenManager) {105using namespace UI;106auto dev = GetI18NCategory(I18NCat::DEVELOPER);107int numOverlays = ARRAY_SIZE(g_debugOverlayList);108if (!(g_Config.iGPUBackend == (int)GPUBackend::VULKAN || g_Config.iGPUBackend == (int)GPUBackend::OPENGL)) {109numOverlays -= 2; // skip the last 2.110}111items->Add(new PopupMultiChoice((int *)&g_Config.iDebugOverlay, dev->T("Debug overlay"), g_debugOverlayList, 0, numOverlays, I18NCat::DEVELOPER, screenManager));112}113114void DevMenuScreen::CreatePopupContents(UI::ViewGroup *parent) {115using namespace UI;116auto dev = GetI18NCategory(I18NCat::DEVELOPER);117auto sy = GetI18NCategory(I18NCat::SYSTEM);118119ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT, 1.0f));120LinearLayout *items = new LinearLayout(ORIENT_VERTICAL);121122#if !defined(MOBILE_DEVICE)123items->Add(new Choice(dev->T("Log View")))->OnClick.Handle(this, &DevMenuScreen::OnLogView);124#endif125items->Add(new Choice(dev->T("Logging Channels")))->OnClick.Handle(this, &DevMenuScreen::OnLogConfig);126items->Add(new Choice(sy->T("Developer Tools")))->OnClick.Handle(this, &DevMenuScreen::OnDeveloperTools);127128// Debug overlay129AddOverlayList(items, screenManager());130131items->Add(new Choice(dev->T("Jit Compare")))->OnClick.Handle(this, &DevMenuScreen::OnJitCompare);132items->Add(new Choice(dev->T("Shader Viewer")))->OnClick.Handle(this, &DevMenuScreen::OnShaderView);133134items->Add(new Choice(dev->T("Toggle Freeze")))->OnClick.Add([](UI::EventParams &e) {135if (PSP_CoreParameter().frozen) {136PSP_CoreParameter().frozen = false;137} else {138PSP_CoreParameter().freezeNext = true;139}140return UI::EVENT_DONE;141});142143items->Add(new Choice(dev->T("Reset limited logging")))->OnClick.Handle(this, &DevMenuScreen::OnResetLimitedLogging);144145items->Add(new Choice(dev->T("GPI/GPO switches/LEDs")))->OnClick.Add([=](UI::EventParams &e) {146screenManager()->push(new GPIGPOScreen(dev->T("GPI/GPO switches/LEDs")));147return UI::EVENT_DONE;148});149150items->Add(new Choice(dev->T("Create frame dump")))->OnClick.Add([](UI::EventParams &e) {151GPURecord::RecordNextFrame([](const Path &dumpPath) {152NOTICE_LOG(Log::System, "Frame dump created at '%s'", dumpPath.c_str());153if (System_GetPropertyBool(SYSPROP_CAN_SHOW_FILE)) {154System_ShowFileInFolder(dumpPath);155} else {156g_OSD.Show(OSDType::MESSAGE_SUCCESS, dumpPath.ToVisualString(), 7.0f);157}158});159return UI::EVENT_DONE;160});161162// This one is not very useful these days, and only really on desktop. Hide it on other platforms.163if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_DESKTOP) {164items->Add(new Choice(dev->T("Dump next frame to log")))->OnClick.Add([](UI::EventParams &e) {165gpu->DumpNextFrame();166return UI::EVENT_DONE;167});168}169170scroll->Add(items);171parent->Add(scroll);172173RingbufferLogListener *ring = LogManager::GetInstance()->GetRingbufferListener();174if (ring) {175ring->SetEnabled(true);176}177}178179UI::EventReturn DevMenuScreen::OnResetLimitedLogging(UI::EventParams &e) {180Reporting::ResetCounts();181return UI::EVENT_DONE;182}183184UI::EventReturn DevMenuScreen::OnLogView(UI::EventParams &e) {185UpdateUIState(UISTATE_PAUSEMENU);186screenManager()->push(new LogScreen());187return UI::EVENT_DONE;188}189190UI::EventReturn DevMenuScreen::OnLogConfig(UI::EventParams &e) {191UpdateUIState(UISTATE_PAUSEMENU);192screenManager()->push(new LogConfigScreen());193return UI::EVENT_DONE;194}195196UI::EventReturn DevMenuScreen::OnDeveloperTools(UI::EventParams &e) {197UpdateUIState(UISTATE_PAUSEMENU);198screenManager()->push(new DeveloperToolsScreen(gamePath_));199return UI::EVENT_DONE;200}201202UI::EventReturn DevMenuScreen::OnJitCompare(UI::EventParams &e) {203UpdateUIState(UISTATE_PAUSEMENU);204screenManager()->push(new JitCompareScreen());205return UI::EVENT_DONE;206}207208UI::EventReturn DevMenuScreen::OnShaderView(UI::EventParams &e) {209UpdateUIState(UISTATE_PAUSEMENU);210if (gpu) // Avoid crashing if chosen while the game is being loaded.211screenManager()->push(new ShaderListScreen());212return UI::EVENT_DONE;213}214215void DevMenuScreen::dialogFinished(const Screen *dialog, DialogResult result) {216UpdateUIState(UISTATE_INGAME);217// Close when a subscreen got closed.218// TODO: a bug in screenmanager causes this not to work here.219// TriggerFinish(DR_OK);220}221222void GPIGPOScreen::CreatePopupContents(UI::ViewGroup *parent) {223using namespace UI;224auto dev = GetI18NCategory(I18NCat::DEVELOPER);225parent->Add(new CheckBox(&g_Config.bShowGPOLEDs, dev->T("Show GPO LEDs")));226for (int i = 0; i < 8; i++) {227std::string name = ApplySafeSubstitutions(dev->T("GPI switch %1"), i);228parent->Add(new BitCheckBox(&g_GPIBits, 1 << i, name));229}230}231232void LogScreen::UpdateLog() {233using namespace UI;234RingbufferLogListener *ring = LogManager::GetInstance()->GetRingbufferListener();235if (!ring)236return;237vert_->Clear();238for (int i = ring->GetCount() - 1; i >= 0; i--) {239TextView *v = vert_->Add(new TextView(ring->TextAt(i), FLAG_DYNAMIC_ASCII, false));240uint32_t color = 0xFFFFFF;241switch (ring->LevelAt(i)) {242case LogLevel::LDEBUG: color = 0xE0E0E0; break;243case LogLevel::LWARNING: color = 0x50FFFF; break;244case LogLevel::LERROR: color = 0x5050FF; break;245case LogLevel::LNOTICE: color = 0x30FF30; break;246case LogLevel::LINFO: color = 0xFFFFFF; break;247case LogLevel::LVERBOSE: color = 0xC0C0C0; break;248}249v->SetTextColor(0xFF000000 | color);250}251toBottom_ = true;252}253254void LogScreen::update() {255UIDialogScreenWithBackground::update();256if (toBottom_) {257toBottom_ = false;258scroll_->ScrollToBottom();259}260}261262void LogScreen::CreateViews() {263using namespace UI;264auto di = GetI18NCategory(I18NCat::DIALOG);265266LinearLayout *outer = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));267root_ = outer;268269scroll_ = outer->Add(new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0)));270LinearLayout *bottom = outer->Add(new LinearLayout(ORIENT_HORIZONTAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT)));271bottom->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);272cmdLine_ = bottom->Add(new TextEdit("", "Command", "Command Line", new LinearLayoutParams(1.0)));273cmdLine_->OnEnter.Handle(this, &LogScreen::OnSubmit);274bottom->Add(new Button(di->T("Submit")))->OnClick.Handle(this, &LogScreen::OnSubmit);275276vert_ = scroll_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));277vert_->SetSpacing(0);278279UpdateLog();280}281282UI::EventReturn LogScreen::OnSubmit(UI::EventParams &e) {283std::string cmd = cmdLine_->GetText();284285// TODO: Can add all sorts of fun stuff here that we can't be bothered writing proper UI for, like various memdumps etc.286287NOTICE_LOG(Log::System, "Submitted: %s", cmd.c_str());288289UpdateLog();290cmdLine_->SetText("");291cmdLine_->SetFocus();292return UI::EVENT_DONE;293}294295void LogConfigScreen::CreateViews() {296using namespace UI;297298auto di = GetI18NCategory(I18NCat::DIALOG);299auto dev = GetI18NCategory(I18NCat::DEVELOPER);300301root_ = new ScrollView(ORIENT_VERTICAL);302303LinearLayout *vert = root_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));304vert->SetSpacing(0);305306LinearLayout *topbar = new LinearLayout(ORIENT_HORIZONTAL);307topbar->Add(new Choice(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);308topbar->Add(new Choice(di->T("Toggle All")))->OnClick.Handle(this, &LogConfigScreen::OnToggleAll);309topbar->Add(new Choice(di->T("Enable All")))->OnClick.Handle(this, &LogConfigScreen::OnEnableAll);310topbar->Add(new Choice(di->T("Disable All")))->OnClick.Handle(this, &LogConfigScreen::OnDisableAll);311topbar->Add(new Choice(dev->T("Log Level")))->OnClick.Handle(this, &LogConfigScreen::OnLogLevel);312313vert->Add(topbar);314315vert->Add(new ItemHeader(dev->T("Logging Channels")));316317LogManager *logMan = LogManager::GetInstance();318319int cellSize = 400;320321UI::GridLayoutSettings gridsettings(cellSize, 64, 5);322gridsettings.fillCells = true;323GridLayout *grid = vert->Add(new GridLayoutList(gridsettings, new LayoutParams(FILL_PARENT, WRAP_CONTENT)));324325for (int i = 0; i < LogManager::GetNumChannels(); i++) {326Log type = (Log)i;327LogChannel *chan = logMan->GetLogChannel(type);328LinearLayout *row = new LinearLayout(ORIENT_HORIZONTAL, new LinearLayoutParams(cellSize - 50, WRAP_CONTENT));329row->SetSpacing(0);330row->Add(new CheckBox(&chan->enabled, "", "", new LinearLayoutParams(50, WRAP_CONTENT)));331row->Add(new PopupMultiChoice((int *)&chan->level, chan->m_shortName, logLevelList, 1, 6, I18NCat::NONE, screenManager(), new LinearLayoutParams(1.0)));332grid->Add(row);333}334}335336UI::EventReturn LogConfigScreen::OnToggleAll(UI::EventParams &e) {337LogManager *logMan = LogManager::GetInstance();338for (int i = 0; i < LogManager::GetNumChannels(); i++) {339LogChannel *chan = logMan->GetLogChannel((Log)i);340chan->enabled = !chan->enabled;341}342return UI::EVENT_DONE;343}344345UI::EventReturn LogConfigScreen::OnEnableAll(UI::EventParams &e) {346LogManager *logMan = LogManager::GetInstance();347for (int i = 0; i < LogManager::GetNumChannels(); i++) {348LogChannel *chan = logMan->GetLogChannel((Log)i);349chan->enabled = true;350}351return UI::EVENT_DONE;352}353354UI::EventReturn LogConfigScreen::OnDisableAll(UI::EventParams &e) {355LogManager *logMan = LogManager::GetInstance();356for (int i = 0; i < LogManager::GetNumChannels(); i++) {357LogChannel *chan = logMan->GetLogChannel((Log)i);358chan->enabled = false;359}360return UI::EVENT_DONE;361}362363UI::EventReturn LogConfigScreen::OnLogLevelChange(UI::EventParams &e) {364RecreateViews();365return UI::EVENT_DONE;366}367368UI::EventReturn LogConfigScreen::OnLogLevel(UI::EventParams &e) {369auto dev = GetI18NCategory(I18NCat::DEVELOPER);370371auto logLevelScreen = new LogLevelScreen(dev->T("Log Level"));372logLevelScreen->OnChoice.Handle(this, &LogConfigScreen::OnLogLevelChange);373if (e.v)374logLevelScreen->SetPopupOrigin(e.v);375screenManager()->push(logLevelScreen);376return UI::EVENT_DONE;377}378379LogLevelScreen::LogLevelScreen(std::string_view title) : ListPopupScreen(title) {380int NUMLOGLEVEL = 6;381std::vector<std::string> list;382for (int i = 0; i < NUMLOGLEVEL; ++i) {383list.push_back(logLevelList[i]);384}385adaptor_ = UI::StringVectorListAdaptor(list, -1);386}387388void LogLevelScreen::OnCompleted(DialogResult result) {389if (result != DR_OK)390return;391int selected = listView_->GetSelected();392LogManager *logMan = LogManager::GetInstance();393394for (int i = 0; i < LogManager::GetNumChannels(); ++i) {395Log type = (Log)i;396LogChannel *chan = logMan->GetLogChannel(type);397if (chan->enabled)398chan->level = (LogLevel)(selected + 1);399}400}401402struct JitDisableFlag {403MIPSComp::JitDisable flag;404const char *name;405};406407// Please do not try to translate these :)408static const JitDisableFlag jitDisableFlags[] = {409{ MIPSComp::JitDisable::ALU, "ALU" },410{ MIPSComp::JitDisable::ALU_IMM, "ALU_IMM" },411{ MIPSComp::JitDisable::ALU_BIT, "ALU_BIT" },412{ MIPSComp::JitDisable::MULDIV, "MULDIV" },413{ MIPSComp::JitDisable::FPU, "FPU" },414{ MIPSComp::JitDisable::FPU_COMP, "FPU_COMP" },415{ MIPSComp::JitDisable::FPU_XFER, "FPU_XFER" },416{ MIPSComp::JitDisable::VFPU_VEC, "VFPU_VEC" },417{ MIPSComp::JitDisable::VFPU_MTX_VTFM, "VFPU_MTX_VTFM" },418{ MIPSComp::JitDisable::VFPU_MTX_VMSCL, "VFPU_MTX_VMSCL" },419{ MIPSComp::JitDisable::VFPU_MTX_VMMUL, "VFPU_MTX_VMMUL" },420{ MIPSComp::JitDisable::VFPU_MTX_VMMOV, "VFPU_MTX_VMMOV" },421{ MIPSComp::JitDisable::VFPU_COMP, "VFPU_COMP" },422{ MIPSComp::JitDisable::VFPU_XFER, "VFPU_XFER" },423{ MIPSComp::JitDisable::LSU, "LSU" },424{ MIPSComp::JitDisable::LSU_UNALIGNED, "LSU_UNALIGNED" },425{ MIPSComp::JitDisable::LSU_FPU, "LSU_FPU" },426{ MIPSComp::JitDisable::LSU_VFPU, "LSU_VFPU" },427{ MIPSComp::JitDisable::SIMD, "SIMD" },428{ MIPSComp::JitDisable::BLOCKLINK, "Block Linking" },429{ MIPSComp::JitDisable::POINTERIFY, "Pointerify" },430{ MIPSComp::JitDisable::STATIC_ALLOC, "Static regalloc" },431{ MIPSComp::JitDisable::CACHE_POINTERS, "Cached pointers" },432{ MIPSComp::JitDisable::REGALLOC_GPR, "GPR Regalloc across instructions" },433{ MIPSComp::JitDisable::REGALLOC_FPR, "FPR Regalloc across instructions" },434};435436void JitDebugScreen::CreateViews() {437using namespace UI;438439auto di = GetI18NCategory(I18NCat::DIALOG);440auto dev = GetI18NCategory(I18NCat::DEVELOPER);441442root_ = new ScrollView(ORIENT_VERTICAL);443444LinearLayout *vert = root_->Add(new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT)));445vert->SetSpacing(0);446447LinearLayout *topbar = new LinearLayout(ORIENT_HORIZONTAL);448topbar->Add(new Choice(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);449topbar->Add(new Choice(di->T("Disable All")))->OnClick.Handle(this, &JitDebugScreen::OnDisableAll);450topbar->Add(new Choice(di->T("Enable All")))->OnClick.Handle(this, &JitDebugScreen::OnEnableAll);451452vert->Add(topbar);453vert->Add(new ItemHeader(dev->T("Disabled JIT functionality")));454455for (auto flag : jitDisableFlags) {456// Do not add translation of these.457vert->Add(new BitCheckBox(&g_Config.uJitDisableFlags, (uint32_t)flag.flag, flag.name));458}459}460461UI::EventReturn JitDebugScreen::OnEnableAll(UI::EventParams &e) {462g_Config.uJitDisableFlags &= ~(uint32_t)MIPSComp::JitDisable::ALL_FLAGS;463return UI::EVENT_DONE;464}465466UI::EventReturn JitDebugScreen::OnDisableAll(UI::EventParams &e) {467g_Config.uJitDisableFlags |= (uint32_t)MIPSComp::JitDisable::ALL_FLAGS;468return UI::EVENT_DONE;469}470471void SystemInfoScreen::update() {472TabbedUIDialogScreenWithGameBackground::update();473g_OSD.NudgeSidebar();474}475476void SystemInfoScreen::CreateTabs() {477using namespace Draw;478using namespace UI;479480auto di = GetI18NCategory(I18NCat::DIALOG);481auto si = GetI18NCategory(I18NCat::SYSINFO);482auto sy = GetI18NCategory(I18NCat::SYSTEM);483auto gr = GetI18NCategory(I18NCat::GRAPHICS);484485LinearLayout *deviceSpecs = AddTab("Device Info", si->T("Device Info"));486487CollapsibleSection *systemInfo = deviceSpecs->Add(new CollapsibleSection(si->T("System Information")));488systemInfo->Add(new InfoItem(si->T("System Name", "Name"), System_GetProperty(SYSPROP_NAME)));489#if PPSSPP_PLATFORM(ANDROID)490systemInfo->Add(new InfoItem(si->T("System Version"), StringFromInt(System_GetPropertyInt(SYSPROP_SYSTEMVERSION))));491#elif PPSSPP_PLATFORM(WINDOWS)492std::string sysVersion = System_GetProperty(SYSPROP_SYSTEMBUILD);493if (!sysVersion.empty()) {494systemInfo->Add(new InfoItem(si->T("OS Build"), sysVersion));495}496#endif497systemInfo->Add(new InfoItem(si->T("Lang/Region"), System_GetProperty(SYSPROP_LANGREGION)));498std::string board = System_GetProperty(SYSPROP_BOARDNAME);499if (!board.empty())500systemInfo->Add(new InfoItem(si->T("Board"), board));501systemInfo->Add(new InfoItem(si->T("ABI"), GetCompilerABI()));502if (System_GetPropertyBool(SYSPROP_DEBUGGER_PRESENT)) {503systemInfo->Add(new InfoItem(si->T("Debugger Present"), di->T("Yes")));504}505506CollapsibleSection *cpuInfo = deviceSpecs->Add(new CollapsibleSection(si->T("CPU Information")));507508// Don't bother showing the CPU name if we don't have one.509if (strcmp(cpu_info.brand_string, "Unknown") != 0) {510cpuInfo->Add(new InfoItem(si->T("CPU Name", "Name"), cpu_info.brand_string));511}512513int totalThreads = cpu_info.num_cores * cpu_info.logical_cpu_count;514std::string cores = StringFromFormat(si->T_cstr("%d (%d per core, %d cores)"), totalThreads, cpu_info.logical_cpu_count, cpu_info.num_cores);515cpuInfo->Add(new InfoItem(si->T("Threads"), cores));516#if PPSSPP_PLATFORM(IOS)517cpuInfo->Add(new InfoItem(si->T("JIT available"), System_GetPropertyBool(SYSPROP_CAN_JIT) ? di->T("Yes") : di->T("No")));518#endif519520CollapsibleSection *gpuInfo = deviceSpecs->Add(new CollapsibleSection(si->T("GPU Information")));521522DrawContext *draw = screenManager()->getDrawContext();523524const std::string apiNameKey = draw->GetInfoString(InfoField::APINAME);525std::string_view apiName = gr->T(apiNameKey);526gpuInfo->Add(new InfoItem(si->T("3D API"), apiName));527528// TODO: Not really vendor, on most APIs it's a device name (GL calls it vendor though).529std::string vendorString;530if (draw->GetDeviceCaps().deviceID != 0) {531vendorString = StringFromFormat("%s (%08x)", draw->GetInfoString(InfoField::VENDORSTRING).c_str(), draw->GetDeviceCaps().deviceID);532} else {533vendorString = draw->GetInfoString(InfoField::VENDORSTRING);534}535gpuInfo->Add(new InfoItem(si->T("Vendor"), vendorString));536std::string vendor = draw->GetInfoString(InfoField::VENDOR);537if (vendor.size())538gpuInfo->Add(new InfoItem(si->T("Vendor (detected)"), vendor));539gpuInfo->Add(new InfoItem(si->T("Driver Version"), draw->GetInfoString(InfoField::DRIVER)));540#ifdef _WIN32541if (GetGPUBackend() != GPUBackend::VULKAN) {542gpuInfo->Add(new InfoItem(si->T("Driver Version"), System_GetProperty(SYSPROP_GPUDRIVER_VERSION)));543}544#if !PPSSPP_PLATFORM(UWP)545if (GetGPUBackend() == GPUBackend::DIRECT3D9) {546gpuInfo->Add(new InfoItem(si->T("D3DCompiler Version"), StringFromFormat("%d", GetD3DCompilerVersion())));547}548#endif549#endif550if (GetGPUBackend() == GPUBackend::OPENGL) {551gpuInfo->Add(new InfoItem(si->T("Core Context"), gl_extensions.IsCoreContext ? di->T("Active") : di->T("Inactive")));552int highp_int_min = gl_extensions.range[1][5][0];553int highp_int_max = gl_extensions.range[1][5][1];554int highp_float_min = gl_extensions.range[1][2][0];555int highp_float_max = gl_extensions.range[1][2][1];556if (highp_int_max != 0) {557char temp[128];558snprintf(temp, sizeof(temp), "%d-%d", highp_int_min, highp_int_max);559gpuInfo->Add(new InfoItem(si->T("High precision int range"), temp));560}561if (highp_float_max != 0) {562char temp[128];563snprintf(temp, sizeof(temp), "%d-%d", highp_int_min, highp_int_max);564gpuInfo->Add(new InfoItem(si->T("High precision float range"), temp));565}566}567gpuInfo->Add(new InfoItem(si->T("Depth buffer format"), DataFormatToString(draw->GetDeviceCaps().preferredDepthBufferFormat)));568569std::string texCompressionFormats;570// Simple non-detailed summary of supported tex compression formats.571if (draw->GetDataFormatSupport(Draw::DataFormat::ETC2_R8G8B8_UNORM_BLOCK)) texCompressionFormats += "ETC2 ";572if (draw->GetDataFormatSupport(Draw::DataFormat::ASTC_4x4_UNORM_BLOCK)) texCompressionFormats += "ASTC ";573if (draw->GetDataFormatSupport(Draw::DataFormat::BC1_RGBA_UNORM_BLOCK)) texCompressionFormats += "BC1-3 ";574if (draw->GetDataFormatSupport(Draw::DataFormat::BC4_UNORM_BLOCK)) texCompressionFormats += "BC4-5 ";575if (draw->GetDataFormatSupport(Draw::DataFormat::BC7_UNORM_BLOCK)) texCompressionFormats += "BC7 ";576gpuInfo->Add(new InfoItem(si->T("Compressed texture formats"), texCompressionFormats));577578CollapsibleSection *osInformation = deviceSpecs->Add(new CollapsibleSection(si->T("OS Information")));579osInformation->Add(new InfoItem(si->T("Memory Page Size"), StringFromFormat(si->T_cstr("%d bytes"), GetMemoryProtectPageSize())));580osInformation->Add(new InfoItem(si->T("RW/RX exclusive"), PlatformIsWXExclusive() ? di->T("Active") : di->T("Inactive")));581#if PPSSPP_PLATFORM(ANDROID)582osInformation->Add(new InfoItem(si->T("Sustained perf mode"), System_GetPropertyBool(SYSPROP_SUPPORTS_SUSTAINED_PERF_MODE) ? di->T("Supported") : di->T("Unsupported")));583#endif584585std::string_view build = si->T("Release");586#ifdef _DEBUG587build = si->T("Debug");588#endif589osInformation->Add(new InfoItem(si->T("PPSSPP build"), build));590591CollapsibleSection *audioInformation = deviceSpecs->Add(new CollapsibleSection(si->T("Audio Information")));592audioInformation->Add(new InfoItem(si->T("Sample rate"), StringFromFormat(si->T_cstr("%d Hz"), System_GetPropertyInt(SYSPROP_AUDIO_SAMPLE_RATE))));593int framesPerBuffer = System_GetPropertyInt(SYSPROP_AUDIO_FRAMES_PER_BUFFER);594if (framesPerBuffer > 0) {595audioInformation->Add(new InfoItem(si->T("Frames per buffer"), StringFromFormat("%d", framesPerBuffer)));596}597#if PPSSPP_PLATFORM(ANDROID)598audioInformation->Add(new InfoItem(si->T("Optimal sample rate"), StringFromFormat(si->T_cstr("%d Hz"), System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_SAMPLE_RATE))));599audioInformation->Add(new InfoItem(si->T("Optimal frames per buffer"), StringFromFormat("%d", System_GetPropertyInt(SYSPROP_AUDIO_OPTIMAL_FRAMES_PER_BUFFER))));600#endif601602CollapsibleSection *displayInfo = deviceSpecs->Add(new CollapsibleSection(si->T("Display Information")));603#if PPSSPP_PLATFORM(ANDROID) || PPSSPP_PLATFORM(UWP)604displayInfo->Add(new InfoItem(si->T("Native resolution"), StringFromFormat("%dx%d",605System_GetPropertyInt(SYSPROP_DISPLAY_XRES),606System_GetPropertyInt(SYSPROP_DISPLAY_YRES))));607#endif608displayInfo->Add(new InfoItem(si->T("UI resolution"), StringFromFormat("%dx%d (%s: %0.2f)",609g_display.dp_xres,610g_display.dp_yres,611si->T_cstr("DPI"),612g_display.dpi)));613displayInfo->Add(new InfoItem(si->T("Pixel resolution"), StringFromFormat("%dx%d",614g_display.pixel_xres,615g_display.pixel_yres)));616617const float insets[4] = {618System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_LEFT),619System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_TOP),620System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_RIGHT),621System_GetPropertyFloat(SYSPROP_DISPLAY_SAFE_INSET_BOTTOM),622};623if (insets[0] != 0.0f || insets[1] != 0.0f || insets[2] != 0.0f || insets[3] != 0.0f) {624displayInfo->Add(new InfoItem(si->T("Screen notch insets"), StringFromFormat("%0.1f %0.1f %0.1f %0.1f", insets[0], insets[1], insets[2], insets[3])));625}626627// Don't show on Windows, since it's always treated as 60 there.628displayInfo->Add(new InfoItem(si->T("Refresh rate"), StringFromFormat(si->T_cstr("%0.2f Hz"), (float)System_GetPropertyFloat(SYSPROP_DISPLAY_REFRESH_RATE))));629std::string presentModes;630if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::FIFO) presentModes += "FIFO, ";631if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::IMMEDIATE) presentModes += "IMMEDIATE, ";632if (draw->GetDeviceCaps().presentModesSupported & Draw::PresentMode::MAILBOX) presentModes += "MAILBOX, ";633if (!presentModes.empty()) {634presentModes.pop_back();635presentModes.pop_back();636}637displayInfo->Add(new InfoItem(si->T("Present modes"), presentModes));638639CollapsibleSection *versionInfo = deviceSpecs->Add(new CollapsibleSection(si->T("Version Information")));640std::string apiVersion;641if (GetGPUBackend() == GPUBackend::OPENGL) {642if (gl_extensions.IsGLES) {643apiVersion = StringFromFormat("v%d.%d.%d ES", gl_extensions.ver[0], gl_extensions.ver[1], gl_extensions.ver[2]);644} else {645apiVersion = StringFromFormat("v%d.%d.%d", gl_extensions.ver[0], gl_extensions.ver[1], gl_extensions.ver[2]);646}647versionInfo->Add(new InfoItem(si->T("API Version"), apiVersion));648} else {649apiVersion = draw->GetInfoString(InfoField::APIVERSION);650if (apiVersion.size() > 30)651apiVersion.resize(30);652versionInfo->Add(new InfoItem(si->T("API Version"), apiVersion));653654if (GetGPUBackend() == GPUBackend::VULKAN) {655std::string deviceApiVersion = draw->GetInfoString(InfoField::DEVICE_API_VERSION);656versionInfo->Add(new InfoItem(si->T("Device API Version"), deviceApiVersion));657}658}659versionInfo->Add(new InfoItem(si->T("Shading Language"), draw->GetInfoString(InfoField::SHADELANGVERSION)));660661#if PPSSPP_PLATFORM(ANDROID)662std::string moga = System_GetProperty(SYSPROP_MOGA_VERSION);663if (moga.empty()) {664moga = si->T("(none detected)");665}666versionInfo->Add(new InfoItem("Moga", moga));667#endif668669if (gstate_c.GetUseFlags()) {670// We're in-game, and can determine these.671// TODO: Call a static version of GPUCommon::CheckGPUFeatures() and derive them here directly.672673CollapsibleSection *gpuFlags = deviceSpecs->Add(new CollapsibleSection(si->T("GPU Flags")));674675for (int i = 0; i < 32; i++) {676if (gstate_c.Use((1 << i))) {677gpuFlags->Add(new TextView(GpuUseFlagToString(i), new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);678}679}680}681682LinearLayout *storage = AddTab("Storage", si->T("Storage"));683684storage->Add(new ItemHeader(si->T("Directories")));685// Intentionally non-translated686storage->Add(new InfoItem("MemStickDirectory", g_Config.memStickDirectory.ToVisualString()));687storage->Add(new InfoItem("InternalDataDirectory", g_Config.internalDataDirectory.ToVisualString()));688storage->Add(new InfoItem("AppCacheDir", g_Config.appCacheDirectory.ToVisualString()));689storage->Add(new InfoItem("DefaultCurrentDir", g_Config.defaultCurrentDirectory.ToVisualString()));690691#if PPSSPP_PLATFORM(ANDROID)692storage->Add(new InfoItem("ExtFilesDir", g_extFilesDir));693bool scoped = System_GetPropertyBool(SYSPROP_ANDROID_SCOPED_STORAGE);694storage->Add(new InfoItem("Scoped Storage", scoped ? di->T("Yes") : di->T("No")));695if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 30) {696// This flag is only relevant on Android API 30+.697storage->Add(new InfoItem("IsStoragePreservedLegacy", Android_IsExternalStoragePreservedLegacy() ? di->T("Yes") : di->T("No")));698}699#endif700701LinearLayout *buildConfig = AddTab("DevSystemInfoBuildConfig", si->T("Build Config"));702703buildConfig->Add(new ItemHeader(si->T("Build Configuration")));704#ifdef JENKINS705buildConfig->Add(new InfoItem(si->T("Built by"), "Jenkins"));706#endif707#ifdef ANDROID_LEGACY708buildConfig->Add(new InfoItem("ANDROID_LEGACY", ""));709#endif710#ifdef _DEBUG711buildConfig->Add(new InfoItem("_DEBUG", ""));712#else713buildConfig->Add(new InfoItem("NDEBUG", ""));714#endif715#ifdef USE_ASAN716buildConfig->Add(new InfoItem("USE_ASAN", ""));717#endif718#ifdef USING_GLES2719buildConfig->Add(new InfoItem("USING_GLES2", ""));720#endif721#ifdef MOBILE_DEVICE722buildConfig->Add(new InfoItem("MOBILE_DEVICE", ""));723#endif724#if PPSSPP_ARCH(ARMV7S)725buildConfig->Add(new InfoItem("ARMV7S", ""));726#endif727#if PPSSPP_ARCH(ARM_NEON)728buildConfig->Add(new InfoItem("ARM_NEON", ""));729#endif730#ifdef _M_SSE731buildConfig->Add(new InfoItem("_M_SSE", StringFromFormat("0x%x", _M_SSE)));732#endif733if (System_GetPropertyBool(SYSPROP_APP_GOLD)) {734buildConfig->Add(new InfoItem("GOLD", ""));735}736737LinearLayout *cpuExtensions = AddTab("DevSystemInfoCPUExt", si->T("CPU Extensions"));738cpuExtensions->Add(new ItemHeader(si->T("CPU Extensions")));739std::vector<std::string> exts = cpu_info.Features();740for (std::string &ext : exts) {741cpuExtensions->Add(new TextView(ext, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);742}743744LinearLayout *driverBugs = AddTab("DevSystemInfoDriverBugs", si->T("Driver bugs"));745746bool anyDriverBugs = false;747for (int i = 0; i < (int)draw->GetBugs().MaxBugIndex(); i++) {748if (draw->GetBugs().Has(i)) {749anyDriverBugs = true;750driverBugs->Add(new TextView(draw->GetBugs().GetBugName(i), new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);751}752}753754if (!anyDriverBugs) {755driverBugs->Add(new TextView(si->T("No GPU driver bugs detected"), new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);756}757758if (GetGPUBackend() == GPUBackend::OPENGL) {759LinearLayout *gpuExtensions = AddTab("DevSystemInfoOGLExt", si->T("OGL Extensions"));760761if (!gl_extensions.IsGLES) {762gpuExtensions->Add(new ItemHeader(si->T("OpenGL Extensions")));763} else if (gl_extensions.GLES3) {764gpuExtensions->Add(new ItemHeader(si->T("OpenGL ES 3.0 Extensions")));765} else {766gpuExtensions->Add(new ItemHeader(si->T("OpenGL ES 2.0 Extensions")));767}768exts.clear();769SplitString(g_all_gl_extensions, ' ', exts);770std::sort(exts.begin(), exts.end());771for (auto &extension : exts) {772gpuExtensions->Add(new TextView(extension, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);773}774775exts.clear();776SplitString(g_all_egl_extensions, ' ', exts);777std::sort(exts.begin(), exts.end());778779// If there aren't any EGL extensions, no need to show the tab.780if (exts.size() > 0) {781LinearLayout *eglExtensions = AddTab("EglExt", si->T("EGL Extensions"));782eglExtensions->SetSpacing(0);783eglExtensions->Add(new ItemHeader(si->T("EGL Extensions")));784for (auto &extension : exts) {785eglExtensions->Add(new TextView(extension, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);786}787}788} else if (GetGPUBackend() == GPUBackend::VULKAN) {789LinearLayout *gpuExtensions = AddTab("DevSystemInfoOGLExt", si->T("Vulkan Features"));790791CollapsibleSection *vulkanFeatures = gpuExtensions->Add(new CollapsibleSection(si->T("Vulkan Features")));792std::vector<std::string> features = draw->GetFeatureList();793for (auto &feature : features) {794vulkanFeatures->Add(new TextView(feature, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);795}796797CollapsibleSection *presentModes = gpuExtensions->Add(new CollapsibleSection(si->T("Present modes")));798for (auto mode : draw->GetPresentModeList(di->T("Current"))) {799presentModes->Add(new TextView(mode, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);800}801802CollapsibleSection *colorFormats = gpuExtensions->Add(new CollapsibleSection(si->T("Display Color Formats")));803for (auto &format : draw->GetSurfaceFormatList()) {804colorFormats->Add(new TextView(format, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);805}806807CollapsibleSection *enabledExtensions = gpuExtensions->Add(new CollapsibleSection(std::string(si->T("Vulkan Extensions")) + " (" + std::string(di->T("Enabled")) + ")"));808std::vector<std::string> extensions = draw->GetExtensionList(true, true);809std::sort(extensions.begin(), extensions.end());810for (auto &extension : extensions) {811enabledExtensions->Add(new TextView(extension, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);812}813// Also get instance extensions814enabledExtensions->Add(new ItemHeader(si->T("Instance")));815extensions = draw->GetExtensionList(false, true);816std::sort(extensions.begin(), extensions.end());817for (auto &extension : extensions) {818enabledExtensions->Add(new TextView(extension, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);819}820821CollapsibleSection *vulkanExtensions = gpuExtensions->Add(new CollapsibleSection(si->T("Vulkan Extensions")));822extensions = draw->GetExtensionList(true, false);823std::sort(extensions.begin(), extensions.end());824for (auto &extension : extensions) {825vulkanExtensions->Add(new TextView(extension, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);826}827828vulkanExtensions->Add(new ItemHeader(si->T("Instance")));829// Also get instance extensions830extensions = draw->GetExtensionList(false, false);831std::sort(extensions.begin(), extensions.end());832for (auto &extension : extensions) {833vulkanExtensions->Add(new TextView(extension, new LayoutParams(FILL_PARENT, WRAP_CONTENT)))->SetFocusable(true);834}835}836837#ifdef _DEBUG838LinearLayout *internals = AddTab("DevSystemInfoInternals", si->T("Internals"));839CreateInternalsTab(internals);840#endif841}842843void SystemInfoScreen::CreateInternalsTab(UI::ViewGroup *internals) {844using namespace UI;845846auto di = GetI18NCategory(I18NCat::DIALOG);847auto si = GetI18NCategory(I18NCat::SYSINFO);848auto sy = GetI18NCategory(I18NCat::SYSTEM);849auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);850851internals->Add(new ItemHeader(si->T("Icon cache")));852IconCacheStats iconStats = g_iconCache.GetStats();853internals->Add(new InfoItem(si->T("Image data count"), StringFromFormat("%d", iconStats.cachedCount)));854internals->Add(new InfoItem(si->T("Texture count"), StringFromFormat("%d", iconStats.textureCount)));855internals->Add(new InfoItem(si->T("Data size"), NiceSizeFormat(iconStats.dataSize)));856internals->Add(new Choice(di->T("Clear")))->OnClick.Add([&](UI::EventParams &) {857g_iconCache.ClearData();858RecreateViews();859return UI::EVENT_DONE;860});861862internals->Add(new ItemHeader(si->T("Notification tests")));863internals->Add(new Choice(si->T("Error")))->OnClick.Add([&](UI::EventParams &) {864std::string str = "Error " + CodepointToUTF8(0x1F41B) + CodepointToUTF8(0x1F41C) + CodepointToUTF8(0x1F914);865g_OSD.Show(OSDType::MESSAGE_ERROR, str);866return UI::EVENT_DONE;867});868internals->Add(new Choice(si->T("Warning")))->OnClick.Add([&](UI::EventParams &) {869g_OSD.Show(OSDType::MESSAGE_WARNING, "Warning", "Some\nAdditional\nDetail");870return UI::EVENT_DONE;871});872internals->Add(new Choice(si->T("Info")))->OnClick.Add([&](UI::EventParams &) {873g_OSD.Show(OSDType::MESSAGE_INFO, "Info");874return UI::EVENT_DONE;875});876// This one is clickable877internals->Add(new Choice(si->T("Success")))->OnClick.Add([&](UI::EventParams &) {878g_OSD.Show(OSDType::MESSAGE_SUCCESS, "Success", 0.0f, "clickable");879g_OSD.SetClickCallback("clickable", [](bool clicked, void *) {880if (clicked) {881System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://www.google.com/");882}883}, nullptr);884return UI::EVENT_DONE;885});886internals->Add(new Choice(sy->T("RetroAchievements")))->OnClick.Add([&](UI::EventParams &) {887g_OSD.Show(OSDType::MESSAGE_WARNING, "RetroAchievements warning", "", "I_RETROACHIEVEMENTS_LOGO");888return UI::EVENT_DONE;889});890internals->Add(new ItemHeader(si->T("Progress tests")));891internals->Add(new Choice(si->T("30%")))->OnClick.Add([&](UI::EventParams &) {892g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 30, 0.0f);893return UI::EVENT_DONE;894});895internals->Add(new Choice(si->T("100%")))->OnClick.Add([&](UI::EventParams &) {896g_OSD.SetProgressBar("testprogress", "Test Progress", 1, 100, 100, 1.0f);897return UI::EVENT_DONE;898});899internals->Add(new Choice(si->T("N/A%")))->OnClick.Add([&](UI::EventParams &) {900g_OSD.SetProgressBar("testprogress", "Test Progress", 0, 0, 0, 0.0f);901return UI::EVENT_DONE;902});903internals->Add(new Choice(si->T("Success")))->OnClick.Add([&](UI::EventParams &) {904g_OSD.RemoveProgressBar("testprogress", true, 0.5f);905return UI::EVENT_DONE;906});907internals->Add(new Choice(si->T("Failure")))->OnClick.Add([&](UI::EventParams &) {908g_OSD.RemoveProgressBar("testprogress", false, 0.5f);909return UI::EVENT_DONE;910});911internals->Add(new ItemHeader(si->T("Achievement tests")));912internals->Add(new Choice(si->T("Leaderboard tracker: Show")))->OnClick.Add([=](UI::EventParams &) {913g_OSD.ShowLeaderboardTracker(1, "My leaderboard tracker", true);914return UI::EVENT_DONE;915});916internals->Add(new Choice(si->T("Leaderboard tracker: Update")))->OnClick.Add([=](UI::EventParams &) {917g_OSD.ShowLeaderboardTracker(1, "Updated tracker", true);918return UI::EVENT_DONE;919});920internals->Add(new Choice(si->T("Leaderboard tracker: Hide")))->OnClick.Add([=](UI::EventParams &) {921g_OSD.ShowLeaderboardTracker(1, nullptr, false);922return UI::EVENT_DONE;923});924925static const char *positions[] = { "Bottom Left", "Bottom Center", "Bottom Right", "Top Left", "Top Center", "Top Right", "Center Left", "Center Right", "None" };926927internals->Add(new ItemHeader(ac->T("Notifications")));928internals->Add(new PopupMultiChoice(&g_Config.iAchievementsLeaderboardTrackerPos, ac->T("Leaderboard tracker"), positions, 0, ARRAY_SIZE(positions), I18NCat::DIALOG, screenManager()))->SetEnabledPtr(&g_Config.bAchievementsEnable);929930#if PPSSPP_PLATFORM(ANDROID)931internals->Add(new Choice(si->T("Exception")))->OnClick.Add([&](UI::EventParams &) {932System_Notify(SystemNotification::TEST_JAVA_EXCEPTION);933return UI::EVENT_DONE;934});935#endif936}937938int ShaderListScreen::ListShaders(DebugShaderType shaderType, UI::LinearLayout *view) {939using namespace UI;940std::vector<std::string> shaderIds_ = gpu->DebugGetShaderIDs(shaderType);941int count = 0;942for (const auto &id : shaderIds_) {943Choice *choice = view->Add(new Choice(gpu->DebugGetShaderString(id, shaderType, SHADER_STRING_SHORT_DESC)));944choice->SetTag(id);945choice->SetDrawTextFlags(FLAG_DYNAMIC_ASCII);946choice->OnClick.Handle(this, &ShaderListScreen::OnShaderClick);947count++;948}949return count;950}951952struct { DebugShaderType type; const char *name; } shaderTypes[] = {953{ SHADER_TYPE_VERTEX, "Vertex" },954{ SHADER_TYPE_FRAGMENT, "Fragment" },955{ SHADER_TYPE_GEOMETRY, "Geometry" },956{ SHADER_TYPE_VERTEXLOADER, "VertexLoader" },957{ SHADER_TYPE_PIPELINE, "Pipeline" },958{ SHADER_TYPE_TEXTURE, "Texture" },959{ SHADER_TYPE_SAMPLER, "Sampler" },960};961962void ShaderListScreen::CreateViews() {963using namespace UI;964965auto di = GetI18NCategory(I18NCat::DIALOG);966967LinearLayout *layout = new LinearLayout(ORIENT_VERTICAL);968root_ = layout;969970tabs_ = new TabHolder(ORIENT_HORIZONTAL, 40, new LinearLayoutParams(1.0));971tabs_->SetTag("DevShaderList");972layout->Add(tabs_);973layout->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);974for (size_t i = 0; i < ARRAY_SIZE(shaderTypes); i++) {975ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0));976LinearLayout *shaderList = new LinearLayoutList(ORIENT_VERTICAL, new LayoutParams(FILL_PARENT, WRAP_CONTENT));977int count = ListShaders(shaderTypes[i].type, shaderList);978scroll->Add(shaderList);979tabs_->AddTab(StringFromFormat("%s (%d)", shaderTypes[i].name, count), scroll);980}981}982983UI::EventReturn ShaderListScreen::OnShaderClick(UI::EventParams &e) {984using namespace UI;985std::string id = e.v->Tag();986DebugShaderType type = shaderTypes[tabs_->GetCurrentTab()].type;987screenManager()->push(new ShaderViewScreen(id, type));988return EVENT_DONE;989}990991void ShaderViewScreen::CreateViews() {992using namespace UI;993994auto di = GetI18NCategory(I18NCat::DIALOG);995996LinearLayout *layout = new LinearLayout(ORIENT_VERTICAL);997root_ = layout;998999layout->Add(new TextView(gpu->DebugGetShaderString(id_, type_, SHADER_STRING_SHORT_DESC), FLAG_DYNAMIC_ASCII | FLAG_WRAP_TEXT, false));10001001ScrollView *scroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(1.0));1002scroll->SetTag("DevShaderView");1003layout->Add(scroll);10041005LinearLayout *lineLayout = new LinearLayoutList(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, WRAP_CONTENT));1006lineLayout->SetSpacing(0.0);1007scroll->Add(lineLayout);10081009std::vector<std::string> lines;1010SplitString(gpu->DebugGetShaderString(id_, type_, SHADER_STRING_SOURCE_CODE), '\n', lines);10111012for (const auto &line : lines) {1013lineLayout->Add(new TextView(line, FLAG_DYNAMIC_ASCII | FLAG_WRAP_TEXT, true));1014}10151016layout->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);1017}10181019bool ShaderViewScreen::key(const KeyInput &ki) {1020if (ki.flags & KEY_CHAR) {1021if (ki.unicodeChar == 'C' || ki.unicodeChar == 'c') {1022System_CopyStringToClipboard(gpu->DebugGetShaderString(id_, type_, SHADER_STRING_SHORT_DESC));1023}1024}1025return UIDialogScreenWithBackground::key(ki);1026}102710281029const std::string framedumpsBaseUrl = "http://framedump.ppsspp.org/repro/";10301031FrameDumpTestScreen::FrameDumpTestScreen() {10321033}10341035FrameDumpTestScreen::~FrameDumpTestScreen() {1036g_DownloadManager.CancelAll();1037}10381039void FrameDumpTestScreen::CreateViews() {1040using namespace UI;10411042root_ = new AnchorLayout(new LayoutParams(FILL_PARENT, FILL_PARENT));1043auto di = GetI18NCategory(I18NCat::DIALOG);10441045TabHolder *tabHolder;1046tabHolder = new TabHolder(ORIENT_VERTICAL, 200, new AnchorLayoutParams(10, 0, 10, 0, false));1047root_->Add(tabHolder);1048AddStandardBack(root_);1049tabHolder->SetTag("DumpTypes");1050root_->SetDefaultFocusView(tabHolder);10511052ViewGroup *dumpsScroll = new ScrollView(ORIENT_VERTICAL, new LinearLayoutParams(FILL_PARENT, FILL_PARENT));1053dumpsScroll->SetTag("GameSettingsGraphics");1054LinearLayout *dumps = new LinearLayoutList(ORIENT_VERTICAL);1055dumps->SetSpacing(0);1056dumpsScroll->Add(dumps);1057tabHolder->AddTab("Dumps", dumpsScroll);10581059dumps->Add(new ItemHeader("GE Frame Dumps"));10601061for (auto &file : files_) {1062std::string url = framedumpsBaseUrl + file;1063Choice *c = dumps->Add(new Choice(file));1064c->SetTag(url);1065c->OnClick.Handle<FrameDumpTestScreen>(this, &FrameDumpTestScreen::OnLoadDump);1066}1067}10681069UI::EventReturn FrameDumpTestScreen::OnLoadDump(UI::EventParams ¶ms) {1070std::string url = params.v->Tag();1071INFO_LOG(Log::Common, "Trying to launch '%s'", url.c_str());1072// Our disc streaming functionality detects the URL and takes over and handles loading framedumps well,1073// except for some reason the game ID.1074// TODO: Fix that since it can be important for compat settings.1075LaunchFile(screenManager(), Path(url));1076return UI::EVENT_DONE;1077}10781079void FrameDumpTestScreen::update() {1080UIScreen::update();10811082if (!listing_) {1083const char *acceptMime = "text/html, */*; q=0.8";1084listing_ = g_DownloadManager.StartDownload(framedumpsBaseUrl, Path(), http::ProgressBarMode::DELAYED, acceptMime);1085}10861087if (listing_ && listing_->Done() && files_.empty()) {1088if (listing_->ResultCode() == 200) {1089std::string listingHtml;1090listing_->buffer().TakeAll(&listingHtml);10911092std::vector<std::string> lines;1093// We rely slightly on nginx listing format here. Not great.1094SplitString(listingHtml, '\n', lines);1095for (auto &line : lines) {1096std::string trimmed = StripSpaces(line);1097if (startsWith(trimmed, "<a href=\"")) {1098trimmed = trimmed.substr(strlen("<a href=\""));1099size_t offset = trimmed.find('\"');1100if (offset != std::string::npos) {1101trimmed = trimmed.substr(0, offset);1102if (endsWith(trimmed, ".ppdmp")) {1103INFO_LOG(Log::Common, "Found ppdmp: '%s'", trimmed.c_str());1104files_.push_back(trimmed);1105}1106}1107}1108}1109} else {1110// something went bad. Too lazy to make UI, so let's just finish this screen.1111TriggerFinish(DialogResult::DR_CANCEL);1112}1113RecreateViews();1114}1115}11161117void TouchTestScreen::touch(const TouchInput &touch) {1118UIDialogScreenWithGameBackground::touch(touch);1119if (touch.flags & TOUCH_DOWN) {1120bool found = false;1121for (int i = 0; i < MAX_TOUCH_POINTS; i++) {1122if (touches_[i].id == touch.id) {1123WARN_LOG(Log::System, "Double touch");1124touches_[i].x = touch.x;1125touches_[i].y = touch.y;1126found = true;1127}1128}1129if (!found) {1130for (int i = 0; i < MAX_TOUCH_POINTS; i++) {1131if (touches_[i].id == -1) {1132touches_[i].id = touch.id;1133touches_[i].x = touch.x;1134touches_[i].y = touch.y;1135break;1136}1137}1138}1139}1140if (touch.flags & TOUCH_MOVE) {1141bool found = false;1142for (int i = 0; i < MAX_TOUCH_POINTS; i++) {1143if (touches_[i].id == touch.id) {1144touches_[i].x = touch.x;1145touches_[i].y = touch.y;1146found = true;1147}1148}1149if (!found) {1150WARN_LOG(Log::System, "Move without touch down: %d", touch.id);1151}1152}1153if (touch.flags & TOUCH_UP) {1154bool found = false;1155for (int i = 0; i < MAX_TOUCH_POINTS; i++) {1156if (touches_[i].id == touch.id) {1157found = true;1158touches_[i].id = -1;1159break;1160}1161}1162if (!found) {1163WARN_LOG(Log::System, "Touch release without touch down");1164}1165}1166}11671168// TODO: Move this screen out into its own file.1169void TouchTestScreen::CreateViews() {1170using namespace UI;11711172auto di = GetI18NCategory(I18NCat::DIALOG);1173auto gr = GetI18NCategory(I18NCat::GRAPHICS);1174root_ = new LinearLayout(ORIENT_VERTICAL);1175LinearLayout *theTwo = new LinearLayout(ORIENT_VERTICAL, new LinearLayoutParams(1.0f));11761177// TODO: This one should use DYNAMIC_ASCII. Though doesn't matter much.1178lastKeyEvents_ = theTwo->Add(new TextView("-", new LayoutParams(FILL_PARENT, WRAP_CONTENT)));11791180root_->Add(theTwo);11811182#if !PPSSPP_PLATFORM(UWP)1183static const char *renderingBackend[] = { "OpenGL", "Direct3D 9", "Direct3D 11", "Vulkan" };1184PopupMultiChoice *renderingBackendChoice = root_->Add(new PopupMultiChoice(&g_Config.iGPUBackend, gr->T("Backend"), renderingBackend, (int)GPUBackend::OPENGL, ARRAY_SIZE(renderingBackend), I18NCat::GRAPHICS, screenManager()));1185renderingBackendChoice->OnChoice.Handle(this, &TouchTestScreen::OnRenderingBackend);11861187if (!g_Config.IsBackendEnabled(GPUBackend::OPENGL))1188renderingBackendChoice->HideChoice((int)GPUBackend::OPENGL);1189if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D9))1190renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D9);1191if (!g_Config.IsBackendEnabled(GPUBackend::DIRECT3D11))1192renderingBackendChoice->HideChoice((int)GPUBackend::DIRECT3D11);1193if (!g_Config.IsBackendEnabled(GPUBackend::VULKAN))1194renderingBackendChoice->HideChoice((int)GPUBackend::VULKAN);1195#endif11961197#if PPSSPP_PLATFORM(ANDROID)1198root_->Add(new Choice(gr->T("Recreate Activity")))->OnClick.Handle(this, &TouchTestScreen::OnRecreateActivity);1199#endif1200root_->Add(new CheckBox(&g_Config.bImmersiveMode, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &TouchTestScreen::OnImmersiveModeChange);1201root_->Add(new Button(di->T("Back")))->OnClick.Handle<UIScreen>(this, &UIScreen::OnBack);1202}12031204void TouchTestScreen::UpdateLogView() {1205while (keyEventLog_.size() > 8) {1206keyEventLog_.erase(keyEventLog_.begin());1207}12081209std::string text;1210for (auto &iter : keyEventLog_) {1211text += iter + "\n";1212}12131214if (lastKeyEvents_) {1215lastKeyEvents_->SetText(text);1216}1217}12181219bool TouchTestScreen::key(const KeyInput &key) {1220UIScreen::key(key);1221char buf[512];1222snprintf(buf, sizeof(buf), "%s (%d) Device ID: %d [%s%s%s%s]", KeyMap::GetKeyName(key.keyCode).c_str(), key.keyCode, key.deviceId,1223(key.flags & KEY_IS_REPEAT) ? "REP" : "",1224(key.flags & KEY_UP) ? "UP" : "",1225(key.flags & KEY_DOWN) ? "DOWN" : "",1226(key.flags & KEY_CHAR) ? "CHAR" : "");1227keyEventLog_.push_back(buf);1228UpdateLogView();1229return true;1230}12311232void TouchTestScreen::axis(const AxisInput &axis) {1233char buf[512];1234snprintf(buf, sizeof(buf), "Axis: %s (%d) (value %1.3f) Device ID: %d",1235KeyMap::GetAxisName(axis.axisId).c_str(), axis.axisId, axis.value, axis.deviceId);12361237keyEventLog_.push_back(buf);1238if (keyEventLog_.size() > 8) {1239keyEventLog_.erase(keyEventLog_.begin());1240}1241UpdateLogView();1242}12431244void TouchTestScreen::DrawForeground(UIContext &dc) {1245Bounds bounds = dc.GetLayoutBounds();12461247double now = dc.FrameStartTime();1248double delta = now - lastFrameTime_;1249lastFrameTime_ = now;12501251dc.BeginNoTex();1252for (int i = 0; i < MAX_TOUCH_POINTS; i++) {1253if (touches_[i].id != -1) {1254dc.Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0);1255}1256}1257dc.Flush();12581259dc.Begin();12601261char buffer[4096];1262for (int i = 0; i < MAX_TOUCH_POINTS; i++) {1263if (touches_[i].id != -1) {1264dc.Draw()->Circle(touches_[i].x, touches_[i].y, 100.0, 3.0, 80, 0.0f, 0xFFFFFFFF, 1.0);1265snprintf(buffer, sizeof(buffer), "%0.1fx%0.1f", touches_[i].x, touches_[i].y);1266dc.DrawText(buffer, touches_[i].x, touches_[i].y + (touches_[i].y > g_display.dp_yres - 100.0f ? -135.0f : 95.0f), 0xFFFFFFFF, ALIGN_HCENTER | FLAG_DYNAMIC_ASCII);1267}1268}12691270char extra_debug[2048]{};12711272#if PPSSPP_PLATFORM(ANDROID)1273truncate_cpy(extra_debug, Android_GetInputDeviceDebugString().c_str());1274#endif12751276snprintf(buffer, sizeof(buffer),1277#if PPSSPP_PLATFORM(ANDROID)1278"display_res: %dx%d\n"1279#endif1280"dp_res: %dx%d pixel_res: %dx%d\n"1281"g_dpi: %0.3f g_dpi_scale: %0.3fx%0.3f\n"1282"g_dpi_scale_real: %0.3fx%0.3f\n"1283"delta: %0.2f ms fps: %0.3f\n%s",1284#if PPSSPP_PLATFORM(ANDROID)1285(int)System_GetPropertyInt(SYSPROP_DISPLAY_XRES), (int)System_GetPropertyInt(SYSPROP_DISPLAY_YRES),1286#endif1287g_display.dp_xres, g_display.dp_yres, g_display.pixel_xres, g_display.pixel_yres,1288g_display.dpi, g_display.dpi_scale_x, g_display.dpi_scale_y,1289g_display.dpi_scale_real_x, g_display.dpi_scale_real_y,1290delta * 1000.0, 1.0 / delta,1291extra_debug);12921293// On Android, also add joystick debug data.1294dc.DrawTextShadow(buffer, bounds.centerX(), bounds.y + 20.0f, 0xFFFFFFFF, FLAG_DYNAMIC_ASCII);1295dc.Flush();1296}12971298void RecreateActivity() {1299const int SYSTEM_JELLYBEAN = 16;1300if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= SYSTEM_JELLYBEAN) {1301INFO_LOG(Log::System, "Sending recreate");1302System_Notify(SystemNotification::FORCE_RECREATE_ACTIVITY);1303INFO_LOG(Log::System, "Got back from recreate");1304} else {1305auto gr = GetI18NCategory(I18NCat::GRAPHICS);1306System_Toast(gr->T_cstr("Must Restart", "You must restart PPSSPP for this change to take effect"));1307}1308}13091310UI::EventReturn TouchTestScreen::OnImmersiveModeChange(UI::EventParams &e) {1311System_Notify(SystemNotification::IMMERSIVE_MODE_CHANGE);1312if (g_Config.iAndroidHwScale != 0) {1313RecreateActivity();1314}1315return UI::EVENT_DONE;1316}13171318UI::EventReturn TouchTestScreen::OnRenderingBackend(UI::EventParams &e) {1319g_Config.Save("GameSettingsScreen::RenderingBackend");1320System_RestartApp("--touchscreentest");1321return UI::EVENT_DONE;1322}13231324UI::EventReturn TouchTestScreen::OnRecreateActivity(UI::EventParams &e) {1325RecreateActivity();1326return UI::EVENT_DONE;1327}132813291330